Skip to content

Commit da3043c

Browse files
committed
chore: moves withdraw example as a react-stories example
1 parent 54ea920 commit da3043c

File tree

15 files changed

+300
-330
lines changed

15 files changed

+300
-330
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import {
2+
type EvmAddress,
3+
type SpokeId,
4+
type UserSupplyItem,
5+
useUserSupplies,
6+
} from '@aave/react';
7+
import { FormControl } from 'baseui/form-control';
8+
import { type OnChangeParams, SingleSelect } from 'baseui/select';
9+
import { useEffect } from 'react';
10+
11+
interface SupplySelectorProps {
12+
spokeId: SpokeId | undefined;
13+
user: EvmAddress;
14+
onChange: (supply: UserSupplyItem | null) => void;
15+
selected: UserSupplyItem | null;
16+
}
17+
18+
export function SupplySelector({
19+
spokeId,
20+
user,
21+
onChange,
22+
selected,
23+
}: SupplySelectorProps) {
24+
const {
25+
data: supplies,
26+
loading,
27+
paused,
28+
} = useUserSupplies({
29+
query: spokeId
30+
? {
31+
userSpoke: {
32+
spoke: spokeId,
33+
user,
34+
},
35+
}
36+
: undefined,
37+
pause: !spokeId,
38+
});
39+
40+
useEffect(() => {
41+
if (supplies?.length === 1) {
42+
onChange(supplies[0]);
43+
}
44+
}, [supplies, onChange]);
45+
46+
const handleChange = (params: OnChangeParams) => {
47+
switch (params.type) {
48+
case 'clear':
49+
onChange(null);
50+
break;
51+
52+
case 'select':
53+
onChange(params.option as UserSupplyItem);
54+
break;
55+
}
56+
};
57+
58+
return (
59+
<FormControl
60+
label='Supply Position'
61+
caption={
62+
supplies?.length === 1
63+
? 'Only one supply position found'
64+
: 'Select the supply position you want to withdraw from'
65+
}
66+
disabled={
67+
paused || loading || supplies?.length === 1 || supplies?.length === 0
68+
}
69+
error={supplies?.length === 0 ? 'No supply positions found' : undefined}
70+
>
71+
<SingleSelect
72+
placeholder='Select a supply position'
73+
valueKey='id'
74+
getValueLabel={({ option }) => {
75+
return option.reserve.asset.underlying.info.symbol;
76+
}}
77+
getOptionLabel={({ option }) =>
78+
`${option.reserve.asset.underlying.info.symbol} β€” ${option.withdrawable.amount.value.toDisplayString(2)} ${option.withdrawable.token.info.symbol}`
79+
}
80+
onChange={handleChange}
81+
options={supplies}
82+
value={selected ? [selected] : undefined}
83+
/>
84+
</FormControl>
85+
);
86+
}
87+
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import {
2+
bigDecimal,
3+
evmAddress,
4+
type UserSupplyItem,
5+
useWithdraw,
6+
} from '@aave/react';
7+
import { useSendTransaction } from '@aave/react/viem';
8+
import { Block } from 'baseui/block';
9+
import { Button } from 'baseui/button';
10+
import { Checkbox } from 'baseui/checkbox';
11+
import { FormControl } from 'baseui/form-control';
12+
import { Input } from 'baseui/input';
13+
import { KIND, Notification } from 'baseui/notification';
14+
import { useState } from 'react';
15+
import type { WalletClient } from 'viem';
16+
17+
interface WithdrawFormProps {
18+
supply: UserSupplyItem;
19+
walletClient: WalletClient;
20+
}
21+
22+
export function WithdrawForm({ supply, walletClient }: WithdrawFormProps) {
23+
const [status, setStatus] = useState<{
24+
kind: keyof typeof KIND;
25+
message: string;
26+
} | null>(null);
27+
const [useMax, setUseMax] = useState(false);
28+
29+
const [sendTransaction] = useSendTransaction(walletClient);
30+
const [withdraw, { loading }] = useWithdraw((plan) => {
31+
switch (plan.__typename) {
32+
case 'TransactionRequest':
33+
setStatus({
34+
kind: KIND.info,
35+
message: 'Sign the Withdraw Transaction in your wallet',
36+
});
37+
return sendTransaction(plan).andTee(() =>
38+
setStatus({
39+
kind: KIND.info,
40+
message: 'Sending Withdraw Transaction…',
41+
}),
42+
);
43+
44+
case 'Erc20ApprovalRequired':
45+
case 'PreContractActionRequired':
46+
setStatus({
47+
kind: KIND.info,
48+
message: 'Sign the Approval Transaction in your wallet',
49+
});
50+
return sendTransaction(plan.transaction).andTee(() =>
51+
setStatus({
52+
kind: KIND.info,
53+
message: 'Sending Approval Transaction…',
54+
}),
55+
);
56+
}
57+
});
58+
59+
const submit = async (e: React.FormEvent<HTMLFormElement>) => {
60+
e.preventDefault();
61+
62+
const form = e.currentTarget;
63+
const amount = form.amount.value as string;
64+
65+
if (!useMax && !amount) {
66+
setStatus({
67+
kind: KIND.info,
68+
message: 'Please enter an amount or enable Max',
69+
});
70+
return;
71+
}
72+
73+
const result = await withdraw({
74+
reserve: supply.reserve.id,
75+
amount: {
76+
erc20: useMax
77+
? { max: true }
78+
: {
79+
exact: bigDecimal(amount),
80+
},
81+
},
82+
sender: evmAddress(walletClient.account!.address),
83+
});
84+
85+
if (result.isErr()) {
86+
switch (result.error.name) {
87+
case 'ValidationError':
88+
setStatus({
89+
kind: KIND.warning,
90+
message: 'Insufficient supplied balance in this reserve',
91+
});
92+
return;
93+
case 'CancelError':
94+
setStatus({ kind: KIND.info, message: 'Transaction cancelled' });
95+
return;
96+
default:
97+
setStatus({ kind: KIND.negative, message: result.error.message });
98+
return;
99+
}
100+
}
101+
102+
setStatus({ kind: KIND.info, message: 'Withdraw successful!' });
103+
};
104+
105+
const currentWithdrawable =
106+
supply.withdrawable.amount.value.toDisplayString(2);
107+
108+
return (
109+
<Block as='form' onSubmit={submit} marginTop='scale600'>
110+
<FormControl
111+
label='Amount'
112+
caption={
113+
useMax
114+
? 'Withdrawing maximum available balance'
115+
: 'Human-friendly amount (e.g. 1.23, 4.56, 7.89)'
116+
}
117+
>
118+
<Input
119+
name='amount'
120+
type='number'
121+
step={0.000000000000000001}
122+
disabled={loading || useMax}
123+
placeholder={currentWithdrawable}
124+
/>
125+
</FormControl>
126+
127+
<FormControl>
128+
<Checkbox
129+
checked={useMax}
130+
onChange={(e) => setUseMax(e.target.checked)}
131+
disabled={loading}
132+
>
133+
Withdraw maximum available
134+
</Checkbox>
135+
</FormControl>
136+
137+
<Button type='submit' disabled={loading} isLoading={loading}>
138+
Withdraw
139+
</Button>
140+
141+
{status && (
142+
<Notification
143+
kind={status.kind}
144+
overrides={{ Body: { style: { width: 'auto' } } }}
145+
>
146+
{status.message}
147+
</Notification>
148+
)}
149+
</Block>
150+
);
151+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import 'viem/window';
2+
3+
import type { Hub, Spoke, UserSupplyItem } from '@aave/react';
4+
import type { Story } from '@ladle/react';
5+
import { HeadingLarge } from 'baseui/typography';
6+
import { useState } from 'react';
7+
import { HubSelector } from './components/HubSelector';
8+
import { SingleUserPosition } from './components/positions';
9+
import { SpokeSelector } from './components/SpokeSelector';
10+
import { SupplySelector } from './components/SupplySelector';
11+
import { WithdrawForm } from './components/WithdrawForm';
12+
import * as config from './config';
13+
import { user, walletClient } from './wallet';
14+
15+
export const WithdrawERC20: Story = () => {
16+
const [hub, setHub] = useState<Hub | null>(null);
17+
const [spoke, setSpoke] = useState<Spoke | null>(null);
18+
const [supply, setSupply] = useState<UserSupplyItem | null>(null);
19+
20+
const handleHubSelect = (hub: Hub | null) => {
21+
setHub(hub);
22+
setSpoke(null);
23+
setSupply(null);
24+
};
25+
26+
const handleSpokeSelect = (spoke: Spoke | null) => {
27+
setSpoke(spoke);
28+
setSupply(null);
29+
};
30+
31+
return (
32+
<>
33+
<HeadingLarge>Withdraw ERC-20 Tokens</HeadingLarge>
34+
35+
<HubSelector
36+
chainId={config.chainId}
37+
onChange={handleHubSelect}
38+
selected={hub}
39+
/>
40+
41+
<SpokeSelector
42+
hubId={hub?.id}
43+
onChange={handleSpokeSelect}
44+
selected={spoke}
45+
/>
46+
47+
{spoke && <SingleUserPosition spokeId={spoke.id} user={user} />}
48+
49+
<SupplySelector
50+
spokeId={spoke?.id}
51+
user={user}
52+
onChange={setSupply}
53+
selected={supply}
54+
/>
55+
56+
{supply && <WithdrawForm supply={supply} walletClient={walletClient} />}
57+
</>
58+
);
59+
};
60+
61+
WithdrawERC20.storyName = 'ERC-20 Tokens';
62+

β€Žexamples/withdraw/README.mdβ€Ž

Lines changed: 0 additions & 5 deletions
This file was deleted.

β€Žexamples/withdraw/index.htmlβ€Ž

Lines changed: 0 additions & 14 deletions
This file was deleted.

β€Žexamples/withdraw/package.jsonβ€Ž

Lines changed: 0 additions & 25 deletions
This file was deleted.

β€Žexamples/withdraw/public/aave.svgβ€Ž

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
Β (0)