Skip to content

Commit

Permalink
feat: add four year vesting (#2248)
Browse files Browse the repository at this point in the history
  • Loading branch information
soulBit authored Jun 9, 2022
1 parent 94b00e7 commit 96955b9
Show file tree
Hide file tree
Showing 13 changed files with 888 additions and 107 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const VestingWithdrawForm: React.FC<VestingWithdrawFormProps> = ({
const { value, loading } = useGetUnlockedVesting(
vesting.staking,
vesting.vestingContract,
vesting.type,
);
const [address, setAddress] = useState(account);

Expand Down
1 change: 1 addition & 0 deletions src/app/components/UserAssets/Vesting/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export type VestGroup =
| 'origin'
| 'team'
| 'reward'
| 'fouryear'
| 'fish'
| 'fishAirdrop';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,13 @@ export function useListOfUserVestings(asset?: Asset) {
address.map(
async (item, index): Promise<Vesting> => {
// 'type' can be 0 or 1, 0 - Team Vesting, 1 - Vesting, fish, fishAirdrop
// 'typeCreation' can be 1, 2, 3 representing Vesting Registry 1, Vesting Registry 2 and Vesting Registry 3
// 'typeCreation' can be 1-4 representing Vesting Registry 1-4
const label = {
'0 1': 'team',
'1 1': 'genesis',
'1 2': 'origin',
'1 3': 'reward',
'1 4': 'fouryear',
'fish vestingRegistryFISH': 'fish',
'fishAirdrop vestingRegistryFISH': 'fishAirdrop',
}[`${type[index]} ${typeCreation[index]}`];
Expand Down
23 changes: 16 additions & 7 deletions src/app/containers/StakePage/components/CurrentVests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,23 @@ import { translations } from 'locales/i18n';
import { StyledTable } from './StyledTable';
import { VestingContract } from './VestingContract';
import { useListOfUserVestings } from '../../../components/UserAssets/Vesting/useListOfUserVestings';
import { VestGroup } from 'app/components/UserAssets/Vesting/types';

interface Props {
onDelegate: (timestamp: number, vestingAddress: string) => void;
interface ICurrentVestsProps {
onDelegate: (
timestamp: number,
vestingAddress: string,
vestingType: VestGroup,
) => void;
paused?: boolean;
frozen?: boolean;
}

export function CurrentVests(props: Props) {
export const CurrentVests: React.FC<ICurrentVestsProps> = ({
onDelegate,
paused,
frozen,
}) => {
const { t } = useTranslation();
const { loading, items } = useListOfUserVestings();

Expand Down Expand Up @@ -69,10 +78,10 @@ export function CurrentVests(props: Props) {
vestingAddress={item.vestingContract}
type={item.type}
onDelegate={timestamp =>
props.onDelegate(timestamp, item.vestingContract)
onDelegate(timestamp, item.vestingContract, item.type)
}
paused={props.paused}
frozen={props.frozen}
paused={paused}
frozen={frozen}
/>
))}
</tbody>
Expand All @@ -81,4 +90,4 @@ export function CurrentVests(props: Props) {
</div>
</>
);
}
};
82 changes: 44 additions & 38 deletions src/app/containers/StakePage/components/VestingContract.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { AbiItem } from 'web3-utils';
import { TxType } from 'store/global/transactions-store/types';
import { TransactionDialog } from 'app/components/TransactionDialog';

interface Props {
interface IVestingContractProps {
vestingAddress: string;
type: VestGroup;
onDelegate: (a: number) => void;
Expand Down Expand Up @@ -64,7 +64,13 @@ const getTokenContractNameByVestingType = (type: VestGroup) => {
}
};

export function VestingContract(props: Props) {
export const VestingContract: React.FC<IVestingContractProps> = ({
vestingAddress,
type,
onDelegate,
paused,
frozen,
}) => {
const { t } = useTranslation();
const { checkMaintenances, States } = useMaintenance();
const {
Expand All @@ -73,8 +79,8 @@ export function VestingContract(props: Props) {
} = checkMaintenances();

const account = useAccount();
const getStakes = useStaking_getStakes(props.vestingAddress);
const lockedAmount = useStaking_balanceOf(props.vestingAddress);
const getStakes = useStaking_getStakes(vestingAddress);
const lockedAmount = useStaking_balanceOf(vestingAddress);
const [stakingPeriodStart, setStakingPeriodStart] = useState('');
const [unlockDate, setUnlockDate] = useState('');
const [vestLoading, setVestLoading] = useState(false);
Expand All @@ -85,10 +91,7 @@ export function VestingContract(props: Props) {
const SOV = AssetsDictionary.get(Asset.SOV);
const CSOV = AssetsDictionary.get(Asset.SOV);
const dollars = useCachedAssetPrice(Asset.SOV, Asset.USDT);
const rbtc = useCachedAssetPrice(
getAssetByVestingType(props.type),
Asset.RBTC,
);
const rbtc = useCachedAssetPrice(getAssetByVestingType(type), Asset.RBTC);
const dollarValue = useMemo(() => {
if (lockedAmount === null) return '';
return bignumber(lockedAmount.value)
Expand All @@ -97,31 +100,29 @@ export function VestingContract(props: Props) {
.toFixed(0);
}, [dollars.value, lockedAmount, SOV.decimals]);

const tokenAddress = getContract(
getTokenContractNameByVestingType(props.type),
).address;
const currency = useStaking_getAccumulatedFees(
props.vestingAddress,
tokenAddress,
const tokenAddress = useMemo(
() => getContract(getTokenContractNameByVestingType(type)).address,
[type],
);
const currency = useStaking_getAccumulatedFees(vestingAddress, tokenAddress);

const rbtcValue = useMemo(() => {
if (currency === null) return '';
return bignumber(currency.value)
.mul(rbtc.value)
.div(10 ** (props.type === 'genesis' ? CSOV.decimals : SOV.decimals))
.div(10 ** (type === 'genesis' ? CSOV.decimals : SOV.decimals))
.toFixed(0);
}, [currency, CSOV.decimals, SOV.decimals, props.type, rbtc.value]);
}, [currency, CSOV.decimals, SOV.decimals, type, rbtc.value]);

useEffect(() => {
async function getVestsList() {
try {
setVestLoading(true);
Promise.all([
vesting_getStartDate(props.vestingAddress).then(
vesting_getStartDate(vestingAddress).then(
res => typeof res === 'string' && setStakingPeriodStart(res),
),
vesting_getEndDate(props.vestingAddress).then(
vesting_getEndDate(vestingAddress).then(
res => typeof res === 'string' && setUnlockDate(res),
),
]).then(_ => setVestLoading(false));
Expand All @@ -132,10 +133,10 @@ export function VestingContract(props: Props) {
}
}
setVestLoading(false);
if (props.vestingAddress !== ethGenesisAddress) {
if (vestingAddress !== ethGenesisAddress) {
getVestsList().catch(console.error);
}
}, [props.vestingAddress, account]);
}, [vestingAddress, account]);

useEffect(() => {
async function getDelegate() {
Expand All @@ -144,15 +145,12 @@ export function VestingContract(props: Props) {
try {
await contractReader
.call('staking', 'delegates', [
props.vestingAddress,
vestingAddress,
getStakes.value['dates'][datesLength - 2],
])
.then(res => {
setDelegateLoading(false);
if (
res.toString().toLowerCase() !==
props.vestingAddress.toLowerCase()
) {
if (res.toString().toLowerCase() !== vestingAddress.toLowerCase()) {
setDelegate(res);
}
return false;
Expand All @@ -162,20 +160,27 @@ export function VestingContract(props: Props) {
setDelegateLoading(false);
}
}
if (unlockDate && !vestLoading && getStakes.value?.dates?.length > 0) {
if (
unlockDate &&
!vestLoading &&
stakingPeriodStart &&
getStakes.value?.dates?.length > 0
) {
getDelegate();
setLocked(Number(unlockDate) > Math.round(new Date().getTime() / 1e3));
}
}, [
props.vestingAddress,
vestingAddress,
vestLoading,
unlockDate,
delegate,
getStakes.value,
stakingPeriodStart,
type,
]);

const { send, ...tx } = useSendToContractAddressTx(
props.vestingAddress.toLowerCase(),
vestingAddress.toLowerCase(),
VestingABI as AbiItem[],
'withdrawTokens',
);
Expand All @@ -198,7 +203,7 @@ export function VestingContract(props: Props) {
<img src={logoSvg} className="tw-ml-3 tw-mr-3" alt="sov" />
</div>
<div className="tw-text-sm tw-font-normal tw-hidden xl:tw-block tw-pl-3">
{t(translations.stake.currentVests.assetType[props.type])}
{t(translations.stake.currentVests.assetType[type])}
</div>
</div>
</td>
Expand Down Expand Up @@ -304,11 +309,11 @@ export function VestingContract(props: Props) {
<button
className={classNames(
'tw-text-primary tw-tracking-normal hover:tw-text-primary hover:tw-underline tw-mr-1 xl:tw-mr-4 tw-p-0 tw-font-normal tw-font-montserrat',
props.paused &&
paused &&
'tw-bg-transparent hover:tw-bg-opacity-0 tw-opacity-50 tw-cursor-not-allowed hover:tw-bg-transparent',
)}
onClick={() => props.onDelegate(Number(unlockDate))}
disabled={props.paused}
onClick={() => onDelegate(Number(unlockDate))}
disabled={paused}
data-action-id="staking-vest-delegateButton"
>
{t(translations.stake.actions.delegate)}
Expand All @@ -335,14 +340,14 @@ export function VestingContract(props: Props) {
type="button"
className={classNames(
'tw-text-primary tw-tracking-normal hover:tw-text-primary hover:tw-underline tw-mr-1 xl:tw-mr-4 tw-p-0 tw-font-normal tw-font-montserrat',
props.frozen &&
frozen &&
'tw-bg-transparent hover:tw-bg-opacity-0 tw-opacity-50 tw-cursor-not-allowed hover:tw-bg-transparent',
)}
onClick={() => setShowWithdraw(true)}
disabled={
!props.vestingAddress ||
props.vestingAddress === ethGenesisAddress ||
props.frozen
!vestingAddress ||
vestingAddress === ethGenesisAddress ||
frozen
}
data-action-id="staking-vest-withdrawButton"
>
Expand All @@ -357,7 +362,8 @@ export function VestingContract(props: Props) {
content={
<>
<WithdrawVesting
vesting={props.vestingAddress}
vesting={vestingAddress}
vestingType={type}
onCloseModal={() => setShowWithdraw(false)}
onWithdraw={handleWithdraw}
/>
Expand All @@ -370,4 +376,4 @@ export function VestingContract(props: Props) {
/>
</>
);
}
};
19 changes: 15 additions & 4 deletions src/app/containers/StakePage/components/WithdrawVesting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,31 @@ import { TxFeeCalculator } from 'app/pages/MarginTradePage/components/TxFeeCalcu
import { discordInvite } from 'utils/classifiers';
import { useMaintenance } from 'app/hooks/useMaintenance';
import { ErrorBadge } from 'app/components/Form/ErrorBadge';
import { VestGroup } from 'app/components/UserAssets/Vesting/types';

interface Props {
interface IWithdrawVestingProps {
vesting: string;
vestingType: VestGroup;
onCloseModal: () => void;
onWithdraw: (receiver: string) => void;
}

export function WithdrawVesting({ vesting, onCloseModal, onWithdraw }: Props) {
export const WithdrawVesting: React.FC<IWithdrawVestingProps> = ({
vesting,
vestingType,
onCloseModal,
onWithdraw,
}) => {
const { t } = useTranslation();
const account = useAccount();
const { checkMaintenance, States } = useMaintenance();
const withdrawVestsLocked = checkMaintenance(States.WITHDRAW_VESTS);
const [address, setAddress] = useState(account);
const { value, loading } = useGetUnlockedVesting('staking', vesting);
const { value, loading } = useGetUnlockedVesting(
'staking',
vesting,
vestingType,
);

const validate = () => {
return !loading && value !== '0' && isAddress(address.toLowerCase());
Expand Down Expand Up @@ -120,4 +131,4 @@ export function WithdrawVesting({ vesting, onCloseModal, onWithdraw }: Props) {
</form>
</>
);
}
};
Loading

0 comments on commit 96955b9

Please sign in to comment.