Skip to content

Commit 25a6d88

Browse files
authored
Merge pull request #7993 from BitGo/WIN-8764-add-dummy-witness
feat(sdk-coin-ada): add dummy witness in legacy build for sponsored tx
2 parents 43036cc + f315f96 commit 25a6d88

3 files changed

Lines changed: 109 additions & 1 deletion

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,4 +450,5 @@ export class Transaction extends BaseTransaction {
450450
export interface SponsorshipInfo {
451451
feeAddress: string;
452452
feeAddressInputBalance: string;
453+
isRebuild?: boolean; // hack to redirect the flow to the legacy build
453454
}

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,14 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder {
476476

477477
/** @inheritdoc */
478478
protected async buildImplementation(): Promise<Transaction> {
479-
if (this._isTokenTransaction || (this._sponsorshipInfo && this._type === TransactionType.Send)) {
479+
/**
480+
* Fee address utxo reservation builds a new transaction that goes through legacy build
481+
* rebuild flag is just a hack to redirect the flow to the legacy build
482+
*/
483+
if (
484+
this._isTokenTransaction ||
485+
(this._sponsorshipInfo && !this._sponsorshipInfo.isRebuild && this._type === TransactionType.Send)
486+
) {
480487
return this.processTokenBuild();
481488
}
482489
const inputs = CardanoWasm.TransactionInputs.new();
@@ -598,6 +605,11 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder {
598605
if (this._type !== TransactionType.Send) {
599606
vkeyWitnesses.add(vkeyWitness);
600607
}
608+
if (this._sponsorshipInfo?.isRebuild) {
609+
const sponsorPrv = CardanoWasm.PrivateKey.generate_ed25519();
610+
const sponsorVkeyWitness = CardanoWasm.make_vkey_witness(txHash, sponsorPrv);
611+
vkeyWitnesses.add(sponsorVkeyWitness);
612+
}
601613
}
602614
witnessSet.set_vkeys(vkeyWitnesses);
603615

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

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,101 @@ describe('ADA Token Operations', async () => {
403403
tx.getFee.should.equal('182485'); // Fee with two witnesses
404404
});
405405

406+
it(`should rebuild a sponsored transaction from hex with isRebuild flag`, async () => {
407+
const feeAddress =
408+
'addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwq2ytjqp';
409+
const quantity = '20';
410+
const senderInputBalance = 5000000;
411+
const feeAddressInputBalance = 20000000;
412+
const totalAssetList = {
413+
[fingerprint]: {
414+
quantity: '100',
415+
policy_id: policyId,
416+
asset_name: asciiEncodedName,
417+
},
418+
};
419+
420+
// Step 1: Build the initial sponsored transaction
421+
const txBuilder = factory.getTransferBuilder();
422+
txBuilder.input({
423+
transaction_id: '3677e75c7ba699bfdc6cd57d42f246f86f63aefd76025006ac78313fad2bba21',
424+
transaction_index: 1,
425+
});
426+
txBuilder.input({
427+
transaction_id: '3677e75c7ba699bfdc6cd57d42f246f86f63aefd76025006ac78313fad2bba22',
428+
transaction_index: 0,
429+
});
430+
431+
txBuilder.output({
432+
address: receiverAddress,
433+
amount: '0',
434+
multiAssets: {
435+
asset_name: asciiEncodedName,
436+
policy_id: policyId,
437+
quantity,
438+
fingerprint,
439+
},
440+
});
441+
442+
txBuilder.changeAddress(senderAddress, senderInputBalance.toString(), totalAssetList);
443+
txBuilder.sponsorshipInfo({
444+
feeAddress: feeAddress,
445+
feeAddressInputBalance: feeAddressInputBalance.toString(),
446+
});
447+
txBuilder.ttl(800000000);
448+
txBuilder.isTokenTransaction();
449+
const initialTx = (await txBuilder.build()) as Transaction;
450+
const initialFee = initialTx.getFee;
451+
const initialTxData = initialTx.toJson();
452+
453+
// Step 2: Rebuild with isRebuild = true
454+
// This simulates rebuilding from scratch but with isRebuild flag to add sponsor witness
455+
const rebuildTxBuilder = factory.getTransferBuilder();
456+
rebuildTxBuilder.input({
457+
transaction_id: '3677e75c7ba699bfdc6cd57d42f246f86f63aefd76025006ac78313fad2bba21',
458+
transaction_index: 1,
459+
});
460+
rebuildTxBuilder.input({
461+
transaction_id: '3677e75c7ba699bfdc6cd57d42f246f86f63aefd76025006ac78313fad2bba22',
462+
transaction_index: 0,
463+
});
464+
465+
rebuildTxBuilder.output({
466+
address: receiverAddress,
467+
amount: '0',
468+
multiAssets: {
469+
asset_name: asciiEncodedName,
470+
policy_id: policyId,
471+
quantity,
472+
fingerprint,
473+
},
474+
});
475+
476+
rebuildTxBuilder.changeAddress(senderAddress, senderInputBalance.toString(), totalAssetList);
477+
rebuildTxBuilder.sponsorshipInfo({
478+
feeAddress: feeAddress,
479+
feeAddressInputBalance: feeAddressInputBalance.toString(),
480+
isRebuild: true,
481+
});
482+
rebuildTxBuilder.ttl(800000000);
483+
rebuildTxBuilder.isTokenTransaction();
484+
485+
const rebuiltTx = (await rebuildTxBuilder.build()) as Transaction;
486+
const rebuiltTxData = rebuiltTx.toJson();
487+
488+
// Verify the rebuilt transaction preserves the same structure
489+
rebuiltTxData.inputs.length.should.equal(initialTxData.inputs.length);
490+
rebuiltTxData.outputs.length.should.equal(initialTxData.outputs.length);
491+
492+
// Fee should be preserved from the original transaction
493+
rebuiltTx.getFee.should.equal(initialFee);
494+
495+
// Verify receiver output is preserved
496+
const receiverOutput = rebuiltTxData.outputs.filter((output) => output.address === receiverAddress);
497+
receiverOutput.length.should.equal(1);
498+
receiverOutput[0].amount.should.equal('1500000');
499+
});
500+
406501
describe('AdaToken verifyTransaction', () => {
407502
let bitgo: TestBitGoAPI;
408503
let adaToken;

0 commit comments

Comments
 (0)