Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions modules/sdk-coin-canton/src/lib/allocationRejectBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { InvalidTransactionError, PublicKey, TransactionType } from '@bitgo/sdk-core';
import { BaseCoin as CoinConfig } from '@bitgo/statics';
import { CantonPrepareCommandResponse, CantonTransferAcceptRejectRequest } from './iface';
import { TransactionBuilder } from './transactionBuilder';
import { Transaction } from './transaction/transaction';
import utils from './utils';

export class AllocationRejectBuilder extends TransactionBuilder {
private _commandId: string;
private _contractId: string;
private _actAsPartyId: string;

constructor(_coinConfig: Readonly<CoinConfig>) {
super(_coinConfig);
}

initBuilder(tx: Transaction): void {
super.initBuilder(tx);
this.setTransactionType();
}

get transactionType(): TransactionType {
return TransactionType.AllocationReject;
}

setTransactionType(): void {
this.transaction.transactionType = TransactionType.AllocationReject;
}

setTransaction(transaction: CantonPrepareCommandResponse): void {
this.transaction.prepareCommand = transaction;
}

/** @inheritDoc */
addSignature(publicKey: PublicKey, signature: Buffer): void {
if (!this.transaction) {
throw new InvalidTransactionError('transaction is empty!');
}
this._signatures.push({ publicKey, signature });
const pubKeyBase64 = utils.getBase64FromHex(publicKey.pub);
this.transaction.signerFingerprint = utils.getAddressFromPublicKey(pubKeyBase64);
this.transaction.signatures = signature.toString('base64');
}

/**
* Sets the unique id for the allocation rejection
* Also sets the _id of the transaction
*
* @param id - A uuid
* @returns The current builder instance for chaining.
* @throws Error if id is empty.
*/
commandId(id: string): this {
if (!id || !id.trim()) {
throw new Error('commandId must be a non-empty string');
}
this._commandId = id.trim();
this.transaction.id = id.trim();
return this;
}

/**
* Sets the allocation rejection contract id
*
* @param id - canton allocation rejection contract id
* @returns The current builder instance for chaining.
* @throws Error if id is empty.
*/
contractId(id: string): this {
if (!id || !id.trim()) {
throw new Error('contractId must be a non-empty string');
}
this._contractId = id.trim();
return this;
}

/**
* Sets the party acting as the rejector
*
* @param id - the party id (address)
* @returns The current builder instance for chaining.
* @throws Error if id is empty.
*/
actAs(id: string): this {
if (!id || !id.trim()) {
throw new Error('actAsPartyId must be a non-empty string');
}
this._actAsPartyId = id.trim();
return this;
}

/**
* Builds and returns the CantonTransferAcceptRejectRequest object from the builder's internal state.
*
* @returns {CantonTransferAcceptRejectRequest} - A fully constructed and validated request object for allocation rejection.
* @throws {Error} If any required field is missing or fails validation.
*/
toRequestObject(): CantonTransferAcceptRejectRequest {
this.validate();

return {
commandId: this._commandId,
contractId: this._contractId,
verboseHashing: false,
actAs: [this._actAsPartyId],
readAs: [],
};
}

private validate(): void {
if (!this._commandId) throw new Error('commandId is missing');
if (!this._contractId) throw new Error('contractId is missing');
if (!this._actAsPartyId) throw new Error('actAs partyId is missing');
}
}
1 change: 1 addition & 0 deletions modules/sdk-coin-canton/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as Interface from './iface';
export { CantonCommandBuilder } from './cantonCommandBuilder';
export { AllocationAllocateBuilder } from './allocationAllocateBuilder';
export { AllocationAllocateWithdrawnBuilder } from './allocationAllocateWithdrawnBuilder';
export { AllocationRejectBuilder } from './allocationRejectBuilder';
export { AllocationRequestBuilder } from './allocationRequestBuilder';
export { CosignDelegationAcceptBuilder } from './cosignDelegationAcceptBuilder';
export { CosignDelegationProposalBuilder } from './cosignDelegationProposalBuilder';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
import { BaseCoin as CoinConfig } from '@bitgo/statics';
import { AllocationAllocateBuilder } from './allocationAllocateBuilder';
import { AllocationAllocateWithdrawnBuilder } from './allocationAllocateWithdrawnBuilder';
import { AllocationRejectBuilder } from './allocationRejectBuilder';
import { AllocationRequestBuilder } from './allocationRequestBuilder';
import { CantonCommandBuilder } from './cantonCommandBuilder';
import { CosignDelegationAcceptBuilder } from './cosignDelegationAcceptBuilder';
Expand Down Expand Up @@ -69,6 +70,9 @@ export class TransactionBuilderFactory extends BaseTransactionBuilderFactory {
case TransactionType.AllocationRequest: {
return this.getAllocationRequestBuilder(tx);
}
case TransactionType.AllocationReject: {
return this.getAllocationRejectBuilder(tx);
}
case TransactionType.CantonCommand: {
return this.getCantonCommandBuilder(tx);
}
Expand All @@ -87,6 +91,10 @@ export class TransactionBuilderFactory extends BaseTransactionBuilderFactory {
return TransactionBuilderFactory.initializeBuilder(tx, new AllocationAllocateWithdrawnBuilder(this._coinConfig));
}

getAllocationRejectBuilder(tx?: Transaction): AllocationRejectBuilder {
return TransactionBuilderFactory.initializeBuilder(tx, new AllocationRejectBuilder(this._coinConfig));
}

getAllocationRequestBuilder(tx?: Transaction): AllocationRequestBuilder {
return TransactionBuilderFactory.initializeBuilder(tx, new AllocationRequestBuilder(this._coinConfig));
}
Expand Down
37 changes: 37 additions & 0 deletions modules/sdk-coin-canton/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,43 @@ export class Utils implements BaseUtils {
break;
}

case TransactionType.AllocationReject: {
// RejectedDvp create node → dvp.{seller=sender, buyer=receiver, terms.deliveries[0].{amount, instrument.{admin, id}}}
const rejectedDvpFields = findCreateNodeFields('RejectedDvp');
if (rejectedDvpFields) {
const dvpField = getField(rejectedDvpFields, 'dvp');
if (dvpField?.oneofKind === 'record') {
const dvpFields = dvpField.record?.fields ?? [];
const sellerData = getField(dvpFields, 'seller');
if (sellerData?.oneofKind === 'party') sender = sellerData.party ?? '';
const buyerData = getField(dvpFields, 'buyer');
if (buyerData?.oneofKind === 'party') receiver = buyerData.party ?? '';
const termsField = getField(dvpFields, 'terms');
if (termsField?.oneofKind === 'record') {
const termsFields = termsField.record?.fields ?? [];
const deliveriesField = getField(termsFields, 'deliveries');
if (deliveriesField?.oneofKind === 'list') {
const firstDelivery = deliveriesField.list?.elements?.[0]?.sum;
if (firstDelivery?.oneofKind === 'record') {
const deliveryFields = firstDelivery.record?.fields ?? [];
const amountData = getField(deliveryFields, 'amount');
if (amountData?.oneofKind === 'numeric') amount = amountData.numeric ?? '';
const instrumentField = getField(deliveryFields, 'instrument');
if (instrumentField?.oneofKind === 'record') {
const instrumentFields = instrumentField.record?.fields ?? [];
const adminData = getField(instrumentFields, 'admin');
if (adminData?.oneofKind === 'party') instrumentAdmin = adminData.party ?? '';
const idData = getField(instrumentFields, 'id');
if (idData?.oneofKind === 'text') instrumentId = idData.text ?? '';
}
}
}
}
}
}
break;
}

case TransactionType.CosignDelegationAccept: {
// exercise CosigningDelegationProposal_Accept → actingParties[0] = signer (sender)
const signerParty = findExerciseActingParty('CosigningDelegationProposal_Accept');
Expand Down
Loading
Loading