Skip to content
Draft
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
19 changes: 12 additions & 7 deletions modules/sdk-coin-ada/src/ada.ts
Original file line number Diff line number Diff line change
Expand Up @@ -507,13 +507,18 @@ export class Ada extends BaseCoin {
value: new BigNumber(output.amount).toNumber(),
},
];
const outputs = [
{
address: output.address,
valueString: output.amount,
coinName: walletCoin,
},
];
const outputs = (
parsedTx.outputs as {
amount: string;
address: string;
tokenTransfers?: { policyId: string; assetName: string; quantity: string }[];
}[]
).map((o) => ({
address: o.address,
valueString: o.amount,
coinName: walletCoin,
...(o.tokenTransfers && { tokenTransfers: o.tokenTransfers }),
}));
const spendAmount = output.amount;
const completedParsedTx = { inputs: inputs, outputs: outputs, spendAmount: spendAmount, type: '' };
const fee = new BigNumber((parsedTx.fee as { fee: string }).fee);
Expand Down
49 changes: 47 additions & 2 deletions modules/sdk-coin-ada/src/lib/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,43 @@ export interface TxData {
pledgeDetails?: PledgeDetails;
}

function extractTokenTransfers(
multiAssets: CardanoWasm.MultiAsset | Asset | undefined
): Array<{ policyId: string; assetName: string; quantity: string }> | undefined {
if (!multiAssets) return undefined;

if (!(multiAssets instanceof CardanoWasm.MultiAsset)) {
return [
{
policyId: multiAssets.policy_id,
assetName:
(multiAssets as Asset & { encoded_asset_name?: string }).encoded_asset_name ?? multiAssets.asset_name,
quantity: multiAssets.quantity,
},
];
}

const transfers: Array<{ policyId: string; assetName: string; quantity: string }> = [];
const scriptHashes = multiAssets.keys();
for (let i = 0; i < scriptHashes.len(); i++) {
const scriptHash = scriptHashes.get(i);
const assets = multiAssets.get(scriptHash);
if (!assets) continue;
const assetNames = assets.keys();
for (let j = 0; j < assetNames.len(); j++) {
const assetName = assetNames.get(j);
const quantity = assets.get(assetName);
if (!quantity) continue;
transfers.push({
policyId: Buffer.from(scriptHash.to_bytes()).toString('hex'),
assetName: Buffer.from(assetName.name()).toString('hex'),
quantity: quantity.to_str(),
});
}
}
return transfers.length > 0 ? transfers : undefined;
}

export class Transaction extends BaseTransaction {
private _transaction: CardanoWasm.Transaction;
private _fee: string;
Expand Down Expand Up @@ -381,7 +418,11 @@ export class Transaction extends BaseTransaction {

/** @inheritdoc */
explainTransaction(): {
outputs: { amount: string; address: string }[];
outputs: {
amount: string;
address: string;
tokenTransfers?: Array<{ policyId: string; assetName: string; quantity: string }>;
}[];
certificates: Cert[];
changeOutputs: string[];
outputAmount: string;
Expand Down Expand Up @@ -414,7 +455,11 @@ export class Transaction extends BaseTransaction {
return {
displayOrder,
id: txJson.id,
outputs: txJson.outputs.map((o) => ({ address: o.address, amount: o.amount })),
outputs: txJson.outputs.map((o) => ({
address: o.address,
amount: o.amount,
...(extractTokenTransfers(o.multiAssets) && { tokenTransfers: extractTokenTransfers(o.multiAssets) }),
})),
outputAmount: outputAmount,
changeOutputs: [],
changeAmount: '0',
Expand Down
55 changes: 55 additions & 0 deletions modules/sdk-coin-ada/test/unit/ada.ts
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,61 @@ describe('ADA', function () {
should.deepEqual(Number(txJson.outputs[0].amount) + fee, testnetUTXO.UTXO_1.value);
});

it('should recover ADA plus token UTXOs - token and ADA both appear in outputs (unsigned sweep)', async function () {
callBack
.withArgs('address_info', { _addresses: [wrwUser.walletAddress0] })
.resolves(endpointResponses.addressInfoResponse.ADAAndTokenUTXOs);

const res = await basecoin.recover({
bitgoKey: wrwUser.bitgoKey,
recoveryDestination: destAddr,
});
res.should.not.be.empty();
const unsignedTx = res.txRequests[0].transactions[0].unsignedTx;
unsignedTx.should.hasOwnProperty('serializedTx');

const tx = new Transaction(basecoin);
tx.fromRawTransaction(unsignedTx.serializedTx);
const txJson = tx.toJson();

txJson.inputs.length.should.equal(2);
should.deepEqual(txJson.inputs[0].transaction_id, testnetUTXO.UTXO_1.tx_hash);
should.deepEqual(txJson.inputs[1].transaction_id, testnetUTXO.UTXO_TOKEN.tx_hash);

txJson.outputs.length.should.equal(2);

const tokenPolicyId = '2533cca6eb42076e144e9f2772c390dece9fce173bc38c72294b3924';
const tokenEncodedAssetName = '5741544552';
const tokenQuantity = '111';
const minADAForToken = 1500000;

const tokenOutput = txJson.outputs.find((o) => o.multiAssets !== undefined);
should.exist(tokenOutput);
should.deepEqual(tokenOutput!.address, destAddr);
should.deepEqual(Number(tokenOutput!.amount), minADAForToken);
const expectedPolicyId = CardanoWasm.ScriptHash.from_bytes(Buffer.from(tokenPolicyId, 'hex'));
const expectedAssetName = CardanoWasm.AssetName.new(Buffer.from(tokenEncodedAssetName, 'hex'));
(tokenOutput!.multiAssets as CardanoWasm.MultiAsset)
.get_asset(expectedPolicyId, expectedAssetName)
.to_str()
.should.equal(tokenQuantity);

const adaOutput = txJson.outputs.find((o) => o.multiAssets === undefined);
should.exist(adaOutput);
should.deepEqual(adaOutput!.address, destAddr);
const fee = Number(tx.explainTransaction().fee.fee);
const totalBalance = testnetUTXO.UTXO_1.value + testnetUTXO.UTXO_TOKEN.value;
should.deepEqual(Number(adaOutput!.amount), totalBalance - minADAForToken - fee);

const explained = tx.explainTransaction();
const explainedTokenOutput = explained.outputs.find((o) => o.tokenTransfers !== undefined);
should.exist(explainedTokenOutput);
explainedTokenOutput!.tokenTransfers!.length.should.equal(1);
should.deepEqual(explainedTokenOutput!.tokenTransfers![0].policyId, tokenPolicyId);
should.deepEqual(explainedTokenOutput!.tokenTransfers![0].assetName, tokenEncodedAssetName);
should.deepEqual(explainedTokenOutput!.tokenTransfers![0].quantity, tokenQuantity);
});

it('should recover ADA plus token UTXOs - token and ADA both appear in outputs (signed)', async function () {
callBack
.withArgs('address_info', { _addresses: [wrwUser.walletAddress0] })
Expand Down
Loading