@fairmint/canton-node-sdk
Version:
Canton Node SDK
157 lines • 6.97 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.transferToPreapproved = transferToPreapproved;
const mining_rounds_1 = require("../mining/mining-rounds");
const get_amulets_for_transfer_1 = require("./get-amulets-for-transfer");
/**
* Transfers coins to multiple parties that have pre-approved transfers enabled
*
* @example
* ```typescript
* const result = await transferToPreapproved(ledgerClient, validatorClient, {
* senderPartyId: 'sender-party-id',
* transfers: [
* {
* recipientPartyId: 'recipient-1',
* amount: '100',
* description: 'Payment for services'
* },
* {
* recipientPartyId: 'recipient-2',
* amount: '50',
* description: 'Bonus payment'
* }
* ]
* });
*
*
* ```;
*
* @param ledgerClient - Ledger JSON API client for submitting commands
* @param validatorClient - Validator API client for getting network information
* @param params - Parameters for the transfers
* @returns Promise resolving to the transfer results
*/
async function transferToPreapproved(ledgerClient, validatorClient, params) {
if (params.transfers.length === 0) {
throw new Error('At least one transfer must be provided');
}
// Get network information
const [amuletRules, miningRoundContext] = await Promise.all([
validatorClient.getAmuletRules(),
(0, mining_rounds_1.getCurrentMiningRoundContext)(validatorClient),
]);
const { openMiningRound: openMiningRoundContractId } = miningRoundContext;
// Get amulet inputs for the sender party
const amulets = await (0, get_amulets_for_transfer_1.getAmuletsForTransfer)({
jsonApiClient: ledgerClient,
readAs: [params.senderPartyId],
});
if (amulets.length === 0) {
throw new Error(`No unlocked amulets found for sender party ${params.senderPartyId}`);
}
// Convert amulets to input format
const inputs = amulets.map((amulet) => ({
tag: 'InputAmulet',
value: amulet.contractId,
}));
const transferResults = [];
// Build base disclosed contracts (shared across all transfers)
const disclosedContracts = [
// AmuletRules contract (required)
{
contractId: amuletRules.amulet_rules.contract.contract_id,
templateId: amuletRules.amulet_rules.contract.template_id,
createdEventBlob: amuletRules.amulet_rules.contract.created_event_blob,
synchronizerId: amuletRules.amulet_rules.domain_id,
},
// Open mining round contract (shared)
{
contractId: miningRoundContext.openMiningRoundContract.contractId,
templateId: miningRoundContext.openMiningRoundContract.templateId,
createdEventBlob: miningRoundContext.openMiningRoundContract.createdEventBlob,
synchronizerId: miningRoundContext.openMiningRoundContract.synchronizerId,
},
];
// Process each transfer
for (const transfer of params.transfers) {
// Look up transfer preapproval for the recipient
const transferPreapprovalResponse = await validatorClient.lookupTransferPreapprovalByParty({
partyId: transfer.recipientPartyId,
});
const { transfer_preapproval } = transferPreapprovalResponse;
const transferPreapprovalContract = transfer_preapproval.contract;
const transferPreapprovalContractId = transferPreapprovalContract.contract_id;
if (!transfer_preapproval.domain_id) {
throw new Error(`No domain ID found for transfer preapproval for party ${transfer.recipientPartyId}`);
}
// Look up featured app right for the recipient
const featuredAppRight = await validatorClient.lookupFeaturedAppRight({
partyId: transfer.recipientPartyId,
});
if (!featuredAppRight.featured_app_right?.contract_id) {
throw new Error(`No featured app right found for party ${transfer.recipientPartyId}`);
}
const featuredAppRightContractId = featuredAppRight.featured_app_right.contract_id;
// Build transfer-specific disclosed contracts
const transferDisclosedContracts = [
...disclosedContracts, // Include base contracts
// Featured app right contract for this recipient
{
contractId: featuredAppRight.featured_app_right.contract_id,
templateId: featuredAppRight.featured_app_right.template_id,
createdEventBlob: featuredAppRight.featured_app_right.created_event_blob,
synchronizerId: amuletRules.amulet_rules.domain_id,
},
// Transfer preapproval contract for this recipient
{
contractId: transferPreapprovalContractId,
templateId: transferPreapprovalContract.template_id,
createdEventBlob: transferPreapprovalContract.created_event_blob,
synchronizerId: transferPreapprovalResponse.transfer_preapproval.domain_id,
},
];
// Create the transfer command using TransferPreapproval_Send
const transferCommand = {
ExerciseCommand: {
templateId: '#splice-amulet:Splice.AmuletRules:TransferPreapproval',
contractId: transferPreapprovalContractId,
choice: 'TransferPreapproval_Send',
choiceArgument: {
context: {
amuletRules: amuletRules.amulet_rules.contract.contract_id,
context: {
openMiningRound: openMiningRoundContractId,
issuingMiningRounds: [],
validatorRights: [],
featuredAppRight: featuredAppRightContractId,
},
},
inputs,
amount: transfer.amount,
sender: params.senderPartyId,
description: transfer.description ?? null,
},
},
};
if (!amuletRules.amulet_rules.domain_id) {
throw new Error('Amulet rules domain ID is required');
}
// Submit the command
const submitParams = {
commands: [transferCommand],
commandId: `transfer-preapproved-${transfer.recipientPartyId}-${Date.now()}`,
actAs: [params.senderPartyId],
disclosedContracts: transferDisclosedContracts,
};
const result = await ledgerClient.submitAndWaitForTransactionTree(submitParams);
transferResults.push({
recipientPartyId: transfer.recipientPartyId,
contractId: transferPreapprovalContractId,
domainId: amuletRules.amulet_rules.domain_id,
transferResult: result,
});
}
return { transferResults };
}
//# sourceMappingURL=transfer-to-preapproved.js.map