Skip to content

Commit 74bd1ac

Browse files
grothemVladimir YumatovfoodakabojanaaveMareskoY
authored
feat: permissioned faucets (#1359)
* feat: captcha faucet * fix: env example * feat: new market, send up faucet address to api * feat: configure new markets * feat: send up token symbol * feat: new ui for captcha faucet * chore: new markets * feat: captcha faucet modal * fix: goerli facuet config * fix: error handling * fix: auto refresh token * fix: updated env types * ci: add turnstile key * fix: cleanup reused code * fix: updated wording in success modal * chore: i18n * refactor: tx success view * fix: remove txHash prop * feat: added v3.0.1 market, use new flag to determine which faucet to use * feat: handle all types of faucets * fix: don't use modal wrapper for permissioned faucets * fix: use latest temp build of utils, fixed race condtion with multiple calls in flight * feat: udated UiPoolDataProvider contracts * fix: v2 market config * fix: updated ui pool data provider for goerli v3 * fix: updated v2 goerli, removed console log * fix: update prod markets with new data provider contracts * fix: better error handling * fix: updated v3 markets * fix: op v3 testnet * feat: goerli market config * fix: add site key to env * fix: updated config for prod v2 markets * fix: market config * chore: use latest utils packages * Fix tests * fix: temporaily disalbe pokt rpcs for testing * fix: attribute name * fix: test e-mode * fix: add back in pokt rpcs * fix: remove dependency Co-authored-by: Vladimir Yumatov <[email protected]> Co-authored-by: Mark Hinschberger <[email protected]> Co-authored-by: bojank93 <[email protected]> Co-authored-by: NikitaY <[email protected]>
1 parent a8b6dee commit 74bd1ac

25 files changed

+505
-113
lines changed

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ NEXT_PUBLIC_API_BASEURL=https://aave-api-v2.aave.com
99
NEXT_PUBLIC_TRANSAK_APP_URL=https://global.transak.com
1010
NEXT_PUBLIC_TRANSAK_API_URL=https://api.transak.com
1111
NEXT_PUBLIC_TRANSAK_API_KEY=799087ea-c2be-44b8-a0fe-56d96b37ee3c
12+
NEXT_PUBLIC_TURNSTILE_SITE_KEY=0x4AAAAAAABe-kMUW_1JUfT7
1213
NEXT_PUBLIC_FORK_BASE_CHAIN_ID=1
1314
NEXT_PUBLIC_FORK_CHAIN_ID=1
1415
NEXT_PUBLIC_FORK_URL_RPC=https://rpc.tenderly.co/fork/1234567

.github/actions/build/action.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ runs:
6161
NEXT_PUBLIC_TRANSAK_APP_URL: 'https://global.transak.com'
6262
NEXT_PUBLIC_TRANSAK_API_URL: 'https://api.transak.com'
6363
NEXT_PUBLIC_TRANSAK_API_KEY: '799087ea-c2be-44b8-a0fe-56d96b37ee3c'
64+
NEXT_PUBLIC_TURNSTILE_SITE_KEY: 0x4AAAAAAABe-kMUW_1JUfT7
6465
NEXT_PUBLIC_FORK_BASE_CHAIN_ID: '${{ inputs.NEXT_PUBLIC_FORK_BASE_CHAIN_ID }}'
6566
NEXT_PUBLIC_FORK_CHAIN_ID: '${{ inputs.NEXT_PUBLIC_FORK_CHAIN_ID }}'
6667
NEXT_PUBLIC_FORK_URL_RPC: '${{ inputs.NEXT_PUBLIC_FORK_URL_RPC }}'

custom.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace NodeJS {
99
NEXT_PUBLIC_ENABLE_STAKING: string;
1010
NEXT_PUBLIC_ENV: string;
1111
NEXT_PUBLIC_API_BASEURL: string;
12+
NEXT_PUBLIC_TURNSTILE_SITE_KEY: string;
1213
NEXT_PUBLIC_FORK_BASE_CHAIN_ID?: string;
1314
NEXT_PUBLIC_FORK_CHAIN_ID?: string;
1415
NEXT_PUBLIC_FORK_URL_RPC?: string;

cypress/support/steps/verification.steps.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -344,9 +344,5 @@ export const verifyCountOfBorrowAssets = (
344344
);
345345
});
346346
});
347-
it('Verifying length', () => {
348-
cy.doSwitchToDashboardBorrowView();
349-
cy.get('[data-cy*=dashboardBorrowListItem_]').should('have.length', assets.length);
350-
});
351347
});
352348
};

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
"test:ci": "jest --ci"
3232
},
3333
"dependencies": {
34-
"@aave/contract-helpers": "1.11.2",
35-
"@aave/math-utils": "1.11.2",
34+
"@aave/contract-helpers": "^1.12.2",
35+
"@aave/math-utils": "^1.12.2",
3636
"@emotion/cache": "11.10.3",
3737
"@emotion/react": "11.10.4",
3838
"@emotion/server": "latest",

pages/faucet.page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import Script from 'next/script';
12
import * as React from 'react';
23
import { FaucetModal } from 'src/components/transactions/Faucet/FaucetModal';
34
import { MainLayout } from 'src/layouts/MainLayout';
@@ -9,6 +10,7 @@ import { ContentContainer } from '../src/components/ContentContainer';
910
export default function Faucet() {
1011
return (
1112
<>
13+
<Script src="https://challenges.cloudflare.com/turnstile/v0/api.js" strategy="lazyOnload" />
1214
<FaucetTopPanel />
1315
<ContentContainer>
1416
<FaucetAssetsList />

src/components/icons/WalletIcon.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ export const WalletIcon = ({ sx, ...rest }: SvgIconProps) => {
1414
<path d="M17.3203 17.398H3.32031C2.78988 17.398 2.28117 17.1872 1.9061 16.8122C1.53103 16.4371 1.32031 15.9284 1.32031 15.3979V6.39795C1.32031 5.86752 1.53103 5.35881 1.9061 4.98374C2.28117 4.60866 2.78988 4.39795 3.32031 4.39795H17.3203C17.8507 4.39795 18.3595 4.60866 18.7345 4.98374C19.1096 5.35881 19.3203 5.86752 19.3203 6.39795V15.3979C19.3203 15.9284 19.1096 16.4371 18.7345 16.8122C18.3595 17.1872 17.8507 17.398 17.3203 17.398Z" />
1515
<path
1616
d="M14.8203 11.3979C14.6877 11.3979 14.5605 11.3453 14.4668 11.2515C14.373 11.1577 14.3203 11.0306 14.3203 10.8979C14.3203 10.7653 14.373 10.6382 14.4668 10.5444C14.5605 10.4506 14.6877 10.3979 14.8203 10.3979C14.9529 10.3979 15.0801 10.4506 15.1739 10.5444C15.2676 10.6382 15.3203 10.7653 15.3203 10.8979C15.3203 11.0306 15.2676 11.1577 15.1739 11.2515C15.0801 11.3453 14.9529 11.3979 14.8203 11.3979Z"
17-
stroke-linecap="round"
18-
stroke-linejoin="round"
17+
strokeLinecap="round"
18+
strokeLinejoin="round"
1919
/>
2020
<path d="M16.3203 4.39793V2.99993C16.3202 2.69343 16.2497 2.39106 16.1142 2.11615C15.9787 1.84124 15.7818 1.60116 15.5387 1.41444C15.2956 1.22773 15.0129 1.09937 14.7124 1.03929C14.4118 0.979209 14.1015 0.989009 13.8053 1.06793L2.80531 4.00093C2.37937 4.11444 2.00285 4.36549 1.7343 4.71506C1.46575 5.06462 1.32021 5.49312 1.32031 5.93393V6.39793" />
2121
</g>
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import { Trans } from '@lingui/macro';
2+
import { Box, Button, CircularProgress, Typography } from '@mui/material';
3+
import { useState } from 'react';
4+
import { ComputedReserveData } from 'src/hooks/app-data-provider/useAppDataProvider';
5+
import { useWeb3Context } from 'src/libs/hooks/useWeb3Context';
6+
import { selectCurrentReserves } from 'src/store/poolSelectors';
7+
import { useRootStore } from 'src/store/root';
8+
9+
import { TxSuccessView } from '../FlowCommons/Success';
10+
import { DetailsNumberLine } from '../FlowCommons/TxModalDetails';
11+
import Turnstile from './Turnstile';
12+
import { getNormalizedMintAmount } from './utils';
13+
14+
export const CaptchaFaucetModalContent = ({ underlyingAsset }: { underlyingAsset: string }) => {
15+
const { readOnlyModeAddress } = useWeb3Context();
16+
const { account, currentMarket, currentMarketData } = useRootStore();
17+
const reserves = useRootStore((state) => selectCurrentReserves(state));
18+
19+
const [captchaToken, setCaptchaToken] = useState<string>('');
20+
const [loading, setLoading] = useState<boolean>(false);
21+
const [captchaLoading, setCaptchaLoading] = useState<boolean>(true);
22+
const [txHash, setTxHash] = useState<string>('');
23+
const [error, setError] = useState<string>('');
24+
25+
const faucetUrl = `${process.env.NEXT_PUBLIC_API_BASEURL}/faucet`;
26+
const siteKey = process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY as string;
27+
28+
const poolReserve = reserves.find(
29+
(reserve) => reserve.underlyingAsset === underlyingAsset
30+
) as ComputedReserveData;
31+
32+
const normalizedAmount = getNormalizedMintAmount(poolReserve.symbol, poolReserve.decimals);
33+
34+
const captchaVerify = (token: string) => {
35+
setCaptchaToken(token);
36+
setCaptchaLoading(false);
37+
};
38+
39+
const faucet = async () => {
40+
try {
41+
setTxHash('');
42+
setLoading(true);
43+
setError('');
44+
const response = await fetch(faucetUrl, {
45+
method: 'POST',
46+
headers: {
47+
Accept: 'application/json',
48+
'Content-Type': 'application/json',
49+
},
50+
body: JSON.stringify({
51+
address: account,
52+
captchaToken,
53+
market: currentMarket,
54+
tokenAddress: poolReserve.underlyingAsset,
55+
tokenSymbol: poolReserve.symbol,
56+
faucetAddress: currentMarketData.addresses.FAUCET,
57+
}),
58+
});
59+
const data = await response.json();
60+
if (!response.ok) {
61+
throw new Error(data.msg);
62+
}
63+
setTxHash(data.msg);
64+
} catch (e: unknown) {
65+
if (e instanceof Error && e.message) {
66+
setError(e.message);
67+
} else {
68+
setError('An error occurred trying to send the transaction');
69+
}
70+
} finally {
71+
setLoading(false);
72+
}
73+
};
74+
75+
if (txHash) {
76+
return (
77+
<TxSuccessView
78+
txHash={txHash}
79+
action={<Trans>will receive</Trans>}
80+
symbol={poolReserve.symbol}
81+
amount={normalizedAmount}
82+
/>
83+
);
84+
}
85+
86+
return (
87+
<>
88+
<Turnstile sitekey={siteKey} onVerify={captchaVerify} autoResetOnExpire />
89+
<Typography variant="h2" sx={{ mb: 6 }}>
90+
<Trans>Faucet</Trans> {poolReserve.symbol}
91+
</Typography>
92+
<Box
93+
sx={(theme) => ({
94+
p: 3,
95+
border: `1px solid ${theme.palette.divider}`,
96+
borderRadius: '4px',
97+
'.MuiBox-root:last-of-type': {
98+
mb: 0,
99+
},
100+
})}
101+
>
102+
<DetailsNumberLine
103+
description={<Trans>Amount</Trans>}
104+
iconSymbol={poolReserve.symbol}
105+
symbol={poolReserve.symbol}
106+
value={normalizedAmount}
107+
/>
108+
</Box>
109+
<Typography variant="helperText" color="error.main">
110+
{error}
111+
</Typography>
112+
<Box sx={{ display: 'flex', flexDirection: 'column', mt: 12 }}>
113+
<Button
114+
variant="contained"
115+
disabled={loading || !captchaToken || readOnlyModeAddress !== undefined}
116+
onClick={faucet}
117+
size="large"
118+
sx={{ minHeight: '44px' }}
119+
>
120+
{(loading || captchaLoading) && (
121+
<CircularProgress color="inherit" size="16px" sx={{ mr: 2 }} />
122+
)}
123+
{<Trans>Faucet {poolReserve.symbol}</Trans>}
124+
</Button>
125+
</Box>
126+
</>
127+
);
128+
};
Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
11
import { Trans } from '@lingui/macro';
22
import React from 'react';
33
import { ModalContextType, ModalType, useModalContext } from 'src/hooks/useModal';
4+
import { useRootStore } from 'src/store/root';
45

56
import { BasicModal } from '../../primitives/BasicModal';
67
import { ModalWrapper } from '../FlowCommons/ModalWrapper';
8+
import { CaptchaFaucetModalContent } from './CaptchaFaucetModalContent';
79
import { FaucetModalContent } from './FaucetModalContent';
810

911
export const FaucetModal = () => {
1012
const { type, close, args } = useModalContext() as ModalContextType<{
1113
underlyingAsset: string;
1214
}>;
1315

16+
const { isFaucetPermissioned } = useRootStore();
17+
1418
return (
1519
<BasicModal open={type === ModalType.Faucet} setOpen={close}>
16-
<ModalWrapper title={<Trans>Faucet</Trans>} underlyingAsset={args.underlyingAsset}>
17-
{(params) => <FaucetModalContent {...params} />}
18-
</ModalWrapper>
20+
{isFaucetPermissioned ? (
21+
<CaptchaFaucetModalContent underlyingAsset={args.underlyingAsset} />
22+
) : (
23+
<ModalWrapper title={<Trans>Faucet</Trans>} underlyingAsset={args.underlyingAsset}>
24+
{(params) => <FaucetModalContent {...params} />}
25+
</ModalWrapper>
26+
)}
1927
</BasicModal>
2028
);
2129
};

src/components/transactions/Faucet/FaucetModalContent.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { mintAmountsPerToken, valueToWei } from '@aave/contract-helpers';
2-
import { normalize } from '@aave/math-utils';
31
import { Trans } from '@lingui/macro';
42
import { useModalContext } from 'src/hooks/useModal';
53

@@ -8,6 +6,7 @@ import { ModalWrapperProps } from '../FlowCommons/ModalWrapper';
86
import { TxSuccessView } from '../FlowCommons/Success';
97
import { DetailsNumberLine, TxModalDetails } from '../FlowCommons/TxModalDetails';
108
import { FaucetActions } from './FaucetActions';
9+
import { getNormalizedMintAmount } from './utils';
1110

1211
export type FaucetModalContentProps = {
1312
underlyingAsset: string;
@@ -17,11 +16,8 @@ export enum ErrorType {}
1716

1817
export const FaucetModalContent = ({ poolReserve, isWrongNetwork }: ModalWrapperProps) => {
1918
const { gasLimit, mainTxState: faucetTxState, txError } = useModalContext();
20-
const defaultValue = valueToWei('1000', 18);
21-
const mintAmount = mintAmountsPerToken[poolReserve.symbol.toUpperCase()]
22-
? mintAmountsPerToken[poolReserve.symbol.toUpperCase()]
23-
: defaultValue;
24-
const normalizedAmount = normalize(mintAmount, poolReserve.decimals);
19+
20+
const normalizedAmount = getNormalizedMintAmount(poolReserve.symbol, poolReserve.decimals);
2521

2622
if (faucetTxState.success)
2723
return (

0 commit comments

Comments
 (0)