@leashmarket/core 0.1.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.
Files changed (106) hide show
  1. package/README.md +32 -0
  2. package/dist/agent/agent.d.ts +8 -0
  3. package/dist/agent/agent.d.ts.map +1 -0
  4. package/dist/agent/agent.js +14 -0
  5. package/dist/agent/agent.js.map +1 -0
  6. package/dist/agent/treasury-pda.d.ts +49 -0
  7. package/dist/agent/treasury-pda.d.ts.map +1 -0
  8. package/dist/agent/treasury-pda.js +55 -0
  9. package/dist/agent/treasury-pda.js.map +1 -0
  10. package/dist/explorer/index.d.ts +55 -0
  11. package/dist/explorer/index.d.ts.map +1 -0
  12. package/dist/explorer/index.js +95 -0
  13. package/dist/explorer/index.js.map +1 -0
  14. package/dist/fees/leash-fee.d.ts +248 -0
  15. package/dist/fees/leash-fee.d.ts.map +1 -0
  16. package/dist/fees/leash-fee.js +246 -0
  17. package/dist/fees/leash-fee.js.map +1 -0
  18. package/dist/format/index.d.ts +55 -0
  19. package/dist/format/index.d.ts.map +1 -0
  20. package/dist/format/index.js +130 -0
  21. package/dist/format/index.js.map +1 -0
  22. package/dist/index.d.ts +25 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +25 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/policy/evaluate.d.ts +17 -0
  27. package/dist/policy/evaluate.d.ts.map +1 -0
  28. package/dist/policy/evaluate.js +46 -0
  29. package/dist/policy/evaluate.js.map +1 -0
  30. package/dist/policy/state.d.ts +8 -0
  31. package/dist/policy/state.d.ts.map +1 -0
  32. package/dist/policy/state.js +2 -0
  33. package/dist/policy/state.js.map +1 -0
  34. package/dist/receipt/build.d.ts +5 -0
  35. package/dist/receipt/build.d.ts.map +1 -0
  36. package/dist/receipt/build.js +9 -0
  37. package/dist/receipt/build.js.map +1 -0
  38. package/dist/receipt/hash.d.ts +15 -0
  39. package/dist/receipt/hash.d.ts.map +1 -0
  40. package/dist/receipt/hash.js +40 -0
  41. package/dist/receipt/hash.js.map +1 -0
  42. package/dist/receipt/verify.d.ts +10 -0
  43. package/dist/receipt/verify.d.ts.map +1 -0
  44. package/dist/receipt/verify.js +36 -0
  45. package/dist/receipt/verify.js.map +1 -0
  46. package/dist/tokens/index.d.ts +86 -0
  47. package/dist/tokens/index.d.ts.map +1 -0
  48. package/dist/tokens/index.js +163 -0
  49. package/dist/tokens/index.js.map +1 -0
  50. package/dist/treasury/balance.d.ts +3 -0
  51. package/dist/treasury/balance.d.ts.map +1 -0
  52. package/dist/treasury/balance.js +13 -0
  53. package/dist/treasury/balance.js.map +1 -0
  54. package/dist/treasury/inspect-token-account.d.ts +46 -0
  55. package/dist/treasury/inspect-token-account.d.ts.map +1 -0
  56. package/dist/treasury/inspect-token-account.js +67 -0
  57. package/dist/treasury/inspect-token-account.js.map +1 -0
  58. package/dist/treasury/list-balances.d.ts +57 -0
  59. package/dist/treasury/list-balances.d.ts.map +1 -0
  60. package/dist/treasury/list-balances.js +115 -0
  61. package/dist/treasury/list-balances.js.map +1 -0
  62. package/dist/treasury/pause.d.ts +32 -0
  63. package/dist/treasury/pause.d.ts.map +1 -0
  64. package/dist/treasury/pause.js +40 -0
  65. package/dist/treasury/pause.js.map +1 -0
  66. package/dist/treasury/withdraw.d.ts +13 -0
  67. package/dist/treasury/withdraw.d.ts.map +1 -0
  68. package/dist/treasury/withdraw.js +4 -0
  69. package/dist/treasury/withdraw.js.map +1 -0
  70. package/dist/wallet/index.d.ts +3 -0
  71. package/dist/wallet/index.d.ts.map +1 -0
  72. package/dist/wallet/index.js +2 -0
  73. package/dist/wallet/index.js.map +1 -0
  74. package/dist/x402/client.d.ts +72 -0
  75. package/dist/x402/client.d.ts.map +1 -0
  76. package/dist/x402/client.js +94 -0
  77. package/dist/x402/client.js.map +1 -0
  78. package/dist/x402/delegate-scheme.d.ts +107 -0
  79. package/dist/x402/delegate-scheme.d.ts.map +1 -0
  80. package/dist/x402/delegate-scheme.js +268 -0
  81. package/dist/x402/delegate-scheme.js.map +1 -0
  82. package/dist/x402/discovery.d.ts +110 -0
  83. package/dist/x402/discovery.d.ts.map +1 -0
  84. package/dist/x402/discovery.js +213 -0
  85. package/dist/x402/discovery.js.map +1 -0
  86. package/dist/x402/envelope.d.ts +65 -0
  87. package/dist/x402/envelope.d.ts.map +1 -0
  88. package/dist/x402/envelope.js +67 -0
  89. package/dist/x402/envelope.js.map +1 -0
  90. package/dist/x402/facilitator.d.ts +45 -0
  91. package/dist/x402/facilitator.d.ts.map +1 -0
  92. package/dist/x402/facilitator.js +61 -0
  93. package/dist/x402/facilitator.js.map +1 -0
  94. package/dist/x402/headers.d.ts +51 -0
  95. package/dist/x402/headers.d.ts.map +1 -0
  96. package/dist/x402/headers.js +84 -0
  97. package/dist/x402/headers.js.map +1 -0
  98. package/dist/x402/parse.d.ts +20 -0
  99. package/dist/x402/parse.d.ts.map +1 -0
  100. package/dist/x402/parse.js +31 -0
  101. package/dist/x402/parse.js.map +1 -0
  102. package/dist/x402/webhook.d.ts +48 -0
  103. package/dist/x402/webhook.d.ts.map +1 -0
  104. package/dist/x402/webhook.js +88 -0
  105. package/dist/x402/webhook.js.map +1 -0
  106. package/package.json +46 -0
@@ -0,0 +1,246 @@
1
+ /**
2
+ * Leash protocol fee primitives.
3
+ *
4
+ * Every Leash-flavoured x402 settlement levies a small protocol fee on
5
+ * top of the seller's quoted price. The buyer signs **one** transaction
6
+ * with **two** `TransferChecked` instructions — one to the seller's
7
+ * `payTo` ATA, one to the Leash treasury's ATA on the same mint — so
8
+ * both legs settle atomically (either both transfers land or neither
9
+ * does). The fee leg is funded out of the same source token account as
10
+ * the seller leg.
11
+ *
12
+ * This module is the shared building block:
13
+ * - {@link LEASH_FEE_BPS_DEFAULT} — 100 bps (1%) by default; overridable
14
+ * by env at the seller (which advertises the fee in `extra['leash.fee']`)
15
+ * and at the facilitator (which enforces it on `verify` / `settle`).
16
+ * - {@link computeFeeAtoms} — `ceilBps(amount, bps)`. Always rounds the
17
+ * fee **up** so dust never leaks out of the treasury when the buyer
18
+ * pays a non-divisible price.
19
+ * - {@link LeashFeeExtra} — wire shape stamped onto `paymentRequirements.extra`
20
+ * so buyer + facilitator agree on bps, amount, mint authority, and ATA.
21
+ * - {@link resolveLeashFeeAuthority} — env-driven lookup of the treasury
22
+ * authority pubkey per network (mainnet / devnet).
23
+ * - {@link getLeashFeeAtaFor} — derive the treasury's ATA for a given
24
+ * `(asset, tokenProgram)` pair so seller + facilitator both end up
25
+ * with the same destination address.
26
+ *
27
+ * Fee math is gross-up:
28
+ * - Seller quotes `net` (e.g. 1 USDC = 1_000_000 atoms).
29
+ * - Buyer pays `gross = net + fee` where `fee = ceil(net * bps / 10_000)`.
30
+ * - At 100 bps: 1 USDC → buyer signs 1.01 USDC; 1.00 USDC lands in seller
31
+ * ATA, 0.01 USDC lands in treasury ATA.
32
+ *
33
+ * Env vars (read at process boot — never inline a stale value):
34
+ * - `LEASH_FEE_BPS` — global default (integer 0..1000). Default 100.
35
+ * - `LEASH_FEE_ENFORCE` — `off | warn | enforce`. Default `warn`.
36
+ * - `LEASH_FEE_ENFORCE_MAINNET` — per-network override; falls back to LEASH_FEE_ENFORCE.
37
+ * - `LEASH_FEE_ENFORCE_DEVNET` — per-network override; falls back to LEASH_FEE_ENFORCE.
38
+ * - `LEASH_FEE_AUTHORITY_MAINNET`— wallet pubkey that owns the mainnet fee ATAs.
39
+ * - `LEASH_FEE_AUTHORITY_DEVNET` — wallet pubkey that owns the devnet fee ATAs.
40
+ *
41
+ * Defaults bake in `3DdcJkvjW7KLtMeko3Zr57jEJWhqRHuPsEBFm1XJYh7W` on both
42
+ * clusters so a fresh deploy collects fees out of the box. Override via
43
+ * env to rotate the treasury without code changes.
44
+ */
45
+ import { address as toAddress } from '@solana/kit';
46
+ import { findAssociatedTokenPda, TOKEN_2022_PROGRAM_ADDRESS } from '@solana-program/token-2022';
47
+ import { TOKEN_PROGRAM_ADDRESS } from '@solana-program/token';
48
+ /**
49
+ * Default protocol fee rate in basis points (1 bps = 0.01%). 100 bps = 1%.
50
+ * Used by both the seller-kit (when stamping `extra['leash.fee']`) and
51
+ * the facilitator (when verifying inbound payment payloads). Override
52
+ * per-network via the `LEASH_FEE_BPS` env var on both surfaces.
53
+ */
54
+ export const LEASH_FEE_BPS_DEFAULT = 100;
55
+ /**
56
+ * Default treasury authority on Solana mainnet. Owns the SPL token
57
+ * accounts (one ATA per stable mint) where Leash collects protocol fees.
58
+ * Set `LEASH_FEE_AUTHORITY_MAINNET` to override (e.g. when migrating to
59
+ * a multisig).
60
+ */
61
+ export const LEASH_FEE_AUTHORITY_MAINNET_DEFAULT = '3DdcJkvjW7KLtMeko3Zr57jEJWhqRHuPsEBFm1XJYh7W';
62
+ /**
63
+ * Default treasury authority on Solana devnet. Same key as mainnet by
64
+ * design — devnet uses a separate ATA per mint anyway, so reusing the
65
+ * authority just simplifies operational tooling. Override via
66
+ * `LEASH_FEE_AUTHORITY_DEVNET`.
67
+ */
68
+ export const LEASH_FEE_AUTHORITY_DEVNET_DEFAULT = '3DdcJkvjW7KLtMeko3Zr57jEJWhqRHuPsEBFm1XJYh7W';
69
+ /**
70
+ * Compute the fee in atomic units for a given seller amount + bps,
71
+ * rounding **up** (`ceil`) so dust never leaks out of the treasury.
72
+ *
73
+ * Examples (bps=100):
74
+ * - amount=1_000_000 → fee=10_000 (0.01 USDC for 1.00 USDC)
75
+ * - amount=1 → fee=1 (1 atom for 1 atom — ceil)
76
+ * - amount=999 → fee=10 (10 atoms for 999 atoms — ceil)
77
+ *
78
+ * @param amount seller leg amount in atomic units
79
+ * @param bps integer 0..10_000
80
+ * @returns atomic fee amount, never negative
81
+ */
82
+ export function computeFeeAtoms(amount, bps) {
83
+ if (!Number.isInteger(bps) || bps < 0 || bps > 10_000) {
84
+ throw new Error(`computeFeeAtoms: bps must be integer in [0, 10000], got ${bps}`);
85
+ }
86
+ if (amount < 0n) {
87
+ throw new Error(`computeFeeAtoms: amount must be non-negative, got ${amount}`);
88
+ }
89
+ if (bps === 0 || amount === 0n)
90
+ return 0n;
91
+ const numerator = amount * BigInt(bps);
92
+ // Ceil division for positive integers: (n + d - 1) / d
93
+ return (numerator + 9999n) / 10000n;
94
+ }
95
+ /**
96
+ * Convenience: given a `net` (seller's quoted) amount, return the
97
+ * `(net, fee, gross)` triple where `gross = net + fee` and
98
+ * `fee = ceilBps(net, bps)`.
99
+ *
100
+ * Use at seller-kit's `buildAccepts` time and on the buyer playground
101
+ * UI to render "you will pay X" disclosures.
102
+ */
103
+ export function applyFeeGrossUp(netAmount, bps = LEASH_FEE_BPS_DEFAULT) {
104
+ const fee = computeFeeAtoms(netAmount, bps);
105
+ return { net: netAmount, fee, gross: netAmount + fee };
106
+ }
107
+ /**
108
+ * Read the configured fee bps from env. Falls back to
109
+ * {@link LEASH_FEE_BPS_DEFAULT} when unset / unparseable. Validates
110
+ * the range so a bad env never surfaces as a 50_000 bps fee.
111
+ */
112
+ export function resolveLeashFeeBps() {
113
+ if (typeof process === 'undefined' || !process.env)
114
+ return LEASH_FEE_BPS_DEFAULT;
115
+ const raw = process.env.LEASH_FEE_BPS;
116
+ if (!raw)
117
+ return LEASH_FEE_BPS_DEFAULT;
118
+ const parsed = Number.parseInt(raw, 10);
119
+ if (!Number.isInteger(parsed) || parsed < 0 || parsed > 1_000) {
120
+ // Cap at 10% (1000 bps) for sanity — anything higher is almost
121
+ // certainly a misconfiguration.
122
+ return LEASH_FEE_BPS_DEFAULT;
123
+ }
124
+ return parsed;
125
+ }
126
+ /**
127
+ * Read the configured fee authority for the given network from env.
128
+ * Falls back to the bundled defaults
129
+ * ({@link LEASH_FEE_AUTHORITY_MAINNET_DEFAULT} /
130
+ * {@link LEASH_FEE_AUTHORITY_DEVNET_DEFAULT}) when unset.
131
+ */
132
+ export function resolveLeashFeeAuthority(network) {
133
+ const envKey = network === 'mainnet' ? 'LEASH_FEE_AUTHORITY_MAINNET' : 'LEASH_FEE_AUTHORITY_DEVNET';
134
+ if (typeof process !== 'undefined' && process.env) {
135
+ const raw = process.env[envKey];
136
+ if (raw && raw.trim().length > 0)
137
+ return raw.trim();
138
+ }
139
+ return network === 'mainnet'
140
+ ? LEASH_FEE_AUTHORITY_MAINNET_DEFAULT
141
+ : LEASH_FEE_AUTHORITY_DEVNET_DEFAULT;
142
+ }
143
+ /**
144
+ * Read the enforcement mode for the given network from env. Per-network
145
+ * vars override the global one. Default is `warn` everywhere so the
146
+ * rollout doesn't break older clients overnight.
147
+ */
148
+ export function resolveLeashFeeEnforcement(network) {
149
+ if (typeof process === 'undefined' || !process.env)
150
+ return 'warn';
151
+ const perNetwork = network === 'mainnet'
152
+ ? process.env.LEASH_FEE_ENFORCE_MAINNET
153
+ : process.env.LEASH_FEE_ENFORCE_DEVNET;
154
+ const global = process.env.LEASH_FEE_ENFORCE;
155
+ const raw = (perNetwork ?? global ?? 'warn').trim().toLowerCase();
156
+ if (raw === 'off' || raw === 'warn' || raw === 'enforce')
157
+ return raw;
158
+ return 'warn';
159
+ }
160
+ /**
161
+ * Derive the Leash fee ATA on the given `(network, asset, tokenProgram)`.
162
+ * Both the seller (when stamping `extra['leash.fee'].feeDestination`)
163
+ * and the facilitator (when verifying the second `TransferChecked`)
164
+ * call this so they always agree on the destination.
165
+ */
166
+ export async function getLeashFeeAtaFor(args) {
167
+ const authority = (args.authority ?? resolveLeashFeeAuthority(args.network));
168
+ const tokenProgram = args.tokenProgram === 'spl-token-2022' ? TOKEN_2022_PROGRAM_ADDRESS : TOKEN_PROGRAM_ADDRESS;
169
+ const [ata] = await findAssociatedTokenPda({
170
+ mint: toAddress(args.asset),
171
+ owner: toAddress(authority),
172
+ tokenProgram,
173
+ });
174
+ return { ata, authority: toAddress(authority) };
175
+ }
176
+ /**
177
+ * Build the static `extra['leash.fee']` payload for a given seller leg.
178
+ *
179
+ * Sync by construction: the wire shape only carries `bps` and
180
+ * `feeAuthority`, so seller-kit / payment-links can stamp this at
181
+ * `buildAccepts` time without making `createSeller` async. The buyer +
182
+ * facilitator derive the dynamic fields (`feeAtomic`, `grossAtomic`,
183
+ * `feeDestination`) on demand via {@link computeLeashFeeForRequirements}.
184
+ */
185
+ export function buildLeashFeeExtra(args) {
186
+ const bps = args.bps ?? resolveLeashFeeBps();
187
+ const feeAuthority = args.authority ?? resolveLeashFeeAuthority(args.network);
188
+ return { v: '1', bps, feeAuthority };
189
+ }
190
+ /**
191
+ * Type guard + parser for an inbound `extra['leash.fee']` block. Returns
192
+ * `null` if the value is absent or malformed (any field missing /
193
+ * wrong type / out-of-range bps). Facilitators use this to decide whether
194
+ * to apply fee verification.
195
+ */
196
+ export function parseLeashFeeExtra(extra) {
197
+ if (!extra || typeof extra !== 'object')
198
+ return null;
199
+ const raw = extra['leash.fee'];
200
+ if (!raw || typeof raw !== 'object')
201
+ return null;
202
+ const obj = raw;
203
+ if (obj.v !== '1')
204
+ return null;
205
+ const { bps, feeAuthority } = obj;
206
+ if (typeof bps !== 'number' ||
207
+ !Number.isInteger(bps) ||
208
+ bps < 0 ||
209
+ bps > 10_000 ||
210
+ typeof feeAuthority !== 'string' ||
211
+ feeAuthority.length === 0) {
212
+ return null;
213
+ }
214
+ return { v: '1', bps, feeAuthority };
215
+ }
216
+ /**
217
+ * Resolve the per-request fee triple `(bps, feeAtomic, feeDestination)`
218
+ * for a given `paymentRequirements` entry. Returns `null` if the
219
+ * requirement carries no `extra['leash.fee']` (vanilla x402 mode).
220
+ *
221
+ * Both the buyer (when constructing the `TransferChecked` fee leg) and
222
+ * the facilitator (when verifying the inbound transaction) call this
223
+ * function so they always derive the same destination ATA + atomic
224
+ * amount from the same inputs.
225
+ */
226
+ export async function computeLeashFeeForRequirements(args) {
227
+ if (!args.extra)
228
+ return null;
229
+ const bps = args.extra.bps;
230
+ const net = BigInt(args.amount);
231
+ const fee = computeFeeAtoms(net, bps);
232
+ const acct = await getLeashFeeAtaFor({
233
+ network: args.network,
234
+ asset: args.asset,
235
+ tokenProgram: args.tokenProgram,
236
+ authority: args.extra.feeAuthority,
237
+ });
238
+ return {
239
+ bps,
240
+ feeAtomic: fee,
241
+ grossAtomic: net + fee,
242
+ feeAuthority: acct.authority,
243
+ feeDestination: acct.ata,
244
+ };
245
+ }
246
+ //# sourceMappingURL=leash-fee.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"leash-fee.js","sourceRoot":"","sources":["../../src/fees/leash-fee.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,EAAE,OAAO,IAAI,SAAS,EAAgB,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAChG,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAI9D;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mCAAmC,GAAG,8CAA8C,CAAC;AAElG;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kCAAkC,GAAG,8CAA8C,CAAC;AAuDjG;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,GAAW;IACzD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,MAAM,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,2DAA2D,GAAG,EAAE,CAAC,CAAC;IACpF,CAAC;IACD,IAAI,MAAM,GAAG,EAAE,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,qDAAqD,MAAM,EAAE,CAAC,CAAC;IACjF,CAAC;IACD,IAAI,GAAG,KAAK,CAAC,IAAI,MAAM,KAAK,EAAE;QAAE,OAAO,EAAE,CAAC;IAC1C,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACvC,uDAAuD;IACvD,OAAO,CAAC,SAAS,GAAG,KAAM,CAAC,GAAG,MAAO,CAAC;AACxC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAC7B,SAAiB,EACjB,MAAc,qBAAqB;IAEnC,MAAM,GAAG,GAAG,eAAe,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAC5C,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,GAAG,GAAG,EAAE,CAAC;AACzD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB;IAChC,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,CAAC,OAAO,CAAC,GAAG;QAAE,OAAO,qBAAqB,CAAC;IACjF,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IACtC,IAAI,CAAC,GAAG;QAAE,OAAO,qBAAqB,CAAC;IACvC,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACxC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,KAAK,EAAE,CAAC;QAC9D,+DAA+D;QAC/D,gCAAgC;QAChC,OAAO,qBAAqB,CAAC;IAC/B,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAAqB;IAC5D,MAAM,MAAM,GACV,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC,4BAA4B,CAAC;IACvF,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAClD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAChC,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;IACtD,CAAC;IACD,OAAO,OAAO,KAAK,SAAS;QAC1B,CAAC,CAAC,mCAAmC;QACrC,CAAC,CAAC,kCAAkC,CAAC;AACzC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,OAAqB;IAC9D,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,CAAC,OAAO,CAAC,GAAG;QAAE,OAAO,MAAM,CAAC;IAClE,MAAM,UAAU,GACd,OAAO,KAAK,SAAS;QACnB,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB;QACvC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC;IAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAC7C,MAAM,GAAG,GAAG,CAAC,UAAU,IAAI,MAAM,IAAI,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAClE,IAAI,GAAG,KAAK,KAAK,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC;IACrE,OAAO,MAAM,CAAC;AAChB,CAAC;AAYD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAWvC;IACC,MAAM,SAAS,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,wBAAwB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAW,CAAC;IACvF,MAAM,YAAY,GAChB,IAAI,CAAC,YAAY,KAAK,gBAAgB,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,qBAAqB,CAAC;IAC9F,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,sBAAsB,CAAC;QACzC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC;QAC3B,KAAK,EAAE,SAAS,CAAC,SAAS,CAAC;QAC3B,YAAY;KACb,CAAC,CAAC;IACH,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC;AAClD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAMlC;IACC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,kBAAkB,EAAE,CAAC;IAC7C,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,IAAI,wBAAwB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9E,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC;AACvC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,KAAiD;IAEjD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACrD,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC;IAC/B,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACjD,MAAM,GAAG,GAAG,GAA8B,CAAC;IAC3C,IAAI,GAAG,CAAC,CAAC,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAC/B,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,GAAG,GAAG,CAAC;IAClC,IACE,OAAO,GAAG,KAAK,QAAQ;QACvB,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC;QACtB,GAAG,GAAG,CAAC;QACP,GAAG,GAAG,MAAM;QACZ,OAAO,YAAY,KAAK,QAAQ;QAChC,YAAY,CAAC,MAAM,KAAK,CAAC,EACzB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC;AACvC,CAAC;AAeD;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAAC,IAUpD;IACC,IAAI,CAAC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;IAC3B,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAChC,MAAM,GAAG,GAAG,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC;QACnC,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY;KACnC,CAAC,CAAC;IACH,OAAO;QACL,GAAG;QACH,SAAS,EAAE,GAAG;QACd,WAAW,EAAE,GAAG,GAAG,GAAG;QACtB,YAAY,EAAE,IAAI,CAAC,SAAS;QAC5B,cAAc,EAAE,IAAI,CAAC,GAAG;KACzB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Token-aware amount formatting / parsing helpers shared by every Leash UI
3
+ * surface (web playground, CLI demos) and downstream consumers.
4
+ *
5
+ * The fundamental invariant: **on the wire, amounts are integer strings in
6
+ * the token's atomic unit** (e.g. 1 USDC = `"1000000"` because USDC has 6
7
+ * decimals). Decimal display ("1.50 USDC") is a UI concern only and must
8
+ * be derived from the mint's `decimals`. Hand-rolling `*1_000_000` math
9
+ * silently breaks for Token-2022 stables with different decimal counts;
10
+ * always pass the right `decimals` here.
11
+ */
12
+ import type { ReceiptV1 } from '@leashmarket/schemas';
13
+ import { type TokenNetwork } from '../tokens/index.js';
14
+ /**
15
+ * Convert an atomic integer string (or bigint) to its decimal string form.
16
+ *
17
+ * @example
18
+ * atomicToDecimal('1000000', 6) // '1'
19
+ * atomicToDecimal('1234500', 6) // '1.2345'
20
+ * atomicToDecimal('2', 6) // '0.000002'
21
+ */
22
+ export declare function atomicToDecimal(amount: bigint | string, decimals: number): string;
23
+ /**
24
+ * Convert a decimal string ("1.50") to its atomic integer (1500000n at 6
25
+ * decimals). Returns `null` for malformed input — callers should treat
26
+ * `null` as "leave the amount field blank, don't proceed".
27
+ *
28
+ * Rejects more decimal places than the token supports rather than silently
29
+ * truncating, because losing dust to rounding when sending stablecoins is
30
+ * a user-trust bug.
31
+ */
32
+ export declare function decimalToAtomic(input: string, decimals: number): bigint | null;
33
+ /**
34
+ * Format a `ReceiptV1.price` for UI display. Atomic integer amounts are
35
+ * resolved through the {@link KNOWN_TOKENS} registry when an `asset` mint is
36
+ * present; otherwise we fall back to USDC's 6 decimals (the playground
37
+ * default). Already-decimal strings (legacy receipts) pass through.
38
+ */
39
+ export declare function formatReceiptPrice(price: ReceiptV1['price'] | undefined | null, network?: TokenNetwork): string | null;
40
+ /** {@link formatReceiptPrice} + the currency ticker. */
41
+ export declare function formatReceiptPriceWithCurrency(price: ReceiptV1['price'] | undefined | null, network?: TokenNetwork): string | null;
42
+ /**
43
+ * USD-style display ("$1.23", "$0.001000") for stablecoin prices. Falls back
44
+ * to {@link formatReceiptPriceWithCurrency} for non-stable currencies. Pad
45
+ * the fractional part to **at least 2 decimals** so `$1` doesn't read as
46
+ * truncated; preserve the long tail for sub-cent amounts so users see
47
+ * exactly what they paid.
48
+ */
49
+ export declare function formatReceiptPriceUsd(price: ReceiptV1['price'] | undefined | null, network?: TokenNetwork): string | null;
50
+ /**
51
+ * Format a raw atomic balance against a known mint. Convenience wrapper
52
+ * around {@link atomicToDecimal} that sources `decimals` from the registry.
53
+ */
54
+ export declare function formatTokenBalance(amount: bigint | string, mint: string, network: TokenNetwork): string;
55
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/format/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAKpE;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAajF;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAU9E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI,EAC5C,OAAO,GAAE,YAAuB,GAC/B,MAAM,GAAG,IAAI,CAMf;AAED,wDAAwD;AACxD,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI,EAC5C,OAAO,GAAE,YAAuB,GAC/B,MAAM,GAAG,IAAI,CAKf;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI,EAC5C,OAAO,GAAE,YAAuB,GAC/B,MAAM,GAAG,IAAI,CASf;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,YAAY,GACpB,MAAM,CAIR"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Token-aware amount formatting / parsing helpers shared by every Leash UI
3
+ * surface (web playground, CLI demos) and downstream consumers.
4
+ *
5
+ * The fundamental invariant: **on the wire, amounts are integer strings in
6
+ * the token's atomic unit** (e.g. 1 USDC = `"1000000"` because USDC has 6
7
+ * decimals). Decimal display ("1.50 USDC") is a UI concern only and must
8
+ * be derived from the mint's `decimals`. Hand-rolling `*1_000_000` math
9
+ * silently breaks for Token-2022 stables with different decimal counts;
10
+ * always pass the right `decimals` here.
11
+ */
12
+ import { lookupToken } from '../tokens/index.js';
13
+ /** USD-pegged stablecoin tickers shown with `$` prefix in {@link formatAmountUsd}. */
14
+ const STABLES = new Set(['USDC', 'USDT', 'USDG', 'PYUSD']);
15
+ /**
16
+ * Convert an atomic integer string (or bigint) to its decimal string form.
17
+ *
18
+ * @example
19
+ * atomicToDecimal('1000000', 6) // '1'
20
+ * atomicToDecimal('1234500', 6) // '1.2345'
21
+ * atomicToDecimal('2', 6) // '0.000002'
22
+ */
23
+ export function atomicToDecimal(amount, decimals) {
24
+ const raw = typeof amount === 'bigint' ? amount.toString() : amount.trim();
25
+ if (!/^\d+$/.test(raw)) {
26
+ throw new Error(`atomicToDecimal: expected integer string, got "${raw}"`);
27
+ }
28
+ if (decimals < 0 || !Number.isInteger(decimals)) {
29
+ throw new Error(`atomicToDecimal: decimals must be a non-negative integer (got ${decimals})`);
30
+ }
31
+ if (decimals === 0)
32
+ return raw;
33
+ const padded = raw.padStart(decimals + 1, '0');
34
+ const whole = padded.slice(0, -decimals);
35
+ const frac = padded.slice(-decimals).replace(/0+$/, '');
36
+ return frac ? `${whole}.${frac}` : whole;
37
+ }
38
+ /**
39
+ * Convert a decimal string ("1.50") to its atomic integer (1500000n at 6
40
+ * decimals). Returns `null` for malformed input — callers should treat
41
+ * `null` as "leave the amount field blank, don't proceed".
42
+ *
43
+ * Rejects more decimal places than the token supports rather than silently
44
+ * truncating, because losing dust to rounding when sending stablecoins is
45
+ * a user-trust bug.
46
+ */
47
+ export function decimalToAtomic(input, decimals) {
48
+ const s = input.trim();
49
+ if (!s)
50
+ return null;
51
+ const m = s.match(/^(\d+)(?:\.(\d+))?$/);
52
+ if (!m)
53
+ return null;
54
+ const whole = m[1] ?? '0';
55
+ const fracRaw = m[2] ?? '';
56
+ if (fracRaw.length > decimals)
57
+ return null;
58
+ const frac = fracRaw.padEnd(decimals, '0');
59
+ return BigInt(whole) * BigInt(10) ** BigInt(decimals) + BigInt(frac || '0');
60
+ }
61
+ /**
62
+ * Format a `ReceiptV1.price` for UI display. Atomic integer amounts are
63
+ * resolved through the {@link KNOWN_TOKENS} registry when an `asset` mint is
64
+ * present; otherwise we fall back to USDC's 6 decimals (the playground
65
+ * default). Already-decimal strings (legacy receipts) pass through.
66
+ */
67
+ export function formatReceiptPrice(price, network = 'devnet') {
68
+ if (!price)
69
+ return null;
70
+ const { amount, currency, asset } = price;
71
+ if (!/^\d+$/.test(amount))
72
+ return amount;
73
+ const decimals = resolveDecimals({ asset, currency, network });
74
+ return atomicToDecimal(amount, decimals);
75
+ }
76
+ /** {@link formatReceiptPrice} + the currency ticker. */
77
+ export function formatReceiptPriceWithCurrency(price, network = 'devnet') {
78
+ if (!price)
79
+ return null;
80
+ const core = formatReceiptPrice(price, network);
81
+ if (core === null)
82
+ return null;
83
+ return `${core} ${price.currency}`;
84
+ }
85
+ /**
86
+ * USD-style display ("$1.23", "$0.001000") for stablecoin prices. Falls back
87
+ * to {@link formatReceiptPriceWithCurrency} for non-stable currencies. Pad
88
+ * the fractional part to **at least 2 decimals** so `$1` doesn't read as
89
+ * truncated; preserve the long tail for sub-cent amounts so users see
90
+ * exactly what they paid.
91
+ */
92
+ export function formatReceiptPriceUsd(price, network = 'devnet') {
93
+ if (!price)
94
+ return null;
95
+ if (!STABLES.has(price.currency))
96
+ return formatReceiptPriceWithCurrency(price, network);
97
+ const core = formatReceiptPrice(price, network);
98
+ if (core === null)
99
+ return null;
100
+ if (/^\d+$/.test(core))
101
+ return `$${core}.00`;
102
+ const [whole, frac = ''] = core.split('.');
103
+ if (frac.length < 2)
104
+ return `$${whole}.${frac.padEnd(2, '0')}`;
105
+ return `$${core}`;
106
+ }
107
+ /**
108
+ * Format a raw atomic balance against a known mint. Convenience wrapper
109
+ * around {@link atomicToDecimal} that sources `decimals` from the registry.
110
+ */
111
+ export function formatTokenBalance(amount, mint, network) {
112
+ const token = lookupToken(mint, network);
113
+ const decimals = token?.decimals ?? 0;
114
+ return atomicToDecimal(amount, decimals);
115
+ }
116
+ function resolveDecimals(args) {
117
+ if (args.asset) {
118
+ const known = lookupToken(args.asset, args.network);
119
+ if (known)
120
+ return known.decimals;
121
+ }
122
+ // Stablecoins are 6 decimals on Solana (USDC/USDT/USDG/PYUSD).
123
+ if (STABLES.has(args.currency))
124
+ return 6;
125
+ // Unknown non-stablecoins: leave atomic untouched. Better to show "5 BONK"
126
+ // than silently divide by 1e6 and surface "0.000005 BONK" — which would be
127
+ // a misleading payment amount in receipts.
128
+ return 0;
129
+ }
130
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/format/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ export * from './agent/agent.js';
2
+ export * from './agent/treasury-pda.js';
3
+ export * from './policy/evaluate.js';
4
+ export * from './policy/state.js';
5
+ export * from './receipt/hash.js';
6
+ export * from './receipt/build.js';
7
+ export * from './receipt/verify.js';
8
+ export * from './x402/client.js';
9
+ export * from './x402/discovery.js';
10
+ export * from './x402/delegate-scheme.js';
11
+ export * from './x402/envelope.js';
12
+ export * from './x402/facilitator.js';
13
+ export * from './x402/headers.js';
14
+ export * from './x402/parse.js';
15
+ export * from './x402/webhook.js';
16
+ export * from './treasury/balance.js';
17
+ export * from './treasury/inspect-token-account.js';
18
+ export * from './treasury/list-balances.js';
19
+ export * from './treasury/pause.js';
20
+ export * from './tokens/index.js';
21
+ export * from './fees/leash-fee.js';
22
+ export * from './explorer/index.js';
23
+ export * from './format/index.js';
24
+ export * from './wallet/index.js';
25
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qCAAqC,CAAC;AACpD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ export * from './agent/agent.js';
2
+ export * from './agent/treasury-pda.js';
3
+ export * from './policy/evaluate.js';
4
+ export * from './policy/state.js';
5
+ export * from './receipt/hash.js';
6
+ export * from './receipt/build.js';
7
+ export * from './receipt/verify.js';
8
+ export * from './x402/client.js';
9
+ export * from './x402/discovery.js';
10
+ export * from './x402/delegate-scheme.js';
11
+ export * from './x402/envelope.js';
12
+ export * from './x402/facilitator.js';
13
+ export * from './x402/headers.js';
14
+ export * from './x402/parse.js';
15
+ export * from './x402/webhook.js';
16
+ export * from './treasury/balance.js';
17
+ export * from './treasury/inspect-token-account.js';
18
+ export * from './treasury/list-balances.js';
19
+ export * from './treasury/pause.js';
20
+ export * from './tokens/index.js';
21
+ export * from './fees/leash-fee.js';
22
+ export * from './explorer/index.js';
23
+ export * from './format/index.js';
24
+ export * from './wallet/index.js';
25
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qCAAqC,CAAC;AACpD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { RulesV1 } from '@leashmarket/schemas';
2
+ import type { PolicyState } from './state.js';
3
+ export type PolicyRequest = {
4
+ method: string;
5
+ url: string;
6
+ estimatedPrice?: string;
7
+ requestHash: string;
8
+ };
9
+ export type PolicyDecision = {
10
+ decision: 'allow';
11
+ } | {
12
+ decision: 'deny';
13
+ reason: string;
14
+ };
15
+ /** Pure policy gate — no IO. */
16
+ export declare function evaluate(req: PolicyRequest, rules: RulesV1, state: PolicyState): PolicyDecision;
17
+ //# sourceMappingURL=evaluate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../../src/policy/evaluate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAc1F,gCAAgC;AAChC,wBAAgB,QAAQ,CAAC,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,GAAG,cAAc,CAiC/F"}
@@ -0,0 +1,46 @@
1
+ function cmpDecimal(a, b) {
2
+ return Number.parseFloat(a) - Number.parseFloat(b);
3
+ }
4
+ function hostFromUrl(url) {
5
+ try {
6
+ return new URL(url).hostname;
7
+ }
8
+ catch {
9
+ return '';
10
+ }
11
+ }
12
+ /** Pure policy gate — no IO. */
13
+ export function evaluate(req, rules, state) {
14
+ if (state.recentRequestHashes.includes(req.requestHash)) {
15
+ return { decision: 'deny', reason: 'replay' };
16
+ }
17
+ const host = hostFromUrl(req.url);
18
+ if (rules.hosts.deny?.some((h) => host === h || host.endsWith(`.${h}`))) {
19
+ return { decision: 'deny', reason: 'denyHost' };
20
+ }
21
+ if (rules.hosts.allow && rules.hosts.allow.length > 0) {
22
+ const ok = rules.hosts.allow.some((h) => host === h || host.endsWith(`.${h}`));
23
+ if (!ok) {
24
+ return { decision: 'deny', reason: 'allowHost' };
25
+ }
26
+ }
27
+ if (req.estimatedPrice && rules.priceCeiling) {
28
+ if (cmpDecimal(req.estimatedPrice, rules.priceCeiling) > 0) {
29
+ return { decision: 'deny', reason: 'priceCeiling' };
30
+ }
31
+ }
32
+ const nextSpend = req.estimatedPrice !== undefined
33
+ ? String(Number.parseFloat(state.spentToday) + Number.parseFloat(req.estimatedPrice))
34
+ : state.spentToday;
35
+ if (cmpDecimal(state.spentToday, rules.budget.daily) > 0) {
36
+ return { decision: 'deny', reason: 'dailyBudgetExceeded' };
37
+ }
38
+ if (cmpDecimal(nextSpend, rules.budget.daily) > 0) {
39
+ return { decision: 'deny', reason: 'dailyBudgetExceeded' };
40
+ }
41
+ if (req.estimatedPrice && cmpDecimal(req.estimatedPrice, rules.budget.perCall) > 0) {
42
+ return { decision: 'deny', reason: 'perCallMax' };
43
+ }
44
+ return { decision: 'allow' };
45
+ }
46
+ //# sourceMappingURL=evaluate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluate.js","sourceRoot":"","sources":["../../src/policy/evaluate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { RulesV1 } from '@leashmarket/schemas';
2
+ export type PolicyState = {
3
+ rules: RulesV1;
4
+ /** Total spent today in minor units or decimal string — v0.1 uses string decimal for simplicity. */
5
+ spentToday: string;
6
+ recentRequestHashes: string[];
7
+ };
8
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/policy/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAEpD,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,oGAAoG;IACpG,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/policy/state.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ import type { ReceiptV1 } from '@leashmarket/schemas';
2
+ export type ReceiptDraft = Omit<ReceiptV1, 'receipt_hash'>;
3
+ export declare function computeReceiptHash(draft: ReceiptDraft): string;
4
+ export declare function finalizeReceipt(draft: ReceiptDraft): ReceiptV1;
5
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/receipt/build.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAGtD,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;AAE3D,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAE9D;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,CAG9D"}
@@ -0,0 +1,9 @@
1
+ import { canonicalJson, sha256Hex } from './hash.js';
2
+ export function computeReceiptHash(draft) {
3
+ return sha256Hex(canonicalJson(draft));
4
+ }
5
+ export function finalizeReceipt(draft) {
6
+ const receipt_hash = computeReceiptHash(draft);
7
+ return { ...draft, receipt_hash };
8
+ }
9
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/receipt/build.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAIrD,MAAM,UAAU,kBAAkB,CAAC,KAAmB;IACpD,OAAO,SAAS,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAmB;IACjD,MAAM,YAAY,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAC/C,OAAO,EAAE,GAAG,KAAK,EAAE,YAAY,EAAE,CAAC;AACpC,CAAC"}