@fairmint/canton-node-sdk
Version:
Canton Node SDK
137 lines • 6.25 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAmuletsForTransfer = getAmuletsForTransfer;
/**
* Gets unlocked amulets owned by the sender party that can be used for transfers. Optionally returns all valid transfer
* inputs (Amulet, AppRewardCoupon, ValidatorRewardCoupon).
*
* @param params - Parameters for getting transfer inputs
* @returns Promise resolving to array of transfer inputs suitable for transfer
*/
async function getAmuletsForTransfer(params) {
const { jsonApiClient, readAs, includeAllTransferInputs } = params;
// Query ledger for active contracts for this party
if (!readAs?.[0]) {
return [];
}
const senderParty = readAs[0];
// Build template IDs to query based on includeAllTransferInputs flag
const templateIds = includeAllTransferInputs
? [
'#splice-amulet:Splice.Amulet:Amulet',
'#splice-amulet:Splice.Amulet:AppRewardCoupon',
'#splice-amulet:Splice.Amulet:ValidatorRewardCoupon',
]
: ['#splice-amulet:Splice.Amulet:Amulet'];
const activeContracts = await jsonApiClient.getActiveContracts({
parties: [senderParty],
templateIds,
});
const allContracts = [];
const contractsArr = Array.isArray(activeContracts) ? activeContracts : [];
contractsArr.forEach((ctr) => {
const typedCtr = ctr;
let payload;
let templateId;
let contractId;
if ('contractEntry' in typedCtr && 'JsActiveContract' in typedCtr.contractEntry) {
const { createdEvent } = typedCtr.contractEntry.JsActiveContract;
payload = createdEvent.createArgument;
({ templateId, contractId } = createdEvent);
}
else if ('contract' in typedCtr) {
const { contract } = typedCtr;
({ payload } = contract);
templateId = contract.contract?.template_id ?? contract.template_id;
contractId = contract.contract?.contract_id ?? contract.contract_id;
}
if (!payload || !templateId || !contractId)
return;
// Filter for valid transfer input contracts
const isUnlockedAmulet = templateId.includes('Splice.Amulet:Amulet') && !templateId.includes('LockedAmulet');
const isAppRewardCoupon = templateId.includes('AppRewardCoupon');
const isValidatorRewardCoupon = templateId.includes('ValidatorRewardCoupon');
if (!isUnlockedAmulet && !isAppRewardCoupon && !isValidatorRewardCoupon)
return;
allContracts.push({ contractId, templateId, payload });
});
// Helper to extract owner/beneficiary and numeric amount from diverse contract shapes
const extract = (contract) => {
const contractPayload = 'payload' in contract ? contract.payload : undefined;
const payload = contractPayload ?? contract.contract?.contract?.payload ?? {};
const contractRecord = contract;
const templateId = 'templateId' in contract ? contract.templateId : undefined;
// Extract owner/beneficiary based on contract type
let ownerFull = '';
if (templateId?.includes('AppRewardCoupon') || templateId?.includes('ValidatorRewardCoupon')) {
// For coupons, beneficiary is optional and falls back to provider
const beneficiary = payload['beneficiary'];
const provider = payload['provider'];
ownerFull = beneficiary ?? provider ?? '';
}
else {
// For amulets, use owner field
ownerFull =
payload['owner'] ??
contractRecord['owner'] ??
contractRecord['partyId'] ??
contractRecord['party_id'] ??
'';
}
// Extract amount based on contract type
let rawAmount = '0';
if (templateId?.includes('AppRewardCoupon') || templateId?.includes('ValidatorRewardCoupon')) {
// For coupons, amount is directly in payload
rawAmount = payload['amount'] ?? '0';
}
else {
// For amulets, amount might be nested
const rawAmountCandidate = payload['amount'] ??
contractRecord['amount'] ??
contractRecord['effective_amount'] ??
contractRecord['effectiveAmount'] ??
contractRecord['initialAmount'] ??
'0';
rawAmount = rawAmountCandidate;
if (typeof rawAmountCandidate === 'object') {
rawAmount = rawAmountCandidate['initialAmount'] ?? '0';
}
}
const numericAmount = parseFloat(rawAmount);
return { owner: ownerFull, numericAmount };
};
// Filter contracts owned by sender (readAs[0]) and with positive balance
const partyContracts = allContracts.filter((c) => {
const { owner, numericAmount } = extract(c);
return numericAmount > 0 && owner === senderParty;
});
if (partyContracts.length === 0) {
return [];
}
// Sort biggest → smallest so we pick high-value contracts first
partyContracts.sort((a, b) => extract(b).numericAmount - extract(a).numericAmount);
// Map to the structure expected by buildAmuletInputs (maintaining backward compatibility)
const result = partyContracts.map((c) => {
const { payload, templateId } = c;
// Extract amount based on contract type
let effectiveAmount = '0';
if (templateId.includes('AppRewardCoupon') || templateId.includes('ValidatorRewardCoupon')) {
// For coupons, amount is directly in payload
effectiveAmount = payload['amount'] ?? '0';
}
else {
// For amulets, amount might be nested
const amtObj = payload['amount'] ?? {};
const intAmount = typeof amtObj === 'object' ? amtObj['initialAmount'] : amtObj;
effectiveAmount = intAmount ?? '0';
}
return {
contractId: c.contractId,
templateId: c.templateId,
effectiveAmount,
owner: extract(c).owner,
};
});
return result;
}
//# sourceMappingURL=get-amulets-for-transfer.js.map