Skip to content
Merged
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
54 changes: 53 additions & 1 deletion src/transaction/TransactionController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ export interface TransactionState extends BaseState {
*/
export const CANCEL_RATE = 1.5;

/**
* Multiplier used to determine a transaction's increased gas fee during speed up
*/
export const SPEED_UP_RATE = 1.1;

/**
* Controller responsible for submitting and managing transactions
*/
Expand Down Expand Up @@ -436,9 +441,15 @@ export class TransactionController extends BaseController<TransactionConfig, Tra
transactionMeta.status = 'rejected';
this.hub.emit(`${transactionMeta.id}:finished`, transactionMeta);
const transactions = this.state.transactions.filter(({ id }) => id !== transactionID);
this.update({ transactions });
this.update({ transactions: [...transactions] });
}

/**
* Attempts to cancel a transaction based on its ID by setting its status to "rejected"
* and emitting a `<tx.id>:finished` hub event.
*
* @param transactionID - ID of the transaction to cancel
*/
async stopTransaction(transactionID: string) {
const transactionMeta = this.state.transactions.find(({ id }) => id === transactionID);
if (!transactionMeta) {
Expand Down Expand Up @@ -470,6 +481,47 @@ export class TransactionController extends BaseController<TransactionConfig, Tra
this.hub.emit(`${transactionMeta.id}:finished`, transactionMeta);
}

/**
* Attemps to speed up a transaction increasing transaction gasPrice by ten percent
*
* @param transactionID - ID of the transaction to speed up
*/
async speedUpTransaction(transactionID: string) {
const transactionMeta = this.state.transactions.find(({ id }) => id === transactionID);
/* istanbul ignore next */
if (!transactionMeta) {
return;
}

/* istanbul ignore next */
if (!this.sign) {
throw new Error('No sign method defined.');
}

const { transactions } = this.state;
const existingGasPrice = transactionMeta.transaction.gasPrice;
/* istanbul ignore next */
const existingGasPriceDecimal = parseInt(existingGasPrice === undefined ? '0x0' : existingGasPrice, 16);
const gasPrice = `0x${(existingGasPriceDecimal * SPEED_UP_RATE).toString(16)}`;
const ethTransaction = new Transaction({ ...transactionMeta.transaction, gasPrice });
await this.sign(ethTransaction, transactionMeta.transaction.from);
const rawTransaction = bufferToHex(ethTransaction.serialize());
const transactionHash = await this.query('sendRawTransaction', [rawTransaction]);
const newTransactionMeta = {
...transactionMeta,
id: random(),
time: Date.now(),
transaction: {
...transactionMeta.transaction,
gasPrice
},
transactionHash
};
transactions.push(newTransactionMeta);
this.update({ transactions: [...transactions] });
this.hub.emit(`${transactionMeta.id}:speedup`, newTransactionMeta);
}

/**
* Estimates required gas for a given transaction
*
Expand Down
26 changes: 26 additions & 0 deletions tests/TransactionController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -515,4 +515,30 @@ describe('TransactionController', () => {
}
});
});

it('should speed up a transaction', async () => {
return new Promise(async (resolve) => {
const controller = new TransactionController({
provider: PROVIDER,
sign: async (transaction: any) => transaction
});
const from = '0xc38bf1ad06ef69f0c04e29dbeb4152b4175f0a8d';
controller.context = {
NetworkController: MOCK_NETWORK
} as any;
controller.onComposed();
await controller.addTransaction({
from,
gas: '0x0',
gasPrice: '0x1',
to: from,
value: '0x0'
});
await controller.speedUpTransaction(controller.state.transactions[0].id);

expect(controller.state.transactions.length).toBe(2);
expect(controller.state.transactions[1].transaction.gasPrice).toBe('0x1.199999999999a');
resolve();
});
});
});