Skip to content

Commit

Permalink
Merge pull request #878 from bancorprotocol/issue-#702
Browse files Browse the repository at this point in the history
Undercut option to duplicate strategy flow
  • Loading branch information
tiagofilipenunes authored Jan 18, 2024
2 parents 1615396 + e4e5e4d commit 49a7d7f
Show file tree
Hide file tree
Showing 14 changed files with 393 additions and 3 deletions.
15 changes: 15 additions & 0 deletions e2e/pages/strategy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,21 @@ const testCases: CreateStrategyTestCase[] = [
fiat: '$3,334.42',
},
},
undercut: {
totalFiat: '$3,344.42',
buy: {
min: '1,501.50 DAI',
max: '1,501.50 DAI',
budget: '10.00 DAI',
fiat: '$10.00',
},
sell: {
min: '1,698.30 DAI',
max: '1,698.30 DAI',
budget: '2.00 ETH',
fiat: '$3,334.42',
},
},
},
},
{
Expand Down
4 changes: 4 additions & 0 deletions e2e/tests/strategy/recurring/duplicate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
} from './../../../utils/strategy';
import { NotificationDriver } from './../../../utils/NotificationDriver';
import { ManageStrategyDriver } from './../../../utils/strategy/ManageStrategyDriver';
import { waitModalOpen } from '../../../utils/modal';

export const duplicateStrategyTest = (testCase: CreateStrategyTestCase) => {
assertRecurringTestCase(testCase);
Expand All @@ -16,6 +17,9 @@ export const duplicateStrategyTest = (testCase: CreateStrategyTestCase) => {
const strategy = await manage.createStrategy(testCase.input);
await strategy.clickManageEntry('manage-strategy-duplicateStrategy');

const modal = await waitModalOpen(page);
await modal.getByTestId('duplicate-strategy-btn').click();

await page.waitForURL('/strategies/create?strategy=*', {
timeout: 10_000,
});
Expand Down
1 change: 1 addition & 0 deletions e2e/tests/strategy/recurring/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export { createRecurringStrategy } from './create';
export { deleteStrategyTest } from './delete';
export { depositStrategyTest } from './deposit';
export { duplicateStrategyTest } from './duplicate';
export { undercutStrategyTest } from './undercut';
export { editPriceStrategyTest } from './edit';
export { pauseStrategyTest } from './pause';
export { renewStrategyTest } from './renew';
Expand Down
59 changes: 59 additions & 0 deletions e2e/tests/strategy/recurring/undercut.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { expect, test } from '@playwright/test';
import {
CreateStrategyTestCase,
MyStrategyDriver,
assertRecurringTestCase,
} from './../../../utils/strategy';
import { NotificationDriver } from './../../../utils/NotificationDriver';
import { ManageStrategyDriver } from './../../../utils/strategy/ManageStrategyDriver';
import { waitModalOpen } from '../../../utils/modal';

export const undercutStrategyTest = (testCase: CreateStrategyTestCase) => {
assertRecurringTestCase(testCase);
const { base, quote } = testCase.input;
const output = testCase.output.undercut;
return test('Undercut', async ({ page }) => {
const manage = new ManageStrategyDriver(page);
const strategy = await manage.createStrategy(testCase.input);
await strategy.clickManageEntry('manage-strategy-duplicateStrategy');

const modal = await waitModalOpen(page);
await modal.getByTestId('undercut-strategy-btn').click();

await page.waitForURL('/strategies/create?strategy=*', {
timeout: 10_000,
});

await page.getByText('Create Strategy').click();
await page.waitForURL('/', { timeout: 10_000 });

const notif = new NotificationDriver(page, 'create-strategy');
await expect(notif.getTitle()).toHaveText('Success');
await expect(notif.getDescription()).toHaveText(
'New strategy was successfully created.'
);

const myStrategies = new MyStrategyDriver(page);
const strategies = await myStrategies.getAllStrategies();
await expect(strategies).toHaveCount(2);

const strategyUndercut = await myStrategies.getStrategy(2);
await expect(strategyUndercut.pair()).toHaveText(`${base}/${quote}`);
await expect(strategyUndercut.status()).toHaveText('Active');
await expect(strategyUndercut.totalBudget()).toHaveText(output.totalFiat);
await expect(strategyUndercut.buyBudget()).toHaveText(output.buy.budget);
await expect(strategyUndercut.buyBudgetFiat()).toHaveText(output.buy.fiat);
await expect(strategyUndercut.sellBudget()).toHaveText(output.sell.budget);
await expect(strategyUndercut.sellBudgetFiat()).toHaveText(
output.sell.fiat
);

const buyTooltip = await strategyUndercut.priceTooltip('buy');
await expect(buyTooltip.startPrice()).toHaveText(output.buy.min);
await buyTooltip.waitForDetached();

const sellTooltip = await strategyUndercut.priceTooltip('sell');
await expect(sellTooltip.startPrice()).toHaveText(output.sell.min);
await sellTooltip.waitForDetached();
});
};
15 changes: 15 additions & 0 deletions e2e/utils/strategy/CreateStrategyDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,21 @@ export interface RecurringStrategyOutput {
fiat: string;
};
};
undercut: {
totalFiat: string;
buy: {
min: string;
max: string;
budget: string;
fiat: string;
};
sell: {
min: string;
max: string;
budget: string;
fiat: string;
};
};
}
export type RecurringStrategyTestCase = TestCase<
RecurringStrategyInput,
Expand Down
1 change: 1 addition & 0 deletions e2e/utils/strategy/MyStrategyDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export class MyStrategyDriver {
minPrice: () => tooltip.getByTestId('min-price'),
maxPrice: () => tooltip.getByTestId('max-price'),
marginalPrice: () => tooltip.getByTestId('marginal-price'),
startPrice: () => tooltip.getByTestId('start-price'),
waitForDetached: async () => {
await this.page.mouse.move(0, 0);
await tooltip.waitFor({ state: 'detached' });
Expand Down
5 changes: 5 additions & 0 deletions src/assets/icons/cut.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,11 @@ export const StrategyBlockManage: FC<Props> = ({
name: 'Duplicate Strategy',
action: () => {
carbonEvents.strategyEdit.strategyDuplicateClick(strategyEvent);
duplicate(strategy);
if (!isOverlapping) {
openModal('duplicateStrategy', { strategy });
} else {
duplicate(strategy);
}
},
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ const OrderTooltip: FC<OrderTooltipProps> = ({ strategy, buy }) => {
<th className="p-8 text-start font-weight-400 text-white/60">
Price
</th>
<td className="p-8 text-end">
<td className="p-8 text-end" data-testid="start-price">
{startPrice} {quote.symbol}
</td>
</tr>
Expand Down
3 changes: 2 additions & 1 deletion src/components/strategies/overview/strategyBlock/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ export const getTooltipTextByStatus = (
};

const tooltipTextByStrategyEditOptionsId = {
duplicateStrategy: 'Create a new strategy with the same details',
duplicateStrategy:
'Create a new strategy with the same details or undercut it',
deleteStrategy:
'Delete the strategy and withdraw all associated funds to your wallet',
pauseStrategy: 'Deactivate the strategy by nulling the prices',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { describe, test, expect } from 'vitest';
import { SafeDecimal } from 'libs/safedecimal';
import { deepCopy, getUndercutStrategy } from './utils';

type StrategyStatus = 'active' | 'noBudget' | 'paused' | 'inactive';

const baseStrategy = {
id: '',
idDisplay: '',
base: {
address: '',
decimals: 18,
symbol: 'ETH',
},
quote: {
address: '',
decimals: 6,
symbol: 'USDC',
},
order0: {
balance: '',
startRate: '',
endRate: '',
marginalRate: '',
},
order1: {
balance: '',
startRate: '',
endRate: '',
marginalRate: '',
},
status: 'active' as StrategyStatus,
encoded: {
id: '',
token0: '',
token1: '',
order0: {
y: '',
z: '',
A: '',
B: '',
},
order1: {
y: '',
z: '',
A: '',
B: '',
},
},
roi: new SafeDecimal('0'),
};

let undercutStrategy = deepCopy(baseStrategy);

describe('Test undercut strategy', () => {
test('getUndercutStrategy with 0.1% rate', () => {
const undercutDifference = 0.001;

baseStrategy.order0.startRate = '0';
baseStrategy.order0.endRate = '1700';
baseStrategy.order1.startRate = '1800';
baseStrategy.order1.endRate = '1900';
undercutStrategy.order0.startRate = '0';
undercutStrategy.order0.endRate = '1701.7';
undercutStrategy.order1.startRate = '1798.2';
undercutStrategy.order1.endRate = '1898.1';

expect(getUndercutStrategy(baseStrategy, undercutDifference)).toStrictEqual(
undercutStrategy
);
});

test('getUndercutStrategy with 0.1% rate and a strategy with one limit order', () => {
const undercutDifference = 0.001;

baseStrategy.order0.startRate = '0';
baseStrategy.order0.endRate = '0';
baseStrategy.order1.startRate = '1900';
baseStrategy.order1.endRate = '1900';
undercutStrategy.order0.startRate = '0';
undercutStrategy.order0.endRate = '0';
undercutStrategy.order1.startRate = '1898.1';
undercutStrategy.order1.endRate = '1898.1';

expect(getUndercutStrategy(baseStrategy, undercutDifference)).toStrictEqual(
undercutStrategy
);
});

test('getUndercutStrategy with 1% rate', () => {
const undercutDifference = 0.01;

baseStrategy.order0.startRate = '1600';
baseStrategy.order0.endRate = '1700';
baseStrategy.order1.startRate = '1800';
baseStrategy.order1.endRate = '1900';
undercutStrategy.order0.startRate = '1616';
undercutStrategy.order0.endRate = '1717';
undercutStrategy.order1.startRate = '1782';
undercutStrategy.order1.endRate = '1881';

expect(getUndercutStrategy(baseStrategy, undercutDifference)).toStrictEqual(
undercutStrategy
);
});

test('getUndercutStrategy with negative rate', () => {
const undercutDifference = -1;

baseStrategy.order0.startRate = '1600';
baseStrategy.order0.endRate = '1700';
baseStrategy.order1.startRate = '1800';
baseStrategy.order1.endRate = '1900';
undercutStrategy.order0.startRate = '1616';
undercutStrategy.order0.endRate = '1717';
undercutStrategy.order1.startRate = '1782';
undercutStrategy.order1.endRate = '1881';

expect(() =>
getUndercutStrategy(baseStrategy, undercutDifference)
).toThrow();
});

test('getUndercutStrategy with rate higher than 100%', () => {
const undercutDifference = 1.01;

baseStrategy.order0.startRate = '1600';
baseStrategy.order0.endRate = '1700';
baseStrategy.order1.startRate = '1800';
baseStrategy.order1.endRate = '1900';
undercutStrategy.order0.startRate = '1616';
undercutStrategy.order0.endRate = '1717';
undercutStrategy.order1.startRate = '1782';
undercutStrategy.order1.endRate = '1881';

expect(() =>
getUndercutStrategy(baseStrategy, undercutDifference)
).toThrow();
});
});
Loading

0 comments on commit 49a7d7f

Please sign in to comment.