459 lines
20 KiB
TypeScript
459 lines
20 KiB
TypeScript
import { expect } from 'chai';
|
|
import { BigNumber } from '@0x/utils';
|
|
import { TRANSFER_FROM_GAS, TRANSFER_GAS, ZERO } from '../../src/constants';
|
|
import { calculateFees } from '../../src/utils/fee_calculator';
|
|
import { WETH_TOKEN_ADDRESS } from '../constants';
|
|
|
|
const RANDOM_ADDRESS1 = '0x70a9f34f9b34c64957b9c401a97bfed35b95049e';
|
|
const RANDOM_ADDRESS2 = '0x70a9f34f9b34c64957b9c401a97bfed35b95049f';
|
|
|
|
describe('calculateGaslessFees', () => {
|
|
describe('integrator fee', () => {
|
|
it('returns the correct result if fee config is undefined', () => {
|
|
const { fees, totalOnChainFeeAmount, onChainTransfers, onChainTransfersGas } = calculateFees({
|
|
feeConfigs: undefined,
|
|
sellToken: WETH_TOKEN_ADDRESS,
|
|
sellTokenAmount: new BigNumber(10e3),
|
|
sellTokenAmountPerWei: new BigNumber(1),
|
|
gasPrice: new BigNumber(10e9),
|
|
quoteGasEstimate: new BigNumber(20e3),
|
|
gasPerOnChainTransfer: TRANSFER_GAS,
|
|
});
|
|
|
|
expect(fees).to.be.undefined;
|
|
expect(totalOnChainFeeAmount).to.eql(ZERO);
|
|
expect(onChainTransfers).to.eql([]);
|
|
expect(onChainTransfersGas).to.eql(ZERO);
|
|
});
|
|
|
|
it('returns the correct result if fee config is empty', () => {
|
|
const { fees, totalOnChainFeeAmount, onChainTransfers, onChainTransfersGas } = calculateFees({
|
|
feeConfigs: {},
|
|
sellToken: WETH_TOKEN_ADDRESS,
|
|
sellTokenAmount: new BigNumber(10e3),
|
|
sellTokenAmountPerWei: new BigNumber(1),
|
|
gasPrice: new BigNumber(10e9),
|
|
quoteGasEstimate: new BigNumber(20e3),
|
|
gasPerOnChainTransfer: TRANSFER_GAS,
|
|
});
|
|
|
|
expect(fees).to.be.not.undefined;
|
|
expect(fees?.integratorFee).to.be.undefined;
|
|
expect(totalOnChainFeeAmount).to.eql(ZERO);
|
|
expect(onChainTransfers).to.eql([]);
|
|
expect(onChainTransfersGas).to.eql(ZERO);
|
|
});
|
|
|
|
it('returns the correct result if integrator fee config is present', () => {
|
|
const { fees, totalOnChainFeeAmount, onChainTransfers, onChainTransfersGas } = calculateFees({
|
|
feeConfigs: {
|
|
integratorFee: {
|
|
type: 'volume',
|
|
feeRecipient: RANDOM_ADDRESS1,
|
|
billingType: 'on-chain',
|
|
volumePercentage: new BigNumber(0.1),
|
|
},
|
|
},
|
|
sellToken: WETH_TOKEN_ADDRESS,
|
|
sellTokenAmount: new BigNumber(10e3),
|
|
sellTokenAmountPerWei: new BigNumber(1),
|
|
gasPrice: new BigNumber(10e9),
|
|
quoteGasEstimate: new BigNumber(20e3),
|
|
gasPerOnChainTransfer: TRANSFER_GAS,
|
|
});
|
|
|
|
expect(fees?.integratorFee).to.eql({
|
|
type: 'volume',
|
|
feeToken: WETH_TOKEN_ADDRESS,
|
|
feeAmount: new BigNumber(1e3),
|
|
feeRecipient: RANDOM_ADDRESS1,
|
|
billingType: 'on-chain',
|
|
volumePercentage: new BigNumber(0.1),
|
|
});
|
|
expect(totalOnChainFeeAmount).to.eql(new BigNumber(1e3));
|
|
expect(onChainTransfers).to.eql([
|
|
{
|
|
feeToken: WETH_TOKEN_ADDRESS,
|
|
feeAmount: new BigNumber(1e3),
|
|
feeRecipient: RANDOM_ADDRESS1,
|
|
},
|
|
]);
|
|
expect(onChainTransfersGas).to.eql(TRANSFER_GAS);
|
|
});
|
|
});
|
|
|
|
describe('0x fee', () => {
|
|
it('returns the correct result if 0x fee config is not present', () => {
|
|
const { fees, totalOnChainFeeAmount, onChainTransfers, onChainTransfersGas } = calculateFees({
|
|
feeConfigs: {},
|
|
sellToken: WETH_TOKEN_ADDRESS,
|
|
sellTokenAmount: new BigNumber(10e3),
|
|
sellTokenAmountPerWei: new BigNumber(1),
|
|
gasPrice: new BigNumber(10e9),
|
|
quoteGasEstimate: new BigNumber(20e3),
|
|
gasPerOnChainTransfer: TRANSFER_FROM_GAS,
|
|
});
|
|
|
|
expect(fees?.integratorFee).to.be.undefined;
|
|
expect(totalOnChainFeeAmount).to.eql(ZERO);
|
|
expect(onChainTransfers).to.eql([]);
|
|
expect(onChainTransfersGas).to.eql(ZERO);
|
|
});
|
|
|
|
it('returns the correct result if 0x fee config is present, the type is volume and the billing type is on-chain', () => {
|
|
const { fees, totalOnChainFeeAmount, onChainTransfers, onChainTransfersGas } = calculateFees({
|
|
feeConfigs: {
|
|
zeroExFee: {
|
|
type: 'volume',
|
|
feeRecipient: RANDOM_ADDRESS1,
|
|
billingType: 'on-chain',
|
|
volumePercentage: new BigNumber(0.1),
|
|
},
|
|
},
|
|
sellToken: WETH_TOKEN_ADDRESS,
|
|
sellTokenAmount: new BigNumber(10e3),
|
|
sellTokenAmountPerWei: new BigNumber(1),
|
|
gasPrice: new BigNumber(10e9),
|
|
quoteGasEstimate: new BigNumber(20e3),
|
|
gasPerOnChainTransfer: TRANSFER_FROM_GAS,
|
|
});
|
|
|
|
expect(fees?.zeroExFee).to.eql({
|
|
type: 'volume',
|
|
feeToken: WETH_TOKEN_ADDRESS,
|
|
feeAmount: new BigNumber(1e3),
|
|
feeRecipient: RANDOM_ADDRESS1,
|
|
billingType: 'on-chain',
|
|
volumePercentage: new BigNumber(0.1),
|
|
});
|
|
expect(totalOnChainFeeAmount).to.eql(new BigNumber(1e3));
|
|
expect(onChainTransfers).to.eql([
|
|
{
|
|
feeToken: WETH_TOKEN_ADDRESS,
|
|
feeAmount: new BigNumber(1e3),
|
|
feeRecipient: RANDOM_ADDRESS1,
|
|
},
|
|
]);
|
|
expect(onChainTransfersGas).to.eql(TRANSFER_FROM_GAS);
|
|
});
|
|
|
|
it('returns the correct result if 0x fee config is present, the type is volume and the billing type is off-chain', () => {
|
|
const { fees, totalOnChainFeeAmount, onChainTransfers, onChainTransfersGas } = calculateFees({
|
|
feeConfigs: {
|
|
integratorFee: {
|
|
type: 'volume',
|
|
feeRecipient: RANDOM_ADDRESS1,
|
|
billingType: 'on-chain',
|
|
volumePercentage: new BigNumber(0.1),
|
|
},
|
|
zeroExFee: {
|
|
type: 'volume',
|
|
feeRecipient: null,
|
|
billingType: 'off-chain',
|
|
volumePercentage: new BigNumber(0.1),
|
|
},
|
|
},
|
|
sellToken: WETH_TOKEN_ADDRESS,
|
|
sellTokenAmount: new BigNumber(10e3),
|
|
sellTokenAmountPerWei: new BigNumber(1),
|
|
gasPrice: new BigNumber(10e9),
|
|
quoteGasEstimate: new BigNumber(20e3),
|
|
gasPerOnChainTransfer: TRANSFER_FROM_GAS,
|
|
});
|
|
|
|
expect(fees?.zeroExFee).to.eql({
|
|
type: 'volume',
|
|
feeToken: WETH_TOKEN_ADDRESS,
|
|
feeAmount: new BigNumber(1e3),
|
|
feeRecipient: null,
|
|
billingType: 'off-chain',
|
|
volumePercentage: new BigNumber(0.1),
|
|
});
|
|
expect(totalOnChainFeeAmount).to.eql(new BigNumber(1e3));
|
|
expect(onChainTransfers).to.eql([
|
|
{
|
|
feeToken: WETH_TOKEN_ADDRESS,
|
|
feeAmount: new BigNumber(1e3),
|
|
feeRecipient: RANDOM_ADDRESS1,
|
|
},
|
|
]);
|
|
expect(onChainTransfersGas).to.eql(TRANSFER_FROM_GAS);
|
|
});
|
|
|
|
it('returns the correct result if 0x fee config is present, the type is integrator_share and billing type is on-chain', () => {
|
|
const { fees, totalOnChainFeeAmount, onChainTransfers, onChainTransfersGas } = calculateFees({
|
|
feeConfigs: {
|
|
integratorFee: {
|
|
type: 'volume',
|
|
feeRecipient: RANDOM_ADDRESS1,
|
|
billingType: 'on-chain',
|
|
volumePercentage: new BigNumber(0.1),
|
|
},
|
|
zeroExFee: {
|
|
type: 'integrator_share',
|
|
feeRecipient: RANDOM_ADDRESS2,
|
|
billingType: 'on-chain',
|
|
integratorSharePercentage: new BigNumber(0.05),
|
|
},
|
|
},
|
|
sellToken: WETH_TOKEN_ADDRESS,
|
|
sellTokenAmount: new BigNumber(10e3),
|
|
sellTokenAmountPerWei: new BigNumber(1),
|
|
gasPrice: new BigNumber(10e9),
|
|
quoteGasEstimate: new BigNumber(20e3),
|
|
gasPerOnChainTransfer: TRANSFER_FROM_GAS,
|
|
});
|
|
|
|
expect(fees?.zeroExFee).to.eql({
|
|
type: 'integrator_share',
|
|
feeToken: WETH_TOKEN_ADDRESS,
|
|
billingType: 'on-chain',
|
|
feeAmount: new BigNumber(50),
|
|
feeRecipient: RANDOM_ADDRESS2,
|
|
integratorSharePercentage: new BigNumber(0.05),
|
|
});
|
|
expect(totalOnChainFeeAmount).to.eql(new BigNumber(1e3));
|
|
expect(onChainTransfers).to.eql([
|
|
{
|
|
feeToken: WETH_TOKEN_ADDRESS,
|
|
feeAmount: new BigNumber(950),
|
|
feeRecipient: RANDOM_ADDRESS1,
|
|
},
|
|
{
|
|
feeToken: WETH_TOKEN_ADDRESS,
|
|
feeAmount: new BigNumber(50),
|
|
feeRecipient: RANDOM_ADDRESS2,
|
|
},
|
|
]);
|
|
expect(onChainTransfersGas).to.eql(TRANSFER_FROM_GAS.times(2));
|
|
});
|
|
|
|
it('returns the correct result if 0x fee config is present, the type is integrator_share and billing type is off-chain', () => {
|
|
const { fees, totalOnChainFeeAmount, onChainTransfers, onChainTransfersGas } = calculateFees({
|
|
feeConfigs: {
|
|
integratorFee: {
|
|
type: 'volume',
|
|
feeRecipient: RANDOM_ADDRESS1,
|
|
billingType: 'on-chain',
|
|
volumePercentage: new BigNumber(0.1),
|
|
},
|
|
zeroExFee: {
|
|
type: 'integrator_share',
|
|
feeRecipient: null,
|
|
billingType: 'off-chain',
|
|
integratorSharePercentage: new BigNumber(0.05),
|
|
},
|
|
},
|
|
sellToken: WETH_TOKEN_ADDRESS,
|
|
sellTokenAmount: new BigNumber(10e3),
|
|
sellTokenAmountPerWei: new BigNumber(1),
|
|
gasPrice: new BigNumber(10e9),
|
|
quoteGasEstimate: new BigNumber(20e3),
|
|
gasPerOnChainTransfer: TRANSFER_FROM_GAS,
|
|
});
|
|
|
|
expect(fees?.zeroExFee).to.eql({
|
|
type: 'integrator_share',
|
|
feeToken: WETH_TOKEN_ADDRESS,
|
|
billingType: 'off-chain',
|
|
feeAmount: new BigNumber(50),
|
|
feeRecipient: null,
|
|
integratorSharePercentage: new BigNumber(0.05),
|
|
});
|
|
expect(totalOnChainFeeAmount).to.eql(new BigNumber(1e3));
|
|
expect(onChainTransfers).to.eql([
|
|
{
|
|
feeToken: WETH_TOKEN_ADDRESS,
|
|
feeAmount: new BigNumber(1e3),
|
|
feeRecipient: RANDOM_ADDRESS1,
|
|
},
|
|
]);
|
|
expect(onChainTransfersGas).to.eql(TRANSFER_FROM_GAS);
|
|
});
|
|
|
|
it('throws if 0x fee type is integrator_share but integrator fee config is not present', () => {
|
|
expect(() => {
|
|
calculateFees({
|
|
feeConfigs: {
|
|
zeroExFee: {
|
|
type: 'integrator_share',
|
|
feeRecipient: RANDOM_ADDRESS2,
|
|
billingType: 'on-chain',
|
|
integratorSharePercentage: new BigNumber(0.05),
|
|
},
|
|
},
|
|
sellToken: WETH_TOKEN_ADDRESS,
|
|
sellTokenAmount: new BigNumber(10e3),
|
|
sellTokenAmountPerWei: new BigNumber(1),
|
|
gasPrice: new BigNumber(10e9),
|
|
quoteGasEstimate: new BigNumber(20e3),
|
|
gasPerOnChainTransfer: TRANSFER_FROM_GAS,
|
|
});
|
|
}).to.throw();
|
|
});
|
|
});
|
|
|
|
describe('gas fee', () => {
|
|
it('returns the correct result if gas fee config is not present', () => {
|
|
const { fees, totalOnChainFeeAmount, onChainTransfers, onChainTransfersGas } = calculateFees({
|
|
feeConfigs: {},
|
|
sellToken: WETH_TOKEN_ADDRESS,
|
|
sellTokenAmount: new BigNumber(10e3),
|
|
sellTokenAmountPerWei: new BigNumber(1),
|
|
gasPrice: new BigNumber(10e9),
|
|
quoteGasEstimate: new BigNumber(20e3),
|
|
gasPerOnChainTransfer: TRANSFER_GAS,
|
|
});
|
|
|
|
expect(fees?.gasFee).to.be.undefined;
|
|
expect(totalOnChainFeeAmount).to.eql(ZERO);
|
|
expect(onChainTransfers).to.eql([]);
|
|
expect(onChainTransfersGas).to.eql(ZERO);
|
|
});
|
|
|
|
it('throws if gas fee config is present but `sellTokenAmountPerWei` is undefined', () => {
|
|
expect(() => {
|
|
calculateFees({
|
|
feeConfigs: {
|
|
integratorFee: {
|
|
type: 'volume',
|
|
feeRecipient: RANDOM_ADDRESS1,
|
|
billingType: 'on-chain',
|
|
volumePercentage: new BigNumber(0.01),
|
|
},
|
|
zeroExFee: {
|
|
type: 'volume',
|
|
feeRecipient: RANDOM_ADDRESS2,
|
|
billingType: 'on-chain',
|
|
volumePercentage: new BigNumber(0.01),
|
|
},
|
|
gasFee: {
|
|
type: 'gas',
|
|
feeRecipient: null,
|
|
billingType: 'off-chain',
|
|
},
|
|
},
|
|
sellToken: WETH_TOKEN_ADDRESS,
|
|
sellTokenAmount: new BigNumber(1e16),
|
|
sellTokenAmountPerWei: undefined,
|
|
gasPrice: new BigNumber(10e9),
|
|
quoteGasEstimate: new BigNumber(20e3),
|
|
gasPerOnChainTransfer: TRANSFER_FROM_GAS,
|
|
});
|
|
}).to.throw();
|
|
});
|
|
|
|
it('returns correct result if integrator fee, 0x and gas fees are all present, billing types are all on-chain and 0x and gas fee have the same fee recipient address', () => {
|
|
const { fees, totalOnChainFeeAmount, onChainTransfers, onChainTransfersGas } = calculateFees({
|
|
feeConfigs: {
|
|
integratorFee: {
|
|
type: 'volume',
|
|
feeRecipient: RANDOM_ADDRESS1,
|
|
billingType: 'on-chain',
|
|
volumePercentage: new BigNumber(0.01),
|
|
},
|
|
zeroExFee: {
|
|
type: 'integrator_share',
|
|
feeRecipient: RANDOM_ADDRESS2,
|
|
billingType: 'on-chain',
|
|
integratorSharePercentage: new BigNumber(0.05),
|
|
},
|
|
gasFee: {
|
|
type: 'gas',
|
|
billingType: 'on-chain',
|
|
feeRecipient: RANDOM_ADDRESS2,
|
|
},
|
|
},
|
|
sellToken: WETH_TOKEN_ADDRESS,
|
|
sellTokenAmount: new BigNumber(1e16),
|
|
sellTokenAmountPerWei: new BigNumber(1),
|
|
gasPrice: new BigNumber(10e9),
|
|
quoteGasEstimate: new BigNumber(20e3),
|
|
gasPerOnChainTransfer: TRANSFER_GAS,
|
|
});
|
|
|
|
const gasFeeAmount = new BigNumber(20e3).plus(TRANSFER_GAS.times(2)).times(new BigNumber(10e9));
|
|
expect(fees?.gasFee).to.eql({
|
|
type: 'gas',
|
|
feeToken: WETH_TOKEN_ADDRESS,
|
|
feeAmount: gasFeeAmount,
|
|
feeRecipient: RANDOM_ADDRESS2,
|
|
billingType: 'on-chain',
|
|
feeTokenAmountPerWei: new BigNumber(1),
|
|
gasPrice: new BigNumber(10e9),
|
|
estimatedGas: new BigNumber(20e3).plus(TRANSFER_GAS.times(2)),
|
|
});
|
|
expect(totalOnChainFeeAmount).to.eql(new BigNumber(1e14).plus(gasFeeAmount));
|
|
expect(onChainTransfers).to.eql([
|
|
{
|
|
feeToken: WETH_TOKEN_ADDRESS,
|
|
feeAmount: new BigNumber(95e12),
|
|
feeRecipient: RANDOM_ADDRESS1,
|
|
},
|
|
{
|
|
feeToken: WETH_TOKEN_ADDRESS,
|
|
feeAmount: new BigNumber(5e12).plus(gasFeeAmount),
|
|
feeRecipient: RANDOM_ADDRESS2,
|
|
},
|
|
]);
|
|
expect(onChainTransfersGas).to.eql(TRANSFER_GAS.times(2));
|
|
});
|
|
|
|
it('returns correct result if integrator fee, 0x and gas fees are all present and gas fee billing type is off-chain', () => {
|
|
const { fees, totalOnChainFeeAmount, onChainTransfers, onChainTransfersGas } = calculateFees({
|
|
feeConfigs: {
|
|
integratorFee: {
|
|
type: 'volume',
|
|
feeRecipient: RANDOM_ADDRESS1,
|
|
billingType: 'on-chain',
|
|
volumePercentage: new BigNumber(0.01),
|
|
},
|
|
zeroExFee: {
|
|
type: 'integrator_share',
|
|
feeRecipient: RANDOM_ADDRESS2,
|
|
billingType: 'on-chain',
|
|
integratorSharePercentage: new BigNumber(0.05),
|
|
},
|
|
gasFee: {
|
|
type: 'gas',
|
|
billingType: 'off-chain',
|
|
feeRecipient: null,
|
|
},
|
|
},
|
|
sellToken: WETH_TOKEN_ADDRESS,
|
|
sellTokenAmount: new BigNumber(1e16),
|
|
sellTokenAmountPerWei: new BigNumber(1),
|
|
gasPrice: new BigNumber(10e9),
|
|
quoteGasEstimate: new BigNumber(20e3),
|
|
gasPerOnChainTransfer: TRANSFER_GAS,
|
|
});
|
|
|
|
const gasFeeAmount = new BigNumber(20e3).plus(TRANSFER_GAS.times(2)).times(new BigNumber(10e9));
|
|
expect(fees?.gasFee).to.eql({
|
|
type: 'gas',
|
|
feeToken: WETH_TOKEN_ADDRESS,
|
|
feeAmount: gasFeeAmount,
|
|
feeRecipient: null,
|
|
billingType: 'off-chain',
|
|
feeTokenAmountPerWei: new BigNumber(1),
|
|
gasPrice: new BigNumber(10e9),
|
|
estimatedGas: new BigNumber(20e3).plus(TRANSFER_GAS.times(2)),
|
|
});
|
|
expect(totalOnChainFeeAmount).to.eql(new BigNumber(1e14));
|
|
expect(onChainTransfers).to.eql([
|
|
{
|
|
feeToken: WETH_TOKEN_ADDRESS,
|
|
feeAmount: new BigNumber(95e12),
|
|
feeRecipient: RANDOM_ADDRESS1,
|
|
},
|
|
{
|
|
feeToken: WETH_TOKEN_ADDRESS,
|
|
feeAmount: new BigNumber(5e12),
|
|
feeRecipient: RANDOM_ADDRESS2,
|
|
},
|
|
]);
|
|
expect(onChainTransfersGas).to.eql(TRANSFER_GAS.times(2));
|
|
});
|
|
});
|
|
});
|