Skip to content

Commit

Permalink
Refactor transaction logic for ease of use (#346)
Browse files Browse the repository at this point in the history
* Refactor transaction logic for ease of use

* move non-witness utxo retrieval to prepare inputs func

* Only add non witness data for ledger

* Skip dust UTXO for send max

* add fiat to btc conversion

* Fix fee calc

* Extract add non witness utxos to own protected function

* Refactor options that would change the structure fo a txn to be immutable

* Mark getBtcEquivalent as deprecated

* Add send max tests

* add combine utxos and send btc tests

* Add send ordinals tests

* Add do not use warning to untested functions

* Remove extra non-witness utxo addition

* Improve ledger input preperation

* Fix input prep
  • Loading branch information
victorkirov authored Feb 15, 2024
1 parent 30b3650 commit 2d298d2
Show file tree
Hide file tree
Showing 11 changed files with 682 additions and 111 deletions.
19 changes: 13 additions & 6 deletions currency/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,27 @@ const getStxFiatEquivalent = (stxAmount: BigNumber, stxBtcRate: BigNumber, btcFi
const getBtcFiatEquivalent = (btcAmount: BigNumber, btcFiatRate: BigNumber): BigNumber =>
satsToBtc(btcAmount).multipliedBy(btcFiatRate);

const getFiatBtcEquivalent = (fiatAmount: BigNumber, btcFiatRate: BigNumber): BigNumber =>
new BigNumber(fiatAmount.dividedBy(btcFiatRate).toFixed(8));

const getStxTokenEquivalent = (fiatAmount: BigNumber, stxBtcRate: BigNumber, btcFiatRate: BigNumber): BigNumber =>
fiatAmount.dividedBy(stxBtcRate).dividedBy(btcFiatRate);

/**
* @deprecated Use getBtcFiatEquivalent instead
*/
const getBtcEquivalent = (fiatAmount: BigNumber, btcFiatRate: BigNumber): BigNumber =>
fiatAmount.dividedBy(btcFiatRate);

export {
fetchBtcFeeRate,
satsToBtc,
btcToSats,
microstacksToStx,
stxToMicrostacks,
getStxFiatEquivalent,
fetchBtcFeeRate,
getBtcEquivalent,
getBtcFiatEquivalent,
getFiatBtcEquivalent,
getStxFiatEquivalent,
getStxTokenEquivalent,
getBtcEquivalent,
microstacksToStx,
satsToBtc,
stxToMicrostacks,
};
14 changes: 11 additions & 3 deletions tests/transactions/bitcoin/actionProcessors.sendBtc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ describe('applySendBtcActionsAndFee', () => {
context as any,
{},
transaction as any,
{},
[
{
type: btcTransaction.ActionType.SEND_BTC,
Expand Down Expand Up @@ -87,9 +88,9 @@ describe('applySendBtcActionsAndFee', () => {
vi.mocked(getTransactionVSize).mockResolvedValueOnce(260);
vi.mocked(getTransactionVSize).mockResolvedValueOnce(190);

await expect(() => applySendBtcActionsAndFee(context as any, {}, transaction as any, [], 10)).rejects.toThrowError(
'No more UTXOs to use. Insufficient funds for this transaction',
);
await expect(() =>
applySendBtcActionsAndFee(context as any, {}, transaction as any, {}, [], 10),
).rejects.toThrowError('No more UTXOs to use. Insufficient funds for this transaction');
});

it("doesn't alter the transaction if no actions and enough for fees", async () => {
Expand Down Expand Up @@ -122,6 +123,7 @@ describe('applySendBtcActionsAndFee', () => {
context as any,
{},
transaction as any,
{},
[],
10,
);
Expand Down Expand Up @@ -169,6 +171,7 @@ describe('applySendBtcActionsAndFee', () => {
context as any,
{},
transaction as any,
{},
[],
5,
);
Expand Down Expand Up @@ -220,6 +223,7 @@ describe('applySendBtcActionsAndFee', () => {
context as any,
{},
transaction as any,
{},
[],
5,
'overrideChangeAddress',
Expand Down Expand Up @@ -295,6 +299,7 @@ describe('applySendBtcActionsAndFee', () => {
context as any,
{},
transaction as any,
{},
[],
10,
);
Expand Down Expand Up @@ -376,6 +381,7 @@ describe('applySendBtcActionsAndFee', () => {
context as any,
{},
transaction as any,
{},
[],
10,
);
Expand Down Expand Up @@ -457,6 +463,7 @@ describe('applySendBtcActionsAndFee', () => {
context as any,
{},
transaction as any,
{},
[],
10,
);
Expand Down Expand Up @@ -529,6 +536,7 @@ describe('applySendBtcActionsAndFee', () => {
context as any,
{},
transaction as any,
{},
[
{
type: btcTransaction.ActionType.SEND_BTC,
Expand Down
18 changes: 9 additions & 9 deletions tests/transactions/bitcoin/actionProcessors.sendUtxo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { applySendUtxoActions } from '../../../transactions/bitcoin/actionProces
describe('applySendUtxoActions', () => {
it('throws if excluded utxo is used for send', async () => {
await expect(() =>
applySendUtxoActions({} as any, { excludeOutpointList: ['txid:0'] }, { inputsLength: 0 } as any, [
applySendUtxoActions({} as any, {}, { inputsLength: 0 } as any, { excludeOutpointList: ['txid:0'] }, [
{
type: btcTransaction.ActionType.SEND_UTXO,
outpoint: 'txid:0',
Expand All @@ -23,7 +23,7 @@ describe('applySendUtxoActions', () => {
context.getUtxo.mockResolvedValueOnce({});

await expect(() =>
applySendUtxoActions(context as any, {}, { inputsLength: 0 } as any, [
applySendUtxoActions(context as any, {}, { inputsLength: 0 } as any, {}, [
{
type: btcTransaction.ActionType.SEND_UTXO,
outpoint: 'txid:0',
Expand All @@ -48,7 +48,7 @@ describe('applySendUtxoActions', () => {
});

await expect(() =>
applySendUtxoActions(context as any, {}, transaction as any, [
applySendUtxoActions(context as any, {}, transaction as any, {}, [
{
type: btcTransaction.ActionType.SEND_UTXO,
outpoint: 'f00d:0',
Expand All @@ -70,7 +70,7 @@ describe('applySendUtxoActions', () => {
const transaction = { inputsLength: 0 };

await expect(() =>
applySendUtxoActions(context as any, {}, transaction as any, [
applySendUtxoActions(context as any, {}, transaction as any, {}, [
{
type: btcTransaction.ActionType.SEND_UTXO,
outpoint: 'f00d:0',
Expand Down Expand Up @@ -101,7 +101,7 @@ describe('applySendUtxoActions', () => {

const transaction = { inputsLength: 0 };

const { inputs, outputs } = await applySendUtxoActions(context as any, {}, transaction as any, [
const { inputs, outputs } = await applySendUtxoActions(context as any, {}, transaction as any, {}, [
{
type: btcTransaction.ActionType.SEND_UTXO,
outpoint: 'f00d:0',
Expand Down Expand Up @@ -145,7 +145,7 @@ describe('applySendUtxoActions', () => {

const transaction = { inputsLength: 0 };

const { inputs, outputs } = await applySendUtxoActions(context as any, {}, transaction as any, [
const { inputs, outputs } = await applySendUtxoActions(context as any, {}, transaction as any, {}, [
{
type: btcTransaction.ActionType.SEND_UTXO,
outpoint: 'f00d:0',
Expand Down Expand Up @@ -195,7 +195,7 @@ describe('applySendUtxoActions', () => {

const transaction = { inputsLength: 0 };

const { inputs, outputs } = await applySendUtxoActions(context as any, {}, transaction as any, [
const { inputs, outputs } = await applySendUtxoActions(context as any, {}, transaction as any, {}, [
{
type: btcTransaction.ActionType.SEND_UTXO,
outpoint: 'f00d:0',
Expand Down Expand Up @@ -242,7 +242,7 @@ describe('applySendUtxoActions', () => {

const transaction = { inputsLength: 0 };

const { inputs, outputs } = await applySendUtxoActions(context as any, {}, transaction as any, [
const { inputs, outputs } = await applySendUtxoActions(context as any, {}, transaction as any, {}, [
{
type: btcTransaction.ActionType.SEND_UTXO,
outpoint: 'f00d:0',
Expand Down Expand Up @@ -304,7 +304,7 @@ describe('applySendUtxoActions', () => {

const transaction = { inputsLength: 0 };

const { inputs, outputs } = await applySendUtxoActions(context as any, {}, transaction as any, [
const { inputs, outputs } = await applySendUtxoActions(context as any, {}, transaction as any, {}, [
{
type: btcTransaction.ActionType.SEND_UTXO,
outpoint: 'f00d:0',
Expand Down
20 changes: 10 additions & 10 deletions tests/transactions/bitcoin/actionProcessors.splitUtxo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('applySplitUtxoActions', () => {
});

await expect(() =>
applySplitUtxoActions({} as any, {}, transaction as any, [
applySplitUtxoActions({} as any, {}, transaction as any, {}, [
{
type: btcTransaction.ActionType.SPLIT_UTXO,
location: 'f00d:0:0',
Expand All @@ -28,7 +28,7 @@ describe('applySplitUtxoActions', () => {
const transaction = { inputsLength: 0 };

await expect(() =>
applySplitUtxoActions({} as any, { excludeOutpointList: ['f00d:0'] }, transaction as any, [
applySplitUtxoActions({} as any, {}, transaction as any, { excludeOutpointList: ['f00d:0'] }, [
{
type: btcTransaction.ActionType.SPLIT_UTXO,
location: 'f00d:0:0',
Expand All @@ -46,7 +46,7 @@ describe('applySplitUtxoActions', () => {
const transaction = { inputsLength: 0 };

await expect(() =>
applySplitUtxoActions(context as any, {}, transaction as any, [
applySplitUtxoActions(context as any, {}, transaction as any, {}, [
{
type: btcTransaction.ActionType.SPLIT_UTXO,
location: 'f00d:0:0',
Expand All @@ -65,7 +65,7 @@ describe('applySplitUtxoActions', () => {
const transaction = { inputsLength: 0 };

await expect(() =>
applySplitUtxoActions(context as any, {}, transaction as any, [
applySplitUtxoActions(context as any, {}, transaction as any, {}, [
{
type: btcTransaction.ActionType.SPLIT_UTXO,
location: 'f00d:0:-1',
Expand All @@ -84,7 +84,7 @@ describe('applySplitUtxoActions', () => {
const transaction = { inputsLength: 0 };

await expect(() =>
applySplitUtxoActions(context as any, {}, transaction as any, [
applySplitUtxoActions(context as any, {}, transaction as any, {}, [
{
type: btcTransaction.ActionType.SPLIT_UTXO,
location: 'f00d:0:100',
Expand All @@ -107,7 +107,7 @@ describe('applySplitUtxoActions', () => {
const transaction = { inputsLength: 0 };

await expect(() =>
applySplitUtxoActions(context as any, {}, transaction as any, [
applySplitUtxoActions(context as any, {}, transaction as any, {}, [
{
type: btcTransaction.ActionType.SPLIT_UTXO,
location: 'f00d:0:600',
Expand All @@ -130,7 +130,7 @@ describe('applySplitUtxoActions', () => {
});
const transaction = { inputsLength: 0 };

const { inputs, outputs } = await applySplitUtxoActions(context as any, {}, transaction as any, [
const { inputs, outputs } = await applySplitUtxoActions(context as any, {}, transaction as any, {}, [
{
type: btcTransaction.ActionType.SPLIT_UTXO,
location: 'f00d:0:600',
Expand Down Expand Up @@ -186,7 +186,7 @@ describe('applySplitUtxoActions', () => {
});
const transaction = { inputsLength: 0 };

const { inputs, outputs } = await applySplitUtxoActions(context as any, {}, transaction as any, [
const { inputs, outputs } = await applySplitUtxoActions(context as any, {}, transaction as any, {}, [
{
type: btcTransaction.ActionType.SPLIT_UTXO,
location: 'f00d:0:600',
Expand Down Expand Up @@ -265,7 +265,7 @@ describe('applySplitUtxoActions', () => {
});
const transaction = { inputsLength: 0 };

const { inputs, outputs } = await applySplitUtxoActions(context as any, {}, transaction as any, [
const { inputs, outputs } = await applySplitUtxoActions(context as any, {}, transaction as any, {}, [
{
type: btcTransaction.ActionType.SPLIT_UTXO,
location: 'f00d:0:600',
Expand Down Expand Up @@ -340,7 +340,7 @@ describe('applySplitUtxoActions', () => {
});
const transaction = { inputsLength: 0 };

const { inputs, outputs } = await applySplitUtxoActions(context as any, {}, transaction as any, [
const { inputs, outputs } = await applySplitUtxoActions(context as any, {}, transaction as any, {}, [
{
type: btcTransaction.ActionType.SPLIT_UTXO,
location: 'f00d:0:600',
Expand Down
45 changes: 4 additions & 41 deletions tests/transactions/bitcoin/enhancedTransaction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,45 +34,6 @@ describe('EnhancedTransaction constructor', () => {
expect(() => new EnhancedTransaction(ctx, [], 1)).throws('No actions provided for transaction context');
});

it('should throw on low fee rate', () => {
const txn = new EnhancedTransaction(
ctx,
[
{
type: ActionType.SEND_BTC,
amount: 100000n,
combinable: false,
toAddress: addresses[0].nativeSegwit,
},
],
1,
);
expect(() => (txn.feeRate = 0)).throws('Fee rate must be a natural number');
expect(() => (txn.feeRate = -1)).throws('Fee rate must be a natural number');
expect(() => (txn.feeRate = 1)).not.toThrow();
});

it('should round decimal fee rate', () => {
const txn = new EnhancedTransaction(
ctx,
[
{
type: ActionType.SEND_BTC,
amount: 100000n,
combinable: false,
toAddress: addresses[0].nativeSegwit,
},
],
1,
);

txn.feeRate = 1.1;
expect(txn.feeRate).equals(1);

txn.feeRate = 1.5;
expect(txn.feeRate).equals(2);
});

describe('should throw if spendable send utxo actions invalid', () => {
it.each([
[
Expand Down Expand Up @@ -223,7 +184,7 @@ describe('EnhancedTransaction summary', () => {

vi.mocked(applySendUtxoActions).mockRejectedValue(new Error('Not enough utxos at desired fee rate'));

await expect(() => txn.getFeeSummary()).rejects.toThrow('Not enough utxos at desired fee rate');
await expect(() => txn.getSummary()).rejects.toThrow('Not enough utxos at desired fee rate');
});

it('compiles transaction and summary correctly', async () => {
Expand Down Expand Up @@ -372,11 +333,12 @@ describe('EnhancedTransaction summary', () => {
actualFee: 500n,
actualFeeRate: 50,
effectiveFeeRate: 50,
dustValue: 2n,
});

// ==========================
// actual thing we're testing
const summary = await txn.getFeeSummary();
const summary = await txn.getSummary();
// ==========================

expect(summary).toEqual({
Expand Down Expand Up @@ -509,6 +471,7 @@ describe('EnhancedTransaction summary', () => {
},
],
},
dustValue: 2n,
});

expect(paymentAddressContext.signInputs).not.toHaveBeenCalled();
Expand Down
Loading

0 comments on commit 2d298d2

Please sign in to comment.