@dalmore/api-contracts 0.0.0-dev.58a860b → 0.0.0-dev.d30d175

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.
@@ -29,7 +29,7 @@ export const assetIdSchema = z.string().refine(
29
29
  message: `Invalid asset ID format. Must be a valid TypeID with "asset" prefix. Example: asset_01j5y5ghx5fg68d663j1fvy2x7`,
30
30
  },
31
31
  );
32
- export enum TemplateType {
32
+ export enum AssetTemplateType {
33
33
  STANDARD = 'STANDARD',
34
34
  TIERED = 'TIERED',
35
35
  }
@@ -51,7 +51,7 @@ export const IAsset = IBaseEntity.extend({
51
51
  .lazy(() => IOffering)
52
52
  .optional()
53
53
  .nullable(), // Use z.lazy here
54
- template: z.nativeEnum(TemplateType),
54
+ template: z.nativeEnum(AssetTemplateType),
55
55
  tiers: z.array(z.number().positive()).nullable(),
56
56
  enableBonus: z.boolean(),
57
57
  });
@@ -102,16 +102,16 @@ const PostAssetBase = z.object({
102
102
  .optional()
103
103
  .openapi({ example: DurationType.DAY }),
104
104
  template: z
105
- .nativeEnum(TemplateType)
106
- .default(TemplateType.STANDARD)
107
- .openapi({ example: TemplateType.STANDARD }),
105
+ .nativeEnum(AssetTemplateType)
106
+ .default(AssetTemplateType.STANDARD)
107
+ .openapi({ example: AssetTemplateType.STANDARD }),
108
108
  tiers: z.array(z.number().positive()).nullable().optional(),
109
109
  enableBonus: z.boolean().default(false).openapi({ example: false }),
110
110
  });
111
111
 
112
- const postAssetRefinement = (data: any, ctx: any) => {
112
+ export const postAssetRefinement = (data: any, ctx: any) => {
113
113
  // If type is bond, yield and duration must be provided (cannot be null or undefined)
114
- if (data.type === AssetType.BOND) {
114
+ if (data.assetType === AssetType.BOND || data.type === AssetType.BOND) {
115
115
  if (data.yield === null || data.yield === undefined) {
116
116
  ctx.addIssue({
117
117
  path: ['yield'],
@@ -139,7 +139,7 @@ const postAssetRefinement = (data: any, ctx: any) => {
139
139
  }
140
140
 
141
141
  // If type is stock, yield and duration must be either null or undefined
142
- if (data.type === AssetType.STOCK) {
142
+ if (data.assetType === AssetType.STOCK || data.type === AssetType.STOCK) {
143
143
  if (data.yield !== null && data.yield !== undefined) {
144
144
  ctx.addIssue({
145
145
  path: ['yield'],
@@ -162,7 +162,7 @@ const postAssetRefinement = (data: any, ctx: any) => {
162
162
  });
163
163
  }
164
164
  }
165
- if (data.template === TemplateType.TIERED) {
165
+ if (data.template === AssetTemplateType.TIERED) {
166
166
  if (data.tiers === null || data.tiers === undefined) {
167
167
  ctx.addIssue({
168
168
  path: ['tiers'],
@@ -172,7 +172,7 @@ const postAssetRefinement = (data: any, ctx: any) => {
172
172
  }
173
173
  }
174
174
  // If template is STANDARD, tiers must be null or undefined
175
- if (data.template === TemplateType.STANDARD) {
175
+ if (data.template === AssetTemplateType.STANDARD) {
176
176
  if (data.tiers !== null && data.tiers !== undefined) {
177
177
  ctx.addIssue({
178
178
  path: ['tiers'],
@@ -233,9 +233,9 @@ export const PutAsset = z.object({
233
233
  .optional()
234
234
  .openapi({ example: DurationType.DAY }),
235
235
  template: z
236
- .nativeEnum(TemplateType)
237
- .default(TemplateType.STANDARD)
238
- .openapi({ example: TemplateType.STANDARD })
236
+ .nativeEnum(AssetTemplateType)
237
+ .default(AssetTemplateType.STANDARD)
238
+ .openapi({ example: AssetTemplateType.STANDARD })
239
239
  .nullable()
240
240
  .optional(),
241
241
  tiers: z.array(z.number().positive()).nullable().optional(),
@@ -262,7 +262,7 @@ export const AssetsIncludeQuery = z.object({
262
262
  assetsInclude.options.includes(include as any),
263
263
  ),
264
264
  {
265
- message: `Invalid include option provided. Valid options are: ${assetsInclude.options.join(', ')}`,
265
+ message: `Invalid include option provided. Valid options are: ${assetsInclude.options.join(',')}`,
266
266
  },
267
267
  )
268
268
  .openapi({
@@ -8,10 +8,17 @@ import {
8
8
  ManagedByType,
9
9
  OfferingVersioningType,
10
10
  ComplianceReview,
11
+ DurationType,
12
+ AssetType,
11
13
  } from './common.types';
12
14
  import { IBaseEntity } from './entity.types';
13
15
  import { IIssuer, issuerIdSchema } from './issuer.types';
14
- import { IAsset } from './asset.types';
16
+ import {
17
+ IAsset,
18
+ postAssetRefinement,
19
+ AssetTemplateType,
20
+ assetIdSchema,
21
+ } from './asset.types';
15
22
  import { fileIdSchema, FileZod } from './file.types';
16
23
  import { accountIdSchema } from './account.types';
17
24
 
@@ -128,20 +135,59 @@ export const PostIssuerOffering = z
128
135
  .nullable()
129
136
  .openapi({ example: 'This is a description of the offering.' }),
130
137
  managedBy: z.nativeEnum(ManagedByType).optional(),
138
+ assetName: z.string().min(2).max(50).openapi({ example: 'Asset name' }),
139
+ assetType: z.nativeEnum(AssetType).openapi({ example: AssetType.STOCK }),
140
+ pricePerUnit: z
141
+ .number()
142
+ .min(0.01)
143
+ .max(10000000000)
144
+ .nullable()
145
+ .openapi({ example: 2000 }),
146
+ totalUnits: z
147
+ .number()
148
+ .min(1)
149
+ .max(10000000000)
150
+ .nullable()
151
+ .openapi({ example: 5200 }),
152
+ yield: z
153
+ .number()
154
+ .min(0.01)
155
+ .max(10000000000)
156
+ .nullable()
157
+ .optional()
158
+ .openapi({ example: 1200 }),
159
+ duration: z
160
+ .number()
161
+ .min(1)
162
+ .max(1000)
163
+ .nullable()
164
+ .optional()
165
+ .openapi({ example: 1 }),
166
+ durationType: z
167
+ .nativeEnum(DurationType)
168
+ .nullable()
169
+ .optional()
170
+ .openapi({ example: DurationType.DAY }),
171
+ template: z
172
+ .nativeEnum(AssetTemplateType)
173
+ .default(AssetTemplateType.STANDARD)
174
+ .openapi({ example: AssetTemplateType.STANDARD }),
175
+ tiers: z.array(z.number().positive()).nullable().optional(),
131
176
  })
132
- .refine(
133
- (data) => {
134
- // Check if both values are present, and if so, ensure minInvestment is less than maxInvestment
135
- if (data.minInvestment && data.maxInvestment) {
136
- return data.minInvestment < data.maxInvestment;
177
+ .superRefine((data, ctx) => {
178
+ // Check if both values are present, and if so, ensure minInvestment is less than maxInvestment
179
+ if (data.minInvestment && data.maxInvestment) {
180
+ if (data.minInvestment >= data.maxInvestment) {
181
+ ctx.addIssue({
182
+ path: ['minInvestment'],
183
+ message: 'Minimum investment must be less than maximum investment.',
184
+ code: z.ZodIssueCode.custom,
185
+ });
137
186
  }
138
- return true; // If one or both values are undefined, skip this check
139
- },
140
- {
141
- message: 'Minimum investment must be less than maximum investment.',
142
- path: ['minInvestment'],
143
- },
144
- );
187
+ }
188
+ // Apply asset-specific refinements
189
+ postAssetRefinement(data, ctx);
190
+ });
145
191
 
146
192
  export type PostIssuerOffering = z.infer<typeof PostIssuerOffering>;
147
193
  export const PatchIssuerOffering = z.object({
@@ -212,6 +258,53 @@ export const PatchIssuerOffering = z.object({
212
258
  managedBy: z.nativeEnum(ManagedByType).optional(),
213
259
  showTotalRaised: z.boolean().optional(),
214
260
  issuerId: issuerIdSchema.optional(),
261
+ assetId: assetIdSchema,
262
+ assetName: z.string().min(2).max(50).optional().openapi({ example: 'Z' }),
263
+ assetType: z
264
+ .nativeEnum(AssetType)
265
+ .optional()
266
+ .openapi({ example: AssetType.STOCK }),
267
+ pricePerUnit: z
268
+ .number()
269
+ .min(0.01)
270
+ .max(10000000000)
271
+ .nullable()
272
+ .optional()
273
+ .openapi({ example: 2000 }),
274
+ totalUnits: z
275
+ .number()
276
+ .min(1)
277
+ .max(10000000000)
278
+ .nullable()
279
+ .optional()
280
+ .openapi({ example: 5200 }),
281
+ yield: z
282
+ .number()
283
+ .min(0.01)
284
+ .max(10000000000)
285
+ .nullable()
286
+ .optional()
287
+ .openapi({ example: 1200 }),
288
+ duration: z
289
+ .number()
290
+ .min(1)
291
+ .max(1000)
292
+ .nullable()
293
+ .optional()
294
+ .openapi({ example: 1 }),
295
+ durationType: z
296
+ .nativeEnum(DurationType)
297
+ .nullable()
298
+ .optional()
299
+ .openapi({ example: DurationType.DAY }),
300
+ template: z
301
+ .nativeEnum(AssetTemplateType)
302
+ .default(AssetTemplateType.STANDARD)
303
+ .openapi({ example: AssetTemplateType.STANDARD })
304
+ .nullable()
305
+ .optional(),
306
+ tiers: z.array(z.number().positive()).nullable().optional(),
307
+ enabled: z.boolean().optional(),
215
308
  });
216
309
  export type PatchIssuerOffering = z.infer<typeof PatchIssuerOffering>;
217
310
 
@@ -15,6 +15,8 @@ import {
15
15
  SortBy,
16
16
  OfferingVersioningType,
17
17
  OfferingOnboardingStatus,
18
+ AssetType,
19
+ DurationType,
18
20
  } from './common.types';
19
21
  import { IBaseEntity } from './entity.types';
20
22
  import { fileIdSchema, FileZod } from './file.types';
@@ -24,6 +26,11 @@ import {
24
26
  InvestorsOfferingsIncludeQuery,
25
27
  } from './investors-offering.types';
26
28
  import { OfferingStatus } from './issuer-offering.types';
29
+ import {
30
+ assetIdSchema,
31
+ postAssetRefinement,
32
+ AssetTemplateType,
33
+ } from './asset.types';
27
34
 
28
35
  export enum OfferingFeeType {
29
36
  FIXED = 'FIXED',
@@ -115,7 +122,7 @@ export const IPaginatedOffering = z.object({
115
122
  });
116
123
  export type IPaginatedOffering = z.infer<typeof IPaginatedOffering>;
117
124
 
118
- export const PatchOffering = z.object({
125
+ export const PatchOfferingBase = z.object({
119
126
  name: z.string().optional(),
120
127
  description: z.string().nullable().optional(),
121
128
  tid: z.string().optional(),
@@ -148,8 +155,8 @@ export const PatchOffering = z.object({
148
155
  .openapi({ example: 5000 })
149
156
  .optional()
150
157
  .nullable(),
151
- startAt: dateSchema.optional(),
152
- endAt: dateSchema.optional(),
158
+ startAt: dateSchema.optional().openapi({ example: '10/20/2024' }),
159
+ endAt: dateSchema.optional().openapi({ example: '10/27/2024' }),
153
160
  platform: z.string().optional(),
154
161
  coverArtId: z
155
162
  .lazy(() => fileIdSchema)
@@ -175,16 +182,107 @@ export const PatchOffering = z.object({
175
182
  showTotalRaised: z.boolean().optional(),
176
183
  issuerId: issuerIdSchema.optional(),
177
184
  });
178
-
185
+ export const PatchOffering = PatchOfferingBase.merge(
186
+ z.object({
187
+ assetId: assetIdSchema,
188
+ assetName: z
189
+ .string()
190
+ .min(2)
191
+ .max(50)
192
+ .optional()
193
+ .openapi({ example: 'Asset name' }),
194
+ assetType: z
195
+ .nativeEnum(AssetType)
196
+ .optional()
197
+ .openapi({ example: AssetType.STOCK }),
198
+ pricePerUnit: z
199
+ .number()
200
+ .min(0.01)
201
+ .max(10000000000)
202
+ .nullable()
203
+ .optional()
204
+ .openapi({ example: 2000 }),
205
+ totalUnits: z
206
+ .number()
207
+ .min(1)
208
+ .max(10000000000)
209
+ .nullable()
210
+ .optional()
211
+ .openapi({ example: 5200 }),
212
+ yield: z
213
+ .number()
214
+ .min(0.01)
215
+ .max(10000000000)
216
+ .nullable()
217
+ .optional()
218
+ .openapi({ example: 1200 }),
219
+ duration: z
220
+ .number()
221
+ .min(1)
222
+ .max(1000)
223
+ .nullable()
224
+ .optional()
225
+ .openapi({ example: 1 }),
226
+ durationType: z
227
+ .nativeEnum(DurationType)
228
+ .nullable()
229
+ .optional()
230
+ .openapi({ example: DurationType.DAY }),
231
+ template: z
232
+ .nativeEnum(AssetTemplateType)
233
+ .default(AssetTemplateType.STANDARD)
234
+ .openapi({ example: AssetTemplateType.STANDARD })
235
+ .nullable()
236
+ .optional(),
237
+ tiers: z.array(z.number().positive()).nullable().optional(),
238
+ }),
239
+ );
179
240
  export type PatchOffering = z.infer<typeof PatchOffering>;
180
241
 
181
- export const PostComplianceOffering = PatchOffering.merge(
242
+ export const PostComplianceOffering = PatchOfferingBase.merge(
182
243
  z.object({
183
244
  accountId: accountIdSchema,
245
+ managedBy: z.nativeEnum(ManagedByType).optional(),
246
+ assetName: z.string().min(2).max(50).openapi({ example: 'Asset name' }),
247
+ assetType: z.nativeEnum(AssetType).openapi({ example: AssetType.STOCK }),
248
+ pricePerUnit: z
249
+ .number()
250
+ .min(0.01)
251
+ .max(10000000000)
252
+ .nullable()
253
+ .openapi({ example: 2000 }),
254
+ totalUnits: z
255
+ .number()
256
+ .min(1)
257
+ .max(10000000000)
258
+ .nullable()
259
+ .openapi({ example: 5200 }),
260
+ yield: z
261
+ .number()
262
+ .min(0.01)
263
+ .max(10000000000)
264
+ .nullable()
265
+ .optional()
266
+ .openapi({ example: 1200 }),
267
+ duration: z
268
+ .number()
269
+ .min(1)
270
+ .max(1000)
271
+ .nullable()
272
+ .optional()
273
+ .openapi({ example: 1 }),
274
+ durationType: z
275
+ .nativeEnum(DurationType)
276
+ .nullable()
277
+ .optional()
278
+ .openapi({ example: DurationType.DAY }),
279
+ template: z
280
+ .nativeEnum(AssetTemplateType)
281
+ .default(AssetTemplateType.STANDARD)
282
+ .openapi({ example: AssetTemplateType.STANDARD }),
283
+ tiers: z.array(z.number().positive()).nullable().optional(),
184
284
  }),
185
- ).extend({
186
- managedBy: z.nativeEnum(ManagedByType).optional(),
187
- });
285
+ ).superRefine(postAssetRefinement);
188
286
  export type PostComplianceOffering = z.infer<typeof PostComplianceOffering>;
189
287
 
190
288
  export const offeringsInclude = z.enum([
@@ -546,11 +644,9 @@ export type PaginatedPendingOfferingSummaryResponse = z.infer<
546
644
  >;
547
645
 
548
646
  export type OfferingUpdateFields = {
549
- platform?: string;
550
- platformSettings?: string | null;
551
647
  managedBy?: ManagedByType | null;
552
- enabled?: boolean;
553
648
  showTotalRaised?: boolean;
649
+ enabled?: boolean;
554
650
  };
555
651
 
556
652
  export const ReviewOfferingOnboarding = z.object({
@@ -55,8 +55,71 @@ import {
55
55
  } from './secondary-trade.types';
56
56
  import { InvestorAccount } from '../../investor-accounts/entities/investor-account.entity';
57
57
  import { Trade } from '../../trades/entities/trade.entity';
58
+ import { fileIdSchema } from './file.types';
58
59
 
59
60
  extendZodWithOpenApi(z);
61
+
62
+ // Zod schemas for attach subdoc endpoints
63
+ export const PostAttachSubdocBody = z.object({
64
+ lineItemId: tradeLineItemIdSchema.openapi({
65
+ example: 'trade_line_item_01kctsycw3fq7sj6hedvy62cja',
66
+ }),
67
+ fileId: z
68
+ .lazy(() => fileIdSchema)
69
+ .openapi({ example: 'file_01je6ht4b8fbmttkzh2hs82xqp' }),
70
+ primarySignatureStatus: z
71
+ .nativeEnum(SignatureStatus)
72
+ .openapi({ example: 'SIGNED' }),
73
+ secondarySignatureStatus: z
74
+ .nativeEnum(SignatureStatus)
75
+ .optional()
76
+ .openapi({ example: 'SIGNED' }),
77
+ });
78
+ export type PostAttachSubdocBody = z.infer<typeof PostAttachSubdocBody>;
79
+
80
+ export const PutAttachSubdocBody = z.object({
81
+ lineItemId: tradeLineItemIdSchema
82
+ .optional()
83
+ .openapi({ example: 'trade_line_item_01kctsycw3fq7sj6hedvy62cja' }),
84
+ fileId: z
85
+ .lazy(() => fileIdSchema)
86
+ .optional()
87
+ .openapi({ example: 'file_01je6ht4b8fbmttkzh2hs82xqp' }),
88
+ primarySignatureStatus: z
89
+ .nativeEnum(SignatureStatus)
90
+ .optional()
91
+ .openapi({ example: 'SIGNED' }),
92
+ secondarySignatureStatus: z
93
+ .nativeEnum(SignatureStatus)
94
+ .optional()
95
+ .openapi({ example: 'SIGNED' }),
96
+ });
97
+ export type PutAttachSubdocBody = z.infer<typeof PutAttachSubdocBody>;
98
+
99
+ export const PatchSubdocSignatureBody = z.object({
100
+ lineItemId: tradeLineItemIdSchema.optional().openapi({
101
+ example: 'trade_line_item_01kctsycw3fq7sj6hedvy62cja',
102
+ description:
103
+ 'Optional. If not provided, updates the first line item with an attached subdoc. Required for trades with multiple line items.',
104
+ }),
105
+ primarySignatureStatus: z.nativeEnum(SignatureStatus).optional().openapi({
106
+ example: 'SIGNED',
107
+ description: 'Primary signer signature status',
108
+ }),
109
+ secondarySignatureStatus: z.nativeEnum(SignatureStatus).optional().openapi({
110
+ example: 'SIGNED',
111
+ description:
112
+ 'Secondary signer signature status. Only applicable for JOINT investor accounts.',
113
+ }),
114
+ });
115
+ export type PatchSubdocSignatureBody = z.infer<typeof PatchSubdocSignatureBody>;
116
+
117
+ export const AttachSubdocResponse = z.object({
118
+ success: z.boolean(),
119
+ message: z.string(),
120
+ });
121
+ export type AttachSubdocResponse = z.infer<typeof AttachSubdocResponse>;
122
+
60
123
  export const CheckResultsSchema = z.object({
61
124
  fundingStatus: z.boolean(),
62
125
  agreementStatus: z.boolean(),
@@ -16,6 +16,10 @@ import {
16
16
  tradeIdOrTidSchema,
17
17
  TradesIncludeQuery,
18
18
  TradeZod,
19
+ PostAttachSubdocBody,
20
+ PutAttachSubdocBody,
21
+ PatchSubdocSignatureBody,
22
+ AttachSubdocResponse,
19
23
  } from '../../../common/types/trade.types';
20
24
  import { z } from 'zod';
21
25
  import {
@@ -115,6 +119,66 @@ export const tradesContract = c.router(
115
119
  500: InternalError,
116
120
  },
117
121
  },
122
+ postAttachSubdoc: {
123
+ summary: 'Attach subdoc to trade',
124
+ method: 'POST',
125
+ path: '/:id/attach-subdoc',
126
+ pathParams: z.object({
127
+ id: tradeIdOrTidSchema,
128
+ }),
129
+ metadata: {
130
+ auth: true,
131
+ },
132
+ body: PostAttachSubdocBody,
133
+ responses: {
134
+ 200: AttachSubdocResponse,
135
+ 400: BadRequestError,
136
+ 401: UnauthorizedError,
137
+ 403: ForbiddenError,
138
+ 404: NotFoundError,
139
+ 500: InternalError,
140
+ },
141
+ },
142
+ putAttachSubdoc: {
143
+ summary: 'Update attached subdoc for trade',
144
+ method: 'PUT',
145
+ path: '/:id/attach-subdoc',
146
+ pathParams: z.object({
147
+ id: tradeIdOrTidSchema,
148
+ }),
149
+ metadata: {
150
+ auth: true,
151
+ },
152
+ body: PutAttachSubdocBody,
153
+ responses: {
154
+ 200: AttachSubdocResponse,
155
+ 400: BadRequestError,
156
+ 401: UnauthorizedError,
157
+ 403: ForbiddenError,
158
+ 404: NotFoundError,
159
+ 500: InternalError,
160
+ },
161
+ },
162
+ patchSubdocSign: {
163
+ summary: 'Update subdoc signature statuses for trade',
164
+ method: 'PATCH',
165
+ path: '/:id/sign',
166
+ pathParams: z.object({
167
+ id: tradeIdOrTidSchema,
168
+ }),
169
+ metadata: {
170
+ auth: true,
171
+ },
172
+ body: PatchSubdocSignatureBody,
173
+ responses: {
174
+ 200: AttachSubdocResponse,
175
+ 400: BadRequestError,
176
+ 401: UnauthorizedError,
177
+ 403: ForbiddenError,
178
+ 404: NotFoundError,
179
+ 500: InternalError,
180
+ },
181
+ },
118
182
  },
119
183
  {
120
184
  pathPrefix: 'trades',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dalmore/api-contracts",
3
- "version": "0.0.0-dev.58a860b",
3
+ "version": "0.0.0-dev.d30d175",
4
4
  "description": "Type-safe API contracts for Dalmore Client Portal",
5
5
  "main": "./contracts/index.ts",
6
6
  "types": "./contracts/index.ts",