Skip to content

Commit 911787c

Browse files
committed
feat(sdk-coin-ada): token sponsorship support
Ticket: WIN-8326
1 parent 74744c2 commit 911787c

2 files changed

Lines changed: 83 additions & 4 deletions

File tree

modules/sdk-coin-ada/src/lib/transactionBuilder.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,11 +227,18 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder {
227227
private addOutputs(outputs) {
228228
if (this._sponsorshipInfo) {
229229
const feeAddressUtxoBalance = CardanoWasm.BigNum.from_str(this._sponsorshipInfo.feeAddressInputBalance);
230-
const feeAddressChange = feeAddressUtxoBalance.checked_sub(this._fee);
230+
let feeAddressChange = feeAddressUtxoBalance.checked_sub(this._fee);
231231
const senderAddressUtxoBalance = CardanoWasm.BigNum.from_str(this._senderBalance);
232-
const senderChangeAfterReceiverDeductions = this.addReceiverOutputs(outputs, senderAddressUtxoBalance);
233-
this.addChangeOutput(senderChangeAfterReceiverDeductions, outputs, this._changeAddress); // Change address is the sender address
234-
this.addChangeOutput(feeAddressChange, outputs, this._sponsorshipInfo.feeAddress);
232+
if (this._isTokenTransaction) {
233+
// Fee address sponsors min ada
234+
feeAddressChange = this.addReceiverOutputs(outputs, feeAddressChange);
235+
this.addChangeOutput(senderAddressUtxoBalance, outputs, this._changeAddress);
236+
this.addChangeOutput(feeAddressChange, outputs, this._sponsorshipInfo.feeAddress);
237+
} else {
238+
const senderChangeAfterReceiverDeductions = this.addReceiverOutputs(outputs, senderAddressUtxoBalance);
239+
this.addChangeOutput(senderChangeAfterReceiverDeductions, outputs, this._changeAddress);
240+
this.addChangeOutput(feeAddressChange, outputs, this._sponsorshipInfo.feeAddress);
241+
}
235242
} else {
236243
const utxoBalance = CardanoWasm.BigNum.from_str(this._senderBalance); // Total UTXO balance
237244
const change = utxoBalance.checked_sub(this._fee);

modules/sdk-coin-ada/test/unit/tokenWithdrawal.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,4 +326,76 @@ describe('ADA Token Operations', async () => {
326326

327327
await txBuilder.build().should.not.be.rejected();
328328
});
329+
330+
it(`should build a sponsored token transaction where fee address sponsors min ADA for receiver`, async () => {
331+
const feeAddress =
332+
'addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwq2ytjqp';
333+
const quantity = '20';
334+
const senderInputBalance = 5000000;
335+
const feeAddressInputBalance = 20000000; // Fee address has enough ADA to sponsor
336+
const totalAssetList = {
337+
[fingerprint]: {
338+
quantity: '100',
339+
policy_id: policyId,
340+
asset_name: asciiEncodedName,
341+
},
342+
};
343+
344+
const txBuilder = factory.getTransferBuilder();
345+
// Sender input (has tokens)
346+
txBuilder.input({
347+
transaction_id: '3677e75c7ba699bfdc6cd57d42f246f86f63aefd76025006ac78313fad2bba21',
348+
transaction_index: 1,
349+
});
350+
// Fee address input (sponsors fees and min ADA)
351+
txBuilder.input({
352+
transaction_id: '3677e75c7ba699bfdc6cd57d42f246f86f63aefd76025006ac78313fad2bba22',
353+
transaction_index: 0,
354+
});
355+
356+
txBuilder.output({
357+
address: receiverAddress,
358+
amount: '0', // Set ADA amount to 0 for token transfer (min ADA is handled by fee address)
359+
multiAssets: {
360+
asset_name: asciiEncodedName,
361+
policy_id: policyId,
362+
quantity,
363+
fingerprint,
364+
},
365+
});
366+
367+
txBuilder.changeAddress(senderAddress, senderInputBalance.toString(), totalAssetList);
368+
txBuilder.sponsorshipInfo({
369+
feeAddress: feeAddress,
370+
feeAddressInputBalance: feeAddressInputBalance.toString(),
371+
});
372+
txBuilder.ttl(800000000);
373+
txBuilder.isTokenTransaction();
374+
const tx = (await txBuilder.build()) as Transaction;
375+
376+
should.equal(tx.type, TransactionType.Send);
377+
const txData = tx.toJson();
378+
379+
txData.inputs.length.should.equal(2);
380+
txData.outputs.length.should.equal(4);
381+
382+
// Validate receiver output - min ADA should be sponsored by fee address
383+
const receiverOutput = txData.outputs.filter((output) => output.address === receiverAddress);
384+
receiverOutput.length.should.equal(1);
385+
receiverOutput[0].amount.should.equal('1500000'); // Minimum ADA for asset output (sponsored by fee address)
386+
(receiverOutput[0].multiAssets! as CardanoWasm.MultiAsset)
387+
.get_asset(policyScriptHash, CardanoWasm.AssetName.new(Buffer.from(asciiEncodedName, 'hex')))
388+
.to_str()
389+
.should.equal(quantity);
390+
391+
// Validate sender change output - should have full sender balance (tokens only, no ADA deduction for receiver)
392+
const senderChangeOutput = txData.outputs.filter((output) => output.address === senderAddress);
393+
senderChangeOutput.length.should.be.equal(2);
394+
395+
// Validate fee address change output - should have remaining ADA after fees and min ADA for receiver
396+
const feeAddressChangeOutput = txData.outputs.filter((output) => output.address === feeAddress);
397+
feeAddressChangeOutput.length.should.equal(1);
398+
// Fee address change should not have any tokens
399+
should.not.exist(feeAddressChangeOutput[0].multiAssets);
400+
});
329401
});

0 commit comments

Comments
 (0)