Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Withdraw #545

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 153 additions & 8 deletions projects/ui/src/components/Silo/Actions/Withdraw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@ import {
TokenValue,
BeanstalkSDK,
FarmToMode,
// FarmFromMode,
// StepGenerator,
} from '@beanstalk/sdk';
import { SEEDS, STALK } from '~/constants/tokens';
import {
TxnPreview,
TokenInputField,
TokenAdornment,
TxnSeparator,
SmartSubmitButton,
FormStateNew,
FormTxnsFormState,
TokenInputField,
} from '~/components/Common/Form';
import useSeason from '~/hooks/beanstalk/useSeason';
import { displayFullBN, tokenValueToBN } from '~/util';
Expand All @@ -42,20 +44,35 @@ import FormTxnProvider from '~/components/Common/Form/FormTxnProvider';
import useFormTxnContext from '~/hooks/sdk/useFormTxnContext';
import { FormTxn, PlantAndDoX, WithdrawFarmStep } from '~/lib/Txn';
import FarmModeField from '~/components/Common/Form/FarmModeField';
// import useToggle from '~/hooks/display/useToggle';
// import PillRow from '~/components/Common/Form/PillRow';
// import TokenIcon from '~/components/Common/TokenIcon';
// import TokenSelectDialogNew, {
// TokenSelectMode,
// } from '~/components/Common/Form/TokenSelectDialogNew';
// import { QuoteHandlerWithParams } from '~/hooks/ledger/useQuoteWithParams';

// -----------------------------------------------------------------------

/// tokenValueToBN is too long
/// remove me when we migrate everything to TokenValue & DecimalBigNumber
const toBN = tokenValueToBN;

type ClaimQuoteHandlerParams = {
toMode?: FarmToMode;
};

type WithdrawFormValues = FormStateNew &
FormTxnsFormState & {
settings: {
destination: FarmToMode;
destination: FarmToMode | undefined;
};
tokenOut: ERC20Token | undefined;
};

// Type 'Element | undefined' is not assignable to type 'ReactElement<any, any> | null'.
// Type 'undefined' is not assignable to type 'ReactElement<any, any> | null'.
// @ts-ignore
const WithdrawForm: FC<
FormikProps<WithdrawFormValues> & {
token: Token;
Expand All @@ -76,6 +93,7 @@ const WithdrawForm: FC<
season,
sdk,
plantAndDoX,
setFieldValue,
}) => {
const { BEAN } = sdk.tokens;

Expand Down Expand Up @@ -123,18 +141,117 @@ const WithdrawForm: FC<

/// derived
const depositedBalance = siloBalance?.amount;
const tokenOut = values.tokenOut || (whitelistedToken as ERC20Token);

const isReady = withdrawResult && !withdrawResult.amount.lt(0);
const isReady =
withdrawResult &&
!withdrawResult.amount.lt(0) &&
values.settings.destination !== undefined;
// && (whitelistedToken.isLP ? values.tokenOut !== undefined : true);

const disabledActions = useMemo(
() => (whitelistedToken.isUnripe ? [FormTxn.ENROOT] : undefined),
[whitelistedToken.isUnripe]
);

//
// const [isTokenSelectVisible, showTokenSelect, hideTokenSelect] = useToggle();
// const pool = useMemo(
// () => sdk.pools.getPoolByLPToken(whitelistedToken),
// [sdk.pools, whitelistedToken]
// );
// const claimableTokens = useMemo(
// () => [
// whitelistedToken,
// ...((whitelistedToken.isLP && pool?.tokens) || []),
// ],
// [pool, whitelistedToken]
// );

//
// const handleSelectTokens = useCallback(
// (_tokens: Set<Token>) => {
// const _token = Array.from(_tokens)[0];
// setFieldValue('tokenOut', _token);
// },
// [setFieldValue]
// );

// const handleQuote = useCallback<
// QuoteHandlerWithParams<QuoteHandlerWithParams>
// >(
// async (_tokenIn, _amountIn, _tokenOut, { toMode }) => {
// if (_tokenIn === _tokenOut) return { amountOut: _amountIn };
// const amountIn = _tokenIn.amount(_amountIn.toString());

// const { curve } = sdk.contracts;

// // Require pooldata to be loaded first.
// if (!pool || !_tokenIn.isLP) return null;

// const work = sdk.farm.create();
// work.add(
// new sdk.farm.actions.RemoveLiquidityOneToken(
// pool.address,
// curve.registries.metaFactory.address,
// _tokenOut.address,
// FarmFromMode.INTERNAL,
// toMode
// )
// );
// const estimate = await work.estimate(amountIn);

// return {
// amountOut: tokenValueToBN(_tokenOut.fromBlockchain(estimate)),
// steps: work.generators as StepGenerator[],
// };
// },
// [sdk.contracts, sdk.farm, pool]
// );

// const claimableBalance = values.tokens[0].amount || ZERO_BN;

// This should be memoized to prevent an infinite reset loop
// const quoteHandlerParams = useMemo(
// () => ({
// quoteSettings: {
// ignoreSameToken: false,
// onReset: () => ({ amountOut: claimableBalance }),
// },
// params: {
// toMode: values.settings.destination || FarmToMode.INTERNAL,
// },
// }),
// [claimableBalance, values.settings.destination]
// );

if (!tokenOut) return;

return (
<Form autoComplete="off" noValidate>
{/* Form Content */}
<Stack gap={1}>
{/* <TokenQuoteProviderWithParams<ClaimQuoteHandlerParams>
name="tokens.0.amount"
token={whitelistedToken}
tokenOut={tokenOut}
state={values.tokens[0]}
//
disabled={!depositedBalance || depositedBalance.eq(0)}
balance={toBN(depositedBalance || TokenValue.ZERO) || ZERO_BN}
balanceLabel="Deposited Balance"
// -----
// FIXME:
// "disableTokenSelect" applies the disabled prop to
// the TokenSelect button. However if we don't pass
// a handler to the button then it's effectively disabled
// but shows with stronger-colored text. param names are
// a bit confusing.
// disableTokenSelect={true}
// handleQuote={handleQuote}
displayQuote={false}
{...quoteHandlerParams}
/> */}
{/* Input Field */}
<TokenInputField
name="tokens.0.amount"
Expand All @@ -145,7 +262,31 @@ const WithdrawForm: FC<
InputProps={InputProps}
/>
{/** Setting: Destination */}
<FarmModeField name="destination" />
<FarmModeField name="settings.destination" />
{/* Setting: Claim LP */}
{/* <>
{whitelistedToken.isLP ? (
<PillRow
isOpen={isTokenSelectVisible}
label="Claim LP as"
onClick={showTokenSelect}
>
{values.tokenOut && <TokenIcon token={values.tokenOut} />}
<Typography variant="body1">
{values.tokenOut ? values.tokenOut.symbol : <>Select Output</>}
</Typography>
</PillRow>
) : null}
<TokenSelectDialogNew
open={isTokenSelectVisible}
handleClose={hideTokenSelect}
handleSubmit={handleSelectTokens}
selected={values.tokenOut ? [values.tokenOut] : []}
balances={undefined} // hide balances from right side of selector
tokenList={claimableTokens as Token[]}
mode={TokenSelectMode.SINGLE}
/>
</> */}
<AddPlantTxnToggle />
{isReady ? (
<Stack direction="column" gap={1}>
Expand Down Expand Up @@ -270,8 +411,9 @@ const WithdrawPropProvider: FC<{
implied: [FormTxn.MOW],
},
settings: {
destination: FarmToMode.INTERNAL,
destination: undefined,
},
tokenOut: undefined,
}),
[sdk.tokens.BEAN, token]
);
Expand Down Expand Up @@ -308,9 +450,12 @@ const WithdrawPropProvider: FC<{
throw new Error("Missing 'Destination' setting.");
}

const withdrawTxn = new WithdrawFarmStep(sdk, token, [
...farmerBalances.deposits,
]);
const withdrawTxn = new WithdrawFarmStep(
sdk,
token,
[...farmerBalances.deposits],
destination
);

withdrawTxn.build(
baseAmount,
Expand Down
14 changes: 14 additions & 0 deletions projects/ui/src/hooks/beanstalk/useStemTipForToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Token } from '@beanstalk/sdk';
import { ethers } from 'ethers';
import { useMemo } from 'react';
import useSilo from '~/hooks/beanstalk/useSilo';

export default function useStemTipForToken(
token: Token
): ethers.BigNumber | null {
const silo = useSilo();
return useMemo(
() => silo.balances[token.address].stemTip ?? null,
[silo, token.address]
);
}
15 changes: 12 additions & 3 deletions projects/ui/src/hooks/farmer/useFarmerDepositCrateFromPlant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import useSeason from '../beanstalk/useSeason';
import useFarmerSilo from './useFarmerSilo';
import { LegacyDepositCrate } from '~/state/farmer/silo';
import { tokenValueToBN } from '~/util';
import { ZERO_BN } from '~/constants';
import useStemTipForToken from '~/hooks/beanstalk/useStemTipForToken';

/// Returns the deposit crate which will be created via calling 'plant'
export default function useFarmerDepositCrateFromPlant() {
Expand All @@ -13,6 +15,7 @@ export default function useFarmerDepositCrateFromPlant() {

/// Beanstalk
const season = useSeason();
const stemTip = useStemTipForToken(sdk.tokens.BEAN);

/// Farmer
const farmerSilo = useFarmerSilo();
Expand All @@ -27,12 +30,18 @@ export default function useFarmerDepositCrateFromPlant() {
// no stalk is grown yet as it is a new deposit from the current season
const grownStalk = STALK.amount(0);

if (!stemTip) throw new Error('No stem tip loaded for BEAN');

// asBN => as DepositCrate from UI;
const asBN: LegacyDepositCrate = {
season,
stem: stemTip,
amount: earned,
bdv: earned,
stalk: tokenValueToBN(stalk),
stalk: {
total: tokenValueToBN(stalk),
base: tokenValueToBN(stalk),
grown: ZERO_BN,
},
seeds: tokenValueToBN(seeds),
};

Expand All @@ -51,7 +60,7 @@ export default function useFarmerDepositCrateFromPlant() {
asBN,
asTV,
};
}, [farmerSilo.beans.earned, sdk.tokens, season]);
}, [farmerSilo.beans.earned, sdk.tokens, season, stemTip]);

return {
crate,
Expand Down
17 changes: 13 additions & 4 deletions projects/ui/src/lib/Txn/FarmSteps/silo/WithdrawFarmStep.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { BeanstalkSDK, Deposit, Token, TokenValue } from '@beanstalk/sdk';
import {
BeanstalkSDK,
Deposit,
FarmToMode,
Token,
TokenValue,
} from '@beanstalk/sdk';
import { FarmStep, PlantAndDoX } from '~/lib/Txn/Interface';

type WithdrawResult = ReturnType<typeof WithdrawFarmStep['calculateWithdraw']>;
Expand All @@ -9,7 +15,8 @@ export class WithdrawFarmStep extends FarmStep {
constructor(
_sdk: BeanstalkSDK,
private _token: Token,
private _crates: Deposit[]
private _crates: Deposit[],
private _toMode: FarmToMode
) {
super(_sdk);
this._token = _token;
Expand Down Expand Up @@ -55,15 +62,17 @@ export class WithdrawFarmStep extends FarmStep {
input: new this._sdk.farm.actions.WithdrawDeposit(
this._token.address,
stems[0],
amounts[0]
amounts[0],
this._toMode
),
});
} else {
this.pushInput({
input: new this._sdk.farm.actions.WithdrawDeposits(
this._token.address,
stems,
amounts
amounts,
this._toMode
),
});
}
Expand Down
14 changes: 8 additions & 6 deletions projects/ui/src/lib/Txn/Interface/PlantAndDoX.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default class PlantAndDoX {
private _season: number
) {
this._earnedBeans = _earnedBeans;
this._season = _season;
this._season = 0; // FIXME
}

/// Returns whether 'plant' can be called
Expand Down Expand Up @@ -66,16 +66,14 @@ export default class PlantAndDoX {

// asTV => as DepositCrate<TokenValue> from SDK;
const crate: TokenSiloBalance['deposits'][number] = {
season: ethers.BigNumber.from(season),
stem: ethers.BigNumber.from(season), // FIXME
amount: earnedBeans,
bdv: earnedBeans,
stalk: {
total: stalk.add(grownStalk),
base: stalk,
grown: grownStalk,
},
baseStalk: stalk,
grownStalk,
seeds,
};

Expand All @@ -95,10 +93,14 @@ export default class PlantAndDoX {
const seeds = BEAN.getSeeds(earnedTV);

const crate: LegacyDepositCrate = {
season,
stem: ethers.BigNumber.from(season), // FIXME
amount: earnedBeans,
bdv: earnedBeans,
stalk: tokenValueToBN(stalk),
stalk: {
total: tokenValueToBN(stalk),
base: tokenValueToBN(stalk),
grown: new BigNumber(0),
},
seeds: tokenValueToBN(seeds),
};

Expand Down
Loading