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

Wallet validator unstaking1 #7403

Merged
merged 16 commits into from
Jan 17, 2023
6 changes: 5 additions & 1 deletion apps/explorer/src/utils/getName.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { Base64DataBuffer } from '@mysten/sui.js';

// TODO : Import from SDK
import { VALDIATOR_NAME } from '~/pages/validator/ValidatorDataTypes';

const textDecoder = new TextDecoder();

export function getName(rawName: string | number[]) {
let name: string;

if (Array.isArray(rawName)) {
name = String.fromCharCode(...rawName);
} else {
name = decodeURIComponent(atob(rawName));
name = textDecoder.decode(new Base64DataBuffer(rawName).getData());
Jibz1 marked this conversation as resolved.
Show resolved Hide resolved
if (!VALDIATOR_NAME.test(name)) {
name = rawName;
}
Expand Down
1 change: 0 additions & 1 deletion apps/wallet/src/shared/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ export function usePageView() {
trackPageview({
url: location.pathname,
});

// Send a network event to Plausible with the page and url params
trackEvent('PageByNetwork', {
props: {
Expand Down
8 changes: 7 additions & 1 deletion apps/wallet/src/ui/app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { useFeature } from '@growthbook/growthbook-react';
import { useEffect } from 'react';
import { Navigate, Route, Routes, useLocation } from 'react-router-dom';

Expand Down Expand Up @@ -30,6 +31,7 @@ import SelectPage from '_pages/initialize/select';
import SiteConnectPage from '_pages/site-connect';
import WelcomePage from '_pages/welcome';
import { setNavVisibility } from '_redux/slices/app';
import { FEATURES } from '_src/shared/experimentation/features';

const HIDDEN_MENU_PATHS = [
'/nft-details',
Expand Down Expand Up @@ -57,6 +59,8 @@ const App = () => {
dispatch(setNavVisibility(menuVisible));
}, [location, dispatch]);

const stakingEnabled = useFeature(FEATURES.STAKING_ENABLED).on;

return (
<Routes>
<Route path="/*" element={<HomePage />}>
Expand All @@ -71,7 +75,9 @@ const App = () => {
<Route path="transactions" element={<TransactionsPage />} />
<Route path="send" element={<TransferCoinPage />} />
<Route path="send/select" element={<CoinsSelectorPage />} />
<Route path="stake/*" element={<Staking />} />
{stakingEnabled ? (
<Route path="stake/*" element={<Staking />} />
) : null}
<Route
path="tx/:txDigest"
element={<TransactionDetailsPage />}
Expand Down
22 changes: 22 additions & 0 deletions apps/wallet/src/ui/app/redux/slices/sui-objects/Coin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,28 @@ export class Coin {
return await signer.executeMoveCall(txn);
}

public static async unStakeCoin(
signer: SignerWithProvider,
delegation: ObjectId,
stakedSuiId: ObjectId,
principalWithdrawAmount: string
): Promise<SuiExecuteTransactionResponse> {
const txn = {
packageObjectId: '0x2',
module: 'sui_system',
function: 'request_withdraw_delegation',
typeArguments: [],
arguments: [
SUI_SYSTEM_STATE_OBJECT_ID,
delegation,
stakedSuiId,
principalWithdrawAmount,
],
gasBudget: DEFAULT_GAS_BUDGET_FOR_STAKE,
};
return signer.executeMoveCall(txn);
}

private static async requestSuiCoinWithExactAmount(
signer: SignerWithProvider,
coins: SuiMoveObject[],
Expand Down
53 changes: 1 addition & 52 deletions apps/wallet/src/ui/app/redux/slices/transactions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@ import {
} from '@reduxjs/toolkit';

import { accountCoinsSelector } from '_redux/slices/account';
import {
fetchAllOwnedAndRequiredObjects,
suiObjectsAdapterSelectors,
} from '_redux/slices/sui-objects';
import { Coin } from '_redux/slices/sui-objects/Coin';
import { fetchAllOwnedAndRequiredObjects } from '_redux/slices/sui-objects';

import type {
SuiAddress,
Expand Down Expand Up @@ -62,48 +58,6 @@ export const sendTokens = createAsyncThunk<
}
);

type StakeTokensTXArgs = {
tokenTypeArg: string;
amount: bigint;
validatorAddress: SuiAddress;
};

export const stakeTokens = createAsyncThunk<
TransactionResult,
StakeTokensTXArgs,
AppThunkConfig
>(
'sui-objects/stake',
async (
{ tokenTypeArg, amount, validatorAddress },
{ getState, extra: { api, keypairVault, background }, dispatch }
) => {
const state = getState();
const coinType = Coin.getCoinTypeFromArg(tokenTypeArg);

const coins: SuiMoveObject[] = suiObjectsAdapterSelectors
.selectAll(state)
.filter(
(anObj) =>
anObj.data.dataType === 'moveObject' &&
anObj.data.type === coinType
)
.map(({ data }) => data as SuiMoveObject);

const response = await Coin.stakeCoin(
api.getSignerInstance(
keypairVault.getKeypair().getPublicKey().toSuiAddress(),
background
),
coins,
amount,
validatorAddress
);
dispatch(fetchAllOwnedAndRequiredObjects());
return response;
}
);

const txAdapter = createEntityAdapter<TransactionResult>({
selectId: (tx) => getTransactionDigest(tx),
});
Expand All @@ -122,11 +76,6 @@ const slice = createSlice({
// @ts-ignore: This causes a compiler error, but it will be removed when we migrate off of Redux.
return txAdapter.setOne(state, payload);
});
builder.addCase(stakeTokens.fulfilled, (state, { payload }) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: This causes a compiler error, but it will be removed when we migrate off of Redux.
return txAdapter.setOne(state, payload);
});
},
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ export function DelegationDetailCard({
return calculateAPY(validatorData, +validatorsData.epoch);
}, [validatorData, validatorsData]);

const delegationId = useMemo(() => {
if (!delegationData || delegationData.delegation_status === 'Pending')
return null;
return delegationData.delegation_status.Active.id.id;
}, [delegationData]);

const stakeByValidatorAddress = `/stake/new?${new URLSearchParams({
address: validatorAddress,
staked: stakedId,
Expand Down Expand Up @@ -223,7 +229,7 @@ export function DelegationDetailCard({
/>
Stake SUI
</Button>
{Boolean(totalStake) && (
{Boolean(totalStake) && delegationId && (
<Button
size="large"
mode="outline"
Expand Down
2 changes: 1 addition & 1 deletion apps/wallet/src/ui/app/staking/home/DelegationCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ interface DelegationCardProps {
stakedId: string;
}

const STATE_TO_COPY = {
export const STATE_TO_COPY = {
[DelegationState.WARM_UP]: 'Starts Earning',
[DelegationState.EARNING]: 'Staking Reward',
[DelegationState.COOL_DOWN]: 'In Cool-down',
Expand Down
9 changes: 1 addition & 8 deletions apps/wallet/src/ui/app/staking/home/index.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,17 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { useFeature } from '@growthbook/growthbook-react';
import { Route, Routes } from 'react-router-dom';

import { DelegationDetail } from '../delegation-detail';
import StakePage from '../stake';
import { Validators } from '../validators';
import { FEATURES } from '_src/shared/experimentation/features';

export function Staking() {
const stakingEnabled = useFeature(FEATURES.STAKING_ENABLED).on;

return (
<Routes>
<Route path="/*" element={<Validators />} />
<Route path="/delegation-detail" element={<DelegationDetail />} />
{stakingEnabled ? (
<Route path="/new" element={<StakePage />} />
) : null}
<Route path="/new" element={<StakePage />} />
</Routes>
);
}
31 changes: 28 additions & 3 deletions apps/wallet/src/ui/app/staking/stake/StakeForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@

import { SUI_TYPE_ARG } from '@mysten/sui.js';
import { ErrorMessage, Field, Form, useFormikContext } from 'formik';
import { useEffect, useRef, memo, useCallback } from 'react';
import { useEffect, useRef, memo, useCallback, useMemo } from 'react';

import { Content } from '_app/shared/bottom-menu-layout';
import { Card } from '_app/shared/card';
import { Text } from '_app/shared/text';
import Alert from '_components/alert';
import NumberInput from '_components/number-input';
import { useFormatCoin } from '_hooks';
import { useCoinDecimals, useFormatCoin } from '_hooks';
import { DEFAULT_GAS_BUDGET_FOR_STAKE } from '_redux/slices/sui-objects/Coin';

import type { FormValues } from './StakingCard';
Expand Down Expand Up @@ -56,12 +56,18 @@ function StakeForm({

const maxToken = maxTokenFormated[0];
const queryResult = maxTokenFormated[2];
const [coinDecimals] = useCoinDecimals(coinType);

const setMaxToken = useCallback(() => {
if (!maxToken) return;
setFieldValue('amount', maxToken);
}, [maxToken, setFieldValue]);

const calculateRemaining = useMemo(() => {
if (!amount || !maxToken) return 0;
return (+maxToken - +amount).toFixed(coinDecimals);
}, [amount, maxToken, coinDecimals]);

return (
<Form
className="flex flex-1 flex-col flex-nowrap"
Expand Down Expand Up @@ -109,7 +115,26 @@ function StakeForm({
</Text>
</div>
}
></Card>
>
{+amount > 0 && (
<div className="py-px flex justify-between w-full">
<Text
variant="body"
weight="medium"
color="steel-darker"
>
Stake Remaining
</Text>
<Text
variant="body"
weight="medium"
color="steel-darker"
>
{calculateRemaining} {symbol}
</Text>
</div>
)}
</Card>
<ErrorMessage
className={st.error}
name="amount"
Expand Down
Loading