Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: custom fees for referrals #749

Merged
merged 1 commit into from
Dec 22, 2024
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
34 changes: 28 additions & 6 deletions lib/api/v2/routers/SwapRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Logger from '../../../Logger';
import { getHexString, stringify } from '../../../Utils';
import { SwapUpdateEvent, SwapVersion } from '../../../consts/Enums';
import ChainSwapRepository from '../../../db/repositories/ChainSwapRepository';
import ReferralRepository from '../../../db/repositories/ReferralRepository';
import SwapRepository from '../../../db/repositories/SwapRepository';
import RateProviderTaproot from '../../../rates/providers/RateProviderTaproot';
import CountryCodes from '../../../service/CountryCodes';
Expand Down Expand Up @@ -1654,13 +1655,17 @@ class SwapRouter extends RouterBase {
return router;
};

private getSubmarine = (_req: Request, res: Response) =>
private getSubmarine = async (req: Request, res: Response) => {
const referral = await this.getReferralFromHeader(req);
successResponse(
res,
RateProviderTaproot.serializePairs(
this.service.rateProvider.providers[SwapVersion.Taproot].submarinePairs,
this.service.rateProvider.providers[
SwapVersion.Taproot
].getSubmarinePairs(referral),
),
);
};

private createSubmarine = async (req: Request, res: Response) => {
const { to, from, invoice, webhook, pairHash, refundPublicKey } =
Expand Down Expand Up @@ -1816,13 +1821,17 @@ class SwapRouter extends RouterBase {
successResponse(res, {});
};

private getReverse = (_req: Request, res: Response) =>
private getReverse = async (req: Request, res: Response) => {
const referral = await this.getReferralFromHeader(req);
successResponse(
res,
RateProviderTaproot.serializePairs(
this.service.rateProvider.providers[SwapVersion.Taproot].reversePairs,
this.service.rateProvider.providers[
SwapVersion.Taproot
].getReversePairs(referral),
),
);
};

private createReverse = async (req: Request, res: Response) => {
const {
Expand Down Expand Up @@ -1960,13 +1969,17 @@ class SwapRouter extends RouterBase {
});
};

private getChain = (_req: Request, res: Response) =>
private getChain = async (req: Request, res: Response) => {
const referral = await this.getReferralFromHeader(req);
successResponse(
res,
RateProviderTaproot.serializePairs(
this.service.rateProvider.providers[SwapVersion.Taproot].chainPairs,
this.service.rateProvider.providers[SwapVersion.Taproot].getChainPairs(
referral,
),
),
);
};

// TODO: claim covenant
private createChain = async (req: Request, res: Response) => {
Expand Down Expand Up @@ -2247,6 +2260,15 @@ class SwapRouter extends RouterBase {

return res;
};

private getReferralFromHeader = async (req: Request) => {
const referral = req.header('referral');
if (referral === undefined) {
return null;
}

return ReferralRepository.getReferralById(referral);
};
}

export default SwapRouter;
28 changes: 27 additions & 1 deletion lib/db/Migration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ const decodeInvoice = (

// TODO: integration tests for actual migrations
class Migration {
private static latestSchemaVersion = 12;
private static latestSchemaVersion = 13;

private toBackFill: number[] = [];

Expand Down Expand Up @@ -574,6 +574,32 @@ class Migration {
break;
}

case 12: {
await this.sequelize
.getQueryInterface()
.addColumn(Referral.tableName, 'submarinePremium', {
type: new DataTypes.INTEGER(),
allowNull: true,
});

await this.sequelize
.getQueryInterface()
.addColumn(Referral.tableName, 'reversePremium', {
type: new DataTypes.INTEGER(),
allowNull: true,
});

await this.sequelize
.getQueryInterface()
.addColumn(Referral.tableName, 'chainPremium', {
type: new DataTypes.INTEGER(),
allowNull: true,
});

await this.finishMigration(versionRow.version, currencies);
break;
}

default:
throw `found unexpected database version ${versionRow.version}`;
}
Expand Down
25 changes: 25 additions & 0 deletions lib/db/models/Referral.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { DataTypes, Model, Sequelize } from 'sequelize';
import { SwapType } from '../../consts/Enums';

type ReferralType = {
id: string;
Expand All @@ -8,6 +9,10 @@ type ReferralType = {

feeShare: number;
routingNode?: string;

submarinePremium?: number;
reversePremium?: number;
chainPremium?: number;
};

class Referral extends Model implements ReferralType {
Expand All @@ -19,6 +24,10 @@ class Referral extends Model implements ReferralType {
public feeShare!: number;
public routingNode?: string;

public submarinePremium?: number;
public reversePremium?: number;
public chainPremium?: number;

public static load = (sequelize: Sequelize): void => {
Referral.init(
{
Expand All @@ -43,6 +52,9 @@ class Referral extends Model implements ReferralType {
allowNull: true,
unique: true,
},
submarinePremium: { type: new DataTypes.INTEGER(), allowNull: true },
reversePremium: { type: new DataTypes.INTEGER(), allowNull: true },
chainPremium: { type: new DataTypes.INTEGER(), allowNull: true },
},
{
sequelize,
Expand All @@ -60,6 +72,19 @@ class Referral extends Model implements ReferralType {
},
);
};

public premiumForType = (type: SwapType) => {
switch (type) {
case SwapType.Submarine:
return this.submarinePremium;

case SwapType.ReverseSubmarine:
return this.reversePremium;

case SwapType.Chain:
return this.chainPremium;
}
};
}

export default Referral;
Expand Down
8 changes: 8 additions & 0 deletions lib/db/repositories/ReferralRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ ORDER BY year, month;
return Referral.findAll();
};

public static getReferralById = (id: string): Promise<Referral | null> => {
return Referral.findOne({
where: {
id,
},
});
};

public static getReferralByApiKey = (
apiKey: string,
): Promise<Referral | null> => {
Expand Down
22 changes: 20 additions & 2 deletions lib/rates/FeeProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
swapTypeToString,
} from '../consts/Enums';
import { PairConfig } from '../consts/Types';
import Referral from '../db/models/Referral';
import WalletLiquid from '../wallet/WalletLiquid';
import WalletManager from '../wallet/WalletManager';
import { Ethereum, Rsk } from '../wallet/ethereum/EvmNetworks';
Expand Down Expand Up @@ -132,6 +133,8 @@ class FeeProvider {
},
};

public static percentFeeDecimals = 2;

private static readonly defaultFee = 1;

// A map between the symbols of the pairs and their percentage fees
Expand All @@ -146,6 +149,16 @@ class FeeProvider {
private getFeeEstimation: (symbol: string) => Promise<Map<string, number>>,
) {}

public static addPremium = (fee: number, premium?: number): number => {
if (premium === null || premium === undefined) {
return fee;
}

return parseFloat(
(fee + premium / 100).toFixed(FeeProvider.percentFeeDecimals),
);
};

public init = (pairs: PairConfig[]): void => {
pairs.forEach((pair) => {
const pairId = getPairId(pair);
Expand Down Expand Up @@ -203,17 +216,20 @@ class FeeProvider {
orderSide: OrderSide,
type: SwapType,
feeType: PercentageFeeType = PercentageFeeType.Calculation,
referral: Referral | null,
): number => {
const percentages = this.percentageFees.get(pair);
if (percentages === undefined) {
return 0;
}

const percentageType = percentages[type];
const percentage =
const percentage = FeeProvider.addPremium(
typeof percentageType === 'number'
? percentageType
: percentageType[orderSide];
: percentageType[orderSide],
referral?.premiumForType(type),
);

return feeType === PercentageFeeType.Calculation
? percentage / 100
Expand All @@ -228,6 +244,7 @@ class FeeProvider {
amount: number,
type: SwapType,
feeType: BaseFeeType,
referral: Referral | null,
): {
baseFee: number;
percentageFee: number;
Expand All @@ -237,6 +254,7 @@ class FeeProvider {
orderSide,
type,
PercentageFeeType.Calculation,
referral,
);

if (percentageFee !== 0) {
Expand Down
4 changes: 4 additions & 0 deletions lib/rates/providers/RateProviderLegacy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,14 @@ class RateProviderLegacy extends RateProviderBase<PairTypeLegacy> {
OrderSide.BUY,
SwapType.Submarine,
PercentageFeeType.Display,
null,
),
percentage: this.feeProvider.getPercentageFee(
id,
OrderSide.BUY,
SwapType.ReverseSubmarine,
PercentageFeeType.Display,
null,
),
minerFees: {
baseAsset: emptyMinerFees,
Expand All @@ -98,12 +100,14 @@ class RateProviderLegacy extends RateProviderBase<PairTypeLegacy> {
OrderSide.BUY,
SwapType.Submarine,
PercentageFeeType.Display,
null,
),
percentage: this.feeProvider.getPercentageFee(
pairId,
OrderSide.BUY,
SwapType.ReverseSubmarine,
PercentageFeeType.Display,
null,
),
minerFees: {
baseAsset: this.feeProvider.minerFees.get(base)![SwapVersion.Legacy],
Expand Down
Loading
Loading