Skip to content

Commit

Permalink
economic strategies adjustment
Browse files Browse the repository at this point in the history
  • Loading branch information
kosecki123 committed Jan 31, 2019
1 parent 4259b68 commit 0e78fb7
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 7 deletions.
31 changes: 24 additions & 7 deletions src/EconomicStrategy/EconomicStrategyManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ export class EconomicStrategyManager {
public async shouldClaimTx(
txRequest: ITransactionRequest,
nextAccount: Address,
fastestGas: BigNumber
gasPrice: BigNumber
): Promise<EconomicStrategyStatus> {
const profitable = await this.isClaimingProfitable(txRequest, fastestGas);
const profitable = await this.isClaimingProfitable(txRequest, gasPrice);
if (!profitable) {
return EconomicStrategyStatus.NOT_PROFITABLE;
}
Expand Down Expand Up @@ -204,17 +204,30 @@ export class EconomicStrategyManager {

private async isClaimingProfitable(
txRequest: ITransactionRequest,
targetGasPrice: BigNumber
claimingGasPrice: BigNumber
): Promise<boolean> {
const paymentModifier = (await txRequest.claimPaymentModifier()).dividedBy(100);
const claimingGasCost = targetGasPrice.times(CLAIMING_GAS_ESTIMATE);
const reward = txRequest.bounty.times(paymentModifier).minus(claimingGasCost);
const paymentModifier = await this.getPaymentModifier(txRequest);
const claimingGasCost = claimingGasPrice.times(CLAIMING_GAS_ESTIMATE);

const { gasPrice } = txRequest;
const executionGasAmount = this.util.calculateGasAmount(txRequest);
let executionSubsidy = new BigNumber(0);

const { average } = await this.gasPriceUtil.getAdvancedNetworkGasPrice();
if (gasPrice < average) {
executionSubsidy = average.minus(gasPrice).times(executionGasAmount);
}

const reward = txRequest.bounty
.times(paymentModifier)
.minus(claimingGasCost)
.minus(executionSubsidy);
const minProfitability = this.strategy.minProfitability;

const isProfitable = reward.greaterThanOrEqualTo(minProfitability);

this.logger.debug(
`isClaimingProfitable: paymentModifier=${paymentModifier} targetGasPrice=${targetGasPrice} bounty=${
`isClaimingProfitable: paymentModifier=${paymentModifier} targetGasPrice=${claimingGasPrice} bounty=${
txRequest.bounty
} reward=${reward} >= minProfitability=${minProfitability} returns ${isProfitable}`,
txRequest.address
Expand All @@ -223,6 +236,10 @@ export class EconomicStrategyManager {
return isProfitable;
}

private async getPaymentModifier(txRequest: ITransactionRequest) {
return (await txRequest.claimPaymentModifier()).dividedBy(100);
}

private get maxSubsidyFactor(): number {
const maxGasSubsidy = this.strategy.maxGasSubsidy / 100;
return maxGasSubsidy + 1;
Expand Down
49 changes: 49 additions & 0 deletions src/EconomicStrategy/ProfitabilityStrategy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { ITransactionRequest, Util, GasPriceUtil } from '@ethereum-alarm-clock/lib';
import BigNumber from 'bignumber.js';
import { ILogger, DefaultLogger } from '../Logger';

const CLAIMING_GAS_ESTIMATE = 100000; // Claiming gas is around 75k, we add a small surplus

export class ProfitabilityStrategy {
private util: Util;
private logger: ILogger;
private gasPriceUtil: GasPriceUtil;

constructor(util: Util, gasPriceUtil: GasPriceUtil, logger: ILogger = new DefaultLogger()) {
this.util = util;
this.gasPriceUtil = gasPriceUtil;
this.logger = logger;
}

public async claimingProfitability(txRequest: ITransactionRequest, claimingGasPrice: BigNumber) {
const paymentModifier = await this.getPaymentModifier(txRequest);
const claimingGasCost = claimingGasPrice.times(CLAIMING_GAS_ESTIMATE);

const executionGasAmount = this.util.calculateGasAmount(txRequest);
let executionSubsidy = new BigNumber(0);

const { average } = await this.gasPriceUtil.getAdvancedNetworkGasPrice();

if (txRequest.gasPrice < average) {
executionSubsidy = average.minus(txRequest.gasPrice).times(executionGasAmount);
}

const reward = txRequest.bounty
.times(paymentModifier)
.minus(claimingGasCost)
.minus(executionSubsidy);

this.logger.debug(
`claimingProfitability: paymentModifier=${paymentModifier} targetGasPrice=${claimingGasPrice} bounty=${
txRequest.bounty
} reward=${reward}`,
txRequest.address
);

return reward;
}

private async getPaymentModifier(txRequest: ITransactionRequest) {
return (await txRequest.claimPaymentModifier()).dividedBy(100);
}
}
127 changes: 127 additions & 0 deletions test/unit/UnitTestProfitabilityStrategy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import {
GasPriceEstimation,
Util,
ITransactionRequest,
GasPriceUtil
} from '@ethereum-alarm-clock/lib';
import BigNumber from 'bignumber.js';
import * as TypeMoq from 'typemoq';

import { ProfitabilityStrategy } from '../../src/EconomicStrategy/ProfitabilityStrategy';
import { assert } from 'chai';

describe('Profitability Strategy Tests', () => {
const MWei = new BigNumber(1000000);
const GWei = MWei.times(1000);
const Szabo = GWei.times(1000);
const Finney = Szabo.times(1000);

const account = '0x123456';
const defaultBounty = Finney.times(20);
const defaultGasPrice = GWei;
const defaultPaymentModifier = new BigNumber(10); //10%
const CLAIMING_GAS_ESTIMATE = 100000;

const util = new Util(null);

const createTxRequest = (
gasPrice = defaultGasPrice,
bounty = defaultBounty,
claimedBy = account,
paymentModifier = defaultPaymentModifier,
temporalUnit = 1,
reservedWindowSize = new BigNumber(3600),
claimWindowEnd = new BigNumber(123155)
) => {
const txRequest = TypeMoq.Mock.ofType<ITransactionRequest>();
txRequest.setup(tx => tx.gasPrice).returns(() => gasPrice);
txRequest.setup(tx => tx.now()).returns(() => Promise.resolve(new BigNumber(123123)));
txRequest.setup(tx => tx.reservedWindowEnd).returns(() => new BigNumber(23423));
txRequest.setup(tx => tx.reservedWindowSize).returns(() => reservedWindowSize);
txRequest.setup(tx => tx.executionWindowEnd).returns(() => new BigNumber(23423));
txRequest.setup(tx => tx.bounty).returns(() => bounty);
txRequest.setup(tx => tx.requiredDeposit).returns(() => MWei);
txRequest.setup(tx => tx.claimPaymentModifier()).returns(async () => paymentModifier);
txRequest.setup(tx => tx.claimedBy).returns(() => claimedBy);
txRequest.setup(tx => tx.address).returns(() => '0x987654321');
txRequest.setup(tx => tx.temporalUnit).returns(() => temporalUnit);
txRequest.setup(tx => tx.claimWindowEnd).returns(() => claimWindowEnd);
txRequest.setup(tx => tx.callGas).returns(() => new BigNumber(21000));

return txRequest;
};

const createGasPriceUtil = (gasPrice = defaultGasPrice) => {
const gasPriceUtil = TypeMoq.Mock.ofType<GasPriceUtil>();
gasPriceUtil.setup(u => u.networkGasPrice()).returns(() => Promise.resolve(gasPrice));
gasPriceUtil.setup(u => u.getGasPrice()).returns(() => Promise.resolve(gasPrice));
gasPriceUtil
.setup(u => u.getAdvancedNetworkGasPrice())
.returns(() =>
Promise.resolve({
safeLow: gasPrice,
average: gasPrice,
fast: gasPrice,
fastest: gasPrice
} as GasPriceEstimation)
);

return gasPriceUtil.object;
};

const calculateExpectedReward = (
txRequest: ITransactionRequest,
paymentModifier: BigNumber | number,
claimingGasCost: BigNumber | number,
executionSubsidy: BigNumber | number
) =>
txRequest.bounty
.times(paymentModifier)
.minus(claimingGasCost)
.minus(executionSubsidy);

describe('claiming profitability', () => {
it('calculates profitability with default values', async () => {
const strategy = new ProfitabilityStrategy(util, createGasPriceUtil());

const paymentModifier = defaultPaymentModifier.div(100);
const claimingGasCost = defaultGasPrice.times(CLAIMING_GAS_ESTIMATE);
const executionSubsidy = 0;

const txRequest = createTxRequest().object;
const expectedReward = calculateExpectedReward(
txRequest,
paymentModifier,
claimingGasCost,
executionSubsidy
);

const result = await strategy.claimingProfitability(txRequest, defaultGasPrice);

assert.isTrue(expectedReward.equals(result));
assert.isTrue(result.greaterThan(0));
});

it('calculates profitability with 0 minimum execution gas price', async () => {
const strategy = new ProfitabilityStrategy(util, createGasPriceUtil());
const paymentModifier = defaultPaymentModifier.div(100);
const claimingGasCost = defaultGasPrice.times(CLAIMING_GAS_ESTIMATE);

const transactionExecutionGasPrice = new BigNumber(0);
const txRequest = createTxRequest(transactionExecutionGasPrice).object;

const executionSubsidy = util.calculateGasAmount(txRequest).times(defaultGasPrice);
const expectedReward = calculateExpectedReward(
txRequest,
paymentModifier,
claimingGasCost,
executionSubsidy
);

const result = await strategy.claimingProfitability(txRequest, defaultGasPrice);

assert.isTrue(expectedReward.equals(result));
assert.isTrue(result.greaterThan(0));
});
});
});

0 comments on commit 0e78fb7

Please sign in to comment.