@hyperscale0/hsx 1.0.130 → 1.0.134
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/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +77 -13
- package/dist/src/compile.js.map +1 -1
- package/dist/src/examples-bundle.js +4 -4
- package/dist/src/examples-bundle.js.map +1 -1
- package/dist/src/std-bundle.js +9 -9
- package/dist/src/std-bundle.js.map +1 -1
- package/docs/README.md +48 -22
- package/examples/cards.hsx +3 -1
- package/examples/marketplace.hsx +3 -1
- package/examples/rental-deposit.hsx +2 -0
- package/examples/wallet.hsx +1 -1
- package/package.json +4 -4
- package/src/compile.ts +100 -13
- package/src/examples-bundle.ts +4 -4
- package/src/std-bundle.ts +9 -9
- package/std/cards.hsx +16 -9
- package/std/escrow.hsx +32 -6
- package/std/financing.hsx +155 -68
- package/std/insurance.hsx +3 -3
- package/std/lending.hsx +16 -8
- package/std/marketplace.hsx +4 -3
- package/std/money.hsx +28 -14
- package/std/savings.hsx +3 -2
- package/std/wallet.hsx +10 -5
package/std/lending.hsx
CHANGED
|
@@ -5,12 +5,17 @@ header lending
|
|
|
5
5
|
// principal/profit distributions and unbound source owners remain unclassified.
|
|
6
6
|
|
|
7
7
|
// Round target is plan.principal from runtime subject.price, not its credit limit.
|
|
8
|
-
//
|
|
8
|
+
// The operator opens the round and closes it before closesAt. Expose
|
|
9
|
+
// round.create and close plus commitment.create and withdraw. A commitment
|
|
9
10
|
// debits the bound investor's original active wallet before closesAt. Investor cap
|
|
10
|
-
// aggregates by investor across wallets.
|
|
11
|
+
// aggregates by investor across wallets. A round still open at closesAt fails,
|
|
12
|
+
// even when fully committed, and refunds those original wallets.
|
|
11
13
|
// Closing needs the exact target; it funds plan.capital and does not disburse the
|
|
12
14
|
// loan. Expose plan.disburse separately after signing and limit approval.
|
|
13
|
-
// Distribution.create needs a recorded
|
|
15
|
+
// Distribution.create needs a recorded settlement matching the plan. Only the
|
|
16
|
+
// payout consumes it: distribute_cash or distribute_loss claims the settlement
|
|
17
|
+
// and marks it distributed, so the plan's unwind_paid keeps that profit, while an
|
|
18
|
+
// abandoned distribution leaves the settlement refundable.
|
|
14
19
|
// Prepare cash or loss first, then create shares against funded commitments of
|
|
15
20
|
// that round. Distribution needs pending share weights equal to the round target.
|
|
16
21
|
// Cash fee is a percentage of retained profit, VAT a percentage of that fee.
|
|
@@ -30,7 +35,7 @@ instrument round(borrower: party, plan: ref<financing.installments> = object(fin
|
|
|
30
35
|
held: account of self
|
|
31
36
|
}
|
|
32
37
|
lifecycle { states: [open, funded, failed], initial: open }
|
|
33
|
-
action create { requires unique "funding_plan" on [self.plan]; requires self.borrower == self.plan.borrower }
|
|
38
|
+
action create { actor: { party: operator }, requires unique "funding_plan" on [self.plan]; requires self.borrower == self.plan.borrower }
|
|
34
39
|
action close {
|
|
35
40
|
from: open, to: funded, actor: { party: operator }
|
|
36
41
|
requires sum "amount" of { instrument: commitments, reference: "round", anchor: self.id, states: [committed], limit: 366 } == self.price
|
|
@@ -40,7 +45,6 @@ instrument round(borrower: party, plan: ref<financing.installments> = object(fin
|
|
|
40
45
|
}
|
|
41
46
|
action fail {
|
|
42
47
|
from: open, to: failed, actor: clock, due: { at: self.closesAt }
|
|
43
|
-
requires self.held.balance < self.price
|
|
44
48
|
invoke: [{ selection: { instrument: commitments, reference: "round", anchor: self.id, states: [committed], limit: 366 }, action: refund, input: {} }]
|
|
45
49
|
}
|
|
46
50
|
}
|
|
@@ -81,7 +85,9 @@ instrument distribution(round: ref<lending.round>, receipt: ref, commitments: re
|
|
|
81
85
|
held: account of self, losses: account(self, claim), residualLoss: account(residual_to, claim, "loss")
|
|
82
86
|
}
|
|
83
87
|
lifecycle { states: [pending, cash_ready, loss_ready, distributed], initial: pending }
|
|
84
|
-
action create {
|
|
88
|
+
action create {
|
|
89
|
+
requires self.round in [funded]; requires self.source in [recorded]; requires self.source.plan == self.round.plan
|
|
90
|
+
}
|
|
85
91
|
action prepare_cash {
|
|
86
92
|
from: pending, to: cash_ready
|
|
87
93
|
requires self.mode == "cash"; requires self.source.kind == "cash"; requires self.source in [recorded]
|
|
@@ -101,9 +107,10 @@ instrument distribution(round: ref<lending.round>, receipt: ref, commitments: re
|
|
|
101
107
|
}
|
|
102
108
|
action distribute_cash { allowZero: true,
|
|
103
109
|
from: cash_ready, to: distributed, actor: { party: operator }
|
|
110
|
+
requires self.source in [recorded]; requires unique "settlement_consumption" on [self.source.plan, self.source.identity]
|
|
104
111
|
requires sum "weight" of { instrument: share, reference: "distribution", anchor: self.id, states: [pending], limit: 366 } == self.round.price
|
|
105
112
|
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 economics { purpose: internal, sourceParty: programOperator }; moves self.vat from self.held to programTax economics { purpose: pass_through, sourceParty: programOperator }
|
|
106
|
-
invoke: [{ selection: { instrument: share, reference: "distribution", anchor: self.id, states: [pending], limit: 366 }, action: cash, input: {} }, { reference: self.id, action: residual_cash, input: {} }]
|
|
113
|
+
invoke: [{ reference: self.source, action: distribute, input: {} }, { selection: { instrument: share, reference: "distribution", anchor: self.id, states: [pending], limit: 366 }, action: cash, input: {} }, { reference: self.id, action: residual_cash, input: {} }]
|
|
107
114
|
}
|
|
108
115
|
action residual_cash { allowZero: true,
|
|
109
116
|
from: distributed, to: distributed, actor: { parent: current() }
|
|
@@ -111,9 +118,10 @@ instrument distribution(round: ref<lending.round>, receipt: ref, commitments: re
|
|
|
111
118
|
}
|
|
112
119
|
action distribute_loss { allowZero: true,
|
|
113
120
|
from: loss_ready, to: distributed, actor: { party: operator }
|
|
121
|
+
requires self.source in [recorded]; requires unique "settlement_consumption" on [self.source.plan, self.source.identity]
|
|
114
122
|
requires sum "weight" of { instrument: share, reference: "distribution", anchor: self.id, states: [pending], limit: 366 } == self.round.price
|
|
115
123
|
moves self.net from self.source.lossAccount to self.losses
|
|
116
|
-
invoke: [{ selection: { instrument: share, reference: "distribution", anchor: self.id, states: [pending], limit: 366 }, action: loss, input: {} }, { reference: self.id, action: residual_loss, input: {} }]
|
|
124
|
+
invoke: [{ reference: self.source, action: distribute, input: {} }, { selection: { instrument: share, reference: "distribution", anchor: self.id, states: [pending], limit: 366 }, action: loss, input: {} }, { reference: self.id, action: residual_loss, input: {} }]
|
|
117
125
|
}
|
|
118
126
|
action residual_loss { allowZero: true,
|
|
119
127
|
from: distributed, to: distributed, actor: { parent: current() }
|
package/std/marketplace.hsx
CHANGED
|
@@ -4,7 +4,8 @@ header marketplace
|
|
|
4
4
|
// earning for the company or passes the buyer's cash through to a bound outside recipient.
|
|
5
5
|
|
|
6
6
|
// Listing, order and reservation are separate agreements. Expose their create
|
|
7
|
-
// and decision actions individually. Order fulfillment does not move escrow cash
|
|
7
|
+
// and decision actions individually. Order fulfillment does not move escrow cash;
|
|
8
|
+
// only the fulfiller marks it, the Product operator unless the seller is bound.
|
|
8
9
|
// Pair an order with escrow.hold and expose its fund, deliver, accept, dispute,
|
|
9
10
|
// verify_return and refund aliases. Refund needs disputed -> return_verified,
|
|
10
11
|
// with payee evidence, then returns the full price to payer; accept pays payee.
|
|
@@ -34,7 +35,7 @@ instrument listing(seller: party) {
|
|
|
34
35
|
action sell { from: active, to: sold, actor: { party: seller }, requires count of { instrument: all(marketplace.reservation), reference: "listing", anchor: self.id, states: [active], limit: 366 } == 0 }
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
instrument order(listing: ref<marketplace.listing>, buyer: party) {
|
|
38
|
+
instrument order(listing: ref<marketplace.listing>, buyer: party, fulfiller: party = programOperator) {
|
|
38
39
|
revisioned: true
|
|
39
40
|
summary: "A buyer's order for one listing at its declared price."
|
|
40
41
|
fields { listing: ref<listing>, buyer: account of buyer, price: money, reservations: integer?, ownReservations: integer?, blockingQuotes: integer?, ownBlockingQuotes: integer? }
|
|
@@ -60,7 +61,7 @@ instrument order(listing: ref<marketplace.listing>, buyer: party) {
|
|
|
60
61
|
requires count of { instrument: current(), reference: "listing", anchor: self.listing, states: [placed, fulfilled], limit: 366 } <= 1
|
|
61
62
|
}
|
|
62
63
|
invariants count of { instrument: current(), reference: "listing", anchor: self.listing, states: [placed, fulfilled], limit: 366 } <= 1
|
|
63
|
-
action fulfill { from: placed, to: fulfilled }
|
|
64
|
+
action fulfill { from: placed, to: fulfilled, actor: { party: fulfiller } }
|
|
64
65
|
action cancel { from: [quoted, placed], to: cancelled, actor: { party: buyer } }
|
|
65
66
|
}
|
|
66
67
|
|
package/std/money.hsx
CHANGED
|
@@ -7,15 +7,20 @@ header money
|
|
|
7
7
|
// Expose each attachment's create and caller actions explicitly. A transfer or
|
|
8
8
|
// split pays once; a hold exposes fund, release and refund separately. Release
|
|
9
9
|
// pays payee and refund returns payer. A hold has no dispute or damage decision.
|
|
10
|
-
//
|
|
11
|
-
//
|
|
10
|
+
// The payee proposes a schedule, which creates its count occurrences, and only
|
|
11
|
+
// the payer activates or cancels it. Each due occurrence pays only while the
|
|
12
|
+
// schedule is active. A due time promises no retry.
|
|
12
13
|
// A pool accepts whole contributions only up to its remaining target and at most
|
|
13
14
|
// 366 paid records. Expose pay to release the exact target. Clock failure refunds
|
|
14
15
|
// original contributors; excess acceptance and partial contribution are absent.
|
|
15
16
|
// A swap reserves both legs before exchange or voids reservations on expiry.
|
|
16
17
|
// Payout instruct reserves across the selected adapter; confirm/reject require
|
|
17
18
|
// matching terminal instruction evidence. An acknowledgement cannot settle it.
|
|
18
|
-
// Metered bill charges a unique usage identity at the fixed unit price
|
|
19
|
+
// Metered bill charges a unique usage identity at the fixed unit price, only
|
|
20
|
+
// under an active mandate the payer created: bills stop at its expiresAt, and at
|
|
21
|
+
// most 366 billed events across every metered attachment on the mandate may sum
|
|
22
|
+
// their payer debit, buyer fee and tax included, to its limit. The payer revokes
|
|
23
|
+
// it to stop billing. Only the payee cancels its own unbilled usage.
|
|
19
24
|
|
|
20
25
|
// Cost: transfer.pay declares one transfer. Hold.create declares one owned account;
|
|
21
26
|
// fund, release and refund each declare one transfer. Schedules and pools add bounded
|
|
@@ -70,16 +75,16 @@ instrument schedule(payer: party, payee: party, amount: money = runtime, count:
|
|
|
70
75
|
summary: "A finite schedule whose dated pieces sum exactly to the declared total."
|
|
71
76
|
fields { amount: money = amount, count: integer = count, dates: list(date, count) }
|
|
72
77
|
lifecycle { states: [draft, active, complete, cancelled], initial: draft }
|
|
73
|
-
action create {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
requires count of { instrument: occurrence, reference: "schedule", anchor: self.id, states: [pending], limit: 366 } == self.count
|
|
78
|
+
action create {
|
|
79
|
+
actor: { party: payee }
|
|
80
|
+
invoke: [{ range: { count: { literal: count }, maximum: count, bind: position }, instrument: occurrence, action: create, input: { schedule: { field: self.id }, position: { field: position } } }]
|
|
77
81
|
}
|
|
82
|
+
action activate { from: draft, to: active, actor: { party: payer } }
|
|
78
83
|
action complete {
|
|
79
84
|
from: active, to: complete
|
|
80
85
|
requires count of { instrument: occurrence, reference: "schedule", anchor: self.id, states: [pending], limit: 366 } == 0
|
|
81
86
|
}
|
|
82
|
-
action cancel { from: [draft, active], to: cancelled }
|
|
87
|
+
action cancel { from: [draft, active], to: cancelled, actor: { party: payer } }
|
|
83
88
|
records {
|
|
84
89
|
occurrence: {
|
|
85
90
|
fields {
|
|
@@ -92,7 +97,7 @@ instrument schedule(payer: party, payee: party, amount: money = runtime, count:
|
|
|
92
97
|
amount: money = sum(self.base, self.adjustment)
|
|
93
98
|
}
|
|
94
99
|
lifecycle { states: [pending, paid, cancelled], initial: pending }
|
|
95
|
-
action create {
|
|
100
|
+
action create { actor: { parent: parent }, input { schedule: ref<parent>, position: integer(1, count) }, requires unique "scheduled_position" on [self.schedule, self.position] }
|
|
96
101
|
action pay { allowZero: true, from: pending, to: paid, actor: clock, due: { at: self.dueAt }, requires self.schedule in [active], moves self.amount from payer to payee }
|
|
97
102
|
action cancel { from: pending, to: cancelled, requires self.schedule in [cancelled] }
|
|
98
103
|
}
|
|
@@ -163,11 +168,20 @@ instrument payout(payer: party, payee: party, adapter: text, max_age: duration,
|
|
|
163
168
|
}
|
|
164
169
|
}
|
|
165
170
|
|
|
166
|
-
instrument
|
|
171
|
+
instrument mandate(payer: party, payee: party) {
|
|
172
|
+
summary: "A payer's permission for one payee to charge up to a total until it expires."
|
|
173
|
+
fields { payer: account of payer, payee: account of payee, limit: money, expiresAt: date }
|
|
174
|
+
lifecycle { states: [active, revoked], initial: active }
|
|
175
|
+
action create { actor: { party: payer }, requires self.limit > "0" }
|
|
176
|
+
action revoke { from: active, to: revoked, actor: { party: payer } }
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
instrument metered(payer: party, payee: party, unit_price: money, units: integer = 1, fee: fee = { seller: 0% }, mandate: ref<money.mandate> = object(money.mandate)) {
|
|
167
180
|
summary: "A unique usage event priced once at the fixed unit price."
|
|
168
|
-
fields { usageIdentity: text, unitPrice: money = unit_price, units: integer = units, amount: money = multiply(self.unitPrice, self.units) }
|
|
181
|
+
fields { usageIdentity: text, mandate: ref<mandate>, payer: account of payer, payee: account of payee, unitPrice: money = unit_price, units: integer = units, amount: money = multiply(self.unitPrice, self.units) }
|
|
169
182
|
lifecycle { states: [recorded, billed, cancelled], initial: recorded }
|
|
170
|
-
|
|
171
|
-
action
|
|
172
|
-
action
|
|
183
|
+
invariants sum "bill_move1_debit" of { instrument: all(money.metered), reference: "mandate", anchor: self.mandate, states: [billed], limit: 366 } <= self.mandate.limit
|
|
184
|
+
action create { requires unique "usage_event" on [self.usageIdentity]; requires self.mandate.payer == self.payer; requires self.mandate.payee == self.payee }
|
|
185
|
+
action bill { allowZero: true, requires self.amount > "0"; from: recorded, to: billed, actor: { party: payee }, deadline: { at: self.mandate.expiresAt }, requires self.mandate in [active], moves self.amount from payer to payee fee fee }
|
|
186
|
+
action cancel { from: recorded, to: cancelled, actor: { party: payee } }
|
|
173
187
|
}
|
package/std/savings.hsx
CHANGED
|
@@ -17,6 +17,7 @@ header savings
|
|
|
17
17
|
// Late and out-of-order collection is admitted while membership is active or
|
|
18
18
|
// received and circle is active. Clock admission does not promise scheduler retry.
|
|
19
19
|
// Circle.close needs every membership received and an empty held account.
|
|
20
|
+
// Only the Product operator disbands a forming circle.
|
|
20
21
|
// Circle creation collects circleDates, one date per member for contributions and payouts.
|
|
21
22
|
|
|
22
23
|
// Cost: circle.create declares one owned account. Each contribution payment
|
|
@@ -37,14 +38,14 @@ instrument circle(contribution: money, members: integer(1, 60), starts: date, me
|
|
|
37
38
|
requires count of { instrument: memberships, reference: "circle", anchor: self.id, states: [received], limit: 60 } == self.members
|
|
38
39
|
requires self.held.balance == "0"
|
|
39
40
|
}
|
|
40
|
-
action disband { from: forming, to: disbanded }
|
|
41
|
+
action disband { from: forming, to: disbanded, actor: { party: programOperator } }
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
instrument membership(circle: ref<savings.circle>, member: party) {
|
|
44
45
|
summary: "A member contributes on the circle calendar and receives the pot in the assigned seat."
|
|
45
46
|
fields { circle: ref<circle>, member: account of member, seat: integer(1, 60), preceding: integer = subtract(self.seat, 1), receivesAt: date = at(self.circle.circleDates, self.seat), contribution: money = self.circle.contribution, total: money = self.circle.pot }
|
|
46
47
|
lifecycle { states: [joined, active, received, withdrawn], initial: joined }
|
|
47
|
-
action create { requires self.seat <= self.circle.members; requires self.circle in [forming]; requires unique "circle_seat" on [self.circle, self.seat]; requires unique "circle_member" on [self.circle, self.member] }
|
|
48
|
+
action create { actor: { party: member }, requires self.seat <= self.circle.members; requires self.circle in [forming]; requires unique "circle_seat" on [self.circle, self.seat]; requires unique "circle_member" on [self.circle, self.member] }
|
|
48
49
|
action activate {
|
|
49
50
|
from: joined, to: active
|
|
50
51
|
requires self.circle in [active]
|
package/std/wallet.hsx
CHANGED
|
@@ -7,7 +7,9 @@ header wallet
|
|
|
7
7
|
// Expose balance.create, activate, topup and withdraw as needed. Holder moves
|
|
8
8
|
// cash only while active. Freeze blocks new topups, withdrawals and spend reserves;
|
|
9
9
|
// close requires no held cash. Only the Product operator freezes or unfreezes.
|
|
10
|
-
//
|
|
10
|
+
// The holder closes the wallet, or the Product operator with operator_close.
|
|
11
|
+
// Only the wallet holder creates a spend record, which checks its amount against
|
|
12
|
+
// spendLimit; bind the spend holder to the balance holder.
|
|
11
13
|
// Expose reserve, pay and cancel for the bound payee. Reserve requires an active
|
|
12
14
|
// wallet before expiresAt; pay posts the whole reservation, cancel/expiry void it.
|
|
13
15
|
// Existing reservations may still post after the wallet freezes. There is no
|
|
@@ -25,7 +27,7 @@ instrument balance(holder: party, spend_limit: money = 100000 SAR, topup_fee: pe
|
|
|
25
27
|
summary: "A holder's reusable balance with an immutable per-spend ceiling."
|
|
26
28
|
fields { holder: account of holder, held: account of self, spendLimit: money = spend_limit, topupFee: money?, topupCredit: money? }
|
|
27
29
|
lifecycle { states: [pending, active, frozen, closed], initial: pending }
|
|
28
|
-
action create { }
|
|
30
|
+
action create { actor: { party: holder } }
|
|
29
31
|
action activate { from: pending, to: active, actor: { party: holder } }
|
|
30
32
|
action topup {
|
|
31
33
|
allowZero: true, requires input.amount > "0"
|
|
@@ -45,15 +47,18 @@ instrument balance(holder: party, spend_limit: money = 100000 SAR, topup_fee: pe
|
|
|
45
47
|
}
|
|
46
48
|
action freeze { from: active, to: frozen, actor: { party: programOperator } }
|
|
47
49
|
action unfreeze { from: frozen, to: active, actor: { party: programOperator } }
|
|
48
|
-
action close { from: [pending, active], to: closed, requires self.held.balance == "0" }
|
|
50
|
+
action close { from: [pending, active], to: closed, actor: { party: holder }, requires self.held.balance == "0" }
|
|
51
|
+
action operator_close { from: [pending, active], to: closed, actor: { party: programOperator }, requires self.held.balance == "0" }
|
|
49
52
|
}
|
|
50
53
|
|
|
51
|
-
instrument spend(wallet: ref<wallet.balance>, payee: party) {
|
|
54
|
+
instrument spend(wallet: ref<wallet.balance>, payee: party, holder: party = party(person)) {
|
|
52
55
|
summary: "Reserve one wallet spend, then post or void the same reservation."
|
|
53
|
-
fields { wallet: ref<wallet>, amount: money, expiresAt: date, reservation: text? }
|
|
56
|
+
fields { wallet: ref<wallet>, holder: account of holder, amount: money, expiresAt: date, reservation: text? }
|
|
54
57
|
lifecycle { states: [pending, reserved, paid, cancelled, expired], initial: pending }
|
|
55
58
|
action create {
|
|
59
|
+
actor: { party: holder }
|
|
56
60
|
requires self.wallet in [active]
|
|
61
|
+
requires self.wallet.holder == self.holder
|
|
57
62
|
requires self.amount <= self.wallet.spendLimit
|
|
58
63
|
}
|
|
59
64
|
action reserve {
|