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
46 changes: 44 additions & 2 deletions modules/sdk-coin-stx/src/stx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ import { ExplainTransactionOptions, StxSignTransactionOptions, StxTransactionExp
import { StxLib } from '.';
import { TransactionBuilderFactory } from './lib';
import { TransactionBuilder } from './lib/transactionBuilder';
import { findContractTokenNameUsingContract, findTokenNameByContract, getAddressDetails } from './lib/utils';
import {
findContractTokenNameUsingContract,
findTokenNameByContract,
getAddressDetails,
getMemoIdAndBaseAddressFromAddress,
} from './lib/utils';
import {
AddressDetails,
NativeStxBalance,
Expand Down Expand Up @@ -98,12 +103,49 @@ export class Stx extends BaseCoin {
}

async verifyTransaction(params: VerifyTransactionOptions): Promise<boolean> {
const { txParams } = params;
const { txPrebuild, txParams } = params;
const { memo } = txParams;
if (Array.isArray(txParams.recipients) && txParams.recipients.length > 1) {
throw new Error(
`${this.getChain()} doesn't support sending to more than 1 destination address within a single transaction. Try again, using only a single recipient.`
);
}
const rawTx = txPrebuild?.txHex;
if (!rawTx) {
throw new Error('missing required tx prebuild property txHex');
}
const explainedTx = await this.explainTransaction({ txHex: rawTx, feeInfo: { fee: '' } });
const recipient = txParams.recipients?.[0];
if (recipient !== undefined && explainedTx) {
const txOutput = explainedTx.outputs[0];
const recipientAddress = getMemoIdAndBaseAddressFromAddress(recipient.address).address;
if (txOutput?.address !== recipientAddress) {
throw new Error(
`Tx destination does not match with expected txParams recipient: expected ${recipientAddress} but got ${txOutput?.address}`
);
}
if (BigInt(txOutput?.amount) !== BigInt(recipient.amount)) {
throw new Error(
`Tx amount does not match with expected txParams recipient amount: expected ${recipient.amount} but got ${txOutput?.amount}`
);
}
// compare memo
let memoInput = '';
if (memo && memo.value) {
memoInput = memo.value;
} else {
const addressDetails = getMemoIdAndBaseAddressFromAddress(recipient.address);
memoInput = addressDetails.memoId ?? '';
}
const memoOutput = explainedTx.memo ?? '';
if (memoInput !== memoOutput) {
throw new Error('Tx memo does not match with expected txParams recipient memo');
}
// compare amount
if (!new BigNumber(recipient.amount).isEqualTo(explainedTx.outputAmount)) {
throw new Error('Tx total amount does not match with expected total amount field');
}
}
return true;
}

Expand Down
87 changes: 87 additions & 0 deletions modules/sdk-coin-stx/test/unit/stx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,8 @@ describe('STX:', function () {
describe('Verify Transaction', function () {
const address1 = '0x174cfd823af8ce27ed0afee3fcf3c3ba259116be';
const address2 = '0x7e85bdc27c050e3905ebf4b8e634d9ad6edd0de6';
const txPrebuild = { txHex: testData.txForExplainTransfer, txInfo: {} };

it('should reject a txPrebuild with more than one recipient', async function () {
const wallet = new Wallet(bitgo, basecoin, {});

Expand All @@ -342,6 +344,91 @@ describe('STX:', function () {
`tstx doesn't support sending to more than 1 destination address within a single transaction. Try again, using only a single recipient.`
);
});

it('should reject when txPrebuild is missing txHex', async function () {
const wallet = new Wallet(bitgo, basecoin, {});
const txParams = {
recipients: [{ amount: '1000', address: testData.txExplainedTransfer.recipient }],
wallet,
walletPassphrase: 'fakeWalletPassphrase',
};
await basecoin
.verifyTransaction({ txPrebuild: {}, txParams })
.should.be.rejectedWith('missing required tx prebuild property txHex');
});

it('should succeed to verify a native STX transfer with matching recipient and amount', async function () {
const wallet = new Wallet(bitgo, basecoin, {});
const txParams = {
recipients: [
{
address: testData.txExplainedTransfer.recipient,
amount: testData.txExplainedTransfer.outputAmount,
},
],
memo: { type: '', value: testData.txExplainedTransfer.memo },
wallet,
};
const result = await basecoin.verifyTransaction({ txPrebuild, txParams });
result.should.equal(true);
});

it('should fail to verify transaction with wrong recipient address', async function () {
const wallet = new Wallet(bitgo, basecoin, {});
const requestedAddress = 'ST11NJTTKGVT6D1HY4NJRVQWMQM7TVAR091EJ8P2Y';
const txParams = {
recipients: [
{
address: requestedAddress,
amount: testData.txExplainedTransfer.outputAmount,
},
],
memo: { type: '', value: testData.txExplainedTransfer.memo },
wallet,
};
await basecoin
.verifyTransaction({ txPrebuild, txParams })
.should.be.rejectedWith(
`Tx destination does not match with expected txParams recipient: expected ${requestedAddress} but got ${testData.txExplainedTransfer.recipient}`
);
});

it('should fail to verify transaction with wrong amount', async function () {
const wallet = new Wallet(bitgo, basecoin, {});
const requestedAmount = '9999';
const txParams = {
recipients: [
{
address: testData.txExplainedTransfer.recipient,
amount: requestedAmount,
},
],
memo: { type: '', value: testData.txExplainedTransfer.memo },
wallet,
};
await basecoin
.verifyTransaction({ txPrebuild, txParams })
.should.be.rejectedWith(
`Tx amount does not match with expected txParams recipient amount: expected ${requestedAmount} but got ${testData.txExplainedTransfer.outputAmount}`
);
});

it('should fail to verify transaction with wrong memo', async function () {
const wallet = new Wallet(bitgo, basecoin, {});
const txParams = {
recipients: [
{
address: testData.txExplainedTransfer.recipient,
amount: testData.txExplainedTransfer.outputAmount,
},
],
memo: { type: '', value: 'wrong memo' },
wallet,
};
await basecoin
.verifyTransaction({ txPrebuild, txParams })
.should.be.rejectedWith('Tx memo does not match with expected txParams recipient memo');
});
});

describe('Recover Transaction STX', function () {
Expand Down
Loading