Skip to content

Commit

Permalink
Merge pull request #168 from lidofinance/develop
Browse files Browse the repository at this point in the history
Develop to main
  • Loading branch information
itaven authored Dec 4, 2023
2 parents e91a0ff + 655f928 commit 084b883
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 33 deletions.
10 changes: 7 additions & 3 deletions features/home/home-page-regular.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FC } from 'react';
import { FC, Fragment } from 'react';
import Head from 'next/head';

import { Layout } from 'shared/components';
Expand All @@ -9,6 +9,7 @@ import { Wallet } from './wallet/wallet';
import { StakeForm } from './stake-form/stake-form';
import { StakeFaq } from './stake-faq/stake-faq';
import { LidoStats } from './lido-stats/lido-stats';
import { GoerliSunsetBanner } from 'shared/banners/goerli-sunset';

const HomePageRegular: FC = () => {
const key = useWeb3Key();
Expand All @@ -24,8 +25,11 @@ const HomePageRegular: FC = () => {
</Head>

<NoSSRWrapper>
<Wallet key={'wallet' + key} />
<StakeForm key={'form' + key} />
<Fragment key={key}>
<GoerliSunsetBanner />
<Wallet />
<StakeForm />
</Fragment>
</NoSSRWrapper>
<LidoStats />
<StakeFaq />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,51 +38,70 @@ describe('tvlJokeValidate', () => {
});
});

const amountPerRequest = bn(100);
const maxAmountPerRequest = bn(100);
const minAmountPerRequest = bn(10);
const maxRequestCount = 100;
describe('validateSplitRequests', () => {
it('should split into 1 request', () => {
const requests = validateSplitRequests(
field,
bn(10),
amountPerRequest,
maxAmountPerRequest,
minAmountPerRequest,
maxRequestCount,
);
expect(requests).toHaveLength(1);
expect(requests[0].eq(bn(10))).toBe(true);
});

it('should split into 2 requests', () => {
const amount = maxAmountPerRequest.add(minAmountPerRequest.mul(5));
const requests = validateSplitRequests(
field,
bn(150),
amountPerRequest,
amount,
maxAmountPerRequest,
minAmountPerRequest,
maxRequestCount,
);
expect(requests).toHaveLength(2);
expect(requests[0].eq(amountPerRequest)).toBe(true);
expect(requests[1].eq(bn(150).sub(amountPerRequest))).toBe(true);
expect(requests[0].eq(maxAmountPerRequest)).toBe(true);
expect(requests[1].eq(amount.sub(maxAmountPerRequest))).toBe(true);
});

it('should split into 2(max+min) requests', () => {
const requests = validateSplitRequests(
field,
maxAmountPerRequest.add(minAmountPerRequest),
maxAmountPerRequest,
minAmountPerRequest,
maxRequestCount,
);
expect(requests).toHaveLength(2);
expect(requests[0].eq(maxAmountPerRequest)).toBe(true);
expect(requests[1].eq(minAmountPerRequest)).toBe(true);
});

it('should split into max requests', () => {
const requests = validateSplitRequests(
field,
amountPerRequest.mul(maxRequestCount),
amountPerRequest,
maxAmountPerRequest.mul(maxRequestCount),
maxAmountPerRequest,
minAmountPerRequest,
maxRequestCount,
);
expect(requests).toHaveLength(maxRequestCount);
requests.forEach((r) => {
expect(r.eq(amountPerRequest)).toBe(true);
expect(r.eq(maxAmountPerRequest)).toBe(true);
});
});

it('should throw right error when more than max', () => {
const fn = () =>
validateSplitRequests(
field,
amountPerRequest.mul(maxRequestCount).add(1),
amountPerRequest,
maxAmountPerRequest.mul(maxRequestCount).add(1),
maxAmountPerRequest,
minAmountPerRequest,
maxRequestCount,
);
expect(fn).toThrow();
Expand All @@ -97,4 +116,24 @@ describe('validateSplitRequests', () => {
expect((e as any).payload.requestCount).toBe(maxRequestCount + 1);
}
});

it('should throw right error when cannot split because of left over', () => {
const fn = () =>
validateSplitRequests(
field,
maxAmountPerRequest.add(minAmountPerRequest).sub(1),
maxAmountPerRequest,
minAmountPerRequest,
maxRequestCount,
);
expect(fn).toThrow();
try {
fn();
} catch (e) {
expect(e).toMatchObject({
field,
type: ValidationSplitRequest.type,
});
}
});
});
35 changes: 24 additions & 11 deletions features/withdrawals/request/request-form-context/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,15 @@ const messageMaxAmount = (max: BigNumber, token: TokensWithdrawable) =>
const validateSplitRequests = (
field: string,
amount: BigNumber,
amountPerRequest: BigNumber,
maxAmountPerRequest: BigNumber,
minAmountPerRequest: BigNumber,
maxRequestCount: number,
): BigNumber[] => {
const maxAmount = amountPerRequest.mul(maxRequestCount);
const maxAmount = maxAmountPerRequest.mul(maxRequestCount);

const lastRequestAmountEther = amount.mod(amountPerRequest);
const lastRequestAmountEther = amount.mod(maxAmountPerRequest);
const restCount = lastRequestAmountEther.gt(0) ? 1 : 0;
const requestCount = amount.div(amountPerRequest).toNumber() + restCount;
const requestCount = amount.div(maxAmountPerRequest).toNumber() + restCount;

const isMoreThanMax = amount.gt(maxAmount);
if (isMoreThanMax) {
Expand All @@ -78,8 +79,19 @@ const validateSplitRequests = (
);
}

if (restCount && lastRequestAmountEther.lt(minAmountPerRequest)) {
const difference = minAmountPerRequest.sub(lastRequestAmountEther);
throw new ValidationSplitRequest(
field,
`Cannot split into valid requests as last request would be less than minimal withdrawal amount. Add ${formatEther(
difference,
)} to withdrawal amount.`,
{ requestCount },
);
}

const requests = Array.from<BigNumber>({ length: requestCount }).fill(
amountPerRequest,
maxAmountPerRequest,
);
if (restCount) {
requests[requestCount - 1] = lastRequestAmountEther;
Expand Down Expand Up @@ -165,20 +177,21 @@ export const RequestFormValidationResolver: Resolver<
return { values, errors: {} };
}

const requests = validateSplitRequests(
validateBignumberMin(
'amount',
amount,
maxAmountPerRequest,
maxRequestCount,
minAmountPerRequest,
messageMinUnstake(minAmountPerRequest, token),
);
validationResults.requests = requests;

validateBignumberMin(
const requests = validateSplitRequests(
'amount',
amount,
maxAmountPerRequest,
minAmountPerRequest,
messageMinUnstake(minAmountPerRequest, token),
maxRequestCount,
);
validationResults.requests = requests;

validateBignumberMax(
'amount',
Expand Down
2 changes: 2 additions & 0 deletions features/withdrawals/withdrawals-tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ClaimDataProvider } from './contexts/claim-data-context';
import { useWithdrawals } from './contexts/withdrawals-context';
import { Claim } from './claim';
import { Request } from './request';
import { GoerliSunsetBanner } from 'shared/banners/goerli-sunset';

const withdrawalRoutes = [
{
Expand All @@ -23,6 +24,7 @@ export const WithdrawalsTabs = () => {
return (
<ClaimDataProvider>
<Switch checked={isClaimTab} routes={withdrawalRoutes} />
<GoerliSunsetBanner />
{isClaimTab ? <Claim /> : <Request />}
</ClaimDataProvider>
);
Expand Down
2 changes: 2 additions & 0 deletions features/wsteth/wrap-unwrap-tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import NoSsrWrapper from 'shared/components/no-ssr-wrapper';

import { WrapFaq } from './shared/wrap-faq/wrap-faq';
import { UnwrapForm } from './unwrap/unwrap-form';
import { GoerliSunsetBanner } from 'shared/banners/goerli-sunset';

const NAV_ROUTES = [
{ name: 'Wrap', path: WRAP_PATH },
Expand All @@ -22,6 +23,7 @@ export const WrapUnwrapTabs = ({ mode }: WrapUnwrapLayoutProps) => {
<>
<NoSsrWrapper>
<Switch checked={isUnwrapMode} routes={NAV_ROUTES} />
<GoerliSunsetBanner />
<Wallet />
{isUnwrapMode ? <UnwrapForm /> : <WrapForm />}
</NoSsrWrapper>
Expand Down
2 changes: 2 additions & 0 deletions pages/rewards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Layout } from 'shared/components';
import { TopCard, RewardsList } from 'features/rewards/features';
import RewardsHistoryProvider from 'providers/rewardsHistory';
import { Fallback } from 'shared/wallet';
import { GoerliSunsetBanner } from 'shared/banners/goerli-sunset';

const Rewards: FC = () => {
return (
Expand All @@ -22,6 +23,7 @@ const Rewards: FC = () => {
</Head>
<RewardsHistoryProvider>
<Fallback />
<GoerliSunsetBanner />
<TopCard />
<RewardsList />
</RewardsHistoryProvider>
Expand Down
28 changes: 28 additions & 0 deletions shared/banners/goerli-sunset/goerli-sunset-banner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { CHAINS } from '@lido-sdk/constants';
import { useSDK } from '@lido-sdk/react';
import { Text, Link } from '@lidofinance/lido-ui';
import { SunsetMessageStyle } from './styles';

const URL_INFORMATION = 'https://docs.lido.fi/deployed-contracts/goerli/';
const URL_HOLESKY =
'https://docs.lido.fi/deployed-contracts/holesky/#hole%C5%A1ky-testnet';

export const GoerliSunsetBanner = () => {
const { chainId } = useSDK();

if (chainId !== CHAINS.Goerli) return null;

return (
<SunsetMessageStyle>
<Text weight={700} size="sm">
Görli Testnet is sunsetting, this process is scheduled till&nbsp;the end
of Q4 2023.
</Text>
<Text weight={400} size="xxs">
Additional information can be found{' '}
<Link href={URL_INFORMATION}>here</Link>, and you can locate the Testnet
staking widget on <Link href={URL_HOLESKY}>Holesky</Link>.
</Text>
</SunsetMessageStyle>
);
};
1 change: 1 addition & 0 deletions shared/banners/goerli-sunset/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './goerli-sunset-banner';
24 changes: 24 additions & 0 deletions shared/banners/goerli-sunset/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import styled from 'styled-components';
import { WalletCardStyle } from 'shared/wallet/card/styles';

export const SunsetMessageStyle = styled(WalletCardStyle)`
text-align: center;
background: radial-gradient(
90% 110% at 50% 100%,
#5f2144 0%,
rgb(247 38 138 / 0%) 100%
),
linear-gradient(0deg, #e54f64, #f89371);
> * {
color: var(--lido-color-accentContrast);
}
a {
color: var(--lido-color-primary);
}
> p:not(:last-child) {
margin-bottom: 6px;
}
`;
4 changes: 2 additions & 2 deletions utils/__tests__/getErrorMessage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ describe('getErrorMessage', () => {
});

it('should return LIMIT_REACHED error message when error reason includes STAKE_LIMIT', () => {
const error = { reason: ['STAKE_LIMIT'] };
const error = { reason: 'STAKE_LIMIT' };
expect(getErrorMessage(error)).toBe(ErrorMessage.LIMIT_REACHED);
});

Expand Down Expand Up @@ -47,7 +47,7 @@ describe('extractCodeFromError', () => {
});

test('extracts error code from reason array', () => {
const error = { reason: ['STAKE_LIMIT'] };
const error = { reason: 'STAKE_LIMIT' };
expect(extractCodeFromError(error)).toBe('LIMIT_REACHED');
});

Expand Down
12 changes: 6 additions & 6 deletions utils/getErrorMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export enum ErrorMessage {
ENABLE_BLIND_SIGNING = 'Please enable blind signing on your Ledger hardware wallet.',
LIMIT_REACHED = 'Transaction could not be completed because stake limit is exhausted. Please wait until the stake limit restores and try again. Otherwise, you can swap your Ethereum on 1inch platform instantly.',
DEVICE_LOCKED = 'Please unlock your Ledger hardware wallet',
INVALID_SIGNATURE = 'Invalid Permit signature. Perhaps it has expired or already been used. Try submitting a withdrawal request again.',
}

export const getErrorMessage = (error: unknown): ErrorMessage => {
Expand All @@ -21,6 +22,8 @@ export const getErrorMessage = (error: unknown): ErrorMessage => {
case 'UNPREDICTABLE_GAS_LIMIT':
case 'INSUFFICIENT_FUNDS':
return ErrorMessage.NOT_ENOUGH_ETHER;
case 'INVALID_SIGNATURE':
return ErrorMessage.INVALID_SIGNATURE;
case 'ACTION_REJECTED':
case 4001:
return ErrorMessage.DENIED_SIG;
Expand All @@ -43,12 +46,9 @@ export const extractCodeFromError = (
// early exit on non object error
if (!error || typeof error != 'object') return 0;

if (
'reason' in error &&
typeof error.reason == 'string' &&
error.reason.includes('STAKE_LIMIT')
) {
return 'LIMIT_REACHED';
if ('reason' in error && typeof error.reason == 'string') {
if (error.reason.includes('STAKE_LIMIT')) return 'LIMIT_REACHED';
if (error.reason.includes('INVALID_SIGNATURE')) return 'INVALID_SIGNATURE';
}

// sometimes we have error message but bad error code
Expand Down

0 comments on commit 084b883

Please sign in to comment.