@hyperscale0/hsx 1.0.0 → 2.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 +28 -4
- package/README.md +58 -38
- package/dist/src/ast.d.ts +4 -2
- package/dist/src/ast.d.ts.map +1 -1
- package/dist/src/cli.d.ts +1 -1
- package/dist/src/cli.js.map +1 -1
- package/dist/src/compile.d.ts +8 -2
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +8 -1
- package/dist/src/compile.js.map +1 -1
- package/dist/src/cost.d.ts +3 -1
- package/dist/src/cost.d.ts.map +1 -1
- package/dist/src/cost.js +124 -94
- package/dist/src/cost.js.map +1 -1
- package/dist/src/diagnostics.d.ts.map +1 -1
- package/dist/src/diagnostics.js +96 -5
- package/dist/src/diagnostics.js.map +1 -1
- package/dist/src/emit.d.ts +3 -1
- package/dist/src/emit.d.ts.map +1 -1
- package/dist/src/emit.js +27 -10
- package/dist/src/emit.js.map +1 -1
- package/dist/src/format.js.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/ir.d.ts.map +1 -1
- package/dist/src/lex.d.ts.map +1 -1
- package/dist/src/lsp/server.js.map +1 -1
- package/dist/src/modules.js +7 -3
- package/dist/src/modules.js.map +1 -1
- package/dist/src/parse.js +3 -3
- package/dist/src/parse.js.map +1 -1
- package/dist/src/std-bundle.js +41 -41
- package/dist/src/std-bundle.js.map +1 -1
- package/dist/src/std-library.js +2 -2
- package/dist/src/typecheck.d.ts.map +1 -1
- package/dist/src/typecheck.js +948 -157
- package/dist/src/typecheck.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/docs/README.md +10 -1
- package/docs/assets/hsx.svg +18 -0
- package/docs/guide/01-first-program.md +1 -1
- package/docs/guide/02-money.md +1 -1
- package/docs/guide/03-instruments.md +44 -3
- package/docs/guide/04-lifecycles.md +18 -3
- package/docs/guide/05-fees-and-splits.md +2 -2
- package/docs/guide/06-schedules.md +1 -1
- package/docs/guide/07-composition.md +8 -2
- package/docs/guide/08-writing-a-module.md +13 -2
- package/docs/guide/09-cost.md +3 -3
- package/docs/guide/10-diagnostics.md +1 -1
- package/docs/llms-full.txt +473 -190
- package/docs/llms.txt +2 -2
- package/docs/playground.md +4 -3
- package/docs/reference/cli.md +2 -2
- package/docs/reference/diagnostics.md +243 -37
- package/docs/reference/grammar.md +2 -2
- package/docs/reference/std/advance.md +2 -2
- package/docs/reference/std/cancellable_booking.md +10 -10
- package/docs/reference/std/captured_payment.md +14 -14
- package/docs/reference/std/conditional_disbursement.md +3 -3
- package/docs/reference/std/credit_facility.md +2 -2
- package/docs/reference/std/held_payment.md +37 -33
- package/docs/reference/std/instant_transfer.md +8 -8
- package/docs/reference/std/metered.md +2 -2
- package/docs/reference/std/pooled_split.md +2 -2
- package/docs/reference/std/premium_forward.md +13 -13
- package/docs/reference/std/reconciled_payout.md +2 -2
- package/docs/reference/std/recurring_collection.md +2 -2
- package/docs/reference/std/rotating_pool.md +2 -2
- package/docs/reference/std/scheduled.md +18 -18
- package/docs/reference/std/security_deposit.md +14 -14
- package/docs/reference/std/settlement_batch.md +2 -2
- package/docs/reference/std/swap.md +18 -18
- package/docs/reference/std/threshold_pool.md +2 -2
- package/docs/reference/std/weighted_distribution.md +2 -2
- package/docs/reference/types.md +1 -1
- package/docs/reference/udl-output.md +1 -1
- package/examples/01-first-program/README.md +1 -1
- package/examples/01-first-program/tip-jar.hsx +1 -1
- package/examples/02-imports-and-archetypes/photo-booth.hsx +1 -1
- package/examples/03-diagnostics/corner-shop-fixed.hsx +1 -1
- package/examples/03-diagnostics/corner-shop.hsx +1 -1
- package/examples/04-complete-product/study-hall.hsx +1 -1
- package/examples/05-watch-club/README.md +0 -3
- package/examples/05-watch-club/watch-club.hsx +1 -1
- package/examples/README.md +35 -4
- package/examples/advance/advance.hsx +1 -1
- package/examples/advance/advance.udl +16 -11
- package/examples/cancellable_booking/cancellable_booking.hsx +1 -1
- package/examples/cancellable_booking/cancellable_booking.udl +13 -11
- package/examples/captured_payment/captured_payment.hsx +1 -1
- package/examples/captured_payment/captured_payment.udl +10 -7
- package/examples/conditional_disbursement/conditional_disbursement.hsx +1 -1
- package/examples/conditional_disbursement/conditional_disbursement.udl +7 -5
- package/examples/cost-table.json +1542 -382
- package/examples/credit_facility/credit_facility.hsx +1 -1
- package/examples/credit_facility/credit_facility.udl +26 -19
- package/examples/held_payment/held_payment.hsx +1 -1
- package/examples/held_payment/held_payment.udl +61 -15
- package/examples/instant_transfer/instant_transfer.hsx +1 -1
- package/examples/instant_transfer/instant_transfer.udl +17 -11
- package/examples/metered/metered.hsx +1 -1
- package/examples/metered/metered.udl +8 -6
- package/examples/pooled_split/pooled_split.hsx +1 -1
- package/examples/pooled_split/pooled_split.udl +16 -13
- package/examples/premium_forward/premium_forward.hsx +1 -1
- package/examples/premium_forward/premium_forward.udl +22 -16
- package/examples/reconciled_payout/reconciled_payout.hsx +1 -1
- package/examples/reconciled_payout/reconciled_payout.udl +4 -2
- package/examples/recurring_collection/recurring_collection.hsx +1 -1
- package/examples/recurring_collection/recurring_collection.udl +20 -15
- package/examples/rotating_pool/rotating_pool.hsx +1 -1
- package/examples/rotating_pool/rotating_pool.udl +92 -82
- package/examples/scheduled/scheduled.hsx +1 -1
- package/examples/scheduled/scheduled.udl +15 -10
- package/examples/security_deposit/security_deposit.hsx +1 -1
- package/examples/security_deposit/security_deposit.udl +19 -5
- package/examples/settlement_batch/settlement_batch.hsx +1 -1
- package/examples/swap/swap.hsx +1 -1
- package/examples/swap/swap.udl +34 -28
- package/examples/threshold_pool/threshold_pool.hsx +1 -1
- package/examples/threshold_pool/threshold_pool.udl +14 -12
- package/examples/weighted_distribution/weighted_distribution.hsx +1 -1
- package/examples/weighted_distribution/weighted_distribution.udl +6 -4
- package/package.json +6 -5
- package/skills/hsx/SKILL.md +26 -26
- package/src/ast.ts +4 -2
- package/src/cli.ts +4 -4
- package/src/compile.ts +14 -3
- package/src/cost.ts +176 -104
- package/src/diagnostics.ts +99 -4
- package/src/emit.ts +32 -18
- package/src/index.ts +2 -1
- package/src/modules.ts +9 -3
- package/src/parse.ts +3 -3
- package/src/std-bundle.ts +41 -41
- package/src/std-library.ts +2 -2
- package/src/typecheck.ts +1251 -235
- package/src/version.ts +1 -1
- package/std/SEMANTICS.md +12 -3
- package/std/{settlements → money_flows}/advance.hsx +1 -1
- package/std/{settlements → money_flows}/cancellable_booking.hsx +4 -3
- package/std/{settlements → money_flows}/captured_payment.hsx +35 -6
- package/std/{settlements → money_flows}/conditional_disbursement.hsx +17 -16
- package/std/{settlements → money_flows}/credit_facility.hsx +1 -2
- package/std/{settlements → money_flows}/held_payment.hsx +61 -25
- package/std/money_flows/index.hsx +3 -0
- package/std/{settlements → money_flows}/instant_transfer.hsx +2 -3
- package/std/{settlements → money_flows}/metered.hsx +1 -1
- package/std/{settlements → money_flows}/pooled_split.hsx +3 -3
- package/std/{settlements → money_flows}/premium_forward.hsx +22 -23
- package/std/{settlements → money_flows}/reconciled_payout.hsx +3 -3
- package/std/{settlements → money_flows}/recurring_collection.hsx +1 -1
- package/std/{settlements → money_flows}/rotating_pool.hsx +8 -6
- package/std/{settlements → money_flows}/scheduled.hsx +22 -27
- package/std/{settlements → money_flows}/security_deposit.hsx +38 -14
- package/std/{settlements → money_flows}/settlement_batch.hsx +3 -3
- package/std/{settlements → money_flows}/swap.hsx +41 -16
- package/std/{settlements → money_flows}/threshold_pool.hsx +5 -5
- package/std/{settlements → money_flows}/weighted_distribution.hsx +6 -6
- package/docs/sessions/2026-09-02-two-instruments.hsx +0 -21
- package/docs/sessions/2026-09-02-two-instruments.md +0 -41
- package/std/settlements/index.hsx +0 -3
package/src/version.ts
CHANGED
package/std/SEMANTICS.md
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
# Settlement semantics frozen before legacy teardown
|
|
2
2
|
|
|
3
3
|
This note records the behavior that the former archetype branches emitted on
|
|
4
|
-
2026-09-01. The
|
|
5
|
-
|
|
6
|
-
the human porting index.
|
|
4
|
+
2026-09-01. The in-place compiler family specs under `test/` are the semantic
|
|
5
|
+
authority. This note is the human porting index.
|
|
7
6
|
|
|
8
7
|
## Direct and held payments
|
|
9
8
|
|
|
@@ -34,6 +33,12 @@ the human porting index.
|
|
|
34
33
|
amount. Void and expiry release the reserve. Correction and external reversal
|
|
35
34
|
use their declared ports and windows. Derived fees use the same floor,
|
|
36
35
|
partition, bearer, and position rules as direct payment.
|
|
36
|
+
- `cancellable_booking` holds the booking amount in escrow away from guest and
|
|
37
|
+
host until the booking ends or is cancelled. Cancellation quotes a penalty
|
|
38
|
+
against the time remaining before the start date, frozen with offer life.
|
|
39
|
+
Lifecycle moves through `created`, `held`, `cancellation_quoted`, `canceled`,
|
|
40
|
+
`settled`, and `completed`. Penalty retention moves penalty funds to host and
|
|
41
|
+
remainder to guest; completion releases full amount to host.
|
|
37
42
|
- `premium_forward` holds premium pieces, then forwards the net pieces after a
|
|
38
43
|
bind decision. It can abandon unbound custody. Policy reference, renewal due,
|
|
39
44
|
endorsement evidence, and lapse actions appear only when declared. Its fee
|
|
@@ -128,3 +133,7 @@ the human porting index.
|
|
|
128
133
|
beneficiary for that cycle. The final close requires the escrow account to be
|
|
129
134
|
drained. Its referenced-membership form delegates those member cycles to a
|
|
130
135
|
published membership instrument and emits only the parent.
|
|
136
|
+
- `reconciled_payout` instructs an external payout with a tolerance dial,
|
|
137
|
+
waits for a matching bank debit statement line, and treats unmatched amounts
|
|
138
|
+
at the settle date as break rows. Lifecycle moves through `created`,
|
|
139
|
+
`instructed`, and `settled`.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
module std.
|
|
1
|
+
module std.money_flows.cancellable_booking
|
|
2
2
|
|
|
3
3
|
export instrument cancellable_booking<C>(
|
|
4
4
|
guest: party,
|
|
@@ -19,8 +19,8 @@ export instrument cancellable_booking<C>(
|
|
|
19
19
|
amount { type: money<C>; description: concat("Booking price in minor units, held away from both the ", words(guest), " and the ", words(host), " until the booking ends"); }
|
|
20
20
|
currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
|
|
21
21
|
starts_at { type: date; description: "When the booking starts; the cancellation penalty is priced against the time left before it"; }
|
|
22
|
-
[guest_account_field] { type: account<C>; description: concat("The ", words(guest), " account the booking is taken from and the refund returns to");
|
|
23
|
-
[host_account_field] { type: account<C>; description: concat("The ", words(host), " account the booking and any penalty end up in");
|
|
22
|
+
[guest_account_field] { type: account<C>; description: concat("The ", words(guest), " account the booking is taken from and the refund returns to"); [reference_filter_key]: { column: role; values: [customer_balance]; }; }
|
|
23
|
+
[host_account_field] { type: account<C>; description: concat("The ", words(host), " account the booking and any penalty end up in"); [reference_filter_key]: { column: role; values: [customer_balance]; }; }
|
|
24
24
|
}
|
|
25
25
|
lifecycle {
|
|
26
26
|
states created held cancellation_quoted canceled settled completed;
|
|
@@ -48,6 +48,7 @@ export instrument cancellable_booking<C>(
|
|
|
48
48
|
agent_description: concat("Move the whole booking price out of the ", words(guest), " account into the holding account. This moves money. Only a created booking takes. Neither party can spend the money until the booking completes or a cancellation is confirmed.");
|
|
49
49
|
summary: concat("Take the booking price from the ", words(guest), " into the holding account");
|
|
50
50
|
moves: [{ key: booking; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", amount); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.bookingHoldingAccountId; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", guest_account_field); }; }; capture: { bookingTransferId: transferId; }; }];
|
|
51
|
+
sandbox_failure_point: funding;
|
|
51
52
|
steps: [];
|
|
52
53
|
}
|
|
53
54
|
action complete {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
module std.
|
|
1
|
+
module std.money_flows.captured_payment
|
|
2
2
|
|
|
3
3
|
export instrument captured_payment<C>(payer: party, payee: party, amount: money<C>, reserve_until: date, correction: condition, external_reversal: condition, capture_mode: text, correction_mode: text, negative_position: text, timeout: text, derived_amount: optional<block>) {
|
|
4
4
|
let(correction_name): correction;
|
|
@@ -22,7 +22,6 @@ export instrument captured_payment<C>(payer: party, payee: party, amount: money<
|
|
|
22
22
|
platformAccountId {
|
|
23
23
|
type: account<C>;
|
|
24
24
|
description: "The product fee collection account";
|
|
25
|
-
pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$";
|
|
26
25
|
[fee_collection_key]: true;
|
|
27
26
|
[reference_filter_key]: { column: role; values: [customer_balance, product_revenue]; };
|
|
28
27
|
}
|
|
@@ -69,6 +68,7 @@ export instrument captured_payment<C>(payer: party, payee: party, amount: money<
|
|
|
69
68
|
agent_description: "Reserve the {amount} against the {payer} in the {payee}'s favor until {reserve_until}. The payment must be created. The money is held, not paid: capture and settle post it, void and expiry release it.";
|
|
70
69
|
summary: "Reserve the {amount} until {reserve_until}";
|
|
71
70
|
moves: [{ key: reservation; amount: amount; from: payer; to: beneficiary; operation: reserve; }];
|
|
71
|
+
sandbox_failure_point: funding;
|
|
72
72
|
steps: [];
|
|
73
73
|
}
|
|
74
74
|
action capture {
|
|
@@ -77,6 +77,7 @@ export instrument captured_payment<C>(payer: party, payee: party, amount: money<
|
|
|
77
77
|
deadline { field: reserve_until; };
|
|
78
78
|
input { type: object; additional_properties: false; properties: { captureAmount: { description: "Positive partial capture amount in minor units"; pattern: "^[1-9][0-9]{0,17}$"; type: string; }; }; required: [captureAmount]; }
|
|
79
79
|
moves: [{ key: post; operation: internal_transfer.post; bind: { amount: { from: input; path: captureAmount; }; currency: { from: instance; path: fields.currency; }; postMode: { from: const; value: partial_only; }; transferId: { from: instance; path: refs.authorizeReservationTransferId; }; }; capture: { capturedAmount: postedAmount; }; }];
|
|
80
|
+
sandbox_failure_point: "release";
|
|
80
81
|
steps: [];
|
|
81
82
|
}
|
|
82
83
|
action capture_more {
|
|
@@ -122,7 +123,22 @@ export instrument captured_payment<C>(payer: party, payee: party, amount: money<
|
|
|
122
123
|
let(correction_capture): camel(concat(correction_name, "_transfer_transfer_id"));
|
|
123
124
|
agent_description: "Return the whole captured amount to the {payer} after the payment settled. Only a settled payment corrects, and only the party the product authorized may call it. The money moves back and there is no undo.";
|
|
124
125
|
summary: "Return the full captured amount on payee correction";
|
|
125
|
-
|
|
126
|
+
when(correction_fields) {
|
|
127
|
+
input {
|
|
128
|
+
type: object;
|
|
129
|
+
additional_properties: false;
|
|
130
|
+
properties {
|
|
131
|
+
for input_name in keys(correction_fields) {
|
|
132
|
+
[input_name]: get(correction_fields, input_name);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
required: keys(correction_fields);
|
|
136
|
+
}
|
|
137
|
+
capture_input: {
|
|
138
|
+
for item in keys(correction_fields) { [item]: item; }
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
port { allowed_parties: correction_allowed; };
|
|
126
142
|
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: refs.capturedAmount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payer, "AccountId"); }; [metadata_clawback]: { from: instance; path: refs.authorizeReservationTransferId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: correction_name; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payee, "AccountId"); }; }; capture: { [correction_capture]: transferId; }; }];
|
|
127
143
|
steps: [];
|
|
128
144
|
}
|
|
@@ -134,10 +150,23 @@ export instrument captured_payment<C>(payer: party, payee: party, amount: money<
|
|
|
134
150
|
let(reversal_capture): camel(concat(reversal_name, "_transfer_transfer_id"));
|
|
135
151
|
agent_description: "Return the whole captured amount to the {payer} on an external reversal. The payment must be settled and the call must land before reversalUntil. The caller passes externalReference, which stays in the receipt. There is no undo.";
|
|
136
152
|
summary: "Return the full captured amount on an external reversal";
|
|
137
|
-
capture(input) { externalReference: externalReference; };
|
|
138
153
|
deadline { field: reversalUntil; };
|
|
139
|
-
|
|
140
|
-
|
|
154
|
+
when(external_reversal_fields) {
|
|
155
|
+
capture_input: {
|
|
156
|
+
for item in keys(external_reversal_fields) { [item]: item; }
|
|
157
|
+
};
|
|
158
|
+
input {
|
|
159
|
+
type: object;
|
|
160
|
+
additional_properties: false;
|
|
161
|
+
properties {
|
|
162
|
+
for input_name in keys(external_reversal_fields) {
|
|
163
|
+
[input_name]: get(external_reversal_fields, input_name);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
required: keys(external_reversal_fields);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
port { allowed_parties: external_reversal_allowed; };
|
|
141
170
|
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: refs.capturedAmount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payer, "AccountId"); }; [metadata_clawback]: { from: instance; path: refs.authorizeReservationTransferId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: reversal_name; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payee, "AccountId"); }; }; capture: { [reversal_capture]: transferId; }; }];
|
|
142
171
|
steps: [];
|
|
143
172
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
module std.
|
|
1
|
+
module std.money_flows.conditional_disbursement
|
|
2
2
|
|
|
3
3
|
export instrument conditional_disbursement<C>(
|
|
4
4
|
source: party,
|
|
@@ -48,21 +48,22 @@ export instrument conditional_disbursement<C>(
|
|
|
48
48
|
action deny {
|
|
49
49
|
agent_description: "Refuse the whole disbursement without moving money. Every child amount under it must still be unapproved, so call this before any approval lands. The caller passes evidenceReference, which stays in the receipt. Denial is final.";
|
|
50
50
|
summary: "Record a denial without moving money";
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
51
|
+
when(decision_fields) {
|
|
52
|
+
input {
|
|
53
|
+
type: object;
|
|
54
|
+
additional_properties: false;
|
|
55
|
+
properties {
|
|
56
|
+
for input_name in keys(decision_fields) {
|
|
57
|
+
[input_name]: get(decision_fields, input_name);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
required: keys(decision_fields);
|
|
61
|
+
}
|
|
62
|
+
capture_input: {
|
|
63
|
+
for item in keys(decision_fields) { [item]: item; }
|
|
60
64
|
};
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
};
|
|
64
|
-
capture_input: { decision_evidence_reference: evidenceReference; };
|
|
65
|
-
port: { allowed_parties: [payer]; };
|
|
65
|
+
}
|
|
66
|
+
port { allowed_parties: decision_allowed; };
|
|
66
67
|
requires aggregate: {
|
|
67
68
|
check: { kind: all_in; };
|
|
68
69
|
instrument_id: child_id;
|
|
@@ -144,7 +145,7 @@ export instrument conditional_disbursement<C>(
|
|
|
144
145
|
type: "object";
|
|
145
146
|
};
|
|
146
147
|
capture_input: { decision_evidence_reference: evidenceReference; };
|
|
147
|
-
port: { allowed_parties:
|
|
148
|
+
port: { allowed_parties: decision_allowed; };
|
|
148
149
|
requires exposure: {
|
|
149
150
|
amount_field: child_amount;
|
|
150
151
|
anchor_field: parent_field;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
module std.
|
|
1
|
+
module std.money_flows.credit_facility
|
|
2
2
|
|
|
3
3
|
export instrument credit_facility<C>(
|
|
4
4
|
lender: party,
|
|
@@ -32,7 +32,6 @@ export instrument credit_facility<C>(
|
|
|
32
32
|
[borrower_field] {
|
|
33
33
|
type: account<C>;
|
|
34
34
|
description: "The {borrower} account";
|
|
35
|
-
pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$";
|
|
36
35
|
[reference_filter]: {
|
|
37
36
|
column: role;
|
|
38
37
|
values: [customer_balance];
|
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
module std.
|
|
1
|
+
module std.money_flows.held_payment
|
|
2
2
|
|
|
3
3
|
export instrument held_payment<C>(payer: party, payee: party, amount: money<C>, release: condition, fees: optional<block>, on_cancel: optional<block>, derived_amount: optional<block>, release_to: optional<party>, whole_amount: optional<block>, release_action: optional<text>, whole_fee: optional<money<C>>, reference: optional<text>, upstream: optional<ref>, id_prefix_override: optional<text>) {
|
|
4
4
|
let(release_name): release;
|
|
5
|
-
let(
|
|
6
|
-
let(release_actor_2): at(release_allowed, 2);
|
|
7
|
-
let(release_multiple): lt(1, len(release_allowed));
|
|
8
|
-
let(release_role): if_eq(release_actor, payer, "payer", "beneficiary");
|
|
9
|
-
let(release_role_2): if_eq(release_actor_2, payer, "payer", "beneficiary");
|
|
10
|
-
let(release_actor_text): if_eq(len(release_allowed), 2, concat(words(release_actor), " or ", words(release_actor_2)), words(release_actor));
|
|
5
|
+
let(release_actor_text): if_eq(len(release_allowed), 1, words(at(release_allowed, 1)), concat("a party allowed by ", release));
|
|
11
6
|
let(payer_account_field): camel(concat(payer, "_account_id"));
|
|
12
7
|
let(payee_account_field): camel(concat(payee, "_account_id"));
|
|
13
8
|
let(release_to_account_field): camel(concat(release_to, "_account_id"));
|
|
@@ -54,12 +49,11 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
|
|
|
54
49
|
let(fee_collection_key): "x-hyperscale-fee-collection-port";
|
|
55
50
|
let(reference_filter_key): "x-hyperscale-reference-filter";
|
|
56
51
|
when(release_to) {
|
|
57
|
-
[release_to_account_field] { type: account<C>; description: concat("The ", words(release_to), " account");
|
|
52
|
+
[release_to_account_field] { type: account<C>; description: concat("The ", words(release_to), " account"); [reference_filter_key]: { column: role; values: [customer_balance]; }; }
|
|
58
53
|
}
|
|
59
54
|
platformAccountId {
|
|
60
55
|
type: account<C>;
|
|
61
56
|
description: "The product fee collection account";
|
|
62
|
-
pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$";
|
|
63
57
|
[fee_collection_key]: true;
|
|
64
58
|
[reference_filter_key]: { column: role; values: [customer_balance, product_revenue]; };
|
|
65
59
|
}
|
|
@@ -107,18 +101,18 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
|
|
|
107
101
|
lifecycle {
|
|
108
102
|
when_not(payee_fee) {
|
|
109
103
|
when_not(cancel_splits) {
|
|
110
|
-
states created funded released;
|
|
104
|
+
states created funded disputed released;
|
|
111
105
|
when(on_cancel) { states cancelled; }
|
|
112
106
|
states abandoned;
|
|
113
107
|
}
|
|
114
|
-
when(cancel_splits) { states created funding_1 funded releasing_1 released cancelling_1 cancelled abandoned; }
|
|
108
|
+
when(cancel_splits) { states created funding_1 funded disputed releasing_1 released cancelling_1 cancelled abandoned; }
|
|
115
109
|
}
|
|
116
110
|
when(payee_fee) {
|
|
117
|
-
when_not(on_cancel) { states created funding_1 funded releasing_1 released abandoned; }
|
|
111
|
+
when_not(on_cancel) { states created funding_1 funded disputed releasing_1 released abandoned; }
|
|
118
112
|
when(on_cancel) {
|
|
119
|
-
when_eq(payer_fee_kind, percent) { states created funding_1 funding_2 funding_3 funded releasing_1 releasing_2 released cancelling_1 cancelling_2 cancelled abandoning_1 abandoned abandoning_2; }
|
|
120
|
-
when_eq(payer_fee_kind, none) { states created funding_1 funding_2 funded releasing_1 releasing_2 released cancelling_1 cancelling_2 cancelled abandoning_1 abandoned; }
|
|
121
|
-
when_eq(payer_fee_kind, binding) { states created funding_1 funding_2 funded releasing_1 releasing_2 released cancelling_1 cancelling_2 cancelled abandoning_1 abandoned; }
|
|
113
|
+
when_eq(payer_fee_kind, percent) { states created funding_1 funding_2 funding_3 funded disputed releasing_1 releasing_2 released cancelling_1 cancelling_2 cancelled abandoning_1 abandoned abandoning_2; }
|
|
114
|
+
when_eq(payer_fee_kind, none) { states created funding_1 funding_2 funded disputed releasing_1 releasing_2 released cancelling_1 cancelling_2 cancelled abandoning_1 abandoned; }
|
|
115
|
+
when_eq(payer_fee_kind, binding) { states created funding_1 funding_2 funded disputed releasing_1 releasing_2 released cancelling_1 cancelling_2 cancelled abandoning_1 abandoned; }
|
|
122
116
|
}
|
|
123
117
|
}
|
|
124
118
|
initial created;
|
|
@@ -172,6 +166,8 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
|
|
|
172
166
|
on unfund_piece_1: funding_1 | abandoning_1 -> abandoned;
|
|
173
167
|
}
|
|
174
168
|
}
|
|
169
|
+
on dispute: funded -> disputed;
|
|
170
|
+
on resume: disputed -> funded;
|
|
175
171
|
on abandon: created -> abandoned;
|
|
176
172
|
}
|
|
177
173
|
parties {
|
|
@@ -193,6 +189,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
|
|
|
193
189
|
agent_description: concat("Move piece 2 of the split amount from the ", words(payer), " into escrow. Piece 1 must be in escrow first. It debits the ", words(payer), " for real, and the hold is still short of funded until piece 3 lands.");
|
|
194
190
|
summary: "Fund piece 2 of the held amount into escrow";
|
|
195
191
|
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece2Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.escrowAccountId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: fund_piece_2; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece2TransferTransferId: transferId; }; }];
|
|
192
|
+
sandbox_failure_point: funding;
|
|
196
193
|
steps: [];
|
|
197
194
|
}
|
|
198
195
|
action fund_piece_3 {
|
|
@@ -202,6 +199,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
|
|
|
202
199
|
agent_description: concat("Move piece 3 of the split amount from the ", words(payer), " into escrow. Pieces 1 and 2 must be in escrow first. It debits the ", words(payer), " for real. Where the product charges a percentage payer fee, one more call collects that fee before the hold counts as funded.");
|
|
203
200
|
summary: "Fund piece 3 of the held amount into escrow";
|
|
204
201
|
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece3Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.escrowAccountId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: fund_piece_3; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece3TransferTransferId: transferId; }; }];
|
|
202
|
+
sandbox_failure_point: funding;
|
|
205
203
|
steps: [];
|
|
206
204
|
}
|
|
207
205
|
when_eq(payer_fee_kind, percent) {
|
|
@@ -346,6 +344,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
|
|
|
346
344
|
}
|
|
347
345
|
}
|
|
348
346
|
when_eq(payer_fee_kind, binding) { moves: [{ key: service_fee; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", payer_fee); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: fields.platformAccountId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: fund_piece_1; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece1ServiceFeeTransferId: transferId; }; }]; }
|
|
347
|
+
sandbox_failure_point: funding;
|
|
349
348
|
steps: [];
|
|
350
349
|
}
|
|
351
350
|
action [release_name] {
|
|
@@ -362,14 +361,13 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
|
|
|
362
361
|
additional_properties: false;
|
|
363
362
|
properties {
|
|
364
363
|
for input_name in keys(release_fields) {
|
|
365
|
-
[input_name]
|
|
364
|
+
[input_name]: get(release_fields, input_name);
|
|
366
365
|
}
|
|
367
366
|
}
|
|
368
367
|
required: keys(release_fields);
|
|
369
368
|
}
|
|
370
369
|
}
|
|
371
|
-
|
|
372
|
-
when(release_multiple) { port { allowed_parties: [release_role, release_role_2]; }; }
|
|
370
|
+
port { allowed_parties: release_allowed; };
|
|
373
371
|
when_not(payee_fee) {
|
|
374
372
|
when_not(cancel_splits) {
|
|
375
373
|
when_not(release_to) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", amount); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payee_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: release_name; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { [release_capture]: transferId; }; }]; }
|
|
@@ -378,6 +376,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
|
|
|
378
376
|
when(cancel_splits) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece1Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payee_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: release_name; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { [release_capture]: transferId; }; }]; }
|
|
379
377
|
}
|
|
380
378
|
when(payee_fee) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece1Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payee_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: release_name; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { [release_capture]: transferId; }; }]; }
|
|
379
|
+
sandbox_failure_point: "release";
|
|
381
380
|
steps: [];
|
|
382
381
|
}
|
|
383
382
|
when(release_deadline) {
|
|
@@ -422,6 +421,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
|
|
|
422
421
|
summary: "Fund piece 2 of the held amount into escrow";
|
|
423
422
|
when_eq(payee_fee_kind, percent) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece2Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.escrowAccountId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: fund_piece_2; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece2TransferTransferId: transferId; }; }]; }
|
|
424
423
|
when_eq(payee_fee_kind, binding) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", payee_fee); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.escrowAccountId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: fund_piece_2; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece2TransferTransferId: transferId; }; }]; }
|
|
424
|
+
sandbox_failure_point: funding;
|
|
425
425
|
steps: [];
|
|
426
426
|
}
|
|
427
427
|
action release_piece_2 {
|
|
@@ -454,6 +454,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
|
|
|
454
454
|
agent_description: concat("Move piece 2 of the split amount from the ", words(payer), " into escrow, which completes funding. Piece 1 must be in escrow first. It debits the ", words(payer), " for real, and the whole held amount then sits in escrow.");
|
|
455
455
|
summary: "Fund piece 2 of the held amount into escrow";
|
|
456
456
|
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece2Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.escrowAccountId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: fund_piece_2; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece2TransferTransferId: transferId; }; }];
|
|
457
|
+
sandbox_failure_point: funding;
|
|
457
458
|
steps: [];
|
|
458
459
|
}
|
|
459
460
|
action release_piece_2 {
|
|
@@ -492,6 +493,21 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
|
|
|
492
493
|
moves: [];
|
|
493
494
|
steps: [];
|
|
494
495
|
}
|
|
496
|
+
action dispute {
|
|
497
|
+
agent_description: concat("Freeze a funded ", words(instrument), " so neither release nor refund can run until the parties settle the disagreement. No money moves and the whole balance stays put. Only a funded ", words(instrument), " disputes.");
|
|
498
|
+
description: concat("Freezes funded ", words(instrument), ", preserving the entire balance while the parties decide what happens next.");
|
|
499
|
+
summary: concat("Dispute a ", words(instrument), " settlement");
|
|
500
|
+
when(release_deadline) { deadline { field: release_deadline; }; }
|
|
501
|
+
moves: [];
|
|
502
|
+
steps: [];
|
|
503
|
+
}
|
|
504
|
+
action resume {
|
|
505
|
+
agent_description: concat("Lift the dispute hold and put the ", words(instrument), " back where it was, funded and ready to release. No money moves and the balance was never touched. Only a disputed ", words(instrument), " resumes. Use this when the parties settled without a refund.");
|
|
506
|
+
description: concat("Ends the dispute hold without moving money and returns the fully funded ", words(instrument), " to its first milestone.");
|
|
507
|
+
summary: concat("Resume a ", words(instrument), " settlement");
|
|
508
|
+
moves: [];
|
|
509
|
+
steps: [];
|
|
510
|
+
}
|
|
495
511
|
}
|
|
496
512
|
when(whole_amount) {
|
|
497
513
|
let(fee_collection_key): "x-hyperscale-fee-collection-port";
|
|
@@ -502,26 +518,31 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
|
|
|
502
518
|
summary: concat("Escrow-held payment from ", words(payer), " to ", words(payee));
|
|
503
519
|
description: concat("Held payment: the ", words(payer), " funds ", amount, " into this settlement's own escrow; ", release_actor_text, " confirms through ", release, " to release");
|
|
504
520
|
distinct_parties: true;
|
|
505
|
-
caller_parked_states: {
|
|
521
|
+
caller_parked_states: {
|
|
522
|
+
created: "The tenant either funds the hold or abandons it before any money moves.";
|
|
523
|
+
disputed: "Dispute resolution is a human judgment; the parties resume the hold to continue.";
|
|
524
|
+
};
|
|
506
525
|
fields {
|
|
507
526
|
currency { type: text; description: "One currency for the hold and its fee"; const: C; }
|
|
508
527
|
amount { type: money<C>; description: "Amount held in custody, in {C} minor units"; }
|
|
509
|
-
[payee]AccountId { type: account<C>; description: "Party the hold protects and pays on release";
|
|
510
|
-
[payer]AccountId { type: account<C>; description: "Party whose money is held until the hold resolves";
|
|
511
|
-
platformAccountId { type: account<C>; description: "Product revenue or customer balance account collecting the custody fee";
|
|
528
|
+
[payee]AccountId { type: account<C>; description: "Party the hold protects and pays on release"; [reference_filter_key]: { column: role; values: [customer_balance]; }; }
|
|
529
|
+
[payer]AccountId { type: account<C>; description: "Party whose money is held until the hold resolves"; [reference_filter_key]: { column: role; values: [customer_balance]; }; }
|
|
530
|
+
platformAccountId { type: account<C>; description: "Product revenue or customer balance account collecting the custody fee"; [fee_collection_key]: true; [reference_filter_key]: { column: role; values: [customer_balance, product_revenue]; }; }
|
|
512
531
|
when(reference) { reference { type: text; description: "The tenant's own identifier for what is held: invoice number, project code, contract reference"; optional: true; max_length: 180; min_length: 1; } }
|
|
513
532
|
[release_deadline] { type: date; description: "Date the hold releases to the payee on its own; fixed at creation and the cutoff for both the release port and cancellation"; }
|
|
514
533
|
whole_fee { type: money<C>; description: "Custody fee charged on top at funding and never placed in escrow"; }
|
|
515
534
|
when(upstream) { upstream { type: ref<instrument>; description: "Hold this one is carved out of, when a chain of holds passes the same money down"; optional: true; pattern: concat("^", id_prefix_override, "_(sandbox|live)_[a-z0-9]{8,64}$"); } }
|
|
516
535
|
}
|
|
517
536
|
lifecycle {
|
|
518
|
-
states created funded released cancelled abandoned;
|
|
537
|
+
states created funded disputed released cancelled abandoned;
|
|
519
538
|
initial created;
|
|
520
539
|
on abandon: created -> abandoned;
|
|
521
540
|
on cancel: funded -> cancelled;
|
|
541
|
+
on dispute: funded -> disputed;
|
|
522
542
|
on fund: created -> funded;
|
|
523
543
|
on [release_action]: funded -> released;
|
|
524
544
|
on release_on_deadline: funded -> released;
|
|
545
|
+
on resume: disputed -> funded;
|
|
525
546
|
}
|
|
526
547
|
parties { beneficiary: payee_account_field; payer: payer_account_field; }
|
|
527
548
|
required: [payer_account_field, payee_account_field, platformAccountId, amount, whole_fee_field, currency, release_deadline];
|
|
@@ -544,10 +565,10 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
|
|
|
544
565
|
steps: [];
|
|
545
566
|
}
|
|
546
567
|
action [release_action] {
|
|
547
|
-
agent_description: concat("Pay the ", words(payee), " the whole ", amount, " out of escrow ahead of ", release_deadline, ". This moves money and does not reverse. Only a funded hold releases, the caller must act as
|
|
568
|
+
agent_description: concat("Pay the ", words(payee), " the whole ", amount, " out of escrow ahead of ", release_deadline, ". This moves money and does not reverse. Only a funded hold releases, the caller must act as ", words(at(release_allowed, 1)), ", and the call is refused once ", release_deadline, " passes. The custody fee stays posted.");
|
|
548
569
|
summary: concat("Release the hold to the ", words(payee), " early");
|
|
549
570
|
deadline: { field: release_deadline; };
|
|
550
|
-
port: { allowed_parties: [
|
|
571
|
+
port: { allowed_parties: [at(release_allowed, 1)]; };
|
|
551
572
|
moves: [{ key: release_action; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", amount); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payee_account_field); }; "metadata.instrumentId": { from: const; value: instrument; }; "metadata.instrumentInstanceId": { from: instance; path: instrumentInstanceId; }; "metadata.phase": { from: const; value: release_action; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { releaseTransferId: transferId; }; }];
|
|
552
573
|
sandbox_failure_point: "release";
|
|
553
574
|
steps: [];
|
|
@@ -574,5 +595,20 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
|
|
|
574
595
|
requires_drained: { path: refs.escrowAccountId; };
|
|
575
596
|
steps: [];
|
|
576
597
|
}
|
|
598
|
+
action dispute {
|
|
599
|
+
agent_description: concat("Freeze a funded ", words(instrument), " so neither release nor refund can run until the parties settle the disagreement. No money moves and the whole balance stays put. Only a funded ", words(instrument), " disputes.");
|
|
600
|
+
description: concat("Freezes funded ", words(instrument), ", preserving the entire balance while the parties decide what happens next.");
|
|
601
|
+
summary: concat("Dispute a ", words(instrument), " settlement");
|
|
602
|
+
deadline: { field: release_deadline; };
|
|
603
|
+
moves: [];
|
|
604
|
+
steps: [];
|
|
605
|
+
}
|
|
606
|
+
action resume {
|
|
607
|
+
agent_description: concat("Lift the dispute hold and put the ", words(instrument), " back where it was, funded and ready to release. No money moves and the balance was never touched. Only a disputed ", words(instrument), " resumes. Use this when the parties settled without a refund.");
|
|
608
|
+
description: concat("Ends the dispute hold without moving money and returns the fully funded ", words(instrument), " to its first milestone.");
|
|
609
|
+
summary: concat("Resume a ", words(instrument), " settlement");
|
|
610
|
+
moves: [];
|
|
611
|
+
steps: [];
|
|
612
|
+
}
|
|
577
613
|
}
|
|
578
614
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
module std.
|
|
1
|
+
module std.money_flows.instant_transfer
|
|
2
2
|
|
|
3
3
|
export instrument instant_transfer<C>(payer: party, payee: party, amount: money<C>, fees: optional<block>, derived_amount: optional<block>) {
|
|
4
4
|
let(payer_fee): get(fees, payer);
|
|
@@ -32,7 +32,6 @@ export instrument instant_transfer<C>(payer: party, payee: party, amount: money<
|
|
|
32
32
|
platformAccountId {
|
|
33
33
|
type: account<C>;
|
|
34
34
|
description: "The product fee collection account";
|
|
35
|
-
pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$";
|
|
36
35
|
[fee_collection_key]: true;
|
|
37
36
|
[reference_filter_key]: { column: role; values: [customer_balance, product_revenue]; };
|
|
38
37
|
}
|
|
@@ -44,7 +43,6 @@ export instrument instant_transfer<C>(payer: party, payee: party, amount: money<
|
|
|
44
43
|
platformAccountId {
|
|
45
44
|
type: account<C>;
|
|
46
45
|
description: "The product fee collection account";
|
|
47
|
-
pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$";
|
|
48
46
|
[fee_collection_key]: true;
|
|
49
47
|
[reference_filter_key]: { column: role; values: [customer_balance, product_revenue]; };
|
|
50
48
|
}
|
|
@@ -155,6 +153,7 @@ export instrument instant_transfer<C>(payer: party, payee: party, amount: money<
|
|
|
155
153
|
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.netOfCarvedFeeAmount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payee_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: pay_piece_1; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { payPiece1TransferTransferId: transferId; }; }];
|
|
156
154
|
}
|
|
157
155
|
}
|
|
156
|
+
sandbox_failure_point: funding;
|
|
158
157
|
steps: [];
|
|
159
158
|
}
|
|
160
159
|
when(fees) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
module std.
|
|
1
|
+
module std.money_flows.pooled_split
|
|
2
2
|
|
|
3
3
|
export instrument pooled_split<C>(payer: party, amount: money<C>, payout_due: date, split: block) {
|
|
4
4
|
let(recipients): keys_except(split, "remainder_to");
|
|
@@ -6,13 +6,13 @@ export instrument pooled_split<C>(payer: party, amount: money<C>, payout_due: da
|
|
|
6
6
|
let(remainder_recipient): get(split, "remainder_to");
|
|
7
7
|
fields {
|
|
8
8
|
currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
|
|
9
|
-
[payer]AccountId { type: account<C>; description: concat("The ", words(payer), " account");
|
|
9
|
+
[payer]AccountId { type: account<C>; description: concat("The ", words(payer), " account"); "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
10
10
|
amount { type: money<C>; description: concat("The pooled period total in {C} minor units; the share fields below partition it exactly"); }
|
|
11
11
|
payout_due { type: date; description: "The period's payout date; the pool distributes from it"; }
|
|
12
12
|
for index in recipient_count {
|
|
13
13
|
let(recipient): at(recipients, index);
|
|
14
14
|
let(share_bps): get(split, recipient);
|
|
15
|
-
[recipient]AccountId { type: account<C>; description: concat("The ", words(recipient), " account");
|
|
15
|
+
[recipient]AccountId { type: account<C>; description: concat("The ", words(recipient), " account"); "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
16
16
|
[recipient]ShareAmount {
|
|
17
17
|
type: money<C>;
|
|
18
18
|
description: concat(div_floor(basis_points(share_bps), 100), "% of ", amount, if_eq(recipient, remainder_recipient, " (carries the integer-division remainder): ", ": "), "the ", words(recipient), "'s share. Computed as floor(", amount, " * ", basis_points(share_bps), " / 10000) in {C} minor units");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
module std.
|
|
1
|
+
module std.money_flows.premium_forward
|
|
2
2
|
|
|
3
3
|
export instrument premium_forward<C>(
|
|
4
4
|
payer: party,
|
|
@@ -17,9 +17,8 @@ export instrument premium_forward<C>(
|
|
|
17
17
|
let(amount_field): concat(amount);
|
|
18
18
|
let(net_bps): sub(10000, commission);
|
|
19
19
|
let(fee_bps): add(commission, 0);
|
|
20
|
-
let(net_percent):
|
|
21
|
-
let(fee_percent):
|
|
22
|
-
let(bind_roles): if_eq(len(bind_allowed), 2, ["payer", "beneficiary"], ["beneficiary"]);
|
|
20
|
+
let(net_percent): percent_text(net_bps);
|
|
21
|
+
let(fee_percent): percent_text(fee_bps);
|
|
23
22
|
let(owner_id_key): "owner.id";
|
|
24
23
|
let(owner_type_key): "owner.type";
|
|
25
24
|
let(metadata_instrument_key): "metadata.instrumentId";
|
|
@@ -39,7 +38,6 @@ export instrument premium_forward<C>(
|
|
|
39
38
|
platform_account_id: {
|
|
40
39
|
type: account;
|
|
41
40
|
description: "The product fee collection account";
|
|
42
|
-
pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$";
|
|
43
41
|
[fee_port_key]: true;
|
|
44
42
|
[reference_filter_key]: { column: role; values: [customer_balance, product_revenue]; };
|
|
45
43
|
}
|
|
@@ -122,6 +120,7 @@ export instrument premium_forward<C>(
|
|
|
122
120
|
};
|
|
123
121
|
capture: { fund_piece_1_transfer_transfer_id: transferId; };
|
|
124
122
|
}];
|
|
123
|
+
sandbox_failure_point: funding;
|
|
125
124
|
steps: [];
|
|
126
125
|
}
|
|
127
126
|
|
|
@@ -142,6 +141,7 @@ export instrument premium_forward<C>(
|
|
|
142
141
|
};
|
|
143
142
|
capture: { fund_piece_2_transfer_transfer_id: transferId; };
|
|
144
143
|
}];
|
|
144
|
+
sandbox_failure_point: funding;
|
|
145
145
|
steps: [];
|
|
146
146
|
}
|
|
147
147
|
|
|
@@ -152,16 +152,13 @@ export instrument premium_forward<C>(
|
|
|
152
152
|
additional_properties: false;
|
|
153
153
|
properties: {
|
|
154
154
|
for item in keys(bind_fields) {
|
|
155
|
-
[item]:
|
|
156
|
-
description: "Required decision reference retained in the operation receipt";
|
|
157
|
-
max_length: 180; min_length: 1; type: string;
|
|
158
|
-
}
|
|
155
|
+
[item]: get(bind_fields, item);
|
|
159
156
|
}
|
|
160
157
|
};
|
|
161
158
|
required: keys(bind_fields);
|
|
162
159
|
type: object;
|
|
163
160
|
};
|
|
164
|
-
port: { allowed_parties:
|
|
161
|
+
port: { allowed_parties: bind_allowed; };
|
|
165
162
|
moves: [{
|
|
166
163
|
key: transfer; operation: "internal_transfer.create";
|
|
167
164
|
bind: {
|
|
@@ -176,6 +173,7 @@ export instrument premium_forward<C>(
|
|
|
176
173
|
};
|
|
177
174
|
capture: { [bind_capture_key]: transferId; };
|
|
178
175
|
}];
|
|
176
|
+
sandbox_failure_point: "release";
|
|
179
177
|
steps: [];
|
|
180
178
|
}
|
|
181
179
|
|
|
@@ -230,21 +228,22 @@ export instrument premium_forward<C>(
|
|
|
230
228
|
action [endorsement] {
|
|
231
229
|
agent_description: "Record one endorsement against a policy that already forwarded, from evidence the caller supplies. The forward must be released first, and only the {carrier} may call it. It moves no money and it does not change the premium.";
|
|
232
230
|
summary: "Record one non-money endorsement from external evidence";
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
max_length: 180; min_length: 1; type: string;
|
|
231
|
+
when(endorsement_fields) {
|
|
232
|
+
input: {
|
|
233
|
+
additional_properties: false;
|
|
234
|
+
properties: {
|
|
235
|
+
for item in keys(endorsement_fields) {
|
|
236
|
+
[item]: get(endorsement_fields, item);
|
|
240
237
|
}
|
|
241
|
-
}
|
|
238
|
+
};
|
|
239
|
+
required: keys(endorsement_fields);
|
|
240
|
+
type: object;
|
|
242
241
|
};
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
port
|
|
242
|
+
capture_input: {
|
|
243
|
+
for item in keys(endorsement_fields) { [item]: item; }
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
port { allowed_parties: endorsement_allowed; };
|
|
248
247
|
steps: [];
|
|
249
248
|
}
|
|
250
249
|
action lapse {
|