Skip to content

Commit

Permalink
feat: add eligibility for transition in delegations and update UI acc… (
Browse files Browse the repository at this point in the history
#406)

* feat: add eligibility for transition in delegations and update UI accordingly

* resolve comments

* resolve test fail

* remove slashed state
  • Loading branch information
jeremy-babylonlabs authored Nov 29, 2024
1 parent 41fca44 commit b868f52
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 39 deletions.
6 changes: 3 additions & 3 deletions src/app/api/getDelegations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface DelegationAPI {
unbonding_tx?: UnbondingTxAPI;
is_overflow: boolean;
transitioned: boolean;
is_eligible_for_transition: boolean;
}

interface StakingTxAPI {
Expand Down Expand Up @@ -53,9 +54,8 @@ export const getDelegations = async (

const params = {
pagination_key: encode(key),
// "pagination_reverse": reverse,
// "pagination_limit": limit,
staker_btc_pk: encode(publicKeyNoCoord),
state: ["active", "unbonded"],
};

const response = await apiWrapper(
Expand Down Expand Up @@ -88,7 +88,7 @@ export const getDelegations = async (
outputIndex: apiDelegation.unbonding_tx.output_index,
}
: undefined,
transitioned: apiDelegation.transitioned,
isEligibleForTransition: apiDelegation.is_eligible_for_transition,
}),
);

Expand Down
64 changes: 42 additions & 22 deletions src/app/components/Delegations/Delegation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,22 +82,34 @@ export const Delegation: React.FC<DelegationProps> = ({
};

const generateActionButton = () => {
// This function generates the transition or withdraw button
// based on the state of the delegation
// It also disables the button if the delegation
// is in an intermediate state (local storage)
if (state === DelegationState.ACTIVE) {
if (
state === DelegationState.ACTIVE ||
delegation.isEligibleForTransition
) {
return (
<div className="flex justify-end lg:justify-start">
<div
className="flex justify-end lg:justify-start"
data-tooltip-id="tooltip-transition"
data-tooltip-content={
state === DelegationState.ACTIVE &&
!delegation.isEligibleForTransition
? "Staking transition is not available yet, come back later"
: ""
}
>
<button
className="btn btn-outline btn-xs inline-flex text-sm font-normal text-primary-dark"
onClick={onTransition}
disabled={
intermediateState === DelegationState.INTERMEDIATE_TRANSITIONING
intermediateState ===
DelegationState.INTERMEDIATE_TRANSITIONING ||
(state === DelegationState.ACTIVE &&
!delegation.isEligibleForTransition)
}
>
Transition
</button>
<Tooltip id="tooltip-transition" className="tooltip-wrap" />
</div>
);
} else if (state === DelegationState.UNBONDED) {
Expand Down Expand Up @@ -143,6 +155,10 @@ export const Delegation: React.FC<DelegationProps> = ({

const { coinName, mempoolApiUrl } = getNetworkConfig();

const renderActionRequired = () => {
return <p className="text-error-main text-sm">Action Required</p>;
};

return (
<div
className={`relative rounded bg-secondary-contrast odd:bg-[#F9F9F9] p-4 text-base text-primary-dark`}
Expand Down Expand Up @@ -180,21 +196,25 @@ export const Delegation: React.FC<DelegationProps> = ({
add its size 12px and gap 4px, 16/2 = 8px
*/}
<div className="relative flex justify-start lg:justify-start order-4">
<div className="flex items-center justify-start gap-1">
<p>{renderState()}</p>
<span
className="cursor-pointer text-xs"
data-tooltip-id={`tooltip-${stakingTxHashHex}`}
data-tooltip-content={renderStateTooltip()}
data-tooltip-place="top"
>
<AiOutlineInfoCircle />
</span>
<Tooltip
id={`tooltip-${stakingTxHashHex}`}
className="tooltip-wrap"
/>
</div>
{delegation.isEligibleForTransition ? (
renderActionRequired()
) : (
<div className="flex items-center justify-start gap-1">
<p>{renderState()}</p>
<span
className="cursor-pointer text-xs"
data-tooltip-id={`tooltip-${stakingTxHashHex}`}
data-tooltip-content={renderStateTooltip()}
data-tooltip-place="top"
>
<AiOutlineInfoCircle />
</span>
<Tooltip
id={`tooltip-${stakingTxHashHex}`}
className="tooltip-wrap"
/>
</div>
)}
</div>
<DelegationPoints
stakingTxHashHex={stakingTxHashHex}
Expand Down
17 changes: 10 additions & 7 deletions src/app/components/Delegations/Delegations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,16 @@ export const Delegations = () => {
isWalletConnected={connected}
address={address}
>
<DelegationsContent
delegationsAPI={delegationsAPI.delegations}
address={address}
btcWalletNetwork={network}
publicKeyNoCoord={publicKeyNoCoord}
isWalletConnected={connected}
/>
{/* If there are no delegations, don't render the content */}
{delegationsAPI.delegations.length > 0 && (
<DelegationsContent
delegationsAPI={delegationsAPI.delegations}
address={address}
btcWalletNetwork={network}
publicKeyNoCoord={publicKeyNoCoord}
isWalletConnected={connected}
/>
)}
</DelegationsPointsProvider>
);
};
Expand Down
8 changes: 7 additions & 1 deletion src/app/components/Staking/Staking.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export const Staking = () => {
const { createDelegationEoi, estimateStakingFee } = useTransactionService();
const { networkInfo } = useAppState();
const latestParam = networkInfo?.params.bbnStakingParams?.latestParam;
const stakingStatus = networkInfo?.stakingStatus;

const [pendingVerificationOpen, setPendingVerificationOpen] = useState(false);
const [stakingTxHashHex, setStakingTxHashHex] = useState<
Expand Down Expand Up @@ -390,7 +391,12 @@ export const Staking = () => {
const renderStakingForm = () => {
// States of the staking form:
// Health check failed
if (!isApiNormal || isGeoBlocked || hasError) {
if (
!isApiNormal ||
isGeoBlocked ||
hasError ||
!stakingStatus?.isStakingOpen
) {
return (
<Message
title="Staking is not available"
Expand Down
2 changes: 1 addition & 1 deletion src/app/types/delegations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export interface Delegation {
stakingTx: StakingTx;
unbondingTx: UnbondingTx | undefined;
isOverflow: boolean;
transitioned: boolean;
isEligibleForTransition: boolean;
}

export interface StakingTx {
Expand Down
2 changes: 1 addition & 1 deletion src/utils/getState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const getState = (state: string) => {
// Create state tooltips for the additional information
export const getStateTooltip = (state: string) => {
switch (state) {
case DelegationState.ACTIVE:
case DelegationState.ACTIVE: // active state is shwon
return "Stake is active";
case DelegationState.UNBONDED:
return "Stake has been unbonded";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ export const toLocalStorageIntermediateDelegation = (
timelock,
},
isOverflow: false,
isEligibleForTransition: false,
unbondingTx: undefined,
transitioned: false,
});
2 changes: 1 addition & 1 deletion tests/helper/generateMockDelegation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function generateMockDelegation(
timelock: 3600,
},
isOverflow: false,
isEligibleForTransition: false,
unbondingTx: undefined,
transitioned: false,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ describe("utils/local_storage/toLocalStorageIntermediateDelegation", () => {
},
isOverflow: false,
unbondingTx: undefined,
transitioned: false,
isEligibleForTransition: false,
});

// Validate startTimestamp is a valid ISO date string
Expand Down Expand Up @@ -79,7 +79,7 @@ describe("utils/local_storage/toLocalStorageIntermediateDelegation", () => {
},
isOverflow: false,
unbondingTx: undefined,
transitioned: false,
isEligibleForTransition: false,
});

// Validate startTimestamp is a valid ISO date string
Expand Down

0 comments on commit b868f52

Please sign in to comment.