@hyperscale0/hsx 1.0.0 → 1.0.124

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.
Files changed (316) hide show
  1. package/CHANGELOG.md +5 -256
  2. package/LICENSE.md +356 -0
  3. package/NOTICE.md +8 -0
  4. package/README.md +44 -44
  5. package/bin/hsx.ts +0 -7
  6. package/dist/bin/hsx.d.ts +0 -4
  7. package/dist/bin/hsx.d.ts.map +1 -1
  8. package/dist/bin/hsx.js +0 -6
  9. package/dist/bin/hsx.js.map +1 -1
  10. package/dist/src/ast.d.ts +113 -308
  11. package/dist/src/ast.d.ts.map +1 -1
  12. package/dist/src/ast.js +7 -71
  13. package/dist/src/ast.js.map +1 -1
  14. package/dist/src/attachment-economics.d.ts +5 -0
  15. package/dist/src/attachment-economics.d.ts.map +1 -0
  16. package/dist/src/attachment-economics.js +55 -0
  17. package/dist/src/attachment-economics.js.map +1 -0
  18. package/dist/src/binding-contract.d.ts +22 -0
  19. package/dist/src/binding-contract.d.ts.map +1 -0
  20. package/dist/src/binding-contract.js +74 -0
  21. package/dist/src/binding-contract.js.map +1 -0
  22. package/dist/src/cli.d.ts +4 -19
  23. package/dist/src/cli.d.ts.map +1 -1
  24. package/dist/src/cli.js +89 -276
  25. package/dist/src/cli.js.map +1 -1
  26. package/dist/src/compile.d.ts +34 -66
  27. package/dist/src/compile.d.ts.map +1 -1
  28. package/dist/src/compile.js +2949 -117
  29. package/dist/src/compile.js.map +1 -1
  30. package/dist/src/cost.d.ts +8 -75
  31. package/dist/src/cost.d.ts.map +1 -1
  32. package/dist/src/cost.js +54 -364
  33. package/dist/src/cost.js.map +1 -1
  34. package/dist/src/diagnostics.d.ts +14 -9
  35. package/dist/src/diagnostics.d.ts.map +1 -1
  36. package/dist/src/diagnostics.js +39 -416
  37. package/dist/src/diagnostics.js.map +1 -1
  38. package/dist/src/examples-bundle.d.ts +3 -0
  39. package/dist/src/examples-bundle.d.ts.map +1 -0
  40. package/dist/src/examples-bundle.js +18 -0
  41. package/dist/src/examples-bundle.js.map +1 -0
  42. package/dist/src/examples.d.ts +10 -0
  43. package/dist/src/examples.d.ts.map +1 -0
  44. package/dist/src/examples.js +2 -0
  45. package/dist/src/examples.js.map +1 -0
  46. package/dist/src/format.d.ts +8 -7
  47. package/dist/src/format.d.ts.map +1 -1
  48. package/dist/src/format.js +9 -175
  49. package/dist/src/format.js.map +1 -1
  50. package/dist/src/header-source.d.ts +3 -0
  51. package/dist/src/header-source.d.ts.map +1 -0
  52. package/dist/src/header-source.js +41 -0
  53. package/dist/src/header-source.js.map +1 -0
  54. package/dist/src/headers.d.ts +64 -0
  55. package/dist/src/headers.d.ts.map +1 -0
  56. package/dist/src/headers.js +167 -0
  57. package/dist/src/headers.js.map +1 -0
  58. package/dist/src/index.d.ts +7 -11
  59. package/dist/src/index.d.ts.map +1 -1
  60. package/dist/src/index.js +4 -9
  61. package/dist/src/index.js.map +1 -1
  62. package/dist/src/keywords.d.ts +56 -0
  63. package/dist/src/keywords.d.ts.map +1 -0
  64. package/dist/src/keywords.js +133 -0
  65. package/dist/src/keywords.js.map +1 -0
  66. package/dist/src/language.d.ts +26 -0
  67. package/dist/src/language.d.ts.map +1 -0
  68. package/dist/src/language.js +440 -0
  69. package/dist/src/language.js.map +1 -0
  70. package/dist/src/lex.d.ts +13 -28
  71. package/dist/src/lex.d.ts.map +1 -1
  72. package/dist/src/lex.js +119 -145
  73. package/dist/src/lex.js.map +1 -1
  74. package/dist/src/parse.d.ts +4 -17
  75. package/dist/src/parse.d.ts.map +1 -1
  76. package/dist/src/parse.js +484 -1036
  77. package/dist/src/parse.js.map +1 -1
  78. package/dist/src/std-bundle.d.ts.map +1 -1
  79. package/dist/src/std-bundle.js +11 -84
  80. package/dist/src/std-bundle.js.map +1 -1
  81. package/dist/src/std-library.d.ts +1 -1
  82. package/dist/src/std-library.d.ts.map +1 -1
  83. package/dist/src/std-library.js +1 -5
  84. package/dist/src/std-library.js.map +1 -1
  85. package/dist/src/tunables.d.ts +7 -0
  86. package/dist/src/tunables.d.ts.map +1 -0
  87. package/dist/src/tunables.js +27 -0
  88. package/dist/src/tunables.js.map +1 -0
  89. package/docs/README.md +528 -26
  90. package/docs/assets/brand/hsx-horizontal-white.svg +1 -0
  91. package/docs/assets/brand/hsx-horizontal.svg +1 -0
  92. package/docs/assets/brand/hsx-stacked-white.svg +1 -0
  93. package/docs/assets/brand/hsx-stacked.svg +1 -0
  94. package/docs/assets/brand/manifest.json +30 -0
  95. package/docs/assets/hsx.svg +11 -0
  96. package/examples/cards.hsx +34 -0
  97. package/examples/collections.hsx +37 -0
  98. package/examples/cost-table.json +633 -385
  99. package/examples/escrow.hsx +24 -0
  100. package/examples/financing.hsx +32 -0
  101. package/examples/insurance.hsx +30 -0
  102. package/examples/lending.hsx +57 -0
  103. package/examples/library.hsx +15 -0
  104. package/examples/marketplace.hsx +32 -0
  105. package/examples/money.hsx +19 -0
  106. package/examples/rental-deposit.hsx +60 -0
  107. package/examples/repair-approval.hsx +44 -0
  108. package/examples/reporting.hsx +28 -0
  109. package/examples/savings.hsx +21 -0
  110. package/examples/serviced.hsx +25 -0
  111. package/examples/wallet.hsx +24 -0
  112. package/package.json +25 -23
  113. package/src/ast.ts +83 -440
  114. package/src/attachment-economics.ts +103 -0
  115. package/src/binding-contract.ts +104 -0
  116. package/src/cli.ts +95 -325
  117. package/src/compile.ts +4063 -209
  118. package/src/cost.ts +71 -574
  119. package/src/diagnostics.ts +57 -437
  120. package/src/examples-bundle.ts +19 -0
  121. package/src/examples.ts +9 -0
  122. package/src/format.ts +12 -256
  123. package/src/header-source.ts +54 -0
  124. package/src/headers.ts +195 -0
  125. package/src/index.ts +18 -33
  126. package/src/keywords.ts +134 -0
  127. package/src/language.ts +629 -0
  128. package/src/lex.ts +131 -192
  129. package/src/parse.ts +489 -1195
  130. package/src/std-bundle.ts +12 -88
  131. package/src/std-library.ts +2 -7
  132. package/src/tunables.ts +36 -0
  133. package/std/cards.hsx +85 -0
  134. package/std/collections.hsx +90 -0
  135. package/std/escrow.hsx +73 -0
  136. package/std/financing.hsx +683 -0
  137. package/std/insurance.hsx +89 -0
  138. package/std/lending.hsx +138 -0
  139. package/std/marketplace.hsx +123 -0
  140. package/std/money.hsx +173 -0
  141. package/std/reporting.hsx +753 -0
  142. package/std/savings.hsx +68 -0
  143. package/std/wallet.hsx +57 -0
  144. package/LICENSE +0 -661
  145. package/LICENSING.md +0 -52
  146. package/TRADEMARKS.md +0 -35
  147. package/dist/src/emit.d.ts +0 -34
  148. package/dist/src/emit.d.ts.map +0 -1
  149. package/dist/src/emit.js +0 -137
  150. package/dist/src/emit.js.map +0 -1
  151. package/dist/src/entry-overrides.d.ts +0 -58
  152. package/dist/src/entry-overrides.d.ts.map +0 -1
  153. package/dist/src/entry-overrides.js +0 -284
  154. package/dist/src/entry-overrides.js.map +0 -1
  155. package/dist/src/ir.d.ts +0 -66
  156. package/dist/src/ir.d.ts.map +0 -1
  157. package/dist/src/ir.js +0 -15
  158. package/dist/src/ir.js.map +0 -1
  159. package/dist/src/limits.d.ts +0 -21
  160. package/dist/src/limits.d.ts.map +0 -1
  161. package/dist/src/limits.js +0 -21
  162. package/dist/src/limits.js.map +0 -1
  163. package/dist/src/lsp/server.d.ts +0 -32
  164. package/dist/src/lsp/server.d.ts.map +0 -1
  165. package/dist/src/lsp/server.js +0 -391
  166. package/dist/src/lsp/server.js.map +0 -1
  167. package/dist/src/modules.d.ts +0 -38
  168. package/dist/src/modules.d.ts.map +0 -1
  169. package/dist/src/modules.js +0 -368
  170. package/dist/src/modules.js.map +0 -1
  171. package/dist/src/typecheck.d.ts +0 -8
  172. package/dist/src/typecheck.d.ts.map +0 -1
  173. package/dist/src/typecheck.js +0 -2958
  174. package/dist/src/typecheck.js.map +0 -1
  175. package/dist/src/version.d.ts +0 -5
  176. package/dist/src/version.d.ts.map +0 -1
  177. package/dist/src/version.js +0 -5
  178. package/dist/src/version.js.map +0 -1
  179. package/docs/guide/01-first-program.md +0 -24
  180. package/docs/guide/02-money.md +0 -25
  181. package/docs/guide/03-instruments.md +0 -22
  182. package/docs/guide/04-lifecycles.md +0 -25
  183. package/docs/guide/05-fees-and-splits.md +0 -45
  184. package/docs/guide/06-schedules.md +0 -22
  185. package/docs/guide/07-composition.md +0 -46
  186. package/docs/guide/08-writing-a-module.md +0 -26
  187. package/docs/guide/09-cost.md +0 -24
  188. package/docs/guide/10-diagnostics.md +0 -27
  189. package/docs/llms-full.txt +0 -1739
  190. package/docs/llms.txt +0 -45
  191. package/docs/playground.md +0 -54
  192. package/docs/reference/cli.md +0 -35
  193. package/docs/reference/diagnostics.md +0 -619
  194. package/docs/reference/grammar.md +0 -39
  195. package/docs/reference/std/advance.md +0 -35
  196. package/docs/reference/std/cancellable_booking.md +0 -35
  197. package/docs/reference/std/captured_payment.md +0 -43
  198. package/docs/reference/std/conditional_disbursement.md +0 -33
  199. package/docs/reference/std/credit_facility.md +0 -35
  200. package/docs/reference/std/held_payment.md +0 -64
  201. package/docs/reference/std/instant_transfer.md +0 -30
  202. package/docs/reference/std/metered.md +0 -27
  203. package/docs/reference/std/pooled_split.md +0 -26
  204. package/docs/reference/std/premium_forward.md +0 -39
  205. package/docs/reference/std/reconciled_payout.md +0 -33
  206. package/docs/reference/std/recurring_collection.md +0 -25
  207. package/docs/reference/std/rotating_pool.md +0 -42
  208. package/docs/reference/std/scheduled.md +0 -51
  209. package/docs/reference/std/security_deposit.md +0 -46
  210. package/docs/reference/std/settlement_batch.md +0 -42
  211. package/docs/reference/std/swap.md +0 -46
  212. package/docs/reference/std/threshold_pool.md +0 -47
  213. package/docs/reference/std/weighted_distribution.md +0 -39
  214. package/docs/reference/types.md +0 -20
  215. package/docs/reference/udl-output.md +0 -13
  216. package/docs/sessions/2026-09-02-two-instruments.hsx +0 -21
  217. package/docs/sessions/2026-09-02-two-instruments.md +0 -41
  218. package/examples/01-first-program/README.md +0 -45
  219. package/examples/01-first-program/tip-jar.hsx +0 -17
  220. package/examples/02-imports-and-archetypes/README.md +0 -96
  221. package/examples/02-imports-and-archetypes/photo-booth.hsx +0 -37
  222. package/examples/03-diagnostics/README.md +0 -68
  223. package/examples/03-diagnostics/corner-shop-fixed.hsx +0 -22
  224. package/examples/03-diagnostics/corner-shop.hsx +0 -19
  225. package/examples/04-complete-product/README.md +0 -84
  226. package/examples/04-complete-product/study-hall.hsx +0 -67
  227. package/examples/05-watch-club/README.md +0 -9
  228. package/examples/05-watch-club/watch-club.hsx +0 -124
  229. package/examples/README.md +0 -18
  230. package/examples/advance/README.md +0 -3
  231. package/examples/advance/advance.hsx +0 -13
  232. package/examples/advance/advance.udl +0 -339
  233. package/examples/cancellable_booking/README.md +0 -3
  234. package/examples/cancellable_booking/cancellable_booking.hsx +0 -14
  235. package/examples/cancellable_booking/cancellable_booking.udl +0 -391
  236. package/examples/captured_payment/README.md +0 -3
  237. package/examples/captured_payment/captured_payment.hsx +0 -21
  238. package/examples/captured_payment/captured_payment.udl +0 -677
  239. package/examples/conditional_disbursement/README.md +0 -3
  240. package/examples/conditional_disbursement/conditional_disbursement.hsx +0 -17
  241. package/examples/conditional_disbursement/conditional_disbursement.udl +0 -412
  242. package/examples/credit_facility/README.md +0 -3
  243. package/examples/credit_facility/credit_facility.hsx +0 -27
  244. package/examples/credit_facility/credit_facility.udl +0 -1373
  245. package/examples/held_payment/README.md +0 -3
  246. package/examples/held_payment/held_payment.hsx +0 -13
  247. package/examples/held_payment/held_payment.udl +0 -507
  248. package/examples/instant_transfer/README.md +0 -3
  249. package/examples/instant_transfer/instant_transfer.hsx +0 -16
  250. package/examples/instant_transfer/instant_transfer.udl +0 -364
  251. package/examples/metered/README.md +0 -3
  252. package/examples/metered/metered.hsx +0 -13
  253. package/examples/metered/metered.udl +0 -247
  254. package/examples/pooled_split/README.md +0 -3
  255. package/examples/pooled_split/pooled_split.hsx +0 -15
  256. package/examples/pooled_split/pooled_split.udl +0 -425
  257. package/examples/premium_forward/README.md +0 -3
  258. package/examples/premium_forward/premium_forward.hsx +0 -19
  259. package/examples/premium_forward/premium_forward.udl +0 -594
  260. package/examples/reconciled_payout/README.md +0 -3
  261. package/examples/reconciled_payout/reconciled_payout.hsx +0 -13
  262. package/examples/reconciled_payout/reconciled_payout.udl +0 -303
  263. package/examples/recurring_collection/README.md +0 -3
  264. package/examples/recurring_collection/recurring_collection.hsx +0 -21
  265. package/examples/recurring_collection/recurring_collection.udl +0 -1130
  266. package/examples/rotating_pool/README.md +0 -3
  267. package/examples/rotating_pool/rotating_pool.hsx +0 -18
  268. package/examples/rotating_pool/rotating_pool.udl +0 -4352
  269. package/examples/scheduled/README.md +0 -3
  270. package/examples/scheduled/scheduled.hsx +0 -12
  271. package/examples/scheduled/scheduled.udl +0 -335
  272. package/examples/security_deposit/README.md +0 -3
  273. package/examples/security_deposit/security_deposit.hsx +0 -21
  274. package/examples/security_deposit/security_deposit.udl +0 -293
  275. package/examples/settlement_batch/README.md +0 -3
  276. package/examples/settlement_batch/settlement_batch.hsx +0 -19
  277. package/examples/settlement_batch/settlement_batch.udl +0 -753
  278. package/examples/swap/README.md +0 -3
  279. package/examples/swap/swap.hsx +0 -19
  280. package/examples/swap/swap.udl +0 -876
  281. package/examples/threshold_pool/README.md +0 -3
  282. package/examples/threshold_pool/threshold_pool.hsx +0 -16
  283. package/examples/threshold_pool/threshold_pool.udl +0 -667
  284. package/examples/weighted_distribution/README.md +0 -3
  285. package/examples/weighted_distribution/weighted_distribution.hsx +0 -20
  286. package/examples/weighted_distribution/weighted_distribution.udl +0 -337
  287. package/skills/hsx/SKILL.md +0 -483
  288. package/src/emit.ts +0 -201
  289. package/src/entry-overrides.ts +0 -428
  290. package/src/ir.ts +0 -99
  291. package/src/limits.ts +0 -20
  292. package/src/lsp/server.ts +0 -460
  293. package/src/modules.ts +0 -446
  294. package/src/typecheck.ts +0 -3876
  295. package/src/version.ts +0 -5
  296. package/std/SEMANTICS.md +0 -130
  297. package/std/settlements/advance.hsx +0 -177
  298. package/std/settlements/cancellable_booking.hsx +0 -87
  299. package/std/settlements/captured_payment.hsx +0 -144
  300. package/std/settlements/conditional_disbursement.hsx +0 -185
  301. package/std/settlements/credit_facility.hsx +0 -167
  302. package/std/settlements/held_payment.hsx +0 -578
  303. package/std/settlements/index.hsx +0 -3
  304. package/std/settlements/instant_transfer.hsx +0 -190
  305. package/std/settlements/metered.hsx +0 -67
  306. package/std/settlements/pooled_split.hsx +0 -68
  307. package/std/settlements/premium_forward.hsx +0 -257
  308. package/std/settlements/reconciled_payout.hsx +0 -77
  309. package/std/settlements/recurring_collection.hsx +0 -27
  310. package/std/settlements/rotating_pool.hsx +0 -252
  311. package/std/settlements/scheduled.hsx +0 -641
  312. package/std/settlements/security_deposit.hsx +0 -166
  313. package/std/settlements/settlement_batch.hsx +0 -122
  314. package/std/settlements/swap.hsx +0 -1114
  315. package/std/settlements/threshold_pool.hsx +0 -221
  316. package/std/settlements/weighted_distribution.hsx +0 -141
@@ -0,0 +1,89 @@
1
+ header insurance
2
+
3
+ // Economics: Company commission is earning; refunds reverse its captured transfer.
4
+ // Outside broker commissions are pass-through. Adapter-owned premium and claim
5
+ // accounts lack an authored source party, so their moves remain unclassified.
6
+
7
+ // Expose cover.create, activate and cancel separately. Activation binds adapter;
8
+ // each slice needs a separately created unique period and an active cover to collect.
9
+ // Collection debits holder for insurerPremium to the insurer and commission to
10
+ // broker as two moves. Broker defaults to programOperator; bind a declared business
11
+ // for a distinct agency. The two amounts sum to premium. Zero portions are allowed.
12
+ // Cover.cancel invokes refund only on paid slices, before each startsAt. It returns
13
+ // insurerPremium from insurer and commission from broker to holder. Active or
14
+ // expired periods are excluded; insufficient recipient funds refuse the reversal.
15
+ // Slice refund is parent-owned, not a public alias. Expose cover.cancel instead.
16
+ // Claims require active cover. Expose claim.create, approve, deny and pay as needed.
17
+ // Inspector approves OR denies from submitted. After approval only pay or clock
18
+ // expiry can consume the reservation; deny is never available after approval.
19
+ // Inspector is a bound party, not necessarily staff. These are ledger transfers;
20
+ // outside settlement needs the boundary protocol, not a claim of provider success.
21
+
22
+ // Cost: each slice collection or refund declares two transfers. Claim approval
23
+ // reserves once; pay posts once and expiry voids once. Cover cancellation adds
24
+ // bounded slice invocations. Run hsx cost for composed counts, not provider prices.
25
+
26
+ instrument cover(holder: party, adapter: text, commission: percent = 0%, broker: party = programOperator, covers: ref, premium: money = runtime) {
27
+ summary: "Recurring cover bound to an outside insurer across an ADL boundary."
28
+ fields { covers: ref<covers>, holder: account of holder, insurer: account(adapter(adapter), cash, "premium"), expiresAt: date }
29
+ lifecycle { states: [pending, active, cancelled, expired], initial: pending }
30
+ action create {}
31
+ action activate {
32
+ from: pending, to: active, actor: { party: programOperator }
33
+ subject { adapter: adapter }
34
+ }
35
+ action cancel {
36
+ from: active, to: cancelled, actor: { party: holder }
37
+ invoke: [{ selection: { instrument: slice, reference: "cover", anchor: self.id, states: [paid], limit: 366 }, action: refund, input: {} }]
38
+ }
39
+ action expire { from: active, to: expired, actor: clock, due: { at: self.expiresAt } }
40
+ records {
41
+ slice: {
42
+ summary: "A whole premium period, collected once and refundable only before it starts."
43
+ fields {
44
+ cover: ref<parent>, covers: ref<covers>, premium: money = premium, startsAt: date, endsAt: date
45
+ commission: money = rate(self.premium, commission)
46
+ insurerPremium: money = subtract(self.premium, self.commission), commissionReceipt: text?
47
+ }
48
+ lifecycle { states: [pending, paid, active, expired, refunded], initial: pending }
49
+ action create {
50
+ requires self.covers == self.cover.covers
51
+ requires self.startsAt < self.endsAt
52
+ requires unique "premium_period" on [self.cover, self.startsAt, self.endsAt]
53
+ }
54
+ action collect {
55
+ allowZero: true
56
+ requires self.premium > "0"
57
+ from: pending, to: paid, actor: { party: holder }
58
+ requires self.cover in [active]
59
+ moves self.insurerPremium from holder to self.cover.insurer
60
+ moves self.commission from holder to broker capture "commissionReceipt" economics { recipient: broker, company: { purpose: earning, sourceParty: holder }, participant: { purpose: pass_through, sourceParty: holder } }
61
+ }
62
+ action start { from: paid, to: active, actor: clock, due: { at: self.startsAt } }
63
+ action expire { from: active, to: expired, actor: clock, due: { at: self.endsAt } }
64
+ action refund {
65
+ allowZero: true
66
+ requires self.premium > "0"
67
+ from: paid, to: refunded, actor: { parent: parent }, deadline: { at: self.startsAt }
68
+ moves self.insurerPremium from self.cover.insurer to holder
69
+ moves self.commission from broker to holder economics { recipient: broker, company: { purpose: earning, sourceParty: broker, reversalOf: self.commissionReceipt }, participant: { purpose: pass_through, sourceParty: broker } }
70
+ }
71
+ }
72
+ }
73
+ }
74
+
75
+ instrument claim(cover: ref<insurance.cover>, inspector: party) {
76
+ summary: "A claim decision reserves one claim payment before settlement."
77
+ fields { cover: ref<cover>, amount: money, expiresAt: date, evidence: text, reservation: text? }
78
+ lifecycle { states: [submitted, approved, paid, denied, expired], initial: submitted }
79
+ action create { requires self.cover in [active] }
80
+ action approve {
81
+ from: submitted, to: approved
82
+ actor: { party: inspector }
83
+ deadline: { at: self.expiresAt }
84
+ moves reserve self.amount from self.cover.insurer to self.cover.holder capture "reservation"
85
+ }
86
+ action pay { from: approved, to: paid, actor: { party: inspector }, deadline: { at: self.expiresAt }, moves post self.reservation }
87
+ action deny { from: submitted, to: denied, actor: { party: inspector } }
88
+ action expire { from: approved, to: expired, actor: clock, due: { at: self.expiresAt }, moves void self.reservation }
89
+ }
@@ -0,0 +1,138 @@
1
+ header lending
2
+
3
+ // Economics: Funding and commitment returns are principal. Distribution tax is
4
+ // pass-through; own-account fee and claim allocations are internal. Mixed
5
+ // principal/profit distributions and unbound source owners remain unclassified.
6
+
7
+ // Round target is plan.principal from runtime subject.price, not its credit limit.
8
+ // Expose round.create and close plus commitment.create and withdraw. A commitment
9
+ // debits the bound investor's original active wallet before closesAt. Investor cap
10
+ // aggregates by investor across wallets. Failure refunds those original wallets.
11
+ // Closing needs the exact target; it funds plan.capital and does not disburse the
12
+ // loan. Expose plan.disburse separately after signing and limit approval.
13
+ // Distribution.create needs a recorded, unconsumed settlement matching the plan.
14
+ // Prepare cash or loss first, then create shares against funded commitments of
15
+ // that round. Distribution needs pending share weights equal to the round target.
16
+ // Cash fee is a percentage of retained profit, VAT a percentage of that fee.
17
+ // Principal plus retained profit less fee and VAT goes to weighted wallets;
18
+ // floored residual goes to residual_to. Loss mode moves claim balances, not cash.
19
+ // Child share execution is parent-owned. No report or clock proves outside payout.
20
+
21
+ // Cost: round.create declares one owned account. Close transfers its balance
22
+ // and invokes commitments; failure invokes their refunds. Distribution adds moves
23
+ // and bounded share invocations. Run hsx cost for counts after policy selection.
24
+
25
+ instrument round(borrower: party, plan: ref<financing.installments> = object(financing.installments), operator: party = programOperator, minimum_ticket: money = 100 SAR, investor_cap: percent = 20%, commitments: ref<lending.commitment> = object(lending.commitment)) {
26
+ summary: "A threshold funding round; failed rounds return each original wallet contribution."
27
+ fields {
28
+ borrower: account of borrower, plan: ref<plan>, price: money = self.plan.principal, closesAt: date
29
+ minimumTicket: money = minimum_ticket, maximumTicket: money = rate(self.price, investor_cap)
30
+ held: account of self
31
+ }
32
+ lifecycle { states: [open, funded, failed], initial: open }
33
+ action create { requires unique "funding_plan" on [self.plan]; requires self.borrower == self.plan.borrower }
34
+ action close {
35
+ from: open, to: funded, actor: { party: operator }
36
+ requires sum "amount" of { instrument: commitments, reference: "round", anchor: self.id, states: [committed], limit: 366 } == self.price
37
+ requires self.held.balance == self.price
38
+ moves self.price from self.held to self.plan.capital economics { purpose: principal, sourceParty: programOperator }
39
+ invoke: [{ selection: { instrument: commitments, reference: "round", anchor: self.id, states: [committed], limit: 366 }, action: fund, input: {} }]
40
+ }
41
+ action fail {
42
+ from: open, to: failed, actor: clock, due: { at: self.closesAt }
43
+ requires self.held.balance < self.price
44
+ invoke: [{ selection: { instrument: commitments, reference: "round", anchor: self.id, states: [committed], limit: 366 }, action: refund, input: {} }]
45
+ }
46
+ }
47
+
48
+ instrument commitment(round: ref<lending.round>, wallet: ref<wallet.balance>, investor: party = party(person)) {
49
+ summary: "One investor's original-wallet contribution, capped cumulatively within the round."
50
+ fields { round: ref<round>, wallet: ref<wallet>, investor: account of investor, amount: money, loss: account(investor, claim, "loss") }
51
+ lifecycle { states: [committed, funded, withdrawn, refunded], initial: committed }
52
+ invariants sum "amount" of { instrument: all(lending.commitment), reference: "round", anchor: self.round, states: [committed, funded], limit: 366, where: { investor: { field: self.investor } } } <= self.round.maximumTicket
53
+ invariants sum "amount" of { instrument: all(lending.commitment), reference: "round", anchor: self.round, states: [committed, funded], limit: 366 } <= self.round.price
54
+ action create {
55
+ actor: { party: investor }
56
+ requires self.round in [open]
57
+ requires self.wallet in [active]
58
+ requires self.wallet.holder == self.investor
59
+ requires self.amount >= self.round.minimumTicket
60
+ deadline: { at: self.round.closesAt }
61
+ moves self.amount from self.wallet.held to self.round.held economics { purpose: principal, sourceParty: programOperator }
62
+ }
63
+ action fund { from: committed, to: funded, actor: { parent: round } }
64
+ action withdraw {
65
+ from: committed, to: withdrawn, actor: { party: investor }
66
+ requires self.round in [open]
67
+ deadline: { at: self.round.closesAt }
68
+ moves self.amount from self.round.held to self.wallet.held economics { purpose: principal, sourceParty: programOperator }
69
+ }
70
+ action refund {
71
+ from: committed, to: refunded, actor: { parent: round }
72
+ moves self.amount from self.round.held to self.wallet.held economics { purpose: principal, sourceParty: programOperator }
73
+ }
74
+ }
75
+
76
+ instrument distribution(round: ref<lending.round>, receipt: ref, commitments: ref<lending.commitment> = object(lending.commitment), mode: enum(cash, loss) = cash, residual_to: party = programOperator, operator: party = programOperator, fee: percent = 1%, tax: percent = 15%) {
77
+ summary: "Frozen weighted shares, floored once, with residual assigned to the chosen party."
78
+ fields {
79
+ round: ref<round>, source: ref<receipt>, mode: enum(cash, loss) = mode
80
+ principal: money?, profit: money?, grossTotal: money?, charge: money?, vat: money?, net: money?
81
+ held: account of self, losses: account(self, claim), residualLoss: account(residual_to, claim, "loss")
82
+ }
83
+ lifecycle { states: [pending, cash_ready, loss_ready, distributed], initial: pending }
84
+ action create { requires self.round in [funded]; requires self.source in [recorded]; requires self.source.plan == self.round.plan; requires unique "settlement_consumption" on [self.source.plan, self.source.identity] }
85
+ action prepare_cash {
86
+ from: pending, to: cash_ready
87
+ requires self.mode == "cash"; requires self.source.kind == "cash"; requires self.source in [recorded]
88
+ calculate: [
89
+ { target: "principal", op: "sum", values: [{ field: self.source.principal }] }
90
+ { target: "profit", op: "sum", values: [{ field: self.source.retainedProfit }] }
91
+ { target: "charge", op: "rate", base: { field: self.profit }, bps: { literal: fee }, rounding: "floor" }
92
+ { target: "vat", op: "rate", base: { field: self.charge }, bps: { literal: tax }, rounding: "floor" }
93
+ { target: "grossTotal", op: "sum", values: [{ field: self.principal }, { field: self.profit }] }
94
+ { target: "net", op: "subtract", base: { field: self.grossTotal }, subtract: [{ field: self.charge }, { field: self.vat }] }
95
+ ]
96
+ }
97
+ action prepare_loss {
98
+ from: pending, to: loss_ready
99
+ requires self.mode == "loss"; requires self.source.kind == "loss"; requires self.source in [recorded]
100
+ calculate: [{ target: "net", op: "sum", values: [{ field: self.source.loss }] }]
101
+ }
102
+ action distribute_cash { allowZero: true,
103
+ from: cash_ready, to: distributed, actor: { party: operator }
104
+ requires sum "weight" of { instrument: share, reference: "distribution", anchor: self.id, states: [pending], limit: 366 } == self.round.price
105
+ 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: {} }]
107
+ }
108
+ action residual_cash { allowZero: true,
109
+ from: distributed, to: distributed, actor: { parent: current() }
110
+ moves self.held.balance from self.held to residual_to
111
+ }
112
+ action distribute_loss { allowZero: true,
113
+ from: loss_ready, to: distributed, actor: { party: operator }
114
+ requires sum "weight" of { instrument: share, reference: "distribution", anchor: self.id, states: [pending], limit: 366 } == self.round.price
115
+ 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: {} }]
117
+ }
118
+ action residual_loss { allowZero: true,
119
+ from: distributed, to: distributed, actor: { parent: current() }
120
+ moves self.losses.balance from self.losses to self.residualLoss economics { purpose: internal, sourceParty: programOperator }
121
+ }
122
+ records {
123
+ share: {
124
+ fields {
125
+ distribution: ref<parent>, commitment: ref<commitments>
126
+ weight: money = self.commitment.amount
127
+ gross: money = ratio(self.distribution.net, self.weight, self.distribution.round.price)
128
+ }
129
+ lifecycle { states: [pending, paid], initial: pending }
130
+ 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 }
131
+ action cash { allowZero: true,
132
+ from: pending, to: paid, actor: { parent: parent }
133
+ moves self.gross from self.distribution.held to self.commitment.wallet.held
134
+ }
135
+ action loss { allowZero: true, from: pending, to: paid, actor: { parent: parent }, moves self.gross from self.distribution.losses to self.commitment.loss economics { purpose: internal, sourceParty: programOperator } }
136
+ }
137
+ }
138
+ }
@@ -0,0 +1,123 @@
1
+ header marketplace
2
+
3
+ // Economics: Deposit funding, escrow credit and returns are principal. Retention is
4
+ // earning for the company or a payout to a bound outside recipient.
5
+
6
+ // Listing, order and reservation are separate agreements. Expose their create
7
+ // and decision actions individually. Order fulfillment does not move escrow cash.
8
+ // Pair an order with escrow.hold and expose its fund, deliver, accept, dispute,
9
+ // verify_return and refund aliases. Refund needs disputed -> return_verified,
10
+ // with payee evidence, then returns the full price to payer; accept pays payee.
11
+ // A reservation holds its own deposit. Activate requires an active listing and
12
+ // no placed order. Only a declared converter may invoke credit. A financing
13
+ // plan on the same hold credits it when the lender disburses. Credit consumes the
14
+ // deposit once, credits at most the contribution, and returns excess to buyer.
15
+ // Expose cancel, seller_cancel or operator_cancel for the intended actors.
16
+ // Buyer cancel and expiry use configured refund percentages; seller/operator
17
+ // cancellation return the whole reservation balance. Converted money is in escrow,
18
+ // so subsequent return verification and refund use the escrow aliases.
19
+
20
+ // Cost: listing and order actions declare no transfers. Reservation.create
21
+ // declares one owned account; activate declares one deposit transfer. Cancellation
22
+ // and credit split that balance into moves. Run hsx cost for the composed counts.
23
+
24
+ instrument listing(seller: party) {
25
+ revisioned: true
26
+ summary: "A seller's offer and its asking price."
27
+ fields { seller: account of seller, title: text, price: money }
28
+ lifecycle { states: [draft, active, withdrawn, sold], initial: draft }
29
+ action create { actor: { party: seller } }
30
+ action publish {
31
+ from: draft, to: active, actor: { party: seller }
32
+ }
33
+ action withdraw { from: active, to: withdrawn, actor: { party: seller }, requires count of { instrument: all(marketplace.reservation), reference: "listing", anchor: self.id, states: [active], limit: 366 } == 0 }
34
+ 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
+
37
+ instrument order(listing: ref<marketplace.listing>, buyer: party) {
38
+ revisioned: true
39
+ summary: "A buyer's order for one listing at its declared price."
40
+ fields { listing: ref<listing>, buyer: account of buyer, price: money, reservations: integer?, ownReservations: integer?, blockingQuotes: integer?, ownBlockingQuotes: integer? }
41
+ lifecycle { states: [quoted, placed, fulfilled, cancelled], initial: quoted }
42
+ action create {
43
+ actor: { party: buyer }
44
+ requires self.listing in [active]
45
+ calculate: [
46
+ { target: blockingQuotes, op: aggregate, selection: { instrument: all(marketplace.reservation), reference: "listing", anchor: self.listing, states: [active], limit: 366, where: { quotePolicy: { literal: "blocked" } } }, measure: count }
47
+ { target: ownBlockingQuotes, op: aggregate, selection: { instrument: all(marketplace.reservation), reference: "listing", anchor: self.listing, states: [active], limit: 366, where: { quotePolicy: { literal: "blocked" }, buyer: { field: self.buyer } } }, measure: count }
48
+ ]
49
+ requires self.blockingQuotes == self.ownBlockingQuotes
50
+ requires self.price == self.listing.price
51
+ }
52
+ action commit {
53
+ from: [quoted, placed], to: placed
54
+ requires self.listing in [active]
55
+ calculate: [
56
+ { target: reservations, op: aggregate, selection: { instrument: all(marketplace.reservation), reference: "listing", anchor: self.listing, states: [active], limit: 366 }, measure: count }
57
+ { target: ownReservations, op: aggregate, selection: { instrument: all(marketplace.reservation), reference: "listing", anchor: self.listing, states: [active], limit: 366, where: { buyer: { field: self.buyer } } }, measure: count }
58
+ ]
59
+ requires self.reservations == self.ownReservations
60
+ requires count of { instrument: current(), reference: "listing", anchor: self.listing, states: [placed, fulfilled], limit: 366 } <= 1
61
+ }
62
+ 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 cancel { from: [quoted, placed], to: cancelled, actor: { party: buyer } }
65
+ }
66
+
67
+ instrument reservation(listing: ref<marketplace.listing>, funds: ref<escrow.hold>, converters: ref[], buyer: party, seller: party, expires_after: duration = 48h, deposit: money = 1000 SAR, cancellation_refund: percent = 100%, expiry_refund: percent = 100%, retained_to: party = programOperator, operator: party = programOperator, competing_quotes: enum(allowed, blocked) = allowed) {
68
+ revisioned: true
69
+ summary: "Exclusive timed listing hold with explicit deposit credit and refund."
70
+ fields {
71
+ listing: ref<listing>, funds: ref<funds>, buyer: account of buyer, seller: account of seller, price: money, terms: text
72
+ held: account of self, quotePolicy: enum(allowed, blocked) = competing_quotes, deposit: money = deposit, proposalExpiresAt: date = after(self.createdAt, expires_after), expiresAt: date?
73
+ credit: money?, refund: money?, retained: money?, dispositionAt: date?, reason: text?
74
+ }
75
+ lifecycle { states: [proposed, active, converted, cancelled, expired], initial: proposed }
76
+ invariants count of { instrument: current(), reference: "listing", anchor: self.listing, states: [active], limit: 366 } <= 1
77
+ action create { actor: { party: buyer }, requires self.listing in [active]; requires self.seller == self.listing.seller; requires self.price == self.listing.price }
78
+ action activate {
79
+ from: proposed, to: active, actor: { party: buyer }, deadline: { at: self.proposalExpiresAt }
80
+ calculate: [{ target: expiresAt, op: shift, date: { field: self.now }, milliseconds: { literal: expires_after }, direction: after }]
81
+ requires self.listing in [active]; requires self.price == self.listing.price
82
+ requires count of { instrument: all(marketplace.order), reference: "listing", anchor: self.listing, states: [placed, fulfilled], limit: 366 } == 0
83
+ moves self.deposit from self.buyer to self.held economics { purpose: principal, sourceParty: buyer }
84
+ }
85
+ action convert_cash {
86
+ from: active, to: active, actor: { party: buyer }, deadline: { at: self.expiresAt }
87
+ invoke: [{ reference: self.funds, action: fund, input: {} }]
88
+ }
89
+ action credit { allowZero: true,
90
+ from: active, to: converted, actor: { parent: converters }, deadline: { at: self.expiresAt }
91
+ input { funds: ref<funds>, contribution: money }
92
+ requires input.funds == self.funds; requires input.funds in [pending, funded]; requires input.funds.payer == self.buyer; requires input.funds.price == self.price
93
+ requires input.contribution <= self.price
94
+ calculate: [{ target: credit, op: minimum, values: [{ field: self.held.balance }, { field: input.contribution }] }, { target: refund, op: subtract, base: { field: self.held.balance }, subtract: [{ field: self.credit }] }]
95
+ set: { dispositionAt: { field: self.now } }
96
+ moves self.credit from self.held to input.funds.held economics { purpose: principal, sourceParty: programOperator }
97
+ moves self.refund from self.held to self.buyer economics { purpose: principal, sourceParty: programOperator }
98
+ }
99
+ action cancel { allowZero: true,
100
+ from: active, to: cancelled, actor: { party: buyer }, input { reason: text }
101
+ calculate: [{ target: refund, op: rate, base: { field: self.held.balance }, bps: { literal: cancellation_refund }, rounding: floor }, { target: retained, op: subtract, base: { field: self.held.balance }, subtract: [{ field: self.refund }] }]
102
+ set: { reason: { field: input.reason }, dispositionAt: { field: self.now } }
103
+ moves self.refund from self.held to self.buyer economics { purpose: principal, sourceParty: programOperator }; moves self.retained from self.held to retained_to economics { recipient: retained_to, company: { purpose: earning, sourceParty: buyer }, participant: { purpose: participant_payout, sourceParty: programOperator } }
104
+ }
105
+ action seller_cancel {
106
+ from: active, to: cancelled, actor: { party: seller }, input { reason: text }
107
+ set: { reason: { field: input.reason }, dispositionAt: { field: self.now } }
108
+ moves self.held.balance from self.held to self.buyer economics { purpose: principal, sourceParty: programOperator }
109
+ }
110
+ action operator_cancel {
111
+ from: active, to: cancelled, actor: { party: operator }, input { reason: text }
112
+ set: { reason: { field: input.reason }, dispositionAt: { field: self.now } }
113
+ moves self.held.balance from self.held to self.buyer economics { purpose: principal, sourceParty: programOperator }
114
+ }
115
+ action withdraw { from: proposed, to: cancelled, actor: { party: buyer } }
116
+ action expire { allowZero: true,
117
+ from: active, to: expired, actor: clock, due: { at: self.expiresAt }
118
+ calculate: [{ target: refund, op: rate, base: { field: self.held.balance }, bps: { literal: expiry_refund }, rounding: floor }, { target: retained, op: subtract, base: { field: self.held.balance }, subtract: [{ field: self.refund }] }]
119
+ set: { dispositionAt: { field: self.now } }
120
+ moves self.refund from self.held to self.buyer economics { purpose: principal, sourceParty: programOperator }; moves self.retained from self.held to retained_to economics { recipient: retained_to, company: { purpose: earning, sourceParty: buyer }, participant: { purpose: participant_payout, sourceParty: programOperator } }
121
+ }
122
+ action expire_proposal { from: proposed, to: expired, actor: clock, due: { at: self.proposalExpiresAt } }
123
+ }
package/std/money.hsx ADDED
@@ -0,0 +1,173 @@
1
+ header money
2
+
3
+ // Economics: Held funding and returns are principal. Release to the company realizes
4
+ // earnings; release to another participant is a payout. Generic transfers, splits,
5
+ // schedules, swaps, boundary payouts and metered fees retain no assumed purpose.
6
+
7
+ // Expose each attachment's create and caller actions explicitly. A transfer or
8
+ // split pays once; a hold exposes fund, release and refund separately. Release
9
+ // pays payee and refund returns payer. A hold has no dispute or damage decision.
10
+ // A schedule needs count unique occurrence records before activation; each due
11
+ // occurrence pays only while the schedule is active. A due time promises no retry.
12
+ // A pool accepts whole contributions only up to its remaining target and at most
13
+ // 366 paid records. Expose pay to release the exact target. Clock failure refunds
14
+ // original contributors; excess acceptance and partial contribution are absent.
15
+ // A swap reserves both legs before exchange or voids reservations on expiry.
16
+ // Payout instruct reserves across the selected adapter; confirm/reject require
17
+ // matching terminal instruction evidence. An acknowledgement cannot settle it.
18
+ // Metered bill charges a unique usage identity at the fixed unit price.
19
+
20
+ // Cost: transfer.pay declares one transfer. Hold.create declares one owned account;
21
+ // fund, release and refund each declare one transfer. Schedules and pools add bounded
22
+ // child invocations. Run hsx cost on the composed program for counts, not SAR prices.
23
+
24
+ instrument transfer(payer: party, payee: party, amount: money = runtime) {
25
+ summary: "One transfer between the bound parties."
26
+ fields { amount: money = amount }
27
+ lifecycle { states: [pending, paid, cancelled], initial: pending }
28
+ action create { actor: { party: payer }, requires self.amount > "0" }
29
+ action pay {
30
+ from: pending, to: paid, actor: { party: payer }
31
+ moves self.amount from payer to payee
32
+ }
33
+ action cancel { from: pending, to: cancelled, actor: { party: payer } }
34
+ }
35
+
36
+ instrument hold(payer: party, payee: party, amount: money = runtime) {
37
+ summary: "Hold the declared amount until release or refund."
38
+ fields { payer: account of payer, payee: account of payee, amount: money = amount, price: money = self.amount, held: account of self }
39
+ lifecycle { states: [pending, funded, released, refunded, cancelled], initial: pending }
40
+ action create { }
41
+ action fund {
42
+ from: pending, to: funded, actor: { party: payer }
43
+ moves self.amount from payer to self.held economics { purpose: principal, sourceParty: payer }
44
+ }
45
+ action confirm {
46
+ from: pending, to: funded
47
+ requires self.held.balance == self.amount
48
+ }
49
+ action release {
50
+ from: funded, to: released, actor: { party: payer }
51
+ moves self.amount from self.held to payee economics { recipient: payee, company: { purpose: earning, sourceParty: payer }, participant: { purpose: participant_payout, sourceParty: programOperator } }
52
+ }
53
+ action refund {
54
+ from: funded, to: refunded, actor: { party: payee }
55
+ moves self.amount from self.held to payer economics { purpose: principal, sourceParty: programOperator }
56
+ }
57
+ action cancel { from: pending, to: cancelled }
58
+ }
59
+
60
+ instrument split(payer: party, amount: money = runtime, shares: split = { programOperator: 100% }) {
61
+ summary: "Split one amount; the last declared recipient receives the rounding remainder."
62
+ fields { amount: money = amount }
63
+ lifecycle { states: [pending, paid, cancelled], initial: pending }
64
+ action create {}
65
+ action pay { allowZero: true, requires self.amount > "0"; from: pending, to: paid, actor: { party: payer }, moves self.amount from payer shares shares }
66
+ action cancel { from: pending, to: cancelled }
67
+ }
68
+
69
+ instrument schedule(payer: party, payee: party, amount: money = runtime, count: integer(1, 366) = 12) {
70
+ summary: "A finite schedule whose dated pieces sum exactly to the declared total."
71
+ fields { amount: money = amount, count: integer = count, dates: list(date, count) }
72
+ lifecycle { states: [draft, active, complete, cancelled], initial: draft }
73
+ action create {}
74
+ action activate {
75
+ from: draft, to: active
76
+ requires count of { instrument: occurrence, reference: "schedule", anchor: self.id, states: [pending], limit: 366 } == self.count
77
+ }
78
+ action complete {
79
+ from: active, to: complete
80
+ requires count of { instrument: occurrence, reference: "schedule", anchor: self.id, states: [pending], limit: 366 } == 0
81
+ }
82
+ action cancel { from: [draft, active], to: cancelled }
83
+ records {
84
+ occurrence: {
85
+ fields {
86
+ schedule: ref<parent>, position: integer(1, count), dueAt: date = at(self.schedule.dates, self.position)
87
+ base: money = divide(self.schedule.amount, count)
88
+ allocated: money = multiply(self.base, count)
89
+ remainder: money = subtract(self.schedule.amount, self.allocated)
90
+ first: integer = divide(1, self.position)
91
+ adjustment: money = multiply(self.remainder, self.first)
92
+ amount: money = sum(self.base, self.adjustment)
93
+ }
94
+ lifecycle { states: [pending, paid, cancelled], initial: pending }
95
+ action create { requires self.schedule in [draft]; requires unique "scheduled_position" on [self.schedule, self.position] }
96
+ 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
+ action cancel { from: pending, to: cancelled, requires self.schedule in [cancelled] }
98
+ }
99
+ }
100
+ }
101
+
102
+ instrument pool(payer: party, payee: party, target: money, closes: date) {
103
+ summary: "Contributions fund one threshold or return to their original contributor."
104
+ fields { target: money = target, closesAt: date = closes, held: account of self }
105
+ lifecycle { states: [open, paid, failed], initial: open }
106
+ action create { }
107
+ action pay {
108
+ from: open, to: paid, actor: { party: payee }
109
+ requires self.held.balance == self.target
110
+ moves self.target from self.held to payee economics { recipient: payee, company: { purpose: earning, sourceParty: payer }, participant: { purpose: participant_payout, sourceParty: programOperator } }
111
+ }
112
+ action fail {
113
+ from: open, to: failed, actor: clock, due: { at: self.closesAt }
114
+ requires self.held.balance < self.target
115
+ invoke: [{ selection: { instrument: contribution, reference: "pool", anchor: self.id, states: [paid], limit: 366 }, action: refund, input: {} }]
116
+ }
117
+ records {
118
+ contribution: {
119
+ fields { pool: ref<parent>, payer: account of payer, amount: money, remaining: money? }
120
+ lifecycle { states: [pending, paid, refunded], initial: pending }
121
+ invariants count of { instrument: current(), reference: "pool", anchor: self.pool, states: [paid], limit: 366 } <= 366
122
+ action create { requires self.pool in [open] }
123
+ action pay { from: pending, to: paid, actor: { party: payer }, deadline: { at: self.pool.closesAt }, requires self.pool in [open], calculate: [{ target: remaining, op: subtract, base: { field: self.pool.target }, subtract: [{ field: self.pool.held.balance }] }], requires self.amount <= self.remaining, moves self.amount from self.payer to self.pool.held economics { purpose: principal, sourceParty: payer } }
124
+ action refund { from: paid, to: refunded, actor: { parent: parent }, moves self.amount from self.pool.held to self.payer economics { purpose: principal, sourceParty: programOperator } }
125
+ }
126
+ }
127
+ }
128
+
129
+ instrument swap(first: party, second: party, first_amount: money, second_amount: money, expires: date) {
130
+ summary: "Two reservations settle together or both return."
131
+ fields { firstAmount: money = first_amount, secondAmount: money = second_amount, expiresAt: date = expires, firstReceipt: text?, secondReceipt: text? }
132
+ lifecycle { states: [pending, first_ready, second_ready, both_ready, exchanged, cancelled], initial: pending }
133
+ action create {}
134
+ 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" }
135
+ 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" }
136
+ 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" }
137
+ 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" }
138
+ action expire_second { from: second_ready, to: cancelled, actor: clock, due: { at: self.expiresAt }, moves void self.secondReceipt }
139
+ action exchange { from: both_ready, to: exchanged, actor: { party: first }, moves post self.firstReceipt; moves post self.secondReceipt }
140
+ action cancel { from: pending, to: cancelled }
141
+ action expire_first { from: first_ready, to: cancelled, actor: clock, due: { at: self.expiresAt }, moves void self.firstReceipt }
142
+ action expire_both { from: both_ready, to: cancelled, actor: clock, due: { at: self.expiresAt }, moves void self.firstReceipt; moves void self.secondReceipt }
143
+ }
144
+
145
+ instrument payout(payer: party, payee: party, adapter: text, max_age: duration, amount: money = runtime) {
146
+ summary: "Reserve funds until matching terminal boundary evidence permits posting."
147
+ fields { amount: money = amount, receipt: text? }
148
+ lifecycle { states: [pending, instructed, settled, rejected], initial: pending }
149
+ action create {}
150
+ action instruct {
151
+ from: pending, to: instructed, actor: { party: payer }
152
+ moves reserve self.amount from payer to payee capture "receipt" boundary adapter
153
+ }
154
+ action confirm {
155
+ from: instructed, to: settled
156
+ requires evidence self.id family boundary check outcome result confirmed maxAge max_age instruction self.receipt
157
+ moves post self.receipt
158
+ }
159
+ action reject {
160
+ from: instructed, to: rejected
161
+ requires evidence self.id family boundary check outcome result rejected maxAge max_age instruction self.receipt
162
+ moves void self.receipt
163
+ }
164
+ }
165
+
166
+ instrument metered(payer: party, payee: party, unit_price: money, units: integer = 1, fee: fee = { seller: 0% }) {
167
+ 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) }
169
+ lifecycle { states: [recorded, billed, cancelled], initial: recorded }
170
+ action create { requires unique "usage_event" on [self.usageIdentity] }
171
+ action bill { allowZero: true, requires self.amount > "0"; from: recorded, to: billed, actor: { party: payee }, moves self.amount from payer to payee fee fee }
172
+ action cancel { from: recorded, to: cancelled }
173
+ }