Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into issue-#1300
Browse files Browse the repository at this point in the history
  • Loading branch information
GrandSchtroumpf committed Jul 1, 2024
2 parents ef57e28 + 2d5e1c8 commit 7859d61
Show file tree
Hide file tree
Showing 52 changed files with 627 additions and 338 deletions.
174 changes: 128 additions & 46 deletions e2e/pages/trade.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,87 @@ import { TradeDriver } from '../utils/TradeDriver';
import { navigateTo } from '../utils/operators';
import { TokenApprovalDriver } from '../utils/TokenApprovalDriver';
import { waitForTenderlyRpc } from '../utils/tenderly';
import { TradeTestCase } from '../utils/trade/types';
import { NotificationDriver } from '../utils/NotificationDriver';

const testCases: TradeTestCase[] = [
{
mode: 'buy' as const,
source: 'USDC',
target: 'ETH',
swaps: [
{
sourceValue: '100',
targetValue: '0.059554032010090174',
},
],
},
{
mode: 'sell' as const,
source: 'USDC',
target: 'USDT',
isLimitedApproval: true,
swaps: [
{
sourceValue: '10',
targetValue: '10.004888',
},
{
sourceValue: '10',
targetValue: '10.004863',
},
{
sourceValue: '10',
targetValue: '10.004838',
},
],
},
{
mode: 'sell' as const,
source: 'USDC',
target: 'USDT',
swaps: [
{
sourceValue: '10',
targetValue: '10.004888',
},
{
sourceValue: '10',
targetValue: '10.004863',
},
],
},
{
mode: 'sell' as const,
source: 'ETH',
target: 'USDC',
swaps: [
{
sourceValue: '0.005',
targetValue: '7.939455',
},
{
sourceValue: '0.005',
targetValue: '7.93526',
},
],
},
];

const testDescription = (testCase: TradeTestCase) => {
const { mode, source, target, isLimitedApproval } = testCase;

const numSwaps = testCase.swaps.length;
const nameSwapSuffix = numSwaps > 1 ? ` ${numSwaps} times` : '';
const approvalSuffix = isLimitedApproval ? ' with limited approval ' : '';

const testName =
mode === 'buy'
? `Buy ${target} with ${source}`
: `Sell ${source} for ${target}`;

return testName + nameSwapSuffix + approvalSuffix;
};

test.describe('Trade', () => {
test.beforeEach(async ({ page }, testInfo) => {
Expand All @@ -22,74 +103,75 @@ test.describe('Trade', () => {
await removeFork(testInfo);
});

const testCases = [
{
mode: 'buy' as const,
source: 'USDC',
target: 'ETH',
sourceValue: '100',
targetValue: '0.059554032010090174',
},
{
mode: 'sell' as const,
source: 'USDC',
target: 'USDT',
sourceValue: '100',
targetValue: '100.047766',
},
];

for (const testCase of testCases) {
const { mode, source, target, sourceValue, targetValue } = testCase;
const testName =
mode === 'buy'
? `Buy ${target} with ${source}`
: `Sell ${source} for ${target}`;
const { source, target, swaps, isLimitedApproval } = testCase;

const testName = testDescription(testCase);

test(testName, async ({ page }) => {
// Store current balance
const debug = new DebugDriver(page);
const balance = {
source: await debug.getBalance(testCase.source).textContent(),
target: await debug.getBalance(testCase.target).textContent(),
const initialBalance = {
source: await debug.getBalance(source).textContent(),
target: await debug.getBalance(target).textContent(),
};

// Test Trade
await navigateTo(page, '/trade?*');
const driver = new TradeDriver(page, testCase);
const tokenApproval = new TokenApprovalDriver(page);

// Select pair
await driver.selectPair();
await driver.setPay();
await expect(driver.getReceiveInput()).toHaveValue(targetValue);

// Verify routing
const routing = await driver.openRouting();
await expect(routing.getSource()).toHaveValue(sourceValue);
await expect(routing.getTarget()).toHaveValue(targetValue);
await routing.close();
for (const swap of swaps) {
const { sourceValue, targetValue } = swap;

await driver.submit();
await waitForTenderlyRpc(page);
await driver.setPay(swap);
await expect(driver.getReceiveInput()).toHaveValue(targetValue);

// Token approval
const tokenApproval = new TokenApprovalDriver(page);
await tokenApproval.checkApproval([testCase.source]);
// Verify routing
const routing = await driver.openRouting();
await expect(routing.getSource()).toHaveValue(sourceValue);
await expect(routing.getTarget()).toHaveValue(targetValue);
await routing.close();

await driver.submit();
await waitForTenderlyRpc(page);

// Token approval
await tokenApproval.checkApproval([source], isLimitedApproval);

// Verify form empty
await driver.awaitSuccess();
expect(driver.getPayInput()).toHaveValue('');
expect(driver.getReceiveInput()).toHaveValue('');

// Verify form empty
await driver.awaitSuccess();
expect(driver.getPayInput()).toHaveValue('');
expect(driver.getReceiveInput()).toHaveValue('');
const notificationDriver = new NotificationDriver(page);
await notificationDriver.closeAll();
}

// Check balance diff
// Check balance diff after all swaps
await navigateTo(page, '/debug');

const sourceDelta = Number(balance.source) - Number(testCase.sourceValue);
const { sourceValue, targetValue } = swaps.reduce(
(acc, swapValues) => {
return {
sourceValue: acc.sourceValue + Number(swapValues.sourceValue),
targetValue: acc.targetValue + Number(swapValues.targetValue),
};
},
{
sourceValue: 0,
targetValue: 0,
}
);
const sourceDelta = Number(initialBalance.source) - Number(sourceValue);
const nextSource = new RegExp(sourceDelta.toString());
await expect(debug.getBalance(testCase.source)).toHaveText(nextSource);
const targetDelta = Number(balance.target) + Number(testCase.targetValue);
await expect(debug.getBalance(source)).toHaveText(nextSource);
const targetDelta = Number(initialBalance.target) + Number(targetValue);
const nextTarget = new RegExp(targetDelta.toString());
await expect(debug.getBalance(testCase.target)).toHaveText(nextTarget);
await expect(debug.getBalance(target)).toHaveText(nextTarget);
});
}
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 4 additions & 2 deletions e2e/utils/TokenApprovalDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,22 @@ import { waitFor } from './operators';
export class TokenApprovalDriver {
private approvedTokens = ['ETH'];
constructor(private page: Page) {}
async checkApproval(tokens: string[]) {
async checkApproval(tokens: string[], limitedApproval?: boolean) {
if (tokens.every((token) => this.approvedTokens.includes(token))) return;
const modal = await waitModalOpen(this.page);
for (const token of tokens) {
if (token === 'ETH') {
const msg = modal.getByTestId(`msg-${token}`);
await expect(msg).toHaveText('Pre-Approved');
} else {
if (limitedApproval)
await modal.getByTestId(`approve-limited-${token}`).click();
await modal.getByTestId(`approve-${token}`).click();
const msg = await waitFor(this.page, `msg-${token}`, 20000);
await expect(msg).toHaveText('Approved');
}
}
this.approvedTokens.push(...tokens);
if (!limitedApproval) this.approvedTokens.push(...tokens);
await modal.getByTestId('approve-submit').click();
}
}
15 changes: 4 additions & 11 deletions e2e/utils/TradeDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,8 @@
import { Page } from '@playwright/test';
import { waitFor } from './operators';
import { closeModal, waitModalClose, waitModalOpen } from './modal';
import { Direction, debugTokens } from './types';

interface TradeTestCase {
mode: Direction;
source: string;
target: string;
sourceValue: string;
targetValue: string;
}
import { debugTokens } from './types';
import { TestCaseSwap, TradeTestCase } from './trade/types';

export class TradeDriver {
public form = this.page.getByTestId(`${this.testCase.mode}-form`);
Expand Down Expand Up @@ -38,8 +31,8 @@ export class TradeDriver {
await waitModalClose(this.page);
}

setPay() {
const { sourceValue } = this.testCase;
setPay(swap: TestCaseSwap) {
const { sourceValue } = swap;
return this.form.getByLabel('You Pay').fill(sourceValue);
}

Expand Down
14 changes: 14 additions & 0 deletions e2e/utils/trade/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { DebugTokens, Direction } from '../types';

export type TestCaseSwap = {
sourceValue: string;
targetValue: string;
};

export type TradeTestCase = {
mode: Direction;
source: DebugTokens;
target: DebugTokens;
isLimitedApproval?: boolean;
swaps: TestCaseSwap[];
};
8 changes: 7 additions & 1 deletion src/components/activity/ActivityNotification.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@ import style from './ActivityNotification.module.css';
interface Props {
notification: NotificationActivity;
close: () => void;
onClick?: () => void;
}

export const ActivityNotification: FC<Props> = ({ notification, close }) => {
export const ActivityNotification: FC<Props> = ({
notification,
close,
onClick,
}) => {
const titleId = useId();
const { activity } = notification;

Expand All @@ -31,6 +36,7 @@ export const ActivityNotification: FC<Props> = ({ notification, close }) => {
</hgroup>
<Link
to="/strategy/$id"
onClick={onClick}
params={{ id: activity.strategy.id }}
className="font-weight-500 flex items-center"
>
Expand Down
1 change: 1 addition & 0 deletions src/components/common/approval/ApproveToken.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ export const ApproveToken: FC<Props> = ({
isOn={!isLimited}
setIsOn={handleLimitChange}
size="sm"
data-testid={`approve-limited-${token.symbol}`}
/>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { cn } from 'utils/helpers';
type MenuItemProps = {
item: {
onClick?: Function;
content: string | ReactElement;
content?: string | ReactElement;
hasSubMenu?: boolean;
disableHoverEffect?: boolean;
className?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ export const MainMenuRightBurger: FC<{
menuMapping,
});

const currentMenuItems = menuContext.top()?.items;
const currentMenuItems = menuContext
.top()
?.items.filter((item) => !!item.content);

return (
<DropdownMenu
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ReactComponent as IconV } from 'assets/icons/v.svg';

export type MenuItemType = {
subMenu?: MenuType;
content: string | ReactElement;
content?: string | ReactElement;
onClick?: Function;
postClickAction?: MenuItemActions;
};
Expand Down Expand Up @@ -43,7 +43,7 @@ export const useBurgerMenuItems = () => {
),
},
{
content: (
content: externalLinks.analytics && (
<NewTabLink className="flex" to={externalLinks.analytics}>
Analytics
</NewTabLink>
Expand Down Expand Up @@ -157,21 +157,21 @@ export const useBurgerMenuItems = () => {
),
},
{
content: (
content: externalLinks.simulatorRepo && (
<NewTabLink className="flex" to={externalLinks.simulatorRepo}>
Simulator Repo
</NewTabLink>
),
},
{
content: (
content: externalLinks.interactiveSim && (
<NewTabLink className="flex" to={externalLinks.interactiveSim}>
Interactive Simulator
</NewTabLink>
),
},
{
content: (
content: externalLinks.duneDashboard && (
<NewTabLink className="flex" to={externalLinks.duneDashboard}>
Dune Dashboard
</NewTabLink>
Expand Down
2 changes: 1 addition & 1 deletion src/components/strategies/UserMarketPrice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ interface MarketPriceProps {
}
/** Use external market price or user price */
export const useUserMarketPrice = ({ base, quote }: MarketPriceProps) => {
const externalMarketPrice = useMarketPrice({ base, quote });
const { marketPrice: externalMarketPrice } = useMarketPrice({ base, quote });
const userMarketPrice = useContext(UserMarketContext);
return userMarketPrice || externalMarketPrice;
};
8 changes: 3 additions & 5 deletions src/components/strategies/common/InputBudget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,9 @@ export const InputBudget: FC<Props> = (props) => {
};

const setMax = () => {
if (max === value) return;
if (max) {
const balanceValue = new SafeDecimal(max).toFixed(token.decimals);
props.onChange(balanceValue);
}
if (!max || max === value) return;
const maxBudget = new SafeDecimal(max).toFixed(token.decimals);
props.onChange(maxBudget);
};

return (
Expand Down
Loading

0 comments on commit 7859d61

Please sign in to comment.