@nuanu-ai/agentify-contracts 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/NOTICE +2 -0
- package/README.md +32 -0
- package/dist/api.d.ts +1948 -0
- package/dist/api.js +956 -0
- package/dist/card.d.ts +423 -0
- package/dist/card.js +700 -0
- package/dist/envelope.d.ts +174 -0
- package/dist/envelope.js +101 -0
- package/dist/events.d.ts +56 -0
- package/dist/events.js +54 -0
- package/dist/evm-address.d.ts +77 -0
- package/dist/evm-address.js +265 -0
- package/dist/handler.d.ts +90 -0
- package/dist/handler.js +102 -0
- package/dist/index.d.ts +1008 -0
- package/dist/index.js +167 -0
- package/dist/merchant.d.ts +281 -0
- package/dist/merchant.js +433 -0
- package/dist/order-status.d.ts +47 -0
- package/dist/order-status.js +102 -0
- package/dist/order.d.ts +50 -0
- package/dist/order.js +78 -0
- package/dist/param-spec.d.ts +145 -0
- package/dist/param-spec.js +147 -0
- package/dist/primitives.d.ts +103 -0
- package/dist/primitives.js +130 -0
- package/dist/quote.d.ts +79 -0
- package/dist/quote.js +92 -0
- package/dist/receipt.d.ts +79 -0
- package/dist/receipt.js +83 -0
- package/dist/results.d.ts +230 -0
- package/dist/results.js +269 -0
- package/dist/selling.d.ts +24 -0
- package/dist/selling.js +35 -0
- package/package.json +46 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One message on the merchant's worker stream.
|
|
3
|
+
*
|
|
4
|
+
* The stream carries three different things — an order, a question about a
|
|
5
|
+
* price, a notification about an order — and it carries them on one connection
|
|
6
|
+
* because the portal promises the merchant one subscription (ADR-0004 §2).
|
|
7
|
+
* Everything on it is therefore wrapped in the same envelope, whose marker says
|
|
8
|
+
* which of the three the wrapper is holding.
|
|
9
|
+
*
|
|
10
|
+
* Three decisions are worth arguing rather than leaving to be inferred.
|
|
11
|
+
*
|
|
12
|
+
* The payload sits under its own key instead of being spread into the
|
|
13
|
+
* envelope, and the reason is a collision that would be silent. An order
|
|
14
|
+
* already carries an `id`, and that identifier is its idempotency key — the
|
|
15
|
+
* thing a handler answers from on a redelivery. Spread flat, the envelope's own
|
|
16
|
+
* identifier and the order's would be one field, one of the two would win, and
|
|
17
|
+
* whichever lost would be lost without a word. Nesting is what keeps the
|
|
18
|
+
* message's identity and the order's identity from ever being the same string.
|
|
19
|
+
*
|
|
20
|
+
* The key is `payload` for every kind rather than `order`, `quote_request` and
|
|
21
|
+
* `order_event` in turn. Naming it after the kind would say the same thing
|
|
22
|
+
* twice, and two things that say the same thing can disagree — a message
|
|
23
|
+
* marked one way and keyed the other has no reading. With one key the marker is
|
|
24
|
+
* the only place that answers "what is this", and it is checked: the schema
|
|
25
|
+
* holds the payload to the document its kind names, so a price question inside
|
|
26
|
+
* an envelope marked as an order is refused at the boundary rather than handed
|
|
27
|
+
* to an order handler.
|
|
28
|
+
*
|
|
29
|
+
* The kinds are the names the contract registry already publishes for those
|
|
30
|
+
* three documents. That is not decoration: the reader furthest from us has the
|
|
31
|
+
* JSON Schema export and nothing else, and a kind invented separately would be
|
|
32
|
+
* a second vocabulary for documents the export names once. A test holds the two
|
|
33
|
+
* in step.
|
|
34
|
+
*
|
|
35
|
+
* What the envelope does not carry is a handle for acking, because the
|
|
36
|
+
* acknowledgement of each kind is something that already exists: an order is
|
|
37
|
+
* acked by whatever its handler answers — the goods, a refusal, or taking the
|
|
38
|
+
* order on, each against the order's own identifier, and the last of those
|
|
39
|
+
* acknowledges without closing anything — a price question by the reply against
|
|
40
|
+
* `price_id`, and an event by nothing at all. A handle here would be a fourth
|
|
41
|
+
* way to name a message that three surfaces already name.
|
|
42
|
+
*
|
|
43
|
+
* That third one is a gap and not a design, and it decides how far each kind
|
|
44
|
+
* travels. Nothing takes a drawn envelope back after a visibility window: an
|
|
45
|
+
* order arrives again because the order machine decided there should be another
|
|
46
|
+
* attempt, and an event, which no machine is watching for a reply, does not
|
|
47
|
+
* arrive again at all. So an order is delivered at least once and an event at
|
|
48
|
+
* most once, and a consumer that guards one of them the way it guards the other
|
|
49
|
+
* has the wrong guard on both.
|
|
50
|
+
*
|
|
51
|
+
* The merchant never sees any of this. The SDK hands a handler an order, not
|
|
52
|
+
* the envelope it arrived in (ADR-0004 §5); the envelope is the format between
|
|
53
|
+
* the gateway and the SDK, and it lives in this package because both of them
|
|
54
|
+
* read it and neither owns it.
|
|
55
|
+
*/
|
|
56
|
+
import { z } from "zod";
|
|
57
|
+
/**
|
|
58
|
+
* What each kind of envelope is holding, by the name the marker uses.
|
|
59
|
+
*
|
|
60
|
+
* The union below is built from these, so the marker and the check behind it
|
|
61
|
+
* cannot come apart.
|
|
62
|
+
*/
|
|
63
|
+
export declare const WORKER_ENVELOPE_PAYLOADS: Readonly<{
|
|
64
|
+
/** A purchase that is paid for as far as its mode requires, waiting to be delivered. */
|
|
65
|
+
readonly order: z.ZodObject<{
|
|
66
|
+
id: z.ZodString;
|
|
67
|
+
merchant_item_id: z.ZodString;
|
|
68
|
+
params: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
69
|
+
price: z.ZodObject<{
|
|
70
|
+
amount: z.ZodString;
|
|
71
|
+
currency: z.ZodString;
|
|
72
|
+
at: z.ZodISODateTime;
|
|
73
|
+
as_of: z.ZodISODateTime;
|
|
74
|
+
}, z.core.$strict>;
|
|
75
|
+
price_id: z.ZodOptional<z.ZodString>;
|
|
76
|
+
test: z.ZodBoolean;
|
|
77
|
+
}, z.core.$strict>;
|
|
78
|
+
/** "How much is this and is it there", asked before a sale goes through. */
|
|
79
|
+
readonly quote_request: z.ZodObject<{
|
|
80
|
+
merchant_item_id: z.ZodString;
|
|
81
|
+
params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
82
|
+
price_id: z.ZodString;
|
|
83
|
+
purpose: z.ZodEnum<{
|
|
84
|
+
purchase: "purchase";
|
|
85
|
+
poll: "poll";
|
|
86
|
+
}>;
|
|
87
|
+
expires_at: z.ZodISODateTime;
|
|
88
|
+
}, z.core.$strict>;
|
|
89
|
+
/** Something that happened to an order without the merchant doing anything. */
|
|
90
|
+
readonly order_event: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
91
|
+
type: z.ZodLiteral<"order.refund_due">;
|
|
92
|
+
order_id: z.ZodString;
|
|
93
|
+
at: z.ZodISODateTime;
|
|
94
|
+
price: z.ZodObject<{
|
|
95
|
+
amount: z.ZodString;
|
|
96
|
+
currency: z.ZodString;
|
|
97
|
+
}, z.core.$strict>;
|
|
98
|
+
reason: z.ZodEnum<{
|
|
99
|
+
refused: "refused";
|
|
100
|
+
deadline_passed: "deadline_passed";
|
|
101
|
+
merchant_left: "merchant_left";
|
|
102
|
+
}>;
|
|
103
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
104
|
+
type: z.ZodLiteral<"order.unpaid_after_confirmation">;
|
|
105
|
+
order_id: z.ZodString;
|
|
106
|
+
at: z.ZodISODateTime;
|
|
107
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
108
|
+
type: z.ZodLiteral<"order.payment_failed_after_delivery">;
|
|
109
|
+
order_id: z.ZodString;
|
|
110
|
+
at: z.ZodISODateTime;
|
|
111
|
+
}, z.core.$strict>], "type">;
|
|
112
|
+
}>;
|
|
113
|
+
/** The marker one envelope carries, naming what its payload is. */
|
|
114
|
+
export type WorkerEnvelopeKind = keyof typeof WORKER_ENVELOPE_PAYLOADS;
|
|
115
|
+
export declare const WORKER_ENVELOPE_KINDS: readonly WorkerEnvelopeKind[];
|
|
116
|
+
export declare const WorkerEnvelopeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
117
|
+
kind: z.ZodLiteral<"order">;
|
|
118
|
+
id: z.ZodString;
|
|
119
|
+
sent_at: z.ZodISODateTime;
|
|
120
|
+
payload: z.ZodObject<{
|
|
121
|
+
id: z.ZodString;
|
|
122
|
+
merchant_item_id: z.ZodString;
|
|
123
|
+
params: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
124
|
+
price: z.ZodObject<{
|
|
125
|
+
amount: z.ZodString;
|
|
126
|
+
currency: z.ZodString;
|
|
127
|
+
at: z.ZodISODateTime;
|
|
128
|
+
as_of: z.ZodISODateTime;
|
|
129
|
+
}, z.core.$strict>;
|
|
130
|
+
price_id: z.ZodOptional<z.ZodString>;
|
|
131
|
+
test: z.ZodBoolean;
|
|
132
|
+
}, z.core.$strict>;
|
|
133
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
134
|
+
kind: z.ZodLiteral<"quote_request">;
|
|
135
|
+
id: z.ZodString;
|
|
136
|
+
sent_at: z.ZodISODateTime;
|
|
137
|
+
payload: z.ZodObject<{
|
|
138
|
+
merchant_item_id: z.ZodString;
|
|
139
|
+
params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
140
|
+
price_id: z.ZodString;
|
|
141
|
+
purpose: z.ZodEnum<{
|
|
142
|
+
purchase: "purchase";
|
|
143
|
+
poll: "poll";
|
|
144
|
+
}>;
|
|
145
|
+
expires_at: z.ZodISODateTime;
|
|
146
|
+
}, z.core.$strict>;
|
|
147
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
148
|
+
kind: z.ZodLiteral<"order_event">;
|
|
149
|
+
id: z.ZodString;
|
|
150
|
+
sent_at: z.ZodISODateTime;
|
|
151
|
+
payload: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
152
|
+
type: z.ZodLiteral<"order.refund_due">;
|
|
153
|
+
order_id: z.ZodString;
|
|
154
|
+
at: z.ZodISODateTime;
|
|
155
|
+
price: z.ZodObject<{
|
|
156
|
+
amount: z.ZodString;
|
|
157
|
+
currency: z.ZodString;
|
|
158
|
+
}, z.core.$strict>;
|
|
159
|
+
reason: z.ZodEnum<{
|
|
160
|
+
refused: "refused";
|
|
161
|
+
deadline_passed: "deadline_passed";
|
|
162
|
+
merchant_left: "merchant_left";
|
|
163
|
+
}>;
|
|
164
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
165
|
+
type: z.ZodLiteral<"order.unpaid_after_confirmation">;
|
|
166
|
+
order_id: z.ZodString;
|
|
167
|
+
at: z.ZodISODateTime;
|
|
168
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
169
|
+
type: z.ZodLiteral<"order.payment_failed_after_delivery">;
|
|
170
|
+
order_id: z.ZodString;
|
|
171
|
+
at: z.ZodISODateTime;
|
|
172
|
+
}, z.core.$strict>], "type">;
|
|
173
|
+
}, z.core.$strict>], "kind">;
|
|
174
|
+
export type WorkerEnvelope = z.infer<typeof WorkerEnvelopeSchema>;
|
package/dist/envelope.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One message on the merchant's worker stream.
|
|
3
|
+
*
|
|
4
|
+
* The stream carries three different things — an order, a question about a
|
|
5
|
+
* price, a notification about an order — and it carries them on one connection
|
|
6
|
+
* because the portal promises the merchant one subscription (ADR-0004 §2).
|
|
7
|
+
* Everything on it is therefore wrapped in the same envelope, whose marker says
|
|
8
|
+
* which of the three the wrapper is holding.
|
|
9
|
+
*
|
|
10
|
+
* Three decisions are worth arguing rather than leaving to be inferred.
|
|
11
|
+
*
|
|
12
|
+
* The payload sits under its own key instead of being spread into the
|
|
13
|
+
* envelope, and the reason is a collision that would be silent. An order
|
|
14
|
+
* already carries an `id`, and that identifier is its idempotency key — the
|
|
15
|
+
* thing a handler answers from on a redelivery. Spread flat, the envelope's own
|
|
16
|
+
* identifier and the order's would be one field, one of the two would win, and
|
|
17
|
+
* whichever lost would be lost without a word. Nesting is what keeps the
|
|
18
|
+
* message's identity and the order's identity from ever being the same string.
|
|
19
|
+
*
|
|
20
|
+
* The key is `payload` for every kind rather than `order`, `quote_request` and
|
|
21
|
+
* `order_event` in turn. Naming it after the kind would say the same thing
|
|
22
|
+
* twice, and two things that say the same thing can disagree — a message
|
|
23
|
+
* marked one way and keyed the other has no reading. With one key the marker is
|
|
24
|
+
* the only place that answers "what is this", and it is checked: the schema
|
|
25
|
+
* holds the payload to the document its kind names, so a price question inside
|
|
26
|
+
* an envelope marked as an order is refused at the boundary rather than handed
|
|
27
|
+
* to an order handler.
|
|
28
|
+
*
|
|
29
|
+
* The kinds are the names the contract registry already publishes for those
|
|
30
|
+
* three documents. That is not decoration: the reader furthest from us has the
|
|
31
|
+
* JSON Schema export and nothing else, and a kind invented separately would be
|
|
32
|
+
* a second vocabulary for documents the export names once. A test holds the two
|
|
33
|
+
* in step.
|
|
34
|
+
*
|
|
35
|
+
* What the envelope does not carry is a handle for acking, because the
|
|
36
|
+
* acknowledgement of each kind is something that already exists: an order is
|
|
37
|
+
* acked by whatever its handler answers — the goods, a refusal, or taking the
|
|
38
|
+
* order on, each against the order's own identifier, and the last of those
|
|
39
|
+
* acknowledges without closing anything — a price question by the reply against
|
|
40
|
+
* `price_id`, and an event by nothing at all. A handle here would be a fourth
|
|
41
|
+
* way to name a message that three surfaces already name.
|
|
42
|
+
*
|
|
43
|
+
* That third one is a gap and not a design, and it decides how far each kind
|
|
44
|
+
* travels. Nothing takes a drawn envelope back after a visibility window: an
|
|
45
|
+
* order arrives again because the order machine decided there should be another
|
|
46
|
+
* attempt, and an event, which no machine is watching for a reply, does not
|
|
47
|
+
* arrive again at all. So an order is delivered at least once and an event at
|
|
48
|
+
* most once, and a consumer that guards one of them the way it guards the other
|
|
49
|
+
* has the wrong guard on both.
|
|
50
|
+
*
|
|
51
|
+
* The merchant never sees any of this. The SDK hands a handler an order, not
|
|
52
|
+
* the envelope it arrived in (ADR-0004 §5); the envelope is the format between
|
|
53
|
+
* the gateway and the SDK, and it lives in this package because both of them
|
|
54
|
+
* read it and neither owns it.
|
|
55
|
+
*/
|
|
56
|
+
import { z } from "zod";
|
|
57
|
+
import { OrderEventSchema } from "./events.js";
|
|
58
|
+
import { OrderSchema } from "./order.js";
|
|
59
|
+
import { IdentifierSchema, TimestampSchema } from "./primitives.js";
|
|
60
|
+
import { QuoteRequestSchema } from "./quote.js";
|
|
61
|
+
/**
|
|
62
|
+
* What each kind of envelope is holding, by the name the marker uses.
|
|
63
|
+
*
|
|
64
|
+
* The union below is built from these, so the marker and the check behind it
|
|
65
|
+
* cannot come apart.
|
|
66
|
+
*/
|
|
67
|
+
export const WORKER_ENVELOPE_PAYLOADS = Object.freeze({
|
|
68
|
+
/** A purchase that is paid for as far as its mode requires, waiting to be delivered. */
|
|
69
|
+
order: OrderSchema,
|
|
70
|
+
/** "How much is this and is it there", asked before a sale goes through. */
|
|
71
|
+
quote_request: QuoteRequestSchema,
|
|
72
|
+
/** Something that happened to an order without the merchant doing anything. */
|
|
73
|
+
order_event: OrderEventSchema,
|
|
74
|
+
});
|
|
75
|
+
export const WORKER_ENVELOPE_KINDS = Object.freeze(Object.keys(WORKER_ENVELOPE_PAYLOADS));
|
|
76
|
+
/**
|
|
77
|
+
* The fields every envelope carries, whatever it is holding.
|
|
78
|
+
*
|
|
79
|
+
* `id` names this message and `sent_at` names when it went out. What the pair is
|
|
80
|
+
* not is a way to recognise a repeat, and reading it as one costs a merchant
|
|
81
|
+
* goods: an order the gateway decides to send again is wrapped in a fresh
|
|
82
|
+
* envelope with a fresh `id`, so the only thing two attempts at one order have
|
|
83
|
+
* in common is the order they carry. The gateway does put an envelope back on the stream with its `id`
|
|
84
|
+
* untouched, but only on the paths where it drew that envelope and then handed
|
|
85
|
+
* it to nobody — so an identifier a worker has actually seen never comes round a
|
|
86
|
+
* second time. What holds still across a repeat is the order's own identifier
|
|
87
|
+
* inside the payload, which is what its handler answers from and what the portal
|
|
88
|
+
* tells a merchant to key on. An event needs neither, having no repeat to
|
|
89
|
+
* recognise.
|
|
90
|
+
*/
|
|
91
|
+
const envelopeOf = (kind, payload) => z.strictObject({
|
|
92
|
+
kind: z.literal(kind),
|
|
93
|
+
id: IdentifierSchema,
|
|
94
|
+
sent_at: TimestampSchema,
|
|
95
|
+
payload,
|
|
96
|
+
});
|
|
97
|
+
export const WorkerEnvelopeSchema = z.discriminatedUnion("kind", [
|
|
98
|
+
envelopeOf("order", WORKER_ENVELOPE_PAYLOADS.order),
|
|
99
|
+
envelopeOf("quote_request", WORKER_ENVELOPE_PAYLOADS.quote_request),
|
|
100
|
+
envelopeOf("order_event", WORKER_ENVELOPE_PAYLOADS.order_event),
|
|
101
|
+
]);
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The things that happen to an order without the merchant doing anything, and
|
|
3
|
+
* that they would otherwise only find by reconciling records by hand.
|
|
4
|
+
*
|
|
5
|
+
* They arrive on the same subscription as the orders and want no answer: an
|
|
6
|
+
* event notifies, it does not ask for work. The catalog is a minimum rather
|
|
7
|
+
* than a closed list, and it grows by a shape being added here — never by a
|
|
8
|
+
* sender inventing a name, because a consumer switching on the type has to be
|
|
9
|
+
* able to read an unknown one as a version mismatch rather than a typo.
|
|
10
|
+
*
|
|
11
|
+
* The names are prefixed by what they are about. Everything here is about an
|
|
12
|
+
* order; something that is not will not have to fight for a name.
|
|
13
|
+
*/
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
export declare const ORDER_EVENT_TYPES: Readonly<{
|
|
16
|
+
/** The money moved and the delivery did not happen. */
|
|
17
|
+
readonly REFUND_DUE: "order.refund_due";
|
|
18
|
+
/** The merchant said they would deliver and the agent never paid. */
|
|
19
|
+
readonly UNPAID_AFTER_CONFIRMATION: "order.unpaid_after_confirmation";
|
|
20
|
+
/** The merchant delivered synchronously and the payment did not execute. */
|
|
21
|
+
readonly PAYMENT_FAILED_AFTER_DELIVERY: "order.payment_failed_after_delivery";
|
|
22
|
+
}>;
|
|
23
|
+
/**
|
|
24
|
+
* How an order came to owe a refund: the merchant refused after the money had
|
|
25
|
+
* moved, their delivery deadline passed, or they left and their open orders
|
|
26
|
+
* closed with them.
|
|
27
|
+
*/
|
|
28
|
+
export declare const RefundDueReasonSchema: z.ZodEnum<{
|
|
29
|
+
refused: "refused";
|
|
30
|
+
deadline_passed: "deadline_passed";
|
|
31
|
+
merchant_left: "merchant_left";
|
|
32
|
+
}>;
|
|
33
|
+
export declare const OrderEventSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
34
|
+
type: z.ZodLiteral<"order.refund_due">;
|
|
35
|
+
order_id: z.ZodString;
|
|
36
|
+
at: z.ZodISODateTime;
|
|
37
|
+
price: z.ZodObject<{
|
|
38
|
+
amount: z.ZodString;
|
|
39
|
+
currency: z.ZodString;
|
|
40
|
+
}, z.core.$strict>;
|
|
41
|
+
reason: z.ZodEnum<{
|
|
42
|
+
refused: "refused";
|
|
43
|
+
deadline_passed: "deadline_passed";
|
|
44
|
+
merchant_left: "merchant_left";
|
|
45
|
+
}>;
|
|
46
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
47
|
+
type: z.ZodLiteral<"order.unpaid_after_confirmation">;
|
|
48
|
+
order_id: z.ZodString;
|
|
49
|
+
at: z.ZodISODateTime;
|
|
50
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
51
|
+
type: z.ZodLiteral<"order.payment_failed_after_delivery">;
|
|
52
|
+
order_id: z.ZodString;
|
|
53
|
+
at: z.ZodISODateTime;
|
|
54
|
+
}, z.core.$strict>], "type">;
|
|
55
|
+
export type RefundDueReason = z.infer<typeof RefundDueReasonSchema>;
|
|
56
|
+
export type OrderEvent = z.infer<typeof OrderEventSchema>;
|
package/dist/events.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The things that happen to an order without the merchant doing anything, and
|
|
3
|
+
* that they would otherwise only find by reconciling records by hand.
|
|
4
|
+
*
|
|
5
|
+
* They arrive on the same subscription as the orders and want no answer: an
|
|
6
|
+
* event notifies, it does not ask for work. The catalog is a minimum rather
|
|
7
|
+
* than a closed list, and it grows by a shape being added here — never by a
|
|
8
|
+
* sender inventing a name, because a consumer switching on the type has to be
|
|
9
|
+
* able to read an unknown one as a version mismatch rather than a typo.
|
|
10
|
+
*
|
|
11
|
+
* The names are prefixed by what they are about. Everything here is about an
|
|
12
|
+
* order; something that is not will not have to fight for a name.
|
|
13
|
+
*/
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
import { IdentifierSchema, MoneySchema, TimestampSchema } from "./primitives.js";
|
|
16
|
+
export const ORDER_EVENT_TYPES = Object.freeze({
|
|
17
|
+
/** The money moved and the delivery did not happen. */
|
|
18
|
+
REFUND_DUE: "order.refund_due",
|
|
19
|
+
/** The merchant said they would deliver and the agent never paid. */
|
|
20
|
+
UNPAID_AFTER_CONFIRMATION: "order.unpaid_after_confirmation",
|
|
21
|
+
/** The merchant delivered synchronously and the payment did not execute. */
|
|
22
|
+
PAYMENT_FAILED_AFTER_DELIVERY: "order.payment_failed_after_delivery",
|
|
23
|
+
});
|
|
24
|
+
/**
|
|
25
|
+
* How an order came to owe a refund: the merchant refused after the money had
|
|
26
|
+
* moved, their delivery deadline passed, or they left and their open orders
|
|
27
|
+
* closed with them.
|
|
28
|
+
*/
|
|
29
|
+
export const RefundDueReasonSchema = z.enum(["refused", "deadline_passed", "merchant_left"]);
|
|
30
|
+
export const OrderEventSchema = z.discriminatedUnion("type", [
|
|
31
|
+
z.strictObject({
|
|
32
|
+
type: z.literal(ORDER_EVENT_TYPES.REFUND_DUE),
|
|
33
|
+
order_id: IdentifierSchema,
|
|
34
|
+
at: TimestampSchema,
|
|
35
|
+
/**
|
|
36
|
+
* The sum owed, which is the price the sale went through at. The payment
|
|
37
|
+
* went from the buyer straight to the merchant's wallet and never passed
|
|
38
|
+
* through us, so sending it back is theirs to do — and an event naming
|
|
39
|
+
* only the order would leave them looking the sum up.
|
|
40
|
+
*/
|
|
41
|
+
price: MoneySchema,
|
|
42
|
+
reason: RefundDueReasonSchema,
|
|
43
|
+
}),
|
|
44
|
+
z.strictObject({
|
|
45
|
+
type: z.literal(ORDER_EVENT_TYPES.UNPAID_AFTER_CONFIRMATION),
|
|
46
|
+
order_id: IdentifierSchema,
|
|
47
|
+
at: TimestampSchema,
|
|
48
|
+
}),
|
|
49
|
+
z.strictObject({
|
|
50
|
+
type: z.literal(ORDER_EVENT_TYPES.PAYMENT_FAILED_AFTER_DELIVERY),
|
|
51
|
+
order_id: IdentifierSchema,
|
|
52
|
+
at: TimestampSchema,
|
|
53
|
+
}),
|
|
54
|
+
]);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The address money can be sent to on an EVM chain, and the one thing that can
|
|
3
|
+
* be checked about one before it is used.
|
|
4
|
+
*
|
|
5
|
+
* An address is twenty bytes and nothing about those bytes is redundant, so a
|
|
6
|
+
* character lost or mistyped in one produces another perfectly valid address —
|
|
7
|
+
* belonging to nobody, or to somebody else, and a payment to it is gone. EIP-55
|
|
8
|
+
* is the only guard anybody has against that: the capital letters in the
|
|
9
|
+
* spelling a wallet shows are not decoration, they are a checksum over the
|
|
10
|
+
* address itself, and a character that is wrong stops the capitals agreeing
|
|
11
|
+
* with the digits. Checked here, a mistyped address is a sentence a merchant
|
|
12
|
+
* reads while they still have the right one in front of them; not checked, it
|
|
13
|
+
* is a stranger's wallet filling up with somebody's sales.
|
|
14
|
+
*
|
|
15
|
+
* Two spellings are accepted and no others. All lower case is the spelling that
|
|
16
|
+
* carries no checksum at all, and it is what a block explorer prints and what
|
|
17
|
+
* half the tooling in this world hands somebody, so refusing it would refuse
|
|
18
|
+
* addresses that are perfectly good. The other is the exact output of the
|
|
19
|
+
* checksum, which is what a wallet puts on the clipboard. Anything between the
|
|
20
|
+
* two is a string that has been through something: a case-mangling copy, a
|
|
21
|
+
* document that shouted it, or a keystroke in the wrong place. The refusal says
|
|
22
|
+
* so and says what to do instead, because "invalid address" leaves a person
|
|
23
|
+
* staring at forty characters that look fine.
|
|
24
|
+
*
|
|
25
|
+
* Of the two, the wallet's is the canon: the door takes both spellings and
|
|
26
|
+
* everything behind it holds one (ADR-0017), and `checksummedAddressOf` below
|
|
27
|
+
* is what writes it. The reason is about the person rather than about the
|
|
28
|
+
* storage, and it is written where that function is.
|
|
29
|
+
*
|
|
30
|
+
* The hash is written out below rather than taken from a package, and that is a
|
|
31
|
+
* decision rather than an oversight. This package's runtime dependency tree is
|
|
32
|
+
* `zod` and nothing else, and it is the whole of what a merchant installs with
|
|
33
|
+
* the SDK — ADR-0003 §8 makes every addition to it a decision of its own, and
|
|
34
|
+
* that decision is not this file's to take. What is written out is therefore
|
|
35
|
+
* kept as small as the job allows: twenty-four rounds of a fixed permutation
|
|
36
|
+
* over one block, with no configuration and no second use, and it is pinned by
|
|
37
|
+
* the vectors the standard publishes, so an implementation that is wrong
|
|
38
|
+
* anywhere fails against somebody else's arithmetic rather than agreeing with
|
|
39
|
+
* itself. The cost is named too: this is hand-written cryptographic code in a
|
|
40
|
+
* published package, and the trigger to replace it with an audited library is
|
|
41
|
+
* the first other thing in this package that needs a hash.
|
|
42
|
+
*/
|
|
43
|
+
import { z } from "zod";
|
|
44
|
+
/**
|
|
45
|
+
* One address written the way the wallet it came out of displays it: the
|
|
46
|
+
* canonical form, and the one anything behind the door keeps.
|
|
47
|
+
*
|
|
48
|
+
* The door takes two spellings and everything behind it holds one (ADR-0017),
|
|
49
|
+
* and this is the one, because it is the one a person recognises. A merchant
|
|
50
|
+
* pastes forty characters out of their wallet and then reads them back on a
|
|
51
|
+
* settings screen; handed the same address in lower case they cannot tell it
|
|
52
|
+
* from a different address without comparing character by character, and the
|
|
53
|
+
* one field in this system where that matters is the field money is sent to.
|
|
54
|
+
* Lower case would be the easier canon to compute and the harder one to check
|
|
55
|
+
* by eye, and the eye is the only thing checking.
|
|
56
|
+
*
|
|
57
|
+
* It refuses what the schema refuses rather than quietly repairing it. Given a
|
|
58
|
+
* mixed-case address whose letters disagree with its digits, capitalising it
|
|
59
|
+
* afresh would produce a perfectly well-formed address that nobody typed —
|
|
60
|
+
* which is the exact failure the checksum exists to catch, arrived at by the
|
|
61
|
+
* thing meant to enforce it.
|
|
62
|
+
*
|
|
63
|
+
* Given an address that is already in this form it answers with the same
|
|
64
|
+
* string, so writing one down twice writes the same thing twice.
|
|
65
|
+
*/
|
|
66
|
+
export declare function checksummedAddressOf(address: string): string;
|
|
67
|
+
/**
|
|
68
|
+
* An address on an EVM chain, in one of the two spellings anybody writes one
|
|
69
|
+
* in.
|
|
70
|
+
*
|
|
71
|
+
* It carries no chain of its own. The same twenty bytes are an address on every
|
|
72
|
+
* EVM network, and which network a payment is made on is settled by the payment
|
|
73
|
+
* requirements rather than by the address inside them; a schema that claimed
|
|
74
|
+
* otherwise would be claiming knowledge this package does not have.
|
|
75
|
+
*/
|
|
76
|
+
export declare const EvmAddressSchema: z.ZodString;
|
|
77
|
+
export type EvmAddress = z.infer<typeof EvmAddressSchema>;
|