Skip to content

Commit 7f0b0c3

Browse files
authored
Merge pull request #8822 from BitGo/CHALO-479
feat(sdk-coin-canton): added allocation request builder
2 parents 8e81bd7 + 45461e7 commit 7f0b0c3

7 files changed

Lines changed: 758 additions & 1 deletion

File tree

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
import { PublicKey, TransactionType } from '@bitgo/sdk-core';
2+
import { BaseCoin as CoinConfig } from '@bitgo/statics';
3+
import { AllocationRequest, CantonPrepareCommandResponse } from './iface';
4+
import { TransactionBuilder } from './transactionBuilder';
5+
import { Transaction } from './transaction/transaction';
6+
7+
/**
8+
* Builder for an AllocationRequest txRequest — an internal, non-signable transaction
9+
* that surfaces the DvP trade leg details to the allocating party. The party reviews
10+
* this request and then submits a separate AllocationAllocate to lock their asset.
11+
*
12+
* setTransaction and addSignature are intentionally not implemented because this
13+
* transaction type is never signed or broadcast directly.
14+
*/
15+
export class AllocationRequestBuilder extends TransactionBuilder {
16+
private _updateId: string;
17+
private _operatorId: string;
18+
private _contractId: string;
19+
private _tradeId: string;
20+
private _transferLegId: string;
21+
private _senderPartyId: string;
22+
private _receiverPartyId: string;
23+
private _amount: number;
24+
private _token: string;
25+
private _receiveToken: string;
26+
private _receiveAmount: number;
27+
private _allocateBefore: string;
28+
private _settleBefore: string;
29+
private _comment?: string;
30+
31+
constructor(_coinConfig: Readonly<CoinConfig>) {
32+
super(_coinConfig);
33+
}
34+
35+
initBuilder(tx: Transaction): void {
36+
super.initBuilder(tx);
37+
this.setTransactionType();
38+
}
39+
40+
get transactionType(): TransactionType {
41+
return TransactionType.AllocationRequest;
42+
}
43+
44+
setTransactionType(): void {
45+
this.transaction.transactionType = TransactionType.AllocationRequest;
46+
}
47+
48+
setTransaction(transaction: CantonPrepareCommandResponse): void {
49+
throw new Error('Not implemented!');
50+
}
51+
52+
/** @inheritDoc */
53+
addSignature(publicKey: PublicKey, signature: Buffer): void {
54+
throw new Error('Not implemented!');
55+
}
56+
57+
/**
58+
* Sets the ledger update id of the AllocationRequest event.
59+
* Also sets the transaction id.
60+
* @param id - ledger update id (txHash)
61+
*/
62+
updateId(id: string): this {
63+
if (!id || !id.trim()) {
64+
throw new Error('updateId must be a non-empty string');
65+
}
66+
this._updateId = id.trim();
67+
this.transaction.id = id.trim();
68+
return this;
69+
}
70+
71+
/**
72+
* Sets the operator party id (settlement executor).
73+
* @param id - operator party id
74+
*/
75+
operatorId(id: string): this {
76+
if (!id || !id.trim()) {
77+
throw new Error('operatorId must be a non-empty string');
78+
}
79+
this._operatorId = id.trim();
80+
return this;
81+
}
82+
83+
/**
84+
* Sets the settlement batch contract id.
85+
* @param id - settlement batch contract id
86+
*/
87+
contractId(id: string): this {
88+
if (!id || !id.trim()) {
89+
throw new Error('contractId must be a non-empty string');
90+
}
91+
this._contractId = id.trim();
92+
return this;
93+
}
94+
95+
/**
96+
* Sets the trade identifier.
97+
* @param id - trade id
98+
*/
99+
tradeId(id: string): this {
100+
if (!id || !id.trim()) {
101+
throw new Error('tradeId must be a non-empty string');
102+
}
103+
this._tradeId = id.trim();
104+
return this;
105+
}
106+
107+
/**
108+
* Sets the specific leg id being allocated (e.g. `${tradeId}-security-leg`).
109+
* @param id - transfer leg id
110+
*/
111+
transferLegId(id: string): this {
112+
if (!id || !id.trim()) {
113+
throw new Error('transferLegId must be a non-empty string');
114+
}
115+
this._transferLegId = id.trim();
116+
return this;
117+
}
118+
119+
/**
120+
* Sets the party performing this allocation (sender of this leg).
121+
* @param id - sender party id
122+
*/
123+
senderPartyId(id: string): this {
124+
if (!id || !id.trim()) {
125+
throw new Error('senderPartyId must be a non-empty string');
126+
}
127+
this._senderPartyId = id.trim();
128+
return this;
129+
}
130+
131+
/**
132+
* Sets the counterparty receiving the allocated asset.
133+
* @param id - receiver party id
134+
*/
135+
receiverPartyId(id: string): this {
136+
if (!id || !id.trim()) {
137+
throw new Error('receiverPartyId must be a non-empty string');
138+
}
139+
this._receiverPartyId = id.trim();
140+
return this;
141+
}
142+
143+
/**
144+
* Sets the quantity to allocate.
145+
* @param amount - allocation amount
146+
*/
147+
amount(amount: number): this {
148+
if (isNaN(amount) || amount <= 0) {
149+
throw new Error('amount must be a positive number');
150+
}
151+
this._amount = amount;
152+
return this;
153+
}
154+
155+
/**
156+
* Sets the BitGo token identifier for the asset being allocated.
157+
* @param token - token identifier
158+
*/
159+
token(token: string): this {
160+
if (!token || !token.trim()) {
161+
throw new Error('token must be a non-empty string');
162+
}
163+
this._token = token.trim();
164+
return this;
165+
}
166+
167+
/**
168+
* Sets the BitGo token identifier the allocating party will receive on settlement.
169+
* @param token - receive token identifier
170+
*/
171+
receiveToken(token: string): this {
172+
if (!token || !token.trim()) {
173+
throw new Error('receiveToken must be a non-empty string');
174+
}
175+
this._receiveToken = token.trim();
176+
return this;
177+
}
178+
179+
/**
180+
* Sets the quantity the allocating party will receive on settlement.
181+
* @param amount - receive amount
182+
*/
183+
receiveAmount(amount: number): this {
184+
if (isNaN(amount) || amount <= 0) {
185+
throw new Error('receiveAmount must be a positive number');
186+
}
187+
this._receiveAmount = amount;
188+
return this;
189+
}
190+
191+
/**
192+
* Sets the ISO 8601 deadline by which allocation must be submitted.
193+
* @param deadline - allocate-before timestamp
194+
*/
195+
allocateBefore(deadline: string): this {
196+
if (!deadline || !deadline.trim()) {
197+
throw new Error('allocateBefore must be a non-empty string');
198+
}
199+
this._allocateBefore = deadline.trim();
200+
return this;
201+
}
202+
203+
/**
204+
* Sets the ISO 8601 deadline by which settlement must complete.
205+
* @param deadline - settle-before timestamp
206+
*/
207+
settleBefore(deadline: string): this {
208+
if (!deadline || !deadline.trim()) {
209+
throw new Error('settleBefore must be a non-empty string');
210+
}
211+
this._settleBefore = deadline.trim();
212+
return this;
213+
}
214+
215+
/**
216+
* Sets an optional free-form comment.
217+
* @param comment - comment string
218+
*/
219+
comment(comment: string): this {
220+
this._comment = comment;
221+
return this;
222+
}
223+
224+
/**
225+
* Builds and returns the AllocationRequest object from the builder's internal state.
226+
*
227+
* @returns {AllocationRequest} - A fully constructed and validated request object.
228+
* @throws {Error} If any required field is missing or fails validation.
229+
*/
230+
toRequestObject(): AllocationRequest {
231+
this.validate();
232+
const result: AllocationRequest = {
233+
updateId: this._updateId,
234+
operatorId: this._operatorId,
235+
contractId: this._contractId,
236+
tradeId: this._tradeId,
237+
transferLegId: this._transferLegId,
238+
senderPartyId: this._senderPartyId,
239+
receiverPartyId: this._receiverPartyId,
240+
amount: this._amount,
241+
token: this._token,
242+
receiveToken: this._receiveToken,
243+
receiveAmount: this._receiveAmount,
244+
allocateBefore: this._allocateBefore,
245+
settleBefore: this._settleBefore,
246+
};
247+
if (this._comment !== undefined) {
248+
result.comment = this._comment;
249+
}
250+
return result;
251+
}
252+
253+
private validate(): void {
254+
if (!this._updateId) throw new Error('updateId is missing');
255+
if (!this._operatorId) throw new Error('operatorId is missing');
256+
if (!this._contractId) throw new Error('contractId is missing');
257+
if (!this._tradeId) throw new Error('tradeId is missing');
258+
if (!this._transferLegId) throw new Error('transferLegId is missing');
259+
if (!this._senderPartyId) throw new Error('senderPartyId is missing');
260+
if (!this._receiverPartyId) throw new Error('receiverPartyId is missing');
261+
if (this._amount === undefined || this._amount === null) throw new Error('amount is missing');
262+
if (!this._token) throw new Error('token is missing');
263+
if (!this._receiveToken) throw new Error('receiveToken is missing');
264+
if (this._receiveAmount === undefined || this._receiveAmount === null) throw new Error('receiveAmount is missing');
265+
if (!this._allocateBefore) throw new Error('allocateBefore is missing');
266+
if (!this._settleBefore) throw new Error('settleBefore is missing');
267+
}
268+
}

modules/sdk-coin-canton/src/lib/iface.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export interface TxData {
2222
amount: string;
2323
acknowledgeData?: TransferAcknowledge;
2424
cosignDelegationProposalData?: CosignDelegationProposal;
25+
allocationRequestData?: AllocationRequest;
2526
memoId?: string;
2627
token?: string;
2728
}
@@ -114,6 +115,7 @@ export interface PartySignature {
114115
export interface TransactionBroadcastData {
115116
acknowledgeData?: TransferAcknowledge;
116117
cosignDelegationProposalData?: CosignDelegationProposal;
118+
allocationRequestData?: AllocationRequest;
117119
prepareCommandResponse?: CantonPrepareCommandResponse;
118120
txType: string;
119121
preparedTransaction?: string;
@@ -185,3 +187,25 @@ export interface CantonAllocationAllocateRequest {
185187
senderPartyId: string;
186188
comment?: string;
187189
}
190+
191+
/**
192+
* Internal (non-signable) data for an AllocationRequest txRequest.
193+
* Surfaces the full DvP trade leg to the allocating party so they can
194+
* review and then submit an AllocationAllocate.
195+
*/
196+
export interface AllocationRequest {
197+
updateId: string;
198+
operatorId: string;
199+
contractId: string;
200+
tradeId: string;
201+
transferLegId: string;
202+
senderPartyId: string;
203+
receiverPartyId: string;
204+
amount: number;
205+
token: string;
206+
receiveToken: string;
207+
receiveAmount: number;
208+
allocateBefore: string;
209+
settleBefore: string;
210+
comment?: string;
211+
}

modules/sdk-coin-canton/src/lib/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as Utils from './utils';
22
import * as Interface from './iface';
33

44
export { AllocationAllocateBuilder } from './allocationAllocateBuilder';
5+
export { AllocationRequestBuilder } from './allocationRequestBuilder';
56
export { CosignDelegationAcceptBuilder } from './cosignDelegationAcceptBuilder';
67
export { CosignDelegationProposalBuilder } from './cosignDelegationProposalBuilder';
78
export { KeyPair } from './keyPair';

0 commit comments

Comments
 (0)