168 lines
7.2 KiB
TypeScript
168 lines
7.2 KiB
TypeScript
import { expect } from 'chai';
|
|
import { BigNumber } from '@0x/utils';
|
|
import 'mocha';
|
|
|
|
import { AffiliateFeeType, ERC20BridgeSource } from '../src/asset-swapper';
|
|
import { AFFILIATE_FEE_TRANSFORMER_GAS, POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS, ZERO } from '../src/constants';
|
|
import { serviceUtils } from '../src/utils/service_utils';
|
|
|
|
import { AFFILIATE_DATA_SELECTOR } from './constants';
|
|
import { randomSellQuote } from './utils/mocks';
|
|
import { randomAddress } from './utils/random';
|
|
|
|
const SUITE_NAME = 'serviceUtils';
|
|
|
|
describe(SUITE_NAME, () => {
|
|
describe('attributeCallData', () => {
|
|
it('it returns a reasonable ID and timestamp', () => {
|
|
const fakeCallData = '0x0000000000000';
|
|
const fakeAffiliate = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
|
|
const attributedCallData = serviceUtils.attributeCallData(fakeCallData, fakeAffiliate).affiliatedData;
|
|
const currentTime = new Date();
|
|
|
|
// parse out items from call data to ensure they are reasonable values
|
|
const selectorPos = attributedCallData.indexOf(AFFILIATE_DATA_SELECTOR);
|
|
const affiliateAddress = '0x'.concat(attributedCallData.substring(selectorPos + 32, selectorPos + 72));
|
|
const randomId = attributedCallData.substring(selectorPos + 118, selectorPos + 128);
|
|
const timestampFromCallDataHex = attributedCallData.substring(selectorPos + 128, selectorPos + 136);
|
|
const timestampFromCallData = parseInt(timestampFromCallDataHex, 16);
|
|
|
|
expect(affiliateAddress).to.be.eq(fakeAffiliate);
|
|
// call data timestamp is within 3 seconds of timestamp created during test
|
|
expect(timestampFromCallData).to.be.greaterThan(currentTime.getTime() / 1000 - 3);
|
|
expect(timestampFromCallData).to.be.lessThan(currentTime.getTime() / 1000 + 3);
|
|
// ID is a 10-digit hex number
|
|
expect(randomId).to.match(/[0-9A-Fa-f]{10}/);
|
|
});
|
|
});
|
|
|
|
// NOTES: the tests runs with Ganache chain id.
|
|
describe('convertToLiquiditySources', () => {
|
|
it('returns the correct liquidity sources for multiple single sources', () => {
|
|
const liquiditySources = serviceUtils.convertToLiquiditySources({
|
|
singleSource: {
|
|
[ERC20BridgeSource.Native]: new BigNumber(0.5),
|
|
[ERC20BridgeSource.UniswapV3]: new BigNumber(0.5),
|
|
},
|
|
multihop: [],
|
|
});
|
|
|
|
expect(liquiditySources).to.be.deep.eq([
|
|
{
|
|
name: '0x',
|
|
proportion: new BigNumber(0.5),
|
|
},
|
|
{
|
|
name: ERC20BridgeSource.UniswapV3,
|
|
proportion: new BigNumber(0.5),
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('returns the correct liquidity sources for a mix of a single source and multihop sources', () => {
|
|
const liquiditySources = serviceUtils.convertToLiquiditySources({
|
|
singleSource: {
|
|
[ERC20BridgeSource.Native]: new BigNumber(0.2),
|
|
},
|
|
multihop: [
|
|
{
|
|
proportion: new BigNumber(0.3),
|
|
intermediateToken: 'intermediate-token-a',
|
|
hops: [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.Curve],
|
|
},
|
|
|
|
{
|
|
proportion: new BigNumber(0.4),
|
|
intermediateToken: 'intermediate-token-b',
|
|
hops: [ERC20BridgeSource.BalancerV2, ERC20BridgeSource.Curve],
|
|
},
|
|
],
|
|
});
|
|
|
|
expect(liquiditySources).to.be.deep.eq([
|
|
{
|
|
name: '0x',
|
|
proportion: new BigNumber(0.2),
|
|
},
|
|
{
|
|
name: ERC20BridgeSource.MultiHop,
|
|
proportion: new BigNumber(0.3),
|
|
intermediateToken: 'intermediate-token-a',
|
|
hops: [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.Curve],
|
|
},
|
|
|
|
{
|
|
name: ERC20BridgeSource.MultiHop,
|
|
proportion: new BigNumber(0.4),
|
|
intermediateToken: 'intermediate-token-b',
|
|
hops: [ERC20BridgeSource.BalancerV2, ERC20BridgeSource.Curve],
|
|
},
|
|
]);
|
|
});
|
|
});
|
|
describe('getAffiliateFeeAmounts', () => {
|
|
it('returns the correct amounts if the fee is zero', () => {
|
|
const affiliateFee = {
|
|
feeType: AffiliateFeeType.PercentageFee,
|
|
recipient: '',
|
|
buyTokenPercentageFee: 0,
|
|
sellTokenPercentageFee: 0,
|
|
};
|
|
const costInfo = serviceUtils.getBuyTokenFeeAmounts(randomSellQuote, affiliateFee);
|
|
expect(costInfo).to.deep.equal({
|
|
buyTokenFeeAmount: ZERO,
|
|
sellTokenFeeAmount: ZERO,
|
|
gasCost: ZERO,
|
|
});
|
|
});
|
|
it('returns the correct amounts if the fee is non-zero', () => {
|
|
const affiliateFee = {
|
|
feeType: AffiliateFeeType.PercentageFee,
|
|
recipient: randomAddress(),
|
|
buyTokenPercentageFee: 0.01,
|
|
sellTokenPercentageFee: 0,
|
|
};
|
|
const costInfo = serviceUtils.getBuyTokenFeeAmounts(randomSellQuote, affiliateFee);
|
|
expect(costInfo).to.deep.equal({
|
|
buyTokenFeeAmount: randomSellQuote.worstCaseQuoteInfo.makerAmount
|
|
.times(affiliateFee.buyTokenPercentageFee)
|
|
.dividedBy(affiliateFee.buyTokenPercentageFee + 1)
|
|
.integerValue(BigNumber.ROUND_DOWN),
|
|
sellTokenFeeAmount: ZERO,
|
|
gasCost: AFFILIATE_FEE_TRANSFORMER_GAS,
|
|
});
|
|
});
|
|
});
|
|
it('returns the correct amounts if the positive slippage fee is non-zero', () => {
|
|
const affiliateFee = {
|
|
feeType: AffiliateFeeType.PositiveSlippageFee,
|
|
recipient: randomAddress(),
|
|
buyTokenPercentageFee: 0,
|
|
sellTokenPercentageFee: 0,
|
|
};
|
|
const costInfo = serviceUtils.getBuyTokenFeeAmounts(randomSellQuote, affiliateFee);
|
|
expect(costInfo).to.deep.equal({
|
|
buyTokenFeeAmount: ZERO,
|
|
sellTokenFeeAmount: ZERO,
|
|
gasCost: POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS,
|
|
});
|
|
});
|
|
it('returns the correct amounts if gasless', () => {
|
|
const affiliateFee = {
|
|
feeType: AffiliateFeeType.GaslessFee,
|
|
recipient: randomAddress(),
|
|
buyTokenPercentageFee: 0,
|
|
sellTokenPercentageFee: 0,
|
|
};
|
|
const costInfo = serviceUtils.getBuyTokenFeeAmounts(randomSellQuote, affiliateFee);
|
|
expect(costInfo).to.deep.equal({
|
|
buyTokenFeeAmount: randomSellQuote.gasPrice
|
|
.times(randomSellQuote.worstCaseQuoteInfo.gas)
|
|
.times(randomSellQuote.makerAmountPerEth)
|
|
.integerValue(BigNumber.ROUND_DOWN),
|
|
sellTokenFeeAmount: ZERO,
|
|
gasCost: AFFILIATE_FEE_TRANSFORMER_GAS,
|
|
});
|
|
});
|
|
});
|