Skip to content

Commit

Permalink
Merge e65b4ef into c564779
Browse files Browse the repository at this point in the history
  • Loading branch information
josipbagaric committed Aug 8, 2018
2 parents c564779 + e65b4ef commit 29aab95
Show file tree
Hide file tree
Showing 10 changed files with 212 additions and 45 deletions.
20 changes: 4 additions & 16 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 4 additions & 6 deletions src/Actions/Actions.ts
Expand Up @@ -4,6 +4,7 @@ import { isExecuted, isTransactionStatusSuccessful } from './Helpers';
import hasPending from './Pending';
import { IWalletReceipt } from '../Wallet';
import { ExecuteStatus, ClaimStatus } from '../Enum';
import { getExecutionGasPrice } from '../EconomicStrategy';
import { TxSendErrors } from '../Enum/TxSendErrors';

export function shortenAddress(address: string) {
Expand Down Expand Up @@ -198,17 +199,14 @@ export default class Actions {
}

private async getExecutionOpts(txRequest: any): Promise<any> {
const gas = txRequest.callGas
.add(180000)
.div(64)
.times(65)
.round();
const gas = this.config.util.calculateGasAmount(txRequest);
const gasPrice = await getExecutionGasPrice(txRequest, this.config);

return {
to: txRequest.address,
value: 0,
gas,
gasPrice: txRequest.gasPrice,
gasPrice,
data: txRequest.executeData
};
}
Expand Down
83 changes: 78 additions & 5 deletions src/EconomicStrategy/EconomicStrategyHelpers.ts
@@ -1,13 +1,14 @@
import { IEconomicStrategy } from './IEconomicStrategy';
import Config from '../Config';
import { BigNumber } from 'bignumber.js';

/**
* Checks whether a transaction requires a deposit that's higher than a
* user-set maximum deposit limit.
* @param {TransactionRequest} txRequest Transaction Request object to check.
* @param {IEconomicStrategy} economicStrategy Economic strategy configuration object.
*/
const exceedsMaxDeposit = (txRequest: any, economicStrategy: IEconomicStrategy) => {
const exceedsMaxDeposit = (txRequest: any, economicStrategy: IEconomicStrategy): boolean => {
const requiredDeposit = txRequest.requiredDeposit;
const maxDeposit = economicStrategy.maxDeposit;

Expand All @@ -22,7 +23,7 @@ const exceedsMaxDeposit = (txRequest: any, economicStrategy: IEconomicStrategy)
* Checks if the balance of the TimeNode is above a set limit.
* @param {Config} config TimeNode configuration object.
*/
const isAboveMinBalanceLimit = async (config: Config) => {
const isAboveMinBalanceLimit = async (config: Config): Promise<boolean> => {
const minBalance = config.economicStrategy.minBalance;
const currentBalance = await config.wallet.getBalanceOf(config.wallet.getAddresses()[0]);

Expand All @@ -38,7 +39,10 @@ const isAboveMinBalanceLimit = async (config: Config) => {
* @param {TransactionRequest} txRequest Transaction Request object to check.
* @param {IEconomicStrategy} economicStrategy Economic strategy configuration object.
*/
const isProfitable = async (txRequest: any, economicStrategy: IEconomicStrategy) => {
const isProfitable = async (
txRequest: any,
economicStrategy: IEconomicStrategy
): Promise<boolean> => {
const paymentModifier = await txRequest.claimPaymentModifier();
const reward = txRequest.bounty.times(paymentModifier);

Expand All @@ -51,7 +55,12 @@ const isProfitable = async (txRequest: any, economicStrategy: IEconomicStrategy)
return true;
};

const shouldClaimTx = async (txRequest: any, config: Config) => {
/**
* Validates all the economic strategy parameters before claiming a certain transaction.
* @param {TransactionRequest} txRequest Transaction Request object to check.
* @param {Config} config Configuration object.
*/
const shouldClaimTx = async (txRequest: any, config: Config): Promise<boolean> => {
if (!config.economicStrategy) {
return true;
}
Expand All @@ -71,4 +80,68 @@ const shouldClaimTx = async (txRequest: any, config: Config) => {
return profitable && enoughBalance && !exceedsDepositLimit;
};

export { shouldClaimTx };
/**
* Calculates the correct gas price to use for execution, taking into consideration
* the economicStrategy `maxGasSubsidy` and the current network conditions.
* @param {TransactionRequest} txRequest Transaction Request object to check.
* @param {Config} config Configuration object.
*/
const getExecutionGasPrice = async (txRequest: any, config: Config): Promise<BigNumber> => {
const currentNetworkPrice = await config.util.networkGasPrice();

if (!config.economicStrategy) {
return currentNetworkPrice;
}

const maxGasSubsidy = config.economicStrategy.maxGasSubsidy;

if (typeof maxGasSubsidy !== 'undefined' && maxGasSubsidy !== null) {
const minGasPrice = txRequest.gasPrice;
const maxGasPrice = minGasPrice.plus(minGasPrice.times(maxGasSubsidy / 100));

if (currentNetworkPrice.lessThan(minGasPrice)) {
return minGasPrice;
} else if (
currentNetworkPrice.greaterThanOrEqualTo(minGasPrice) &&
currentNetworkPrice.lessThan(maxGasPrice)
) {
return currentNetworkPrice;
} else if (currentNetworkPrice.greaterThanOrEqualTo(maxGasPrice)) {
return maxGasPrice;
}
}

return currentNetworkPrice;
};

/**
* Checks if the transaction is profitable to be executed when considering the
* current network gas prices.
* @param {TransactionRequest} txRequest Transaction Request object to check.
* @param {Config} config Configuration object.
*/
const shouldExecuteTx = async (txRequest: any, config: Config): Promise<boolean> => {
const isClaimedByMe = config.wallet.getAddresses().indexOf(txRequest.claimedBy) !== -1;

const gasPrice = await this.getExecutionGasPrice(txRequest, config);
const gasAmount = config.util.calculateGasAmount(txRequest);
const reimbursement = txRequest.gasPrice.times(gasAmount);
const deposit = isClaimedByMe ? txRequest.requiredDeposit : new BigNumber(0);

const paymentModifier = await txRequest.claimPaymentModifier();
const reward = txRequest.bounty.times(paymentModifier);

const gasCost = gasPrice.times(gasAmount);
const expectedReward = deposit.plus(reward).plus(reimbursement);
const shouldExecute = gasCost.lessThanOrEqualTo(expectedReward);

config.logger.debug(
`[${
txRequest.address
}] shouldExecuteTx ret ${shouldExecute} gasCost=${gasCost.toNumber()} expectedReward=${expectedReward.toNumber()}`
);

return shouldExecute;
};

export { shouldClaimTx, shouldExecuteTx, getExecutionGasPrice };
25 changes: 25 additions & 0 deletions src/EconomicStrategy/IEconomicStrategy.ts
@@ -1,7 +1,32 @@
import BigNumber from 'bignumber.js';

export interface IEconomicStrategy {
/**
* Maximum deposit a TimeNode would be willing
* to stake while claiming a transaction.
*/
maxDeposit?: BigNumber;

/**
* Minimum balance a TimeNode has to
* have in order to claim a transaction.
*/
minBalance?: BigNumber;

/**
* Minimum profitability a scheduled transactions
* has to bring in order for the TimeNode to claim it.
*/
minProfitability?: BigNumber;

/**
* A number which defines the percentage with which
* the TimeNode would be able to subsidize the amount of gas
* it sends at the time of the execution.
*
* e.g. If the scheduled transaction has set the gas price to 20 gwei
* and `maxGasSubsidy` is set to 50, the TimeNode would be willing
* to subsidize gas costs to up to 30 gwei.
*/
maxGasSubsidy?: number;
}
2 changes: 1 addition & 1 deletion src/EconomicStrategy/index.ts
@@ -1,2 +1,2 @@
export { IEconomicStrategy } from './IEconomicStrategy';
export { shouldClaimTx } from './EconomicStrategyHelpers';
export { shouldClaimTx, shouldExecuteTx, getExecutionGasPrice } from './EconomicStrategyHelpers';
34 changes: 21 additions & 13 deletions src/Router/Router.ts
@@ -1,7 +1,7 @@
import Actions from '../Actions';
import Config from '../Config';
import { TxStatus, ClaimStatus, ExecuteStatus } from '../Enum';
import { shouldClaimTx } from '../EconomicStrategy';
import { shouldClaimTx, shouldExecuteTx } from '../EconomicStrategy';

import W3Util from '../Util';
import { ITxRequest } from '../Types';
Expand Down Expand Up @@ -110,21 +110,29 @@ export default class Router {
return TxStatus.ExecutionWindow;
}

try {
const executed: ExecuteStatus = await this.actions.execute(txRequest);
const shouldExecute = await shouldExecuteTx(txRequest, this.config);

if (executed === ExecuteStatus.SUCCESS) {
this.config.logger.info(`[${txRequest.address}] executed`);
if (shouldExecute) {
try {
const executionStatus: ExecuteStatus = await this.actions.execute(txRequest);

return TxStatus.Executed;
} else {
this.config.logger.debug(`[${txRequest.address}] error: ${executed}`);
}
} catch (e) {
this.config.logger.error(`[${txRequest.address}] execution failed`);
if (executionStatus === ExecuteStatus.SUCCESS) {
this.config.logger.info(`[${txRequest.address}] executed`);

return TxStatus.Executed;
} else {
this.config.logger.debug(`[${txRequest.address}] error: ${executionStatus}`);
}
} catch (e) {
this.config.logger.error(`[${txRequest.address}] execution failed`);

//TODO handle gracefully?
throw new Error(e);
//TODO handle gracefully?
throw new Error(e);
}
} else {
this.config.logger.info(
`[${txRequest.address}] not profitable to execute. Gas price too high`
);
}

return TxStatus.ExecutionWindow;
Expand Down
9 changes: 9 additions & 0 deletions src/Util.ts
@@ -1,4 +1,5 @@
import { IBlock } from './Types';
import BigNumber from 'bignumber.js';

export default class W3Util {
public web3: any;
Expand All @@ -7,6 +8,14 @@ export default class W3Util {
this.web3 = web3;
}

public calculateGasAmount(txRequest: any): BigNumber {
return txRequest.callGas
.add(180000)
.div(64)
.times(65)
.round();
}

public estimateGas(opts: any): Promise<any> {
return new Promise((resolve, reject) => {
this.web3.eth.estimateGas(opts, (e: any, r: any) => {
Expand Down
4 changes: 2 additions & 2 deletions test/e2e/TestScheduleTx.ts
Expand Up @@ -73,7 +73,7 @@ export const scheduleTestTx = async () => {
const { callValue } = SCHEDULED_TX_PARAMS;

const callGas = new BigNumber(1000000);
const gasPrice = new BigNumber(1);
const gasPrice = new BigNumber(web3.toWei(20, 'gwei'));
const fee = new BigNumber(0);
const bounty = new BigNumber(0);

Expand All @@ -98,7 +98,7 @@ export const scheduleTestTx = async () => {
callValue,
'255', // windowSize
latestBlock + 270, // windowStart
1, // gasPrice
gasPrice, // gasPrice
fee,
bounty,
'0', // requiredDeposit
Expand Down
3 changes: 2 additions & 1 deletion test/helpers/mockConfig.ts
Expand Up @@ -21,7 +21,8 @@ const mockConfig = (preConfig?: any) => {
economicStrategy: {
maxDeposit: new BigNumber(0),
minBalance: new BigNumber(0),
minProfitability: new BigNumber(0)
minProfitability: new BigNumber(0),
maxGasSubsidy: 100
},
logger: new MockLogger(),
ms: 4000,
Expand Down

0 comments on commit 29aab95

Please sign in to comment.