@polygonlabs/staker-pool-allocations-schemas 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.
- package/MIGRATION.md +5 -0
- package/dist/allocation-history.d.ts +30 -0
- package/dist/allocation-history.d.ts.map +1 -0
- package/dist/allocation-history.js +29 -0
- package/dist/allocation-history.js.map +1 -0
- package/dist/allocation.d.ts +31 -0
- package/dist/allocation.d.ts.map +1 -0
- package/dist/allocation.js +40 -0
- package/dist/allocation.js.map +1 -0
- package/dist/codegen.d.ts +6 -0
- package/dist/codegen.d.ts.map +1 -0
- package/dist/codegen.js +32 -0
- package/dist/codegen.js.map +1 -0
- package/dist/common.d.ts +19 -0
- package/dist/common.d.ts.map +1 -0
- package/dist/common.js +63 -0
- package/dist/common.js.map +1 -0
- package/dist/distribution.d.ts +60 -0
- package/dist/distribution.d.ts.map +1 -0
- package/dist/distribution.js +43 -0
- package/dist/distribution.js.map +1 -0
- package/dist/error.d.ts +10 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +15 -0
- package/dist/error.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/management.d.ts +80 -0
- package/dist/management.d.ts.map +1 -0
- package/dist/management.js +126 -0
- package/dist/management.js.map +1 -0
- package/dist/proof.d.ts +10 -0
- package/dist/proof.d.ts.map +1 -0
- package/dist/proof.js +25 -0
- package/dist/proof.js.map +1 -0
- package/dist/registry-management.d.ts +525 -0
- package/dist/registry-management.d.ts.map +1 -0
- package/dist/registry-management.js +33 -0
- package/dist/registry-management.js.map +1 -0
- package/dist/registry-public.d.ts +1323 -0
- package/dist/registry-public.d.ts.map +1 -0
- package/dist/registry-public.js +31 -0
- package/dist/registry-public.js.map +1 -0
- package/dist/routes-management/distributions.d.ts +137 -0
- package/dist/routes-management/distributions.d.ts.map +1 -0
- package/dist/routes-management/distributions.js +72 -0
- package/dist/routes-management/distributions.js.map +1 -0
- package/dist/routes-management/excluded-addresses.d.ts +132 -0
- package/dist/routes-management/excluded-addresses.d.ts.map +1 -0
- package/dist/routes-management/excluded-addresses.js +84 -0
- package/dist/routes-management/excluded-addresses.js.map +1 -0
- package/dist/routes-management/operational.d.ts +48 -0
- package/dist/routes-management/operational.d.ts.map +1 -0
- package/dist/routes-management/operational.js +47 -0
- package/dist/routes-management/operational.js.map +1 -0
- package/dist/routes-public/addresses.d.ts +79 -0
- package/dist/routes-public/addresses.d.ts.map +1 -0
- package/dist/routes-public/addresses.js +45 -0
- package/dist/routes-public/addresses.js.map +1 -0
- package/dist/routes-public/allocations.d.ts +155 -0
- package/dist/routes-public/allocations.d.ts.map +1 -0
- package/dist/routes-public/allocations.js +81 -0
- package/dist/routes-public/allocations.js.map +1 -0
- package/dist/routes-public/distributions.d.ts +123 -0
- package/dist/routes-public/distributions.d.ts.map +1 -0
- package/dist/routes-public/distributions.js +55 -0
- package/dist/routes-public/distributions.js.map +1 -0
- package/dist/routes-public/operational.d.ts +46 -0
- package/dist/routes-public/operational.d.ts.map +1 -0
- package/dist/routes-public/operational.js +36 -0
- package/dist/routes-public/operational.js.map +1 -0
- package/dist/routes-public/proofs.d.ts +50 -0
- package/dist/routes-public/proofs.d.ts.map +1 -0
- package/dist/routes-public/proofs.js +34 -0
- package/dist/routes-public/proofs.js.map +1 -0
- package/dist/routes-public/wire.d.ts +60 -0
- package/dist/routes-public/wire.d.ts.map +1 -0
- package/dist/routes-public/wire.js +62 -0
- package/dist/routes-public/wire.js.map +1 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/dist/unclaimed-proofs.d.ts +20 -0
- package/dist/unclaimed-proofs.d.ts.map +1 -0
- package/dist/unclaimed-proofs.js +22 -0
- package/dist/unclaimed-proofs.js.map +1 -0
- package/dist/zod.d.ts +3 -0
- package/dist/zod.d.ts.map +1 -0
- package/dist/zod.js +8 -0
- package/dist/zod.js.map +1 -0
- package/openapi.json +1215 -0
- package/package.json +49 -0
- package/src/allocation-history.ts +35 -0
- package/src/allocation.ts +51 -0
- package/src/codegen.ts +50 -0
- package/src/common.ts +76 -0
- package/src/distribution.ts +52 -0
- package/src/error.ts +17 -0
- package/src/index.ts +16 -0
- package/src/management.ts +158 -0
- package/src/proof.ts +27 -0
- package/src/registry-management.ts +36 -0
- package/src/registry-public.ts +33 -0
- package/src/routes-management/distributions.ts +87 -0
- package/src/routes-management/excluded-addresses.ts +103 -0
- package/src/routes-management/operational.ts +60 -0
- package/src/routes-public/addresses.ts +58 -0
- package/src/routes-public/allocations.ts +104 -0
- package/src/routes-public/distributions.ts +69 -0
- package/src/routes-public/operational.ts +55 -0
- package/src/routes-public/proofs.ts +46 -0
- package/src/routes-public/wire.ts +74 -0
- package/src/unclaimed-proofs.ts +27 -0
- package/src/zod.ts +9 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { RouteWithOpId, TypedRegistry } from '@polygonlabs/openapi-registry';
|
|
2
|
+
|
|
3
|
+
import { ErrorResponseSchema } from '@polygonlabs/openapi-registry/error-schemas';
|
|
4
|
+
|
|
5
|
+
import { NetworkSchema, DistributionIdSchema } from '../common.ts';
|
|
6
|
+
import {
|
|
7
|
+
DistributionDeleteResponseSchema,
|
|
8
|
+
DistributionTriggerRequestSchema,
|
|
9
|
+
DistributionTriggerResponseSchema
|
|
10
|
+
} from '../management.ts';
|
|
11
|
+
import { z } from '../zod.ts';
|
|
12
|
+
|
|
13
|
+
// Management distribution routes. Both carry `security: [{ ApiKeyAuth: [] }]`
|
|
14
|
+
// — the registry-driven router runs the ApiKeyAuth handler before request
|
|
15
|
+
// validation, so an unauthenticated request 401s without parsing the body.
|
|
16
|
+
// 400 (validation), 401 (auth), and 500 are auto-injected by TypedRegistry
|
|
17
|
+
// from the declared request/security shape; only the handler-emitted codes
|
|
18
|
+
// (404, 409) are declared explicitly here.
|
|
19
|
+
export const addManagementDistributionRoutes = <
|
|
20
|
+
Ops extends Record<string, RouteWithOpId>,
|
|
21
|
+
Schemes extends Record<string, true>
|
|
22
|
+
>(
|
|
23
|
+
r: TypedRegistry<Ops, Schemes>
|
|
24
|
+
) =>
|
|
25
|
+
r
|
|
26
|
+
.registerPath({
|
|
27
|
+
operationId: 'triggerDistribution',
|
|
28
|
+
method: 'post',
|
|
29
|
+
path: '/v1/{network}/management/distributions',
|
|
30
|
+
summary: 'Trigger a new distribution run',
|
|
31
|
+
description:
|
|
32
|
+
'Kicks off the in-process worker against the specified PoS block range. ' +
|
|
33
|
+
"Async — returns 202 with the row's current status and the worker runs " +
|
|
34
|
+
'out-of-band on the same pod. Operators poll the public distributions ' +
|
|
35
|
+
'endpoint (or the management :id view, Phase 1 follow-up) for terminal ' +
|
|
36
|
+
'state.\n\n' +
|
|
37
|
+
'Requires `x-api-key` header. Idempotent on `distributionId` when supplied. ' +
|
|
38
|
+
'Set `force: true` to re-run a completed/failed distribution covering the ' +
|
|
39
|
+
'same range — the prior row + allocations are hard-deleted before the ' +
|
|
40
|
+
'worker enqueues a new run.',
|
|
41
|
+
security: [{ ApiKeyAuth: [] }],
|
|
42
|
+
request: {
|
|
43
|
+
params: z.object({ network: NetworkSchema }),
|
|
44
|
+
body: {
|
|
45
|
+
content: { 'application/json': { schema: DistributionTriggerRequestSchema } }
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
responses: {
|
|
49
|
+
202: {
|
|
50
|
+
description: 'Trigger accepted. Status reflects the row at the moment of acceptance.',
|
|
51
|
+
content: { 'application/json': { schema: DistributionTriggerResponseSchema } }
|
|
52
|
+
},
|
|
53
|
+
409: {
|
|
54
|
+
description: '`force: true` on a non-terminal distribution.',
|
|
55
|
+
content: { 'application/json': { schema: ErrorResponseSchema } }
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
.registerPath({
|
|
60
|
+
operationId: 'deleteDistribution',
|
|
61
|
+
method: 'delete',
|
|
62
|
+
path: '/v1/{network}/management/distributions/{distributionId}',
|
|
63
|
+
summary: 'Remove a stale distribution',
|
|
64
|
+
description:
|
|
65
|
+
'Requires `x-api-key`. Hard-deletes a `pending` / `running` / `failed` distribution ' +
|
|
66
|
+
'and its allocations — recovery for an orphaned run (e.g. a worker OOM-killed mid-run ' +
|
|
67
|
+
'leaves the row stuck at `running`). Refuses `completed` distributions (409): those are ' +
|
|
68
|
+
'published merkle roots claimers depend on — replace one via a force re-run instead.',
|
|
69
|
+
security: [{ ApiKeyAuth: [] }],
|
|
70
|
+
request: {
|
|
71
|
+
params: z.object({ network: NetworkSchema, distributionId: DistributionIdSchema })
|
|
72
|
+
},
|
|
73
|
+
responses: {
|
|
74
|
+
200: {
|
|
75
|
+
description: 'The distribution and its allocations were removed.',
|
|
76
|
+
content: { 'application/json': { schema: DistributionDeleteResponseSchema } }
|
|
77
|
+
},
|
|
78
|
+
404: {
|
|
79
|
+
description: 'No distribution with that (network, id).',
|
|
80
|
+
content: { 'application/json': { schema: ErrorResponseSchema } }
|
|
81
|
+
},
|
|
82
|
+
409: {
|
|
83
|
+
description: 'The distribution is `completed` and cannot be deleted.',
|
|
84
|
+
content: { 'application/json': { schema: ErrorResponseSchema } }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { RouteWithOpId, TypedRegistry } from '@polygonlabs/openapi-registry';
|
|
2
|
+
|
|
3
|
+
import { AddressSchema, NetworkSchema } from '../common.ts';
|
|
4
|
+
import {
|
|
5
|
+
AddExcludedAddressRequestSchema,
|
|
6
|
+
ManagedExcludedAddressSchema,
|
|
7
|
+
RemoveExcludedAddressResponseSchema
|
|
8
|
+
} from '../management.ts';
|
|
9
|
+
import { z } from '../zod.ts';
|
|
10
|
+
|
|
11
|
+
// Response-side variants of the excluded-address schemas.
|
|
12
|
+
//
|
|
13
|
+
// The registry-driven router re-encodes every response via `z.encode`
|
|
14
|
+
// against the declared schema, and `z.encode` rejects unidirectional
|
|
15
|
+
// `.transform()`s ("Encountered unidirectional transform during encode").
|
|
16
|
+
// `AddressSchema` lowercases via a one-way transform, so the shared
|
|
17
|
+
// response schemas that embed it can't sit in a response slot. These
|
|
18
|
+
// variants swap the address field for a plain lowercase-only validator —
|
|
19
|
+
// handlers always emit the normalised form, so the constraint tightens
|
|
20
|
+
// nothing on the wire. Same OpenAPI component names as the originals, so
|
|
21
|
+
// the served spec is unchanged. Request-side use of `AddressSchema`
|
|
22
|
+
// (body, path params) is unaffected: request validation decodes forward.
|
|
23
|
+
const AddressOutputSchema = z
|
|
24
|
+
.string()
|
|
25
|
+
.regex(/^0x[0-9a-f]{40}$/)
|
|
26
|
+
.openapi({ example: '0xabc0000000000000000000000000000000000001' });
|
|
27
|
+
|
|
28
|
+
const ManagedExcludedAddressOutputSchema = ManagedExcludedAddressSchema.extend({
|
|
29
|
+
address: AddressOutputSchema
|
|
30
|
+
}).openapi('ManagedExcludedAddress');
|
|
31
|
+
|
|
32
|
+
const ExcludedAddressListOutputSchema = z
|
|
33
|
+
.object({
|
|
34
|
+
items: z.array(ManagedExcludedAddressOutputSchema)
|
|
35
|
+
})
|
|
36
|
+
.openapi('ExcludedAddressListResponse');
|
|
37
|
+
|
|
38
|
+
const RemoveExcludedAddressOutputSchema = RemoveExcludedAddressResponseSchema.extend({
|
|
39
|
+
address: AddressOutputSchema
|
|
40
|
+
}).openapi('RemoveExcludedAddressResponse');
|
|
41
|
+
|
|
42
|
+
// Excluded-address CRUD. All three carry `security: [{ ApiKeyAuth: [] }]`;
|
|
43
|
+
// 400/401/500 are auto-injected by TypedRegistry from the declared
|
|
44
|
+
// request/security shape.
|
|
45
|
+
export const addExcludedAddressRoutes = <
|
|
46
|
+
Ops extends Record<string, RouteWithOpId>,
|
|
47
|
+
Schemes extends Record<string, true>
|
|
48
|
+
>(
|
|
49
|
+
r: TypedRegistry<Ops, Schemes>
|
|
50
|
+
) =>
|
|
51
|
+
r
|
|
52
|
+
.registerPath({
|
|
53
|
+
operationId: 'listExcludedAddresses',
|
|
54
|
+
method: 'get',
|
|
55
|
+
path: '/v1/{network}/management/excluded-addresses',
|
|
56
|
+
summary: 'List addresses excluded from the merkle tree (settled off-chain)',
|
|
57
|
+
description:
|
|
58
|
+
'Requires `x-api-key`. Returns the curated excluded-address list for the network.',
|
|
59
|
+
security: [{ ApiKeyAuth: [] }],
|
|
60
|
+
request: { params: z.object({ network: NetworkSchema }) },
|
|
61
|
+
responses: {
|
|
62
|
+
200: {
|
|
63
|
+
description: 'The excluded-address list.',
|
|
64
|
+
content: { 'application/json': { schema: ExcludedAddressListOutputSchema } }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
.registerPath({
|
|
69
|
+
operationId: 'addExcludedAddress',
|
|
70
|
+
method: 'post',
|
|
71
|
+
path: '/v1/{network}/management/excluded-addresses',
|
|
72
|
+
summary: 'Add (or re-label) an excluded address',
|
|
73
|
+
description:
|
|
74
|
+
'Requires `x-api-key`. Upserts on (network, address) — re-POSTing an existing ' +
|
|
75
|
+
'address updates its label. Takes effect on the next distribution run.',
|
|
76
|
+
security: [{ ApiKeyAuth: [] }],
|
|
77
|
+
request: {
|
|
78
|
+
params: z.object({ network: NetworkSchema }),
|
|
79
|
+
body: { content: { 'application/json': { schema: AddExcludedAddressRequestSchema } } }
|
|
80
|
+
},
|
|
81
|
+
responses: {
|
|
82
|
+
200: {
|
|
83
|
+
description: 'The upserted excluded-address row.',
|
|
84
|
+
content: { 'application/json': { schema: ManagedExcludedAddressOutputSchema } }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
.registerPath({
|
|
89
|
+
operationId: 'removeExcludedAddress',
|
|
90
|
+
method: 'delete',
|
|
91
|
+
path: '/v1/{network}/management/excluded-addresses/{address}',
|
|
92
|
+
summary: 'Remove an excluded address',
|
|
93
|
+
description:
|
|
94
|
+
'Requires `x-api-key`. Idempotent — `removed: false` when the address was not present.',
|
|
95
|
+
security: [{ ApiKeyAuth: [] }],
|
|
96
|
+
request: { params: z.object({ network: NetworkSchema, address: AddressSchema }) },
|
|
97
|
+
responses: {
|
|
98
|
+
200: {
|
|
99
|
+
description: 'Removal result.',
|
|
100
|
+
content: { 'application/json': { schema: RemoveExcludedAddressOutputSchema } }
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { RouteWithOpId, TypedRegistry } from '@polygonlabs/openapi-registry';
|
|
2
|
+
|
|
3
|
+
import { z } from '../zod.ts';
|
|
4
|
+
|
|
5
|
+
// /health-check is the K8s liveness probe — answers as long as the process
|
|
6
|
+
// can serve HTTP at all. Registered at root (no /v1 prefix) because dry-dock
|
|
7
|
+
// pod specs declare the probe path as `/health-check`, not `/v1/...`.
|
|
8
|
+
//
|
|
9
|
+
// /readiness is the degraded-mode signal — distinguishes "fully ready" from
|
|
10
|
+
// "partially degraded but still serving" once dependencies are wired (DB +
|
|
11
|
+
// subgraphs). Still a 200 stub; path matches the lst-api precedent.
|
|
12
|
+
//
|
|
13
|
+
// Neither carries `security` — pod probes are unauthenticated by design.
|
|
14
|
+
export const OperationalStatusResponseSchema = z
|
|
15
|
+
.object({ status: z.literal('ok') })
|
|
16
|
+
.openapi('OperationalStatusResponse');
|
|
17
|
+
|
|
18
|
+
export type OperationalStatusResponse = z.infer<typeof OperationalStatusResponseSchema>;
|
|
19
|
+
|
|
20
|
+
// Generic over the parent's `Ops` and `Schemes` so the helper preserves
|
|
21
|
+
// everything the caller had already registered; the chained `registerPath`
|
|
22
|
+
// returns carry the cumulative narrow back through `.with(...)`.
|
|
23
|
+
export const addOperationalRoutes = <
|
|
24
|
+
Ops extends Record<string, RouteWithOpId>,
|
|
25
|
+
Schemes extends Record<string, true>
|
|
26
|
+
>(
|
|
27
|
+
r: TypedRegistry<Ops, Schemes>
|
|
28
|
+
) =>
|
|
29
|
+
r
|
|
30
|
+
.registerPath({
|
|
31
|
+
operationId: 'getHealthCheck',
|
|
32
|
+
method: 'get',
|
|
33
|
+
path: '/health-check',
|
|
34
|
+
summary: 'Liveness probe',
|
|
35
|
+
description:
|
|
36
|
+
'K8s liveness probe — answers as long as the process can serve HTTP. ' +
|
|
37
|
+
'Unauthenticated; dry-dock pod specs hit this exact root path.',
|
|
38
|
+
responses: {
|
|
39
|
+
200: {
|
|
40
|
+
description: 'Service is alive.',
|
|
41
|
+
content: { 'application/json': { schema: OperationalStatusResponseSchema } }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
.registerPath({
|
|
46
|
+
operationId: 'getReadiness',
|
|
47
|
+
method: 'get',
|
|
48
|
+
path: '/readiness',
|
|
49
|
+
summary: 'Readiness probe',
|
|
50
|
+
description:
|
|
51
|
+
'Degraded-mode signal — distinguishes "fully ready" from "partially ' +
|
|
52
|
+
'degraded but still serving". Currently a 200 stub; the real DB + ' +
|
|
53
|
+
'subgraph reachability probe is outstanding work. Unauthenticated.',
|
|
54
|
+
responses: {
|
|
55
|
+
200: {
|
|
56
|
+
description: 'Service is ready.',
|
|
57
|
+
content: { 'application/json': { schema: OperationalStatusResponseSchema } }
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Per-address read routes spanning all completed distributions on one
|
|
2
|
+
// network: the cross-distribution allocation history (with live claim
|
|
3
|
+
// status) and the batched unclaimed-proofs payload.
|
|
4
|
+
import type { RouteWithOpId, TypedRegistry } from '@polygonlabs/openapi-registry';
|
|
5
|
+
|
|
6
|
+
import { AddressSchema, NetworkSchema } from '../common.ts';
|
|
7
|
+
import { UnclaimedProofsResponseSchema } from '../unclaimed-proofs.ts';
|
|
8
|
+
import { z } from '../zod.ts';
|
|
9
|
+
import { AllocationHistoryResponseWireSchema } from './wire.ts';
|
|
10
|
+
|
|
11
|
+
export const addAddressRoutes = <
|
|
12
|
+
Ops extends Record<string, RouteWithOpId>,
|
|
13
|
+
Schemes extends Record<string, true>
|
|
14
|
+
>(
|
|
15
|
+
r: TypedRegistry<Ops, Schemes>
|
|
16
|
+
) =>
|
|
17
|
+
r
|
|
18
|
+
.registerPath({
|
|
19
|
+
operationId: 'getAddressAllocationHistory',
|
|
20
|
+
method: 'get',
|
|
21
|
+
path: '/v1/{network}/addresses/{address}/allocations',
|
|
22
|
+
summary: 'Per-address cross-distribution allocation history with live claim status',
|
|
23
|
+
description:
|
|
24
|
+
'Returns every allocation row for the given address across all completed ' +
|
|
25
|
+
'distributions on the network, enriched with `status: "claimed" | "unclaimed"` ' +
|
|
26
|
+
'derived live at request time by querying the pos-staker-pool-claims subgraph ' +
|
|
27
|
+
'(spec §3.6, D10). Empty array if the address never qualified.',
|
|
28
|
+
request: {
|
|
29
|
+
params: z.object({ network: NetworkSchema, address: AddressSchema })
|
|
30
|
+
},
|
|
31
|
+
responses: {
|
|
32
|
+
200: {
|
|
33
|
+
description: 'History rows ordered most-recently-completed first.',
|
|
34
|
+
content: { 'application/json': { schema: AllocationHistoryResponseWireSchema } }
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
.registerPath({
|
|
39
|
+
operationId: 'getUnclaimedProofs',
|
|
40
|
+
method: 'get',
|
|
41
|
+
path: '/v1/{network}/addresses/{address}/unclaimed-proofs',
|
|
42
|
+
summary: 'Per-address unclaimed-proofs array — the primary claim-flow UI endpoint',
|
|
43
|
+
description:
|
|
44
|
+
"Returns the address's outstanding allocations with merkle proofs ready to " +
|
|
45
|
+
'submit to the claimer contract in one batched transaction. Proof generation is ' +
|
|
46
|
+
'on demand from the persisted leaves (spec D9); the in-memory tree cache absorbs ' +
|
|
47
|
+
'repeat calls for the same distribution. Empty array if every allocation has been ' +
|
|
48
|
+
'claimed (or the address has none).',
|
|
49
|
+
request: {
|
|
50
|
+
params: z.object({ network: NetworkSchema, address: AddressSchema })
|
|
51
|
+
},
|
|
52
|
+
responses: {
|
|
53
|
+
200: {
|
|
54
|
+
description: 'Batch of unclaimed-proof entries ordered oldest-distribution first.',
|
|
55
|
+
content: { 'application/json': { schema: UnclaimedProofsResponseSchema } }
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Allocation read routes scoped to a single distribution: the paginated
|
|
2
|
+
// claimable (in-tree) list, the off-chain (merkle-excluded) settlement
|
|
3
|
+
// list, and the single-address lookup.
|
|
4
|
+
import type { RouteWithOpId, TypedRegistry } from '@polygonlabs/openapi-registry';
|
|
5
|
+
|
|
6
|
+
import { ErrorResponseSchema } from '@polygonlabs/openapi-registry/error-schemas';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
AddressSchema,
|
|
10
|
+
DistributionIdSchema,
|
|
11
|
+
NetworkSchema,
|
|
12
|
+
PaginationQuerySchema
|
|
13
|
+
} from '../common.ts';
|
|
14
|
+
import { z } from '../zod.ts';
|
|
15
|
+
import {
|
|
16
|
+
AllocationListResponseWireSchema,
|
|
17
|
+
AllocationRowWireSchema,
|
|
18
|
+
ExcludedAllocationsResponseWireSchema
|
|
19
|
+
} from './wire.ts';
|
|
20
|
+
|
|
21
|
+
export const addAllocationRoutes = <
|
|
22
|
+
Ops extends Record<string, RouteWithOpId>,
|
|
23
|
+
Schemes extends Record<string, true>
|
|
24
|
+
>(
|
|
25
|
+
r: TypedRegistry<Ops, Schemes>
|
|
26
|
+
) =>
|
|
27
|
+
r
|
|
28
|
+
.registerPath({
|
|
29
|
+
operationId: 'listAllocations',
|
|
30
|
+
method: 'get',
|
|
31
|
+
path: '/v1/{network}/distributions/{distributionId}/allocations',
|
|
32
|
+
summary: 'List allocations for a completed distribution',
|
|
33
|
+
description: 'Paginated by `leafIndex` ascending.',
|
|
34
|
+
request: {
|
|
35
|
+
params: z.object({ network: NetworkSchema, distributionId: DistributionIdSchema }),
|
|
36
|
+
query: PaginationQuerySchema
|
|
37
|
+
},
|
|
38
|
+
responses: {
|
|
39
|
+
200: {
|
|
40
|
+
description: 'Page of allocation rows.',
|
|
41
|
+
content: { 'application/json': { schema: AllocationListResponseWireSchema } }
|
|
42
|
+
},
|
|
43
|
+
// Same 400 override rationale as `listDistributions`: the opaque
|
|
44
|
+
// cursor decodes in the handler and 400s with `info.code:
|
|
45
|
+
// 'INVALID_CURSOR'`, which the auto-injected ValidationErrorResponse
|
|
46
|
+
// shape would strip.
|
|
47
|
+
400: {
|
|
48
|
+
description:
|
|
49
|
+
'Invalid pagination cursor (`info.code: "INVALID_CURSOR"`) or ' +
|
|
50
|
+
'request failed schema validation.',
|
|
51
|
+
content: { 'application/json': { schema: ErrorResponseSchema } }
|
|
52
|
+
},
|
|
53
|
+
404: {
|
|
54
|
+
description: 'Distribution not found or not yet completed.',
|
|
55
|
+
content: { 'application/json': { schema: ErrorResponseSchema } }
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
.registerPath({
|
|
60
|
+
operationId: 'listExcludedAllocations',
|
|
61
|
+
method: 'get',
|
|
62
|
+
path: '/v1/{network}/distributions/{distributionId}/excluded-allocations',
|
|
63
|
+
summary: 'List off-chain (merkle-excluded) allocations for a distribution',
|
|
64
|
+
description:
|
|
65
|
+
'Recipients allocated but excluded from the merkle tree (excluded addresses) — ' +
|
|
66
|
+
'not claimable on-chain, settled manually. Not paginated (small bounded set). ' +
|
|
67
|
+
'`totalAmount` equals the distribution `total_allocated - claimable_total`.',
|
|
68
|
+
request: {
|
|
69
|
+
params: z.object({ network: NetworkSchema, distributionId: DistributionIdSchema })
|
|
70
|
+
},
|
|
71
|
+
responses: {
|
|
72
|
+
200: {
|
|
73
|
+
description: 'The off-chain allocation set.',
|
|
74
|
+
content: { 'application/json': { schema: ExcludedAllocationsResponseWireSchema } }
|
|
75
|
+
},
|
|
76
|
+
404: {
|
|
77
|
+
description: 'Distribution not found or not yet completed.',
|
|
78
|
+
content: { 'application/json': { schema: ErrorResponseSchema } }
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
.registerPath({
|
|
83
|
+
operationId: 'getAllocation',
|
|
84
|
+
method: 'get',
|
|
85
|
+
path: '/v1/{network}/distributions/{distributionId}/allocations/{address}',
|
|
86
|
+
summary: 'Get a single address allocation in a distribution',
|
|
87
|
+
request: {
|
|
88
|
+
params: z.object({
|
|
89
|
+
network: NetworkSchema,
|
|
90
|
+
distributionId: DistributionIdSchema,
|
|
91
|
+
address: AddressSchema
|
|
92
|
+
})
|
|
93
|
+
},
|
|
94
|
+
responses: {
|
|
95
|
+
200: {
|
|
96
|
+
description: 'Allocation row.',
|
|
97
|
+
content: { 'application/json': { schema: AllocationRowWireSchema } }
|
|
98
|
+
},
|
|
99
|
+
404: {
|
|
100
|
+
description: 'Distribution or allocation not found.',
|
|
101
|
+
content: { 'application/json': { schema: ErrorResponseSchema } }
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Distribution read routes: paginated list + single-distribution detail.
|
|
2
|
+
// Only completed runs are surfaced on the public API; pending/running/
|
|
3
|
+
// failed rows 404 (the completed-only filter lives in the db package).
|
|
4
|
+
import type { RouteWithOpId, TypedRegistry } from '@polygonlabs/openapi-registry';
|
|
5
|
+
|
|
6
|
+
import { ErrorResponseSchema } from '@polygonlabs/openapi-registry/error-schemas';
|
|
7
|
+
|
|
8
|
+
import { DistributionIdSchema, NetworkSchema, PaginationQuerySchema } from '../common.ts';
|
|
9
|
+
import { DistributionDetailSchema, DistributionListResponseSchema } from '../distribution.ts';
|
|
10
|
+
import { z } from '../zod.ts';
|
|
11
|
+
|
|
12
|
+
export const addDistributionRoutes = <
|
|
13
|
+
Ops extends Record<string, RouteWithOpId>,
|
|
14
|
+
Schemes extends Record<string, true>
|
|
15
|
+
>(
|
|
16
|
+
r: TypedRegistry<Ops, Schemes>
|
|
17
|
+
) =>
|
|
18
|
+
r
|
|
19
|
+
.registerPath({
|
|
20
|
+
operationId: 'listDistributions',
|
|
21
|
+
method: 'get',
|
|
22
|
+
path: '/v1/{network}/distributions',
|
|
23
|
+
summary: 'List completed distributions',
|
|
24
|
+
description:
|
|
25
|
+
'Paginated list of completed distributions, oldest first by id (e.g. `2026-04`). ' +
|
|
26
|
+
'Non-completed rows are not surfaced on the public API.',
|
|
27
|
+
request: {
|
|
28
|
+
params: z.object({ network: NetworkSchema }),
|
|
29
|
+
query: PaginationQuerySchema
|
|
30
|
+
},
|
|
31
|
+
responses: {
|
|
32
|
+
200: {
|
|
33
|
+
description: 'Page of distribution summaries.',
|
|
34
|
+
content: { 'application/json': { schema: DistributionListResponseSchema } }
|
|
35
|
+
},
|
|
36
|
+
// Override the auto-injected ValidationErrorResponse 400: this route
|
|
37
|
+
// also emits a handler-thrown `BadRequest` for an undecodable opaque
|
|
38
|
+
// cursor, whose `info` is `{ code: 'INVALID_CURSOR' }` rather than
|
|
39
|
+
// section-keyed validation trees. The generic ErrorResponse shape is
|
|
40
|
+
// the honest superset of both bodies — encoding the cursor body
|
|
41
|
+
// against the narrower ValidationErrorResponse would silently strip
|
|
42
|
+
// `info.code`.
|
|
43
|
+
400: {
|
|
44
|
+
description:
|
|
45
|
+
'Invalid pagination cursor (`info.code: "INVALID_CURSOR"`) or ' +
|
|
46
|
+
'request failed schema validation.',
|
|
47
|
+
content: { 'application/json': { schema: ErrorResponseSchema } }
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
.registerPath({
|
|
52
|
+
operationId: 'getDistribution',
|
|
53
|
+
method: 'get',
|
|
54
|
+
path: '/v1/{network}/distributions/{distributionId}',
|
|
55
|
+
summary: 'Get a single completed distribution',
|
|
56
|
+
request: {
|
|
57
|
+
params: z.object({ network: NetworkSchema, distributionId: DistributionIdSchema })
|
|
58
|
+
},
|
|
59
|
+
responses: {
|
|
60
|
+
200: {
|
|
61
|
+
description: 'Distribution detail.',
|
|
62
|
+
content: { 'application/json': { schema: DistributionDetailSchema } }
|
|
63
|
+
},
|
|
64
|
+
404: {
|
|
65
|
+
description: 'Distribution not found or not yet completed.',
|
|
66
|
+
content: { 'application/json': { schema: ErrorResponseSchema } }
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Operational routes for the public REST API.
|
|
2
|
+
//
|
|
3
|
+
// /health-check is the K8s liveness probe — answers as long as the process
|
|
4
|
+
// can serve HTTP at all. Registered at root (no /v1 prefix) because
|
|
5
|
+
// dry-dock pod specs declare the probe path as `/health-check`, not
|
|
6
|
+
// `/v1/...`. /readiness is the degraded-mode signal; still a 200 stub
|
|
7
|
+
// pending the real DB + subgraph reachability probe. Both paths and their
|
|
8
|
+
// `{"status":"ok"}` bodies are load-bearing for dry-dock probes — never
|
|
9
|
+
// rename or version them.
|
|
10
|
+
import type { RouteWithOpId, TypedRegistry } from '@polygonlabs/openapi-registry';
|
|
11
|
+
|
|
12
|
+
import { z } from '../zod.ts';
|
|
13
|
+
|
|
14
|
+
// Exported (and aliased to its registered name in the package barrel) so
|
|
15
|
+
// the @polygonlabs/zod-to-openapi-heyapi codegen audit can resolve it.
|
|
16
|
+
export const OperationalStatusSchema = z
|
|
17
|
+
.object({ status: z.literal('ok') })
|
|
18
|
+
.openapi('OperationalStatus');
|
|
19
|
+
|
|
20
|
+
// Generic over the parent's `Ops` and `Schemes` accumulators so the helper
|
|
21
|
+
// preserves everything registered before `.with(addOperationalRoutes)`.
|
|
22
|
+
export const addOperationalRoutes = <
|
|
23
|
+
Ops extends Record<string, RouteWithOpId>,
|
|
24
|
+
Schemes extends Record<string, true>
|
|
25
|
+
>(
|
|
26
|
+
r: TypedRegistry<Ops, Schemes>
|
|
27
|
+
) =>
|
|
28
|
+
r
|
|
29
|
+
.registerPath({
|
|
30
|
+
operationId: 'getHealthCheck',
|
|
31
|
+
method: 'get',
|
|
32
|
+
path: '/health-check',
|
|
33
|
+
summary: 'Liveness check (K8s probe)',
|
|
34
|
+
responses: {
|
|
35
|
+
200: {
|
|
36
|
+
description: 'Service is alive.',
|
|
37
|
+
content: { 'application/json': { schema: OperationalStatusSchema } }
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
.registerPath({
|
|
42
|
+
operationId: 'getReadiness',
|
|
43
|
+
method: 'get',
|
|
44
|
+
path: '/readiness',
|
|
45
|
+
summary: 'Readiness check',
|
|
46
|
+
description:
|
|
47
|
+
'Distinguishes "fully ready" from "partially degraded but still serving". ' +
|
|
48
|
+
'Currently a 200 stub — the real DB + subgraph reachability probe is outstanding work.',
|
|
49
|
+
responses: {
|
|
50
|
+
200: {
|
|
51
|
+
description: 'Service is ready.',
|
|
52
|
+
content: { 'application/json': { schema: OperationalStatusSchema } }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Single-allocation merkle-proof route.
|
|
2
|
+
import type { RouteWithOpId, TypedRegistry } from '@polygonlabs/openapi-registry';
|
|
3
|
+
|
|
4
|
+
import { ErrorResponseSchema } from '@polygonlabs/openapi-registry/error-schemas';
|
|
5
|
+
|
|
6
|
+
import { AddressSchema, DistributionIdSchema, NetworkSchema } from '../common.ts';
|
|
7
|
+
import { z } from '../zod.ts';
|
|
8
|
+
import { ProofResponseWireSchema } from './wire.ts';
|
|
9
|
+
|
|
10
|
+
export const addProofRoutes = <
|
|
11
|
+
Ops extends Record<string, RouteWithOpId>,
|
|
12
|
+
Schemes extends Record<string, true>
|
|
13
|
+
>(
|
|
14
|
+
r: TypedRegistry<Ops, Schemes>
|
|
15
|
+
) =>
|
|
16
|
+
r.registerPath({
|
|
17
|
+
operationId: 'getProof',
|
|
18
|
+
method: 'get',
|
|
19
|
+
path: '/v1/{network}/distributions/{distributionId}/allocations/{address}/proof',
|
|
20
|
+
summary: 'Get the merkle proof for a single allocation',
|
|
21
|
+
description:
|
|
22
|
+
'Rebuilds (or cache-hits) the merkle tree from the persisted allocations ' +
|
|
23
|
+
'table and extracts the proof for the requested address. The returned ' +
|
|
24
|
+
'tuple `(merkleRoot, address, amount, leafIndex, proof)` can be passed ' +
|
|
25
|
+
'straight to a Solidity verifier built around the same `[address, uint256]` ' +
|
|
26
|
+
'leaf encoding.',
|
|
27
|
+
request: {
|
|
28
|
+
params: z.object({
|
|
29
|
+
network: NetworkSchema,
|
|
30
|
+
distributionId: DistributionIdSchema,
|
|
31
|
+
address: AddressSchema
|
|
32
|
+
})
|
|
33
|
+
},
|
|
34
|
+
responses: {
|
|
35
|
+
200: {
|
|
36
|
+
description: 'Proof bundle ready to submit to the claimer contract.',
|
|
37
|
+
content: { 'application/json': { schema: ProofResponseWireSchema } }
|
|
38
|
+
},
|
|
39
|
+
404: {
|
|
40
|
+
description:
|
|
41
|
+
'Distribution not found, not yet completed, address has no allocation, ' +
|
|
42
|
+
'or the allocation is excluded from the tree (settled off-chain).',
|
|
43
|
+
content: { 'application/json': { schema: ErrorResponseSchema } }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Wire-shape response variants for the public-API registry.
|
|
2
|
+
//
|
|
3
|
+
// The registry-driven router (`@polygonlabs/express/registry`) re-encodes
|
|
4
|
+
// every response through `z.encode` before send. `AddressSchema` carries a
|
|
5
|
+
// unidirectional `.transform()` (request-boundary lowercasing) which
|
|
6
|
+
// `z.encode` rejects ("Encountered unidirectional transform during
|
|
7
|
+
// encode"), so response slots cannot reference it or any schema embedding
|
|
8
|
+
// it. The variants below override only the `address` field with a
|
|
9
|
+
// no-transform lowercase-address schema; the wire contract is unchanged
|
|
10
|
+
// because handlers always emit lowercased addresses (the DB stores them
|
|
11
|
+
// lowercased and request params are normalised on the way in). Every other
|
|
12
|
+
// field is reused from the canonical schema by reference (`.shape`
|
|
13
|
+
// spread), so a field added or changed upstream flows through.
|
|
14
|
+
//
|
|
15
|
+
// Built via `z.object({ ...Parent.shape, address })` rather than
|
|
16
|
+
// `.extend()`: an `.extend()` child keeps zod-to-openapi lineage to its
|
|
17
|
+
// parent and generates as `allOf` + `$ref` to a parent component, but both
|
|
18
|
+
// parent and child here deliberately carry the same `.openapi('Name')`
|
|
19
|
+
// registration (the component names are the public contract), which would
|
|
20
|
+
// collide at generation time. A fresh `z.object` has no lineage, so each
|
|
21
|
+
// name resolves to exactly one component.
|
|
22
|
+
import { AllocationHistoryRowSchema } from '../allocation-history.ts';
|
|
23
|
+
import {
|
|
24
|
+
AllocationListResponseSchema,
|
|
25
|
+
AllocationRowSchema,
|
|
26
|
+
ExcludedAllocationRowSchema,
|
|
27
|
+
ExcludedAllocationsResponseSchema
|
|
28
|
+
} from '../allocation.ts';
|
|
29
|
+
import { ProofResponseSchema } from '../proof.ts';
|
|
30
|
+
import { z } from '../zod.ts';
|
|
31
|
+
|
|
32
|
+
// Lowercased 0x-prefixed 40-hex EVM address as it appears on the wire.
|
|
33
|
+
// Response-side counterpart of `AddressSchema` (which lowercases via a
|
|
34
|
+
// transform and therefore can't be encoded).
|
|
35
|
+
const ApiAddressSchema = z
|
|
36
|
+
.string()
|
|
37
|
+
.regex(/^0x[0-9a-f]{40}$/)
|
|
38
|
+
.openapi({ example: '0xabc0000000000000000000000000000000000001' });
|
|
39
|
+
|
|
40
|
+
export const AllocationRowWireSchema = z
|
|
41
|
+
.object({ ...AllocationRowSchema.shape, address: ApiAddressSchema })
|
|
42
|
+
.openapi('AllocationRow');
|
|
43
|
+
|
|
44
|
+
export const AllocationListResponseWireSchema = z
|
|
45
|
+
.object({
|
|
46
|
+
...AllocationListResponseSchema.shape,
|
|
47
|
+
items: z.array(AllocationRowWireSchema)
|
|
48
|
+
})
|
|
49
|
+
.openapi('AllocationListResponse');
|
|
50
|
+
|
|
51
|
+
export const ExcludedAllocationRowWireSchema = z
|
|
52
|
+
.object({ ...ExcludedAllocationRowSchema.shape, address: ApiAddressSchema })
|
|
53
|
+
.openapi('ExcludedAllocationRow');
|
|
54
|
+
|
|
55
|
+
export const ExcludedAllocationsResponseWireSchema = z
|
|
56
|
+
.object({
|
|
57
|
+
...ExcludedAllocationsResponseSchema.shape,
|
|
58
|
+
items: z.array(ExcludedAllocationRowWireSchema)
|
|
59
|
+
})
|
|
60
|
+
.openapi('ExcludedAllocationsResponse');
|
|
61
|
+
|
|
62
|
+
export const AllocationHistoryRowWireSchema = z
|
|
63
|
+
.object({ ...AllocationHistoryRowSchema.shape, address: ApiAddressSchema })
|
|
64
|
+
.openapi('AllocationHistoryRow');
|
|
65
|
+
|
|
66
|
+
export const AllocationHistoryResponseWireSchema = z
|
|
67
|
+
.object({
|
|
68
|
+
items: z.array(AllocationHistoryRowWireSchema)
|
|
69
|
+
})
|
|
70
|
+
.openapi('AllocationHistoryResponse');
|
|
71
|
+
|
|
72
|
+
export const ProofResponseWireSchema = z
|
|
73
|
+
.object({ ...ProofResponseSchema.shape, address: ApiAddressSchema })
|
|
74
|
+
.openapi('ProofResponse');
|