Skip to content

Commit

Permalink
Merge pull request #81 from atomex-protocol/feat/atm-361-implement-ge…
Browse files Browse the repository at this point in the history
…t-redeem-reward

Get redeem reward and improve price manager
  • Loading branch information
skubarenko authored Aug 29, 2022
2 parents e6cb5f0 + 9193f4b commit 46bc24b
Show file tree
Hide file tree
Showing 27 changed files with 376 additions and 89 deletions.
6 changes: 4 additions & 2 deletions src/atomex/atomex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { AuthorizationManager } from '../authorization/index';
import type { BalanceManager } from '../blockchain/balanceManager';
import type { AtomexProtocolV1, WalletsManager } from '../blockchain/index';
import type { AtomexService, Currency } from '../common/index';
import { NewOrderRequest, ExchangeManager, symbolsHelper } from '../exchange/index';
import { NewOrderRequest, ExchangeManager, symbolsHelper, PriceManager } from '../exchange/index';
import type { Swap, SwapManager } from '../swaps/index';
import { toFixedBigNumber } from '../utils/converters';
import type { AtomexContext } from './atomexContext';
Expand All @@ -21,6 +21,7 @@ export class Atomex implements AtomexService {
readonly authorization: AuthorizationManager;
readonly exchangeManager: ExchangeManager;
readonly balanceManager: BalanceManager;
readonly priceManager: PriceManager;
readonly swapManager: SwapManager;
readonly wallets: WalletsManager;
readonly atomexContext: AtomexContext;
Expand All @@ -38,6 +39,7 @@ export class Atomex implements AtomexService {
this.exchangeManager = options.managers.exchangeManager;
this.swapManager = options.managers.swapManager;
this.balanceManager = options.managers.balanceManager;
this.priceManager = options.managers.priceManager;

if (options.blockchains)
for (const blockchainName of Object.keys(options.blockchains))
Expand Down Expand Up @@ -159,7 +161,7 @@ export class Atomex implements AtomexService {
}

async convertCurrency(fromAmount: BigNumber.Value, fromCurrency: Currency['id'], toCurrency: Currency['id']): Promise<BigNumber | undefined> {
const price = await this.atomexContext.managers.priceManager.getAveragePrice({ baseCurrency: fromCurrency, quoteCurrency: toCurrency });
const price = await this.priceManager.getAveragePrice({ baseCurrency: fromCurrency, quoteCurrency: toCurrency });
if (!price)
return undefined;

Expand Down
4 changes: 3 additions & 1 deletion src/atomex/atomexSwapPreviewManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,10 +243,12 @@ export class AtomexSwapPreviewManager {

const fromAtomexProtocol = (fromCurrencyInfo.atomexProtocol as AtomexProtocolV1);
const toAtomexProtocol = (toCurrencyInfo.atomexProtocol as AtomexProtocolV1);

const toRedeemFees = await toAtomexProtocol.getRedeemFees({});
const [fromInitiateFees, toRedeemOrRewardForRedeem, fromRefundFees, toInitiateFees, fromRedeemFees] = await Promise.all([
// TODO: fill parameters
fromAtomexProtocol.getInitiateFees({}),
useWatchTower ? toAtomexProtocol.getRedeemReward(0, 0) : toAtomexProtocol.getRedeemFees({}),
useWatchTower ? toAtomexProtocol.getRedeemReward(toRedeemFees) : toRedeemFees,
fromAtomexProtocol.getRefundFees({}),

toAtomexProtocol.getInitiateFees({}),
Expand Down
3 changes: 2 additions & 1 deletion src/atomex/models/atomexOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {
CurrencyBalanceProvider, WalletsManager, SwapTransactionsProvider
} from '../../blockchain/index';
import type { Currency } from '../../common/index';
import type { ExchangeManager, ExchangeService } from '../../exchange/index';
import type { ExchangeManager, ExchangeService, PriceManager } from '../../exchange/index';
import type { SwapManager } from '../../swaps/index';
import type { AtomexContext } from '../atomexContext';

Expand All @@ -21,6 +21,7 @@ export interface AtomexManagers {
exchangeManager: ExchangeManager;
swapManager: SwapManager;
balanceManager: BalanceManager;
priceManager: PriceManager;
}

export interface AtomexServices {
Expand Down
15 changes: 9 additions & 6 deletions src/atomexBuilder/atomexBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ export class AtomexBuilder {
authorizationManager: this.atomexContext.managers.authorizationManager,
exchangeManager: this.atomexContext.managers.exchangeManager,
swapManager: this.atomexContext.managers.swapManager,
balanceManager: this.atomexContext.managers.balanceManager
balanceManager: this.atomexContext.managers.balanceManager,
priceManager: this.atomexContext.managers.priceManager,
},
blockchains
});
Expand Down Expand Up @@ -133,10 +134,12 @@ export class AtomexBuilder {
}

protected createPriceManager(): PriceManager {
return new MixedPriceManager(new Map<string, PriceProvider>([
['binance', new BinancePriceProvider()],
['kraken', new KrakenPriceProvider()],
['atomex', new AtomexPriceProvider(this.atomexContext.services.exchangeService)]
]));
return new MixedPriceManager(
this.atomexContext.providers.currenciesProvider,
new Map<string, PriceProvider>([
['binance', new BinancePriceProvider()],
['kraken', new KrakenPriceProvider()],
['atomex', new AtomexPriceProvider(this.atomexContext.services.exchangeService)]
]));
}
}
2 changes: 1 addition & 1 deletion src/blockchain/atomexProtocolV1/atomexProtocolV1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export interface AtomexProtocolV1 extends AtomexProtocol {

redeem(params: AtomexProtocolV1RedeemParameters): Promise<Transaction>;
getRedeemFees(params: Partial<AtomexProtocolV1InitiateParameters>): Promise<FeesInfo>;
getRedeemReward(nativeTokenPriceInUsd: number, nativeTokenPriceInCurrency: number): Promise<FeesInfo>;
getRedeemReward(redeemFee: FeesInfo): Promise<FeesInfo>;

refund(params: AtomexProtocolV1RefundParameters): Promise<Transaction>;
getRefundFees(params: Partial<AtomexProtocolV1InitiateParameters>): Promise<FeesInfo>;
Expand Down
54 changes: 54 additions & 0 deletions src/blockchain/atomexProtocolV1/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { Currency } from '../../common';
import type { PriceManager } from '../../exchange';
import type { AtomexBlockchainProvider } from '../atomexBlockchainProvider';
import type { FeesInfo } from '../models/index';

export const getRedeemRewardInNativeCurrency = async (
currencyOrId: Currency | Currency['id'],
redeemFee: FeesInfo,
priceManager: PriceManager
): Promise<FeesInfo> => {
const nativeTokenPriceInUsd = await priceManager.getAveragePrice({ baseCurrency: currencyOrId, quoteCurrency: 'USD' });
if (!nativeTokenPriceInUsd)
throw new Error(`Price for ${currencyOrId} in USD not found`);

const maxRewardPercentValue = 30;
const maxRewardPercent = 0.15;
const maxRewardForRedeemDeviation = 0.05;

const redeemFeeInUsd = redeemFee.estimated.multipliedBy(nativeTokenPriceInUsd);
const k = maxRewardPercentValue / Math.log((1 - maxRewardPercent) / maxRewardForRedeemDeviation);
const p = (1 - maxRewardPercent) / Math.exp(redeemFeeInUsd.toNumber() / k) + maxRewardPercent;

const rewardForRedeem = redeemFee.estimated.multipliedBy(1 + p);
const result: FeesInfo = { estimated: rewardForRedeem, max: rewardForRedeem };

return result;
};

export const getRedeemRewardInToken = async (
currencyOrId: Currency | Currency['id'],
redeemFee: FeesInfo,
priceManager: PriceManager,
blockchainProvider: AtomexBlockchainProvider
): Promise<FeesInfo> => {
const currency = typeof currencyOrId === 'string' ? blockchainProvider.getCurrency(currencyOrId) : currencyOrId;
if (!currency)
throw new Error(`Currency info not found for ${currencyOrId}`);

const nativeCurrency = blockchainProvider.getNativeCurrencyInfo(currency.blockchain)?.currency;
if (!nativeCurrency)
throw new Error(`Native currency not found fir ${currency.blockchain}`);

const nativeTokenPriceInCurrency = await priceManager.getAveragePrice({ baseCurrency: nativeCurrency, quoteCurrency: currencyOrId });

if (!nativeTokenPriceInCurrency)
throw new Error(`Price for ${nativeCurrency.id} in ${currencyOrId} not found`);

const inNativeToken = await getRedeemRewardInNativeCurrency(nativeCurrency.id, redeemFee, priceManager);

return {
estimated: inNativeToken.estimated.multipliedBy(nativeTokenPriceInCurrency),
max: inNativeToken.max.multipliedBy(nativeTokenPriceInCurrency)
};
};
1 change: 1 addition & 0 deletions src/blockchain/atomexProtocolV1/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { isAtomexProtocolV1 } from './guards';
export * as atomexProtocolV1Helper from './helper';

export type { AtomexProtocolV1 } from './atomexProtocolV1';
export type { AtomexProtocolV1Options } from './options';
Expand Down
13 changes: 7 additions & 6 deletions src/ethereum/atomexProtocol/erc20EthereumWeb3AtomexProtocolV1.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { atomexProtocolV1Helper } from '../../blockchain/atomexProtocolV1';
import type {
AtomexBlockchainProvider,
AtomexProtocolV1InitiateParameters, AtomexProtocolV1RedeemParameters, AtomexProtocolV1RefundParameters,
Expand All @@ -6,16 +7,18 @@ import type {
import type { AtomexNetwork } from '../../common/index';
import type { DeepReadonly } from '../../core/index';
import { Web3AtomexProtocolV1 } from '../../evm/index';
import type { PriceManager } from '../../exchange';
import type { ERC20EthereumWeb3AtomexProtocolV1Options } from '../models/index';

export class ERC20EthereumWeb3AtomexProtocolV1 extends Web3AtomexProtocolV1 {
constructor(
atomexNetwork: AtomexNetwork,
protected readonly atomexProtocolOptions: DeepReadonly<ERC20EthereumWeb3AtomexProtocolV1Options>,
atomexBlockchainProvider: AtomexBlockchainProvider,
walletsManager: WalletsManager
walletsManager: WalletsManager,
priceManager: PriceManager
) {
super('ethereum', atomexNetwork, atomexProtocolOptions, atomexBlockchainProvider, walletsManager);
super('ethereum', atomexNetwork, atomexProtocolOptions, atomexBlockchainProvider, walletsManager, priceManager);
}

get currencyId() {
Expand All @@ -34,10 +37,8 @@ export class ERC20EthereumWeb3AtomexProtocolV1 extends Web3AtomexProtocolV1 {
throw new Error('Method not implemented.');
}

async getRedeemReward(_nativeTokenPriceInUsd: number, _nativeTokenPriceInCurrency: number): Promise<FeesInfo> {
const redeemFees = await this.getInitiateFees({});

return { estimated: redeemFees.estimated.multipliedBy(2), max: redeemFees.max.multipliedBy(2) };
getRedeemReward(redeemFee: FeesInfo): Promise<FeesInfo> {
return atomexProtocolV1Helper.getRedeemRewardInToken(this.currencyId, redeemFee, this.priceManager, this.atomexBlockchainProvider);
}

getRedeemFees(params: Partial<AtomexProtocolV1InitiateParameters>): Promise<FeesInfo> {
Expand Down
13 changes: 7 additions & 6 deletions src/ethereum/atomexProtocol/ethereumWeb3AtomexProtocolV1.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { atomexProtocolV1Helper } from '../../blockchain/atomexProtocolV1';
import type {
AtomexBlockchainProvider,
AtomexProtocolV1InitiateParameters, AtomexProtocolV1RedeemParameters, AtomexProtocolV1RefundParameters,
Expand All @@ -6,16 +7,18 @@ import type {
import type { AtomexNetwork } from '../../common/index';
import type { DeepReadonly } from '../../core/index';
import { Web3AtomexProtocolV1 } from '../../evm/index';
import type { PriceManager } from '../../exchange';
import type { EthereumWeb3AtomexProtocolV1Options } from '../models/index';

export class EthereumWeb3AtomexProtocolV1 extends Web3AtomexProtocolV1 {
constructor(
atomexNetwork: AtomexNetwork,
protected readonly atomexProtocolOptions: DeepReadonly<EthereumWeb3AtomexProtocolV1Options>,
atomexBlockchainProvider: AtomexBlockchainProvider,
walletsManager: WalletsManager
walletsManager: WalletsManager,
priceManager: PriceManager
) {
super('ethereum', atomexNetwork, atomexProtocolOptions, atomexBlockchainProvider, walletsManager);
super('ethereum', atomexNetwork, atomexProtocolOptions, atomexBlockchainProvider, walletsManager, priceManager);
}

initiate(_params: AtomexProtocolV1InitiateParameters): Promise<Transaction> {
Expand All @@ -30,10 +33,8 @@ export class EthereumWeb3AtomexProtocolV1 extends Web3AtomexProtocolV1 {
throw new Error('Method not implemented.');
}

async getRedeemReward(_nativeTokenPriceInUsd: number, _nativeTokenPriceInCurrency: number): Promise<FeesInfo> {
const redeemFees = await this.getInitiateFees({});

return { estimated: redeemFees.estimated.multipliedBy(2), max: redeemFees.max.multipliedBy(2) };
getRedeemReward(redeemFee: FeesInfo): Promise<FeesInfo> {
return atomexProtocolV1Helper.getRedeemRewardInNativeCurrency(this.currencyId, redeemFee, this.priceManager);
}

getRedeemFees(params: Partial<AtomexProtocolV1InitiateParameters>): Promise<FeesInfo> {
Expand Down
6 changes: 4 additions & 2 deletions src/ethereum/config/defaultOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@ const createAtomexProtocol = (
atomexContext.atomexNetwork,
atomexProtocolOptions,
atomexContext.providers.blockchainProvider,
atomexContext.managers.walletsManager
atomexContext.managers.walletsManager,
atomexContext.managers.priceManager
);
case 'erc-20':
return new ERC20EthereumWeb3AtomexProtocolV1(
atomexContext.atomexNetwork,
atomexProtocolOptions,
atomexContext.providers.blockchainProvider,
atomexContext.managers.walletsManager
atomexContext.managers.walletsManager,
atomexContext.managers.priceManager
);
default:
throw new Error(`Unknown Ethereum currency: ${(currency as EthereumCurrency).id}`);
Expand Down
12 changes: 7 additions & 5 deletions src/evm/atomexProtocol/web3AtomexProtocolV1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
} from '../../blockchain/index';
import type { AtomexNetwork } from '../../common/index';
import type { DeepReadonly } from '../../core/index';
import type { PriceManager } from '../../exchange';
import { web3Helper } from '../helpers';
import type { Web3AtomexProtocolV1Options } from '../models/index';

Expand All @@ -20,7 +21,8 @@ export abstract class Web3AtomexProtocolV1 implements AtomexProtocolV1 {
readonly atomexNetwork: AtomexNetwork,
protected readonly atomexProtocolOptions: DeepReadonly<Web3AtomexProtocolV1Options>,
protected readonly atomexBlockchainProvider: AtomexBlockchainProvider,
protected readonly walletsManager: WalletsManager
protected readonly walletsManager: WalletsManager,
protected readonly priceManager: PriceManager
) {
}

Expand All @@ -32,7 +34,7 @@ export abstract class Web3AtomexProtocolV1 implements AtomexProtocolV1 {
return this.atomexProtocolOptions.swapContractAddress;
}

abstract initiate(_params: AtomexProtocolV1InitiateParameters): Promise<Transaction>;
abstract initiate(params: AtomexProtocolV1InitiateParameters): Promise<Transaction>;

async getInitiateFees(params: Partial<AtomexProtocolV1InitiateParameters>): Promise<FeesInfo> {
const toolkit = await this.getReadonlyWeb3();
Expand All @@ -48,9 +50,9 @@ export abstract class Web3AtomexProtocolV1 implements AtomexProtocolV1 {
return Promise.resolve(result);
}

abstract redeem(_params: AtomexProtocolV1RedeemParameters): Promise<Transaction>;
abstract redeem(params: AtomexProtocolV1RedeemParameters): Promise<Transaction>;

abstract getRedeemReward(_nativeTokenPriceInUsd: number, _nativeTokenPriceInCurrency: number): Promise<FeesInfo>;
abstract getRedeemReward(redeemFee: FeesInfo): Promise<FeesInfo>;

async getRedeemFees(_params: Partial<AtomexProtocolV1InitiateParameters>): Promise<FeesInfo> {
const toolkit = await this.getReadonlyWeb3();
Expand All @@ -64,7 +66,7 @@ export abstract class Web3AtomexProtocolV1 implements AtomexProtocolV1 {
return Promise.resolve(result);
}

abstract refund(_params: AtomexProtocolV1RefundParameters): Promise<Transaction>;
abstract refund(params: AtomexProtocolV1RefundParameters): Promise<Transaction>;

async getRefundFees(_params: Partial<AtomexProtocolV1InitiateParameters>): Promise<FeesInfo> {
const toolkit = await this.getReadonlyWeb3();
Expand Down
Loading

0 comments on commit 46bc24b

Please sign in to comment.