Skip to content
Open
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';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exposed secret in modules/sdk-coin-ada/test/unit/ada.ts - low severity
Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

Reply @AikidoSec ignore: [REASON] to ignore this issue.
More Info

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