@hyperscale0/hsx 2.4.1 → 3.0.0
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 +2 -408
- 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 +76 -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 +77 -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
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
header insurance
|
|
2
|
+
|
|
3
|
+
instrument cover(holder: party, insurer: party, commission: percent = 0%, covers: ref, premium: money = runtime) {
|
|
4
|
+
summary: "Recurring cover with separate premium slices and commission clawback."
|
|
5
|
+
fields { covers: ref<covers>, holder: account of holder, insurer: account of insurer, expiresAt: date }
|
|
6
|
+
lifecycle { states: [pending, active, cancelled, expired], initial: pending }
|
|
7
|
+
action create {}
|
|
8
|
+
action activate { from: pending, to: active, actor: { party: insurer } }
|
|
9
|
+
action cancel {
|
|
10
|
+
from: active, to: cancelled
|
|
11
|
+
invoke: [{ selection: { instrument: slice, reference: "cover", anchor: self.id, states: [paid], limit: 366 }, action: refund, input: {} }]
|
|
12
|
+
}
|
|
13
|
+
action expire { from: active, to: expired, actor: clock, due: { at: self.expiresAt } }
|
|
14
|
+
records {
|
|
15
|
+
slice: {
|
|
16
|
+
summary: "A whole premium period, collected once and refundable only before it starts."
|
|
17
|
+
fields {
|
|
18
|
+
cover: ref<parent>, covers: ref<covers>, premium: money = premium, startsAt: date, endsAt: date
|
|
19
|
+
commission: money = rate(self.premium, commission)
|
|
20
|
+
carrierNet: money = subtract(self.premium, self.commission)
|
|
21
|
+
}
|
|
22
|
+
lifecycle { states: [pending, paid, active, expired, refunded], initial: pending }
|
|
23
|
+
action create {
|
|
24
|
+
requires self.covers == self.cover.covers
|
|
25
|
+
requires self.startsAt < self.endsAt
|
|
26
|
+
requires unique "premium_period" on [self.cover, self.startsAt, self.endsAt]
|
|
27
|
+
}
|
|
28
|
+
action collect {
|
|
29
|
+
from: pending, to: paid
|
|
30
|
+
requires self.cover in [active]
|
|
31
|
+
moves self.premium from holder to insurer
|
|
32
|
+
moves self.commission from insurer to programOperator
|
|
33
|
+
}
|
|
34
|
+
action start { from: paid, to: active, actor: clock, due: { at: self.startsAt } }
|
|
35
|
+
action expire { from: active, to: expired, actor: clock, due: { at: self.endsAt } }
|
|
36
|
+
action refund {
|
|
37
|
+
from: paid, to: refunded, deadline: { at: self.startsAt }
|
|
38
|
+
moves self.commission from programOperator to insurer
|
|
39
|
+
moves self.premium from insurer to holder
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
instrument claim(cover: ref<insurance.cover>, approved_by: approval) {
|
|
46
|
+
summary: "A carrier decision reserves one claim payment before settlement."
|
|
47
|
+
fields { cover: ref<cover>, amount: money, expiresAt: date, evidence: text, reservation: text? }
|
|
48
|
+
lifecycle { states: [submitted, approved, paid, denied, expired], initial: submitted }
|
|
49
|
+
action create { requires self.cover in [active] }
|
|
50
|
+
action approve {
|
|
51
|
+
from: submitted, to: approved, actor: { party: approved_by }
|
|
52
|
+
requires approval by approved_by
|
|
53
|
+
deadline: { at: self.expiresAt }
|
|
54
|
+
moves reserve self.amount from self.cover.insurer to self.cover.holder capture "reservation"
|
|
55
|
+
}
|
|
56
|
+
action pay { from: approved, to: paid, deadline: { at: self.expiresAt }, moves post self.reservation }
|
|
57
|
+
action deny { from: submitted, to: denied, requires approval by approved_by for approve is "declined" }
|
|
58
|
+
action expire { from: approved, to: expired, actor: clock, due: { at: self.expiresAt }, moves void self.reservation }
|
|
59
|
+
}
|
package/std/lending.hsx
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
header lending
|
|
2
|
+
|
|
3
|
+
instrument round(borrower: party, capital: party, plan: ref<financing.installments> = object(financing.installments), months: integer(1, 366) = 3, profit: percent = 3%, minimum_ticket: money = 100 SAR, investor_cap: percent = 20%, commitments: ref<lending.commitment> = object(lending.commitment)) {
|
|
4
|
+
summary: "A threshold funding round; failed rounds return each original wallet contribution."
|
|
5
|
+
fields {
|
|
6
|
+
borrower: account of borrower, capital: account(capital, cash, "capital"), plan: ref<plan>, price: money, closesAt: date
|
|
7
|
+
months: integer = months, profit: money = rate(self.price, profit)
|
|
8
|
+
minimumTicket: money = minimum_ticket, maximumTicket: money = rate(self.price, investor_cap)
|
|
9
|
+
held: account of self
|
|
10
|
+
}
|
|
11
|
+
lifecycle { states: [open, funded, failed], initial: open }
|
|
12
|
+
action create { requires unique "funding_plan" on [self.plan]; requires self.months == self.plan.months; requires self.profit == self.plan.profit; requires self.price == self.plan.principal; requires self.borrower == self.plan.funds.payer }
|
|
13
|
+
action close {
|
|
14
|
+
from: open, to: funded
|
|
15
|
+
requires sum "amount" of { instrument: commitments, reference: "round", anchor: self.id, states: [committed], limit: 366 } == self.price
|
|
16
|
+
requires self.held.balance == self.price
|
|
17
|
+
moves self.price from self.held to self.plan.capital
|
|
18
|
+
invoke: [{ selection: { instrument: commitments, reference: "round", anchor: self.id, states: [committed], limit: 366 }, action: fund, input: {} }]
|
|
19
|
+
}
|
|
20
|
+
action fail {
|
|
21
|
+
from: open, to: failed, actor: clock, due: { at: self.closesAt }
|
|
22
|
+
requires self.held.balance < self.price
|
|
23
|
+
invoke: [{ selection: { instrument: commitments, reference: "round", anchor: self.id, states: [committed], limit: 366 }, action: refund, input: {} }]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
instrument commitment(round: ref<lending.round>, wallet: ref<wallet.balance>, investor: party = party(person)) {
|
|
28
|
+
summary: "One investor's original-wallet contribution, capped cumulatively within the round."
|
|
29
|
+
fields { round: ref<round>, wallet: ref<wallet>, amount: money, loss: account(investor, claim, "loss") }
|
|
30
|
+
lifecycle { states: [committed, funded, withdrawn, refunded], initial: committed }
|
|
31
|
+
invariants sum "amount" of { instrument: all(lending.commitment), reference: "round", anchor: self.round, states: [committed, funded], limit: 366, where: { wallet: { field: self.wallet } } } <= self.round.maximumTicket
|
|
32
|
+
invariants sum "amount" of { instrument: all(lending.commitment), reference: "round", anchor: self.round, states: [committed, funded], limit: 366 } <= self.round.price
|
|
33
|
+
action create {
|
|
34
|
+
requires self.round in [open]
|
|
35
|
+
requires self.wallet in [active]
|
|
36
|
+
requires self.amount >= self.round.minimumTicket
|
|
37
|
+
deadline: { at: self.round.closesAt }
|
|
38
|
+
moves self.amount from self.wallet.held to self.round.held
|
|
39
|
+
}
|
|
40
|
+
action fund { from: committed, to: funded, actor: { parent: round } }
|
|
41
|
+
action withdraw {
|
|
42
|
+
from: committed, to: withdrawn
|
|
43
|
+
requires self.round in [open]
|
|
44
|
+
deadline: { at: self.round.closesAt }
|
|
45
|
+
moves self.amount from self.round.held to self.wallet.held
|
|
46
|
+
}
|
|
47
|
+
action refund {
|
|
48
|
+
from: committed, to: refunded, actor: { parent: round }
|
|
49
|
+
moves self.amount from self.round.held to self.wallet.held
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
instrument distribution(round: ref<lending.round>, receipt: ref, commitments: ref<lending.commitment> = object(lending.commitment), mode: enum(cash, loss) = cash, residual_to: party = programOperator, fee: percent = 1%, tax: percent = 15%) {
|
|
54
|
+
summary: "Frozen weighted shares, floored once, with residual assigned to the chosen party."
|
|
55
|
+
fields {
|
|
56
|
+
round: ref<round>, source: ref<receipt>, mode: enum(cash, loss) = mode
|
|
57
|
+
principal: money?, profit: money?, grossTotal: money?, charge: money?, vat: money?, net: money?
|
|
58
|
+
held: account of self, losses: account(self, claim), residualLoss: account(residual_to, claim, "loss")
|
|
59
|
+
}
|
|
60
|
+
lifecycle { states: [pending, cash_ready, loss_ready, distributed], initial: pending }
|
|
61
|
+
action create { requires self.round in [funded]; requires self.source.plan == self.round.plan; requires unique "settlement_consumption" on [self.source.plan, self.source.identity] }
|
|
62
|
+
action prepare_cash {
|
|
63
|
+
from: pending, to: cash_ready
|
|
64
|
+
requires self.mode == "cash"; requires self.source.kind == "cash"
|
|
65
|
+
calculate: [
|
|
66
|
+
{ target: "principal", op: "sum", values: [{ field: self.source.principal }] }
|
|
67
|
+
{ target: "profit", op: "sum", values: [{ field: self.source.profit }] }
|
|
68
|
+
{ target: "charge", op: "rate", base: { field: self.profit }, bps: { literal: fee }, rounding: "floor" }
|
|
69
|
+
{ target: "vat", op: "rate", base: { field: self.charge }, bps: { literal: tax }, rounding: "floor" }
|
|
70
|
+
{ target: "grossTotal", op: "sum", values: [{ field: self.principal }, { field: self.profit }] }
|
|
71
|
+
{ target: "net", op: "subtract", base: { field: self.grossTotal }, subtract: [{ field: self.charge }, { field: self.vat }] }
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
action prepare_loss {
|
|
75
|
+
from: pending, to: loss_ready
|
|
76
|
+
requires self.mode == "loss"; requires self.source.kind == "loss"
|
|
77
|
+
calculate: [{ target: "net", op: "sum", values: [{ field: self.source.loss }] }]
|
|
78
|
+
}
|
|
79
|
+
action distribute_cash {
|
|
80
|
+
from: cash_ready, to: distributed
|
|
81
|
+
requires sum "weight" of { instrument: share, reference: "distribution", anchor: self.id, states: [pending], limit: 366 } == self.round.price
|
|
82
|
+
moves self.principal from self.source.capital to self.held; moves self.profit from self.source.profitIncome to self.held; moves self.charge from self.held to programOperator; moves self.vat from self.held to programTax
|
|
83
|
+
invoke: [{ selection: { instrument: share, reference: "distribution", anchor: self.id, states: [pending], limit: 366 }, action: cash, input: {} }, { reference: self.id, action: residual_cash, input: {} }]
|
|
84
|
+
}
|
|
85
|
+
action residual_cash {
|
|
86
|
+
from: distributed, to: distributed, actor: { parent: current() }
|
|
87
|
+
moves self.held.balance from self.held to residual_to
|
|
88
|
+
}
|
|
89
|
+
action distribute_loss {
|
|
90
|
+
from: loss_ready, to: distributed
|
|
91
|
+
requires sum "weight" of { instrument: share, reference: "distribution", anchor: self.id, states: [pending], limit: 366 } == self.round.price
|
|
92
|
+
moves self.net from self.source.lossAccount to self.losses
|
|
93
|
+
invoke: [{ selection: { instrument: share, reference: "distribution", anchor: self.id, states: [pending], limit: 366 }, action: loss, input: {} }, { reference: self.id, action: residual_loss, input: {} }]
|
|
94
|
+
}
|
|
95
|
+
action residual_loss {
|
|
96
|
+
from: distributed, to: distributed, actor: { parent: current() }
|
|
97
|
+
moves self.losses.balance from self.losses to self.residualLoss
|
|
98
|
+
}
|
|
99
|
+
records {
|
|
100
|
+
share: {
|
|
101
|
+
fields {
|
|
102
|
+
distribution: ref<parent>, commitment: ref<commitments>
|
|
103
|
+
weight: money = self.commitment.amount
|
|
104
|
+
gross: money = ratio(self.distribution.net, self.weight, self.distribution.round.price)
|
|
105
|
+
}
|
|
106
|
+
lifecycle { states: [pending, paid], initial: pending }
|
|
107
|
+
action create { requires self.distribution in [cash_ready, loss_ready]; requires self.commitment in [funded]; requires unique "distribution_weight" on [self.distribution, self.commitment]; requires self.commitment.round == self.distribution.round }
|
|
108
|
+
action cash {
|
|
109
|
+
from: pending, to: paid, actor: { parent: parent }
|
|
110
|
+
moves self.gross from self.distribution.held to self.commitment.wallet.held
|
|
111
|
+
}
|
|
112
|
+
action loss { from: pending, to: paid, actor: { parent: parent }, moves self.gross from self.distribution.losses to self.commitment.loss }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
header marketplace
|
|
2
|
+
|
|
3
|
+
instrument listing(seller: party) {
|
|
4
|
+
summary: "A seller's offer and its asking price."
|
|
5
|
+
fields { seller: account of seller, title: text, price: money }
|
|
6
|
+
lifecycle { states: [draft, active, withdrawn, sold], initial: draft }
|
|
7
|
+
action create { actor: { party: seller } }
|
|
8
|
+
action publish { from: draft, to: active, actor: { party: seller } }
|
|
9
|
+
action withdraw { from: active, to: withdrawn, actor: { party: seller } }
|
|
10
|
+
action sell { from: active, to: sold, actor: { party: seller } }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
instrument order(listing: ref<marketplace.listing>, buyer: party) {
|
|
14
|
+
summary: "A buyer's order for one listing at its declared price."
|
|
15
|
+
fields { listing: ref<listing>, buyer: account of buyer, price: money }
|
|
16
|
+
lifecycle { states: [placed, fulfilled, cancelled], initial: placed }
|
|
17
|
+
action create {
|
|
18
|
+
actor: { party: buyer }
|
|
19
|
+
requires self.listing in [active]
|
|
20
|
+
requires self.price == self.listing.price
|
|
21
|
+
}
|
|
22
|
+
action fulfill { from: placed, to: fulfilled }
|
|
23
|
+
action cancel { from: placed, to: cancelled, actor: { party: buyer } }
|
|
24
|
+
}
|
package/std/money.hsx
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
header money
|
|
2
|
+
|
|
3
|
+
instrument transfer(payer: party, payee: party, amount: money = runtime) {
|
|
4
|
+
summary: "One transfer between the bound parties."
|
|
5
|
+
fields { amount: money = amount }
|
|
6
|
+
lifecycle { states: [pending, paid, cancelled], initial: pending }
|
|
7
|
+
action create { actor: { party: payer } }
|
|
8
|
+
action pay {
|
|
9
|
+
from: pending, to: paid, actor: { party: payer }
|
|
10
|
+
moves self.amount from payer to payee
|
|
11
|
+
}
|
|
12
|
+
action cancel { from: pending, to: cancelled, actor: { party: payer } }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
instrument hold(payer: party, payee: party, amount: money = runtime) {
|
|
16
|
+
summary: "Hold the declared amount until release or refund."
|
|
17
|
+
fields { payer: account of payer, payee: account of payee, amount: money = amount, price: money = self.amount, held: account of self }
|
|
18
|
+
lifecycle { states: [pending, funded, released, refunded, cancelled], initial: pending }
|
|
19
|
+
action create { }
|
|
20
|
+
action fund {
|
|
21
|
+
from: pending, to: funded, actor: { party: payer }
|
|
22
|
+
moves self.amount from payer to self.held
|
|
23
|
+
}
|
|
24
|
+
action confirm {
|
|
25
|
+
from: pending, to: funded
|
|
26
|
+
requires self.held.balance == self.amount
|
|
27
|
+
}
|
|
28
|
+
action release {
|
|
29
|
+
from: funded, to: released
|
|
30
|
+
moves self.amount from self.held to payee
|
|
31
|
+
}
|
|
32
|
+
action refund {
|
|
33
|
+
from: funded, to: refunded
|
|
34
|
+
moves self.amount from self.held to payer
|
|
35
|
+
}
|
|
36
|
+
action cancel { from: pending, to: cancelled }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
instrument split(payer: party, amount: money = runtime, shares: split = { programOperator: 100% }) {
|
|
40
|
+
summary: "Split one amount; the last declared recipient receives the rounding remainder."
|
|
41
|
+
fields { amount: money = amount }
|
|
42
|
+
lifecycle { states: [pending, paid, cancelled], initial: pending }
|
|
43
|
+
action create {}
|
|
44
|
+
action pay { from: pending, to: paid, moves self.amount from payer shares shares }
|
|
45
|
+
action cancel { from: pending, to: cancelled }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
instrument schedule(payer: party, payee: party, amount: money = runtime, count: integer(1, 366) = 12) {
|
|
49
|
+
summary: "A finite schedule whose dated pieces sum exactly to the declared total."
|
|
50
|
+
fields { amount: money = amount, count: integer = count, dates: list(date, count) }
|
|
51
|
+
lifecycle { states: [draft, active, complete, cancelled], initial: draft }
|
|
52
|
+
action create {}
|
|
53
|
+
action activate {
|
|
54
|
+
from: draft, to: active
|
|
55
|
+
requires count of { instrument: occurrence, reference: "schedule", anchor: self.id, states: [pending], limit: 366 } == self.count
|
|
56
|
+
}
|
|
57
|
+
action complete {
|
|
58
|
+
from: active, to: complete
|
|
59
|
+
requires count of { instrument: occurrence, reference: "schedule", anchor: self.id, states: [pending], limit: 366 } == 0
|
|
60
|
+
}
|
|
61
|
+
action cancel { from: [draft, active], to: cancelled }
|
|
62
|
+
records {
|
|
63
|
+
occurrence: {
|
|
64
|
+
fields {
|
|
65
|
+
schedule: ref<parent>, position: integer(1, count), dueAt: date = at(self.schedule.dates, self.position)
|
|
66
|
+
base: money = divide(self.schedule.amount, count)
|
|
67
|
+
allocated: money = multiply(self.base, count)
|
|
68
|
+
remainder: money = subtract(self.schedule.amount, self.allocated)
|
|
69
|
+
first: integer = divide(1, self.position)
|
|
70
|
+
adjustment: money = multiply(self.remainder, self.first)
|
|
71
|
+
amount: money = sum(self.base, self.adjustment)
|
|
72
|
+
}
|
|
73
|
+
lifecycle { states: [pending, paid, cancelled], initial: pending }
|
|
74
|
+
action create { requires self.schedule in [draft]; requires unique "scheduled_position" on [self.schedule, self.position] }
|
|
75
|
+
action pay { from: pending, to: paid, actor: clock, due: { at: self.dueAt }, requires self.schedule in [active], moves self.amount from payer to payee }
|
|
76
|
+
action cancel { from: pending, to: cancelled, requires self.schedule in [cancelled] }
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
instrument pool(payer: party, payee: party, target: money, closes: date) {
|
|
82
|
+
summary: "Contributions fund one threshold or return to their original contributor."
|
|
83
|
+
fields { target: money = target, closesAt: date = closes, held: account of self }
|
|
84
|
+
lifecycle { states: [open, paid, failed], initial: open }
|
|
85
|
+
action create { }
|
|
86
|
+
action pay {
|
|
87
|
+
from: open, to: paid
|
|
88
|
+
requires self.held.balance == self.target
|
|
89
|
+
moves self.target from self.held to payee
|
|
90
|
+
}
|
|
91
|
+
action fail {
|
|
92
|
+
from: open, to: failed, actor: clock, due: { at: self.closesAt }
|
|
93
|
+
requires self.held.balance < self.target
|
|
94
|
+
invoke: [{ selection: { instrument: contribution, reference: "pool", anchor: self.id, states: [paid], limit: 366 }, action: refund, input: {} }]
|
|
95
|
+
}
|
|
96
|
+
records {
|
|
97
|
+
contribution: {
|
|
98
|
+
fields { pool: ref<parent>, payer: account of payer, amount: money }
|
|
99
|
+
lifecycle { states: [pending, paid, refunded], initial: pending }
|
|
100
|
+
action create { requires self.pool in [open] }
|
|
101
|
+
action pay { from: pending, to: paid, deadline: { at: self.pool.closesAt }, requires self.pool in [open], moves self.amount from self.payer to self.pool.held }
|
|
102
|
+
action refund { from: paid, to: refunded, actor: { parent: parent }, moves self.amount from self.pool.held to self.payer }
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
instrument swap(first: party, second: party, first_amount: money, second_amount: money, expires: date) {
|
|
108
|
+
summary: "Two reservations settle together or both return."
|
|
109
|
+
fields { firstAmount: money = first_amount, secondAmount: money = second_amount, expiresAt: date = expires, firstReceipt: text?, secondReceipt: text? }
|
|
110
|
+
lifecycle { states: [pending, first_ready, second_ready, both_ready, exchanged, cancelled], initial: pending }
|
|
111
|
+
action create {}
|
|
112
|
+
action fund_first { from: pending, to: first_ready, actor: { party: first }, deadline: { at: self.expiresAt }, moves reserve self.firstAmount from first to second capture "firstReceipt" }
|
|
113
|
+
action fund_second { from: first_ready, to: both_ready, actor: { party: second }, deadline: { at: self.expiresAt }, moves reserve self.secondAmount from second to first capture "secondReceipt" }
|
|
114
|
+
action start_second { from: pending, to: second_ready, actor: { party: second }, deadline: { at: self.expiresAt }, moves reserve self.secondAmount from second to first capture "secondReceipt" }
|
|
115
|
+
action finish_first { from: second_ready, to: both_ready, actor: { party: first }, deadline: { at: self.expiresAt }, moves reserve self.firstAmount from first to second capture "firstReceipt" }
|
|
116
|
+
action expire_second { from: second_ready, to: cancelled, actor: clock, due: { at: self.expiresAt }, moves void self.secondReceipt }
|
|
117
|
+
action exchange { from: both_ready, to: exchanged, moves post self.firstReceipt; moves post self.secondReceipt }
|
|
118
|
+
action cancel { from: pending, to: cancelled }
|
|
119
|
+
action expire_first { from: first_ready, to: cancelled, actor: clock, due: { at: self.expiresAt }, moves void self.firstReceipt }
|
|
120
|
+
action expire_both { from: both_ready, to: cancelled, actor: clock, due: { at: self.expiresAt }, moves void self.firstReceipt; moves void self.secondReceipt }
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
instrument deposit(payer: party, payee: party, amount: money, expires: date, approval: approval) {
|
|
124
|
+
fields { amount: money = amount, expiresAt: date = expires, reservation: text? }
|
|
125
|
+
lifecycle { states: [pending, held, claimed, returned, cancelled], initial: pending }
|
|
126
|
+
action create {}
|
|
127
|
+
action fund { from: pending, to: held, actor: { party: payer }, moves reserve self.amount from payer to payee capture "reservation" }
|
|
128
|
+
action claim { from: held, to: claimed, deadline: { at: self.expiresAt }, requires approval by approval, moves post self.reservation }
|
|
129
|
+
action return { from: held, to: returned, actor: clock, due: { at: self.expiresAt }, moves void self.reservation }
|
|
130
|
+
action cancel { from: pending, to: cancelled }
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
instrument payout(payer: party, payee: party, amount: money = runtime, reconcile_within: duration = 3d) {
|
|
134
|
+
summary: "A bank-linked transfer with confirmation bound to its captured identity."
|
|
135
|
+
fields { amount: money = amount, bank: account(payee, cash, external, "bank"), receipt: text?, expectedAt: date = after(self.createdAt, reconcile_within) }
|
|
136
|
+
lifecycle { states: [pending, instructed, settled, exception, reversed], initial: pending }
|
|
137
|
+
action create {}
|
|
138
|
+
action instruct { from: pending, to: instructed, actor: { party: payer }, moves self.amount from payer to self.bank capture "receipt" }
|
|
139
|
+
action confirm {
|
|
140
|
+
from: [instructed, exception], to: settled
|
|
141
|
+
requires self.receipt.status == "settled"
|
|
142
|
+
}
|
|
143
|
+
action expire {
|
|
144
|
+
from: instructed, to: exception, actor: clock, due: { at: self.expectedAt }
|
|
145
|
+
requires self.receipt.status != "settled"
|
|
146
|
+
}
|
|
147
|
+
action reverse {
|
|
148
|
+
from: [instructed, exception], to: reversed
|
|
149
|
+
requires self.receipt.status == "reversed"
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
instrument metered(payer: party, payee: party, unit_price: money, units: integer = 1, fee: fee = { seller: 0% }) {
|
|
154
|
+
summary: "A unique usage event priced once at the fixed unit price."
|
|
155
|
+
fields { usageIdentity: text, unitPrice: money = unit_price, units: integer = units, amount: money = multiply(self.unitPrice, self.units) }
|
|
156
|
+
lifecycle { states: [recorded, billed, cancelled], initial: recorded }
|
|
157
|
+
action create { requires unique "usage_event" on [self.usageIdentity] }
|
|
158
|
+
action bill { from: recorded, to: billed, moves self.amount from payer to payee fee fee }
|
|
159
|
+
action cancel { from: recorded, to: cancelled }
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
instrument batch(payer: party, payee: party, closes: date, approval: approval, reconcile_within: duration = 3d) {
|
|
163
|
+
summary: "A frozen positive net of credit and debit entries, paid once."
|
|
164
|
+
fields { closesAt: date = closes, credits: money, debits: money, amount: money = subtract(self.credits, self.debits), bank: account(payee, cash, external, "bank"), receipt: text?, instructedAt: date?, expectedAt: date? }
|
|
165
|
+
lifecycle { states: [open, closed, approved, instructed, exception, paid, reversed], initial: open }
|
|
166
|
+
action create {}
|
|
167
|
+
action close {
|
|
168
|
+
from: open, to: closed, actor: clock, due: { at: self.closesAt }
|
|
169
|
+
requires sum "amount" of { instrument: credit, reference: "batch", anchor: self.id, states: [recorded], limit: 366 } == self.credits
|
|
170
|
+
requires sum "amount" of { instrument: debit, reference: "batch", anchor: self.id, states: [recorded], limit: 366 } == self.debits
|
|
171
|
+
requires self.amount > "0"
|
|
172
|
+
}
|
|
173
|
+
action approve { from: closed, to: approved, requires approval by approval }
|
|
174
|
+
action pay {
|
|
175
|
+
from: approved, to: instructed
|
|
176
|
+
set: { instructedAt: { field: self.now } }
|
|
177
|
+
calculate: [{ target: "expectedAt", op: "shift", date: { field: self.now }, milliseconds: { literal: reconcile_within }, direction: "after" }]
|
|
178
|
+
moves self.amount from payer to self.bank capture "receipt"
|
|
179
|
+
}
|
|
180
|
+
action confirm { from: [instructed, exception], to: paid, requires self.receipt.status == "settled" }
|
|
181
|
+
action expire { from: instructed, to: exception, actor: clock, due: { at: self.expectedAt }, requires self.receipt.status != "settled" }
|
|
182
|
+
action reverse { from: [instructed, exception], to: reversed, requires self.receipt.status == "reversed" }
|
|
183
|
+
records {
|
|
184
|
+
credit: {
|
|
185
|
+
fields { batch: ref<parent>, identity: text, amount: money }
|
|
186
|
+
lifecycle { states: [recorded], initial: recorded }
|
|
187
|
+
action create { requires self.batch in [open]; requires unique "batch_credit" on [self.identity] }
|
|
188
|
+
}
|
|
189
|
+
debit: {
|
|
190
|
+
fields { batch: ref<parent>, identity: text, amount: money }
|
|
191
|
+
lifecycle { states: [recorded], initial: recorded }
|
|
192
|
+
action create { requires self.batch in [open]; requires unique "batch_debit" on [self.identity] }
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
package/std/savings.hsx
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
header savings
|
|
2
|
+
|
|
3
|
+
instrument circle(contribution: money, members: integer(1, 366), starts: date, memberships: ref<savings.membership> = object(savings.membership)) {
|
|
4
|
+
summary: "A fixed group contributes on its explicit calendar and takes the pot in order."
|
|
5
|
+
fields { contribution: money = contribution, members: integer = members, startsAt: date = starts, dates: list(date, members), pot: money = multiply(self.contribution, self.members), held: account of self }
|
|
6
|
+
lifecycle { states: [forming, active, closed, disbanded], initial: forming }
|
|
7
|
+
action create { }
|
|
8
|
+
action activate {
|
|
9
|
+
from: forming, to: active, actor: clock, due: { at: self.startsAt }
|
|
10
|
+
requires count of { instrument: memberships, reference: "circle", anchor: self.id, states: [joined], limit: 60 } == self.members
|
|
11
|
+
}
|
|
12
|
+
action close {
|
|
13
|
+
from: active, to: closed
|
|
14
|
+
requires count of { instrument: memberships, reference: "circle", anchor: self.id, states: [paid], limit: 60 } == self.members
|
|
15
|
+
requires self.held.balance == "0"
|
|
16
|
+
}
|
|
17
|
+
action disband { from: forming, to: disbanded }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
instrument membership(circle: ref<savings.circle>, member: party) {
|
|
21
|
+
fields { circle: ref<circle>, member: account of member, position: integer(1, 60), preceding: integer = subtract(self.position, 1), receivesAt: date = at(self.circle.dates, self.position), contribution: money = self.circle.contribution, total: money = self.circle.pot, dates: list(date, 60) }
|
|
22
|
+
lifecycle { states: [joined, active, paid, withdrawn], initial: joined }
|
|
23
|
+
action create { requires self.position <= self.circle.members; requires self.circle in [forming]; requires unique "circle_seat" on [self.circle, self.position]; requires unique "circle_member" on [self.circle, self.member] }
|
|
24
|
+
action activate {
|
|
25
|
+
from: joined, to: active
|
|
26
|
+
requires self.circle in [active]
|
|
27
|
+
requires count of { instrument: contribution, reference: "membership", anchor: self.id, states: [pending], limit: 60 } == self.circle.members
|
|
28
|
+
}
|
|
29
|
+
action receive {
|
|
30
|
+
from: active, to: paid, actor: clock, due: { at: self.receivesAt }
|
|
31
|
+
requires self.circle in [active]
|
|
32
|
+
requires count of { instrument: all(savings.membership), reference: "circle", anchor: self.circle, states: [paid], limit: 60 } == self.preceding
|
|
33
|
+
moves self.total from self.circle.held to self.member
|
|
34
|
+
}
|
|
35
|
+
action withdraw { from: joined, to: withdrawn, requires self.circle in [disbanded] }
|
|
36
|
+
records {
|
|
37
|
+
contribution: {
|
|
38
|
+
fields { membership: ref<parent>, position: integer(1, 60), dueAt: date = at(self.membership.circle.dates, self.position), amount: money = self.membership.contribution }
|
|
39
|
+
lifecycle { states: [pending, paid], initial: pending }
|
|
40
|
+
action create { requires self.position <= self.membership.circle.members; requires self.membership in [joined]; requires unique "seat_period" on [self.membership, self.position] }
|
|
41
|
+
action pay { from: pending, to: paid, actor: clock, due: { at: self.dueAt }, requires self.membership in [active, paid]; requires self.membership.circle in [active], moves self.amount from self.membership.member to self.membership.circle.held }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
package/std/travel.hsx
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
header travel
|
|
2
|
+
|
|
3
|
+
instrument package(price: money, supplier_cost: money, departure: date) {
|
|
4
|
+
constraints { supplier_cost: at_most(price) }
|
|
5
|
+
summary: "Immutable package price, supplier cost and departure."
|
|
6
|
+
fields { price: money = price, supplierCost: money = supplier_cost, departure: date = departure }
|
|
7
|
+
lifecycle { states: [draft, published, closed], initial: draft }
|
|
8
|
+
action create { requires self.supplierCost <= self.price }
|
|
9
|
+
action publish { from: draft, to: published }
|
|
10
|
+
action close { from: published, to: closed, actor: clock, due: { at: self.departure } }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
instrument booking(package: ref<travel.package>, buyer: party, supplier: approval, cover: ref<insurance.cover>, deposit: percent = 30%, balance_before: duration = 14d, confirm_within: duration = 24h, tax: percent = 15%, early_before: duration = 30d, middle_before: duration = 15d, middle_penalty: percent = 10%, late_penalty: percent = 30%) {
|
|
14
|
+
constraints { early_before: greater_than(middle_before) }
|
|
15
|
+
summary: "A deposited booking with three cancellation bands and supplier confirmation."
|
|
16
|
+
fields {
|
|
17
|
+
package: ref<package>, bookingReference: text, buyer: account of buyer, supplier: account of supplier
|
|
18
|
+
price: money = self.package.price, supplierCost: money = self.package.supplierCost
|
|
19
|
+
deposit: money = rate(self.price, deposit), balance: money = subtract(self.price, self.deposit)
|
|
20
|
+
margin: money = subtract(self.price, self.supplierCost)
|
|
21
|
+
tax: money = rate(self.margin, tax), operatorNet: money = subtract(self.margin, self.tax)
|
|
22
|
+
middlePenalty: money = rate(self.supplierCost, middle_penalty)
|
|
23
|
+
latePenalty: money = rate(self.supplierCost, late_penalty)
|
|
24
|
+
balanceAt: date = before(self.package.departure, balance_before)
|
|
25
|
+
earlyAt: date = before(self.package.departure, early_before)
|
|
26
|
+
middleAt: date = before(self.package.departure, middle_before)
|
|
27
|
+
lateAt: date = after(self.middleAt, 1ms)
|
|
28
|
+
confirmBy: date = after(self.package.departure, confirm_within)
|
|
29
|
+
held: account of self
|
|
30
|
+
refund: money?, retainedMargin: money?, retainedTax: money?, retainedNet: money?
|
|
31
|
+
}
|
|
32
|
+
lifecycle { states: [created, deposit_paid, paid, completed, cancelled, refunded], initial: created }
|
|
33
|
+
action create {
|
|
34
|
+
requires self.package in [published]
|
|
35
|
+
requires unique "booking_reference" on [self.bookingReference]
|
|
36
|
+
}
|
|
37
|
+
action deposit {
|
|
38
|
+
from: created, to: deposit_paid, actor: { party: buyer }, deadline: { at: self.balanceAt }
|
|
39
|
+
requires count of { instrument: child(cover, slice), reference: "covers", anchor: self.id, states: [pending], limit: 366 } >= 1
|
|
40
|
+
moves self.deposit from buyer to self.held
|
|
41
|
+
invoke: [{ selection: { instrument: child(cover, slice), reference: "covers", anchor: self.id, states: [pending], limit: 366 }, action: collect, input: {} }]
|
|
42
|
+
}
|
|
43
|
+
action balance {
|
|
44
|
+
from: deposit_paid, to: paid, actor: { party: buyer }, deadline: { at: self.balanceAt }
|
|
45
|
+
moves self.balance from buyer to self.held
|
|
46
|
+
}
|
|
47
|
+
action confirm {
|
|
48
|
+
from: paid, to: completed, actor: { party: supplier }
|
|
49
|
+
due: { at: self.package.departure }, deadline: { at: self.confirmBy }
|
|
50
|
+
requires approval by supplier
|
|
51
|
+
moves self.supplierCost from self.held to supplier
|
|
52
|
+
moves self.tax from self.held to programTax
|
|
53
|
+
moves self.operatorNet from self.held to programOperator
|
|
54
|
+
}
|
|
55
|
+
action timeout {
|
|
56
|
+
from: paid, to: refunded, actor: clock, due: { at: self.confirmBy }
|
|
57
|
+
moves self.price from self.held to buyer
|
|
58
|
+
}
|
|
59
|
+
action cancel_early {
|
|
60
|
+
from: [deposit_paid, paid], to: cancelled, deadline: { at: self.earlyAt }
|
|
61
|
+
moves self.held.balance from self.held to buyer
|
|
62
|
+
}
|
|
63
|
+
action cancel_middle {
|
|
64
|
+
from: [deposit_paid, paid], to: cancelled, due: { at: self.earlyAt }, deadline: { at: self.lateAt }
|
|
65
|
+
calculate: [
|
|
66
|
+
{ target: refund, op: "subtract", base: { field: self.held.balance }, subtract: [{ field: self.deposit }] }
|
|
67
|
+
{ target: retainedMargin, op: "subtract", base: { field: self.deposit }, subtract: [{ field: self.middlePenalty }] }
|
|
68
|
+
{ target: retainedTax, op: "rate", base: { field: self.retainedMargin }, bps: { literal: tax }, rounding: "floor" }
|
|
69
|
+
{ target: retainedNet, op: "subtract", base: { field: self.retainedMargin }, subtract: [{ field: self.retainedTax }] }
|
|
70
|
+
]
|
|
71
|
+
moves self.refund from self.held to buyer
|
|
72
|
+
moves self.middlePenalty from self.held to supplier
|
|
73
|
+
moves self.retainedTax from self.held to programTax
|
|
74
|
+
moves self.retainedNet from self.held to programOperator
|
|
75
|
+
}
|
|
76
|
+
action cancel_late {
|
|
77
|
+
from: [deposit_paid, paid], to: cancelled, actor: caller, due: { at: self.lateAt }
|
|
78
|
+
calculate: [
|
|
79
|
+
{ target: retainedMargin, op: "subtract", base: { field: self.held.balance }, subtract: [{ field: self.latePenalty }] }
|
|
80
|
+
{ target: retainedTax, op: "rate", base: { field: self.retainedMargin }, bps: { literal: tax }, rounding: "floor" }
|
|
81
|
+
{ target: retainedNet, op: "subtract", base: { field: self.retainedMargin }, subtract: [{ field: self.retainedTax }] }
|
|
82
|
+
]
|
|
83
|
+
moves self.latePenalty from self.held to supplier
|
|
84
|
+
moves self.retainedTax from self.held to programTax
|
|
85
|
+
moves self.retainedNet from self.held to programOperator
|
|
86
|
+
}
|
|
87
|
+
action default_balance {
|
|
88
|
+
from: deposit_paid, to: cancelled, actor: clock, due: { at: self.balanceAt }
|
|
89
|
+
calculate: [
|
|
90
|
+
{ target: retainedMargin, op: "subtract", base: { field: self.held.balance }, subtract: [{ field: self.latePenalty }] }
|
|
91
|
+
{ target: retainedTax, op: "rate", base: { field: self.retainedMargin }, bps: { literal: tax }, rounding: "floor" }
|
|
92
|
+
{ target: retainedNet, op: "subtract", base: { field: self.retainedMargin }, subtract: [{ field: self.retainedTax }] }
|
|
93
|
+
]
|
|
94
|
+
moves self.latePenalty from self.held to supplier
|
|
95
|
+
moves self.retainedTax from self.held to programTax
|
|
96
|
+
moves self.retainedNet from self.held to programOperator
|
|
97
|
+
}
|
|
98
|
+
}
|
package/std/wallet.hsx
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
header wallet
|
|
2
|
+
|
|
3
|
+
instrument balance(holder: party, spend_limit: money = 100000 SAR) {
|
|
4
|
+
summary: "A holder's reusable balance with an immutable per-spend ceiling."
|
|
5
|
+
fields { holder: account of holder, held: account of self, spendLimit: money = spend_limit }
|
|
6
|
+
lifecycle { states: [pending, active, frozen, closed], initial: pending }
|
|
7
|
+
action create { }
|
|
8
|
+
action activate { from: pending, to: active, actor: { party: holder } }
|
|
9
|
+
action topup {
|
|
10
|
+
from: active, to: active, actor: { party: holder }
|
|
11
|
+
input { amount: money }
|
|
12
|
+
moves input.amount from holder to self.held
|
|
13
|
+
}
|
|
14
|
+
action withdraw {
|
|
15
|
+
from: active, to: active, actor: { party: holder }
|
|
16
|
+
input { amount: money }
|
|
17
|
+
moves input.amount from self.held to holder
|
|
18
|
+
}
|
|
19
|
+
action freeze { from: active, to: frozen, }
|
|
20
|
+
action unfreeze { from: frozen, to: active, }
|
|
21
|
+
action close { from: [pending, active], to: closed, requires self.held.balance == "0" }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
instrument spend(wallet: ref<wallet.balance>, payee: party) {
|
|
25
|
+
summary: "Reserve one wallet spend, then post or void the same reservation."
|
|
26
|
+
fields { wallet: ref<wallet>, amount: money, expiresAt: date, reservation: text? }
|
|
27
|
+
lifecycle { states: [pending, reserved, paid, cancelled, expired], initial: pending }
|
|
28
|
+
action create {
|
|
29
|
+
requires self.wallet in [active]
|
|
30
|
+
requires self.amount <= self.wallet.spendLimit
|
|
31
|
+
}
|
|
32
|
+
action reserve {
|
|
33
|
+
from: pending, to: reserved
|
|
34
|
+
requires self.wallet in [active]
|
|
35
|
+
deadline: { at: self.expiresAt }
|
|
36
|
+
moves reserve self.amount from self.wallet.held to payee capture "reservation"
|
|
37
|
+
}
|
|
38
|
+
action pay { from: reserved, to: paid, moves post self.reservation }
|
|
39
|
+
action cancel { from: reserved, to: cancelled, moves void self.reservation }
|
|
40
|
+
action expire { from: reserved, to: expired, actor: clock, due: { at: self.expiresAt }, moves void self.reservation }
|
|
41
|
+
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export interface HsxDiagnosticCatalogEntry {
|
|
2
|
-
readonly code: `HSX${number}`;
|
|
3
|
-
readonly stage: "bind" | "lower" | "parse" | "typecheck";
|
|
4
|
-
readonly title: string;
|
|
5
|
-
readonly fix: string;
|
|
6
|
-
readonly example: string | null;
|
|
7
|
-
readonly reason?: string;
|
|
8
|
-
}
|
|
9
|
-
/** Stable public descriptions for every diagnostic code emitted by HSX. */
|
|
10
|
-
export declare const hsxDiagnostics: readonly HsxDiagnosticCatalogEntry[];
|
|
11
|
-
/** Preserve the UDL diagnostic family when reporting a typed clause. */
|
|
12
|
-
export declare function hsxClauseDiagnosticCode(code: string): string;
|
|
13
|
-
//# sourceMappingURL=diagnostics.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../src/diagnostics.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,MAAM,EAAE,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,WAAW,CAAC;IACzD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAiBD,2EAA2E;AAC3E,eAAO,MAAM,cAAc,EAAE,SAAS,yBAAyB,EAqkBrD,CAAC;AAEX,wEAAwE;AACxE,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAU5D"}
|