Skip to content

Commit

Permalink
Merge pull request #4876 from Giveth/fix_recurr_donations
Browse files Browse the repository at this point in the history
Fix recurring donations
  • Loading branch information
kkatusic authored Dec 2, 2024
2 parents 16294c3 + 058091c commit 83737bf
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useState, type FC, useEffect } from 'react';
import { Button, Flex } from '@giveth/ui-design-system';
import { useAccount, useBalance } from 'wagmi';
import { useIntl } from 'react-intl';
import { ethers } from 'ethers';
import { Framework } from '@superfluid-finance/sdk-core';
import { ISuperToken, IToken } from '@/types/superFluid';
import { AddressZero } from '@/lib/constants/constants';
Expand Down Expand Up @@ -125,17 +126,26 @@ export const DepositSuperToken: FC<IDepositSuperTokenProps> = ({

// EThx is not a Wrapper Super Token and should load separately
let superTokenAsset;
let newAmount = amount;
if (superToken.symbol === 'ETHx') {
superTokenAsset = await sf.loadNativeAssetSuperToken(
superToken.id,
);
} else {
superTokenAsset = await sf.loadWrapperSuperToken(superToken.id);
}
if (token && token.decimals === 6) {
const divisor = BigInt(10 ** token.decimals);
const currentAmount = Number(amount) / Number(divisor);
newAmount = ethers.utils
.parseUnits(currentAmount.toString(), 18)
.toBigInt();
}
console.log('token', token);
console.log('supertoken', superToken);
const upgradeOperation = await superTokenAsset.upgrade({
amount: amount.toString(),
amount: newAmount.toString(),
});

const tx = await upgradeOperation.exec(signer);
const res = await tx.wait();
if (!res.status) {
Expand Down Expand Up @@ -165,7 +175,6 @@ export const DepositSuperToken: FC<IDepositSuperTokenProps> = ({
closeModal();
}
};

return (
<Wrapper>
{step === EModifySuperTokenSteps.MODIFY ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { EModifySuperTokenSteps } from './common';
import config from '@/configuration';

interface IModifySuperTokenModalProps extends IModal {
selectedToken: IToken;
selectedToken: ISuperToken | IToken;
tokenStreams: ISuperfluidStream[];
refreshBalance: () => void;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ export const WithDrawSuperToken: FC<IWithDrawSuperTokenProps> = ({
title='Withdraw from this stream balance'
amount={amount}
price={tokenPrice}
token={token!}
token={superToken!}
/>
<RunOutInfo
superTokenBalance={
Expand Down
20 changes: 16 additions & 4 deletions src/components/views/donate/Recurring/RecurringDonationCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,17 +142,26 @@ export const RecurringDonationCard = () => {
if (selectedRecurringToken.token.isSuperToken) {
setAmount(balance.value || 0n);
}
if (selectedRecurringToken.token.decimals === 6) {
setAmount(0n);
setPerMonthAmount(0n);
}
}, [selectedRecurringToken, balance]);

const underlyingToken = selectedRecurringToken?.token.underlyingToken;

// Introduce a scaling factor to handle tokens with different decimals
const scaleFactor =
selectedRecurringToken?.token.decimals === 6 ? 10000n : 1n;

// total means project + giveth
const totalPerSec = perMonthAmount / ONE_MONTH_SECONDS;
const totalPerSec = perMonthAmount / (ONE_MONTH_SECONDS / scaleFactor);
const projectPerMonth =
(perMonthAmount * BigInt(100 - donationToGiveth)) / 100n;
const givethPerMonth = perMonthAmount - projectPerMonth;
const tokenBalance = balance?.value;
const tokenStream = tokenStreams[selectedRecurringToken?.token.id || ''];
const tokenStream =
tokenStreams[selectedRecurringToken?.token.id.toLowerCase() || ''];

const anchorContractAddress = useMemo(
() => findAnchorContractAddress(project.anchorContracts),
Expand All @@ -168,7 +177,8 @@ export const RecurringDonationCard = () => {
0n,
) || 0n;
const totalStreamPerSec = totalPerSec + otherStreamsPerSec;
const totalStreamPerMonth = totalStreamPerSec * ONE_MONTH_SECONDS;
const totalStreamPerMonth =
totalStreamPerSec * (ONE_MONTH_SECONDS / scaleFactor);
const streamRunOutInMonth =
totalStreamPerSec > 0 ? amount / totalStreamPerMonth : 0n;
const isTotalStreamExceed =
Expand Down Expand Up @@ -767,7 +777,9 @@ export const RecurringDonationCard = () => {
{showTopUpModal && selectedRecurringToken && (
<ModifySuperTokenModal
tokenStreams={
tokenStreams[selectedRecurringToken?.token.id || '']
tokenStreams[
selectedRecurringToken?.token.id.toLowerCase() || ''
]
}
setShowModal={setShowTopUpModal}
selectedToken={selectedRecurringToken?.token!}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,9 @@ export const Item: FC<IItemProps> = ({
<Flex gap='4px'>
<B>
{limitFraction(
formatUnits(
amount,
token.underlyingToken?.decimals || 18,
),
formatUnits(amount, token.decimals || 18),
)}
&nbsp;{token.symbol}
&nbsp;{token.underlyingToken?.symbol || token.symbol}
</B>
{subtext}
<UsdValue>
Expand All @@ -47,7 +44,7 @@ export const Item: FC<IItemProps> = ({
.multipliedBy(amount.toString())
.toFixed(0),
),
token.underlyingToken?.decimals || 18,
token.decimals || 18,
),
2,
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Framework, type Operation } from '@superfluid-finance/sdk-core';
import { useAccount } from 'wagmi';
import { useIntl } from 'react-intl';
import { formatUnits } from 'viem';
import { ethers } from 'ethers';
import { Modal } from '@/components/modals/Modal';
import { useModalAnimation } from '@/hooks/useModalAnimation';
import { IModal } from '@/types/common';
Expand Down Expand Up @@ -227,10 +228,32 @@ const RecurringDonationInnerModal: FC<IRecurringDonationInnerModalProps> = ({

const operations: Operation[] = [];

let newAmount = amount;
let newPerMonthAmount = perMonthAmount;

// This is a special case with tokens that have 6 decimals
// We need to convert the amount to 18 decimals for the upgrade operation
// And also for the flow rate calculation
if (selectedRecurringToken.token.decimals === 6) {
const divisor = BigInt(
10 ** selectedRecurringToken.token.decimals,
);
const currentAmount = Number(amount) / Number(divisor);
newAmount = ethers.utils
.parseUnits(currentAmount.toString(), 18)
.toBigInt();

const currentPerMonth =
Number(perMonthAmount) / Number(divisor);
newPerMonthAmount = ethers.utils
.parseUnits(currentPerMonth.toString(), 18)
.toBigInt();
}

// Upgrade the token to super token
if (!isUpdating && !selectedRecurringToken.token.isSuperToken) {
const upgradeOperation = await superToken.upgrade({
amount: amount.toString(),
amount: newAmount.toString(),
});

//Upgrading ETHx is a special case and can't be batched
Expand All @@ -245,8 +268,8 @@ const RecurringDonationInnerModal: FC<IRecurringDonationInnerModalProps> = ({
throw new Error('Project wallet address not found');
}

const _flowRate =
(perMonthAmount * BigInt(100 - donationToGiveth)) /
let _flowRate =
(newPerMonthAmount * BigInt(100 - donationToGiveth)) /
100n /
ONE_MONTH_SECONDS;

Expand Down Expand Up @@ -279,7 +302,7 @@ const RecurringDonationInnerModal: FC<IRecurringDonationInnerModalProps> = ({
}

const _newFlowRate =
(perMonthAmount * BigInt(donationToGiveth)) /
(newPerMonthAmount * BigInt(donationToGiveth)) /
100n /
ONE_MONTH_SECONDS;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const SelectTokenInnerModal: FC<ISelectTokenModalProps> = ({
setShowModal,
}) => {
const [tokens, setTokens] = useState<ISuperToken[]>([]);
const [underlyingTokens, setUnderlyingTokens] = useState<ISuperToken[]>([]);
const [balances, setBalances] = useState<IBalances>({});

const { formatMessage } = useIntl();
Expand Down Expand Up @@ -84,11 +85,12 @@ const SelectTokenInnerModal: FC<ISelectTokenModalProps> = ({
return acc;
}, {} as IBalances);

const filteredTokens = superTokens.filter(
token => !(newBalances[token.symbol] > 0n),
);
const filteredTokens = superTokens.filter(token => {
return !(newBalances[token.underlyingToken.symbol] > 0n);
});

setTokens(filteredTokens);
setUnderlyingTokens(superTokens);

// Update the state with the new balances
setBalances(newBalances);
Expand Down Expand Up @@ -127,12 +129,14 @@ const SelectTokenInnerModal: FC<ISelectTokenModalProps> = ({
</TitleSubheader>
{Object.keys(tokenStreams).map(tokenId => {
const token = superTokens.find(
token => token.id === tokenId,
token =>
token.id.toLowerCase() ===
tokenId.toLowerCase(),
) as IToken;
return token ? (
<StreamInfo
key={tokenId}
stream={tokenStreams[tokenId]}
stream={tokenStreams[tokenId.toLowerCase()]}
balance={balances[token.symbol]}
disable={
!balances[token.symbol] ||
Expand All @@ -150,7 +154,7 @@ const SelectTokenInnerModal: FC<ISelectTokenModalProps> = ({
) : null;
})}
{superTokens.map(token =>
tokenStreams[token.id] ||
tokenStreams[token.id.toLowerCase()] ||
balances[token.symbol] === 0n ? null : (
<TokenInfo
key={token.symbol}
Expand Down Expand Up @@ -185,33 +189,38 @@ const SelectTokenInnerModal: FC<ISelectTokenModalProps> = ({
id: 'label.superfluid_eligible_tokens_description',
})}
</TitleSubheader>
{tokens.length > 0 ? (
tokens.map(token => (
<TokenInfo
key={token.underlyingToken.symbol}
token={token.underlyingToken}
balance={
balances[token.underlyingToken.symbol]
}
disable={
balances[
token.underlyingToken.symbol
] === undefined ||
balances[
token.underlyingToken.symbol
] === 0n
}
onClick={() => {
setSelectedRecurringToken({
token: token.underlyingToken,
balance:
balances[
token.underlyingToken.symbol
],
});
setShowModal(false);
}}
/>
{underlyingTokens.length > 0 ? (
underlyingTokens.map(token => (
<>
<TokenInfo
key={token.underlyingToken?.symbol}
token={token.underlyingToken}
balance={
balances[
token.underlyingToken.symbol
]
}
disable={
balances[
token.underlyingToken.symbol
] === undefined ||
balances[
token.underlyingToken.symbol
] === 0n
}
onClick={() => {
setSelectedRecurringToken({
token: token.underlyingToken,
balance:
balances[
token.underlyingToken
.symbol
],
});
setShowModal(false);
}}
/>
</>
))
) : (
<Caption>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, type FC } from 'react';
import { useState, useMemo, type FC } from 'react';
import styled from 'styled-components';
import { P, brandColors, semanticColors } from '@giveth/ui-design-system';
import { formatUnits } from 'viem';
Expand All @@ -8,7 +8,7 @@ import { TokenIcon } from '@/components/views/donate/TokenIcon/TokenIcon';
import { TableCell } from './ActiveStreamsSection';
import { ISuperfluidStream } from '@/types/superFluid';
import { ONE_MONTH_SECONDS } from '@/lib/constants/constants';
import { limitFraction } from '@/helpers/number';
import { limitFraction, formatDonation } from '@/helpers/number';
import config from '@/configuration';
import { countActiveStreams } from '@/helpers/donate';
import { findTokenByAddress } from '@/helpers/superfluid';
Expand All @@ -19,6 +19,13 @@ interface IStreamRowProps {
}

export const StreamRow: FC<IStreamRowProps> = ({ tokenStream }) => {
const superToken = useMemo(
() =>
config.OPTIMISM_CONFIG.SUPER_FLUID_TOKENS.find(
s => s.id === tokenStream[0].token.id,
),
[tokenStream],
);
const [showModifyModal, setShowModifyModal] = useState(false);
const { address, chain } = useAccount();
const { switchChain } = useSwitchChain();
Expand All @@ -39,7 +46,7 @@ export const StreamRow: FC<IStreamRowProps> = ({ tokenStream }) => {
0n,
);
const monthlyFlowRate = totalFlowRate * ONE_MONTH_SECONDS;
const { symbol, decimals } = tokenStream[0].token;
// const { symbol } = tokenStream[0].token;
const runOutMonth =
monthlyFlowRate > 0 && balance?.value
? balance?.value / monthlyFlowRate
Expand All @@ -57,12 +64,12 @@ export const StreamRow: FC<IStreamRowProps> = ({ tokenStream }) => {
)
: '0'}
</P>
<P>{symbol}</P>
<P>{superToken?.symbol}</P>
</TableCell>
<TableCell>
<P color={semanticColors.jade[500]}>
{limitFraction(
formatUnits(monthlyFlowRate || 0n, decimals),
{formatDonation(
limitFraction(formatUnits(BigInt(monthlyFlowRate), 18)),
)}
&nbsp;
{underlyingSymbol}
Expand Down Expand Up @@ -109,11 +116,11 @@ export const StreamRow: FC<IStreamRowProps> = ({ tokenStream }) => {
Deposit/Withdraw
</ModifyButton>
</TableCell>
{showModifyModal && (
{showModifyModal && superToken && (
<ModifySuperTokenModal
tokenStreams={tokenStream}
setShowModal={setShowModifyModal}
selectedToken={tokenStream[0].token}
selectedToken={superToken}
refreshBalance={refetch}
/>
)}
Expand Down
Loading

0 comments on commit 83737bf

Please sign in to comment.