Skip to content

Commit

Permalink
Map Stake['state'] to relevant staking status (#1950)
Browse files Browse the repository at this point in the history
Adjusts the `status` retrieval of deposit and exit requests to map the "earliest" validator `state`s as the relevant `status`:

- Validate expected `Stake['state']` values.
- Add `StakingState` <> `StakingStatus` mapping.
- Add/update test coverage accordingly.
  • Loading branch information
iamacook authored Sep 24, 2024
1 parent 9722b16 commit fa3c730
Show file tree
Hide file tree
Showing 11 changed files with 448 additions and 561 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { Builder, IBuilder } from '@/__tests__/builder';
import { Stake } from '@/datasources/staking-api/entities/stake.entity';
import {
Stake,
StakeState,
} from '@/datasources/staking-api/entities/stake.entity';
import { KilnDecoder } from '@/domain/staking/contracts/decoders/kiln-decoder.helper';
import { faker } from '@faker-js/faker';

Expand All @@ -11,7 +14,7 @@ export function stakeBuilder(): IBuilder<Stake> {
length: KilnDecoder.KilnPublicKeyLength,
}) as `0x${string}`,
)
.with('state', faker.lorem.words())
.with('state', faker.helpers.arrayElement(Object.values(StakeState)))
.with('effective_balance', faker.string.numeric())
.with('rewards', faker.string.numeric())
.with('net_claimable_consensus_rewards', faker.string.numeric());
Expand Down
53 changes: 28 additions & 25 deletions src/datasources/staking-api/entities/__tests__/stake.entity.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { stakeBuilder } from '@/datasources/staking-api/entities/__tests__/stake.entity.builder';
import { StakeSchema } from '@/datasources/staking-api/entities/stake.entity';
import {
StakeSchema,
StakeState,
} from '@/datasources/staking-api/entities/stake.entity';
import { faker } from '@faker-js/faker';

describe('StakeSchema', () => {
Expand All @@ -11,26 +14,33 @@ describe('StakeSchema', () => {
expect(result.success).toBe(true);
});

it.each(['validator_address' as const, 'state' as const])(
'should not validate non-string %s values',
(key) => {
const stake = stakeBuilder().build();
it('should fallback to unknown for an invalid state', () => {
const stake = stakeBuilder()
.with('state', 'invalid_state' as unknown as StakeState)
.build();

// @ts-expect-error - $key is expected to be a string
stake[key] = faker.number.int();
const result = StakeSchema.safeParse(stake);

const result = StakeSchema.safeParse(stake);
expect(result.success && result.data.state).toBe(StakeState.Unknown);
});

expect(!result.success && result.error.issues.length).toBe(1);
expect(!result.success && result.error.issues[0]).toStrictEqual({
code: 'invalid_type',
expected: 'string',
received: 'number',
message: 'Expected string, received number',
path: [key],
});
},
);
it('should not validate non-string validator_address values', () => {
const stake = stakeBuilder().build();

// @ts-expect-error - validator_address is expected to be a string
stake.validator_address = faker.number.int();

const result = StakeSchema.safeParse(stake);

expect(!result.success && result.error.issues.length).toBe(1);
expect(!result.success && result.error.issues[0]).toStrictEqual({
code: 'invalid_type',
expected: 'string',
received: 'number',
message: 'Expected string, received number',
path: ['validator_address'],
});
});

it('should not validate a `validator_address` with an invalid length', () => {
const stake = stakeBuilder().with('validator_address', '0x00').build();
Expand Down Expand Up @@ -75,13 +85,6 @@ describe('StakeSchema', () => {
path: ['validator_address'],
received: 'undefined',
},
{
code: 'invalid_type',
expected: 'string',
message: 'Required',
path: ['state'],
received: 'undefined',
},
{
code: 'invalid_type',
expected: 'string',
Expand Down
18 changes: 17 additions & 1 deletion src/datasources/staking-api/entities/stake.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,25 @@ import { HexSchema } from '@/validation/entities/schemas/hex.schema';
import { NumericStringSchema } from '@/validation/entities/schemas/numeric-string.schema';
import { z } from 'zod';

export enum StakeState {
Unknown = 'unknown',
Unstaked = 'unstaked',
PendingQueued = 'pending_queued',
DepositInProgress = 'deposit_in_progress',
PendingInitialized = 'pending_initialized',
ActiveOngoing = 'active_ongoing',
ExitRequested = 'exit_requested',
ActiveExiting = 'active_exiting',
ExitedUnslashed = 'exited_unslashed',
WithdrawalPossible = 'withdrawal_possible',
WithdrawalDone = 'withdrawal_done',
ActiveSlashed = 'active_slashed',
ExitedSlashed = 'exited_slashed',
}

export const StakeSchema = z.object({
validator_address: HexSchema.refine((value) => value.length === 98),
state: z.string(),
state: z.nativeEnum(StakeState).catch(StakeState.Unknown),
effective_balance: NumericStringSchema,
rewards: NumericStringSchema,
// Only returned if onchain_v1_include_net_rewards query is true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
DecodedType,
} from '@/routes/transactions/entities/confirmation-view/confirmation-view.entity';
import { NativeStakingDepositInfo } from '@/routes/transactions/entities/staking/native-staking-deposit-info.entity';
import { StakingDepositStatus } from '@/routes/transactions/entities/staking/staking.entity';
import { StakingStatus } from '@/routes/transactions/entities/staking/staking.entity';
import { TokenInfo } from '@/routes/transactions/entities/swaps/token-info.entity';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';

Expand All @@ -17,9 +17,9 @@ export class NativeStakingDepositConfirmationView
type = DecodedType.KilnNativeStakingDeposit;

@ApiProperty({
enum: StakingDepositStatus,
enum: StakingStatus,
})
status: StakingDepositStatus;
status: StakingStatus;

@ApiProperty()
method: string;
Expand Down Expand Up @@ -69,7 +69,7 @@ export class NativeStakingDepositConfirmationView
constructor(args: {
method: string;
parameters: DataDecodedParameter[] | null;
status: StakingDepositStatus;
status: StakingStatus;
estimatedEntryTime: number;
estimatedExitTime: number;
estimatedWithdrawalTime: number;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
StakingDepositStatus,
StakingDepositStatusInfo,
StakingStatus,
StakingTimeInfo,
StakingFinancialInfo,
} from '@/routes/transactions/entities/staking/staking.entity';
Expand All @@ -11,9 +10,7 @@ import {
} from '@/routes/transactions/entities/transaction-info.entity';
import { ApiProperty } from '@nestjs/swagger';

export type NativeStakingDepositInfo = StakingDepositStatusInfo &
StakingTimeInfo &
StakingFinancialInfo;
export type NativeStakingDepositInfo = StakingTimeInfo & StakingFinancialInfo;

export class NativeStakingDepositTransactionInfo
extends TransactionInfo
Expand All @@ -22,8 +19,8 @@ export class NativeStakingDepositTransactionInfo
@ApiProperty({ enum: [TransactionInfoType.NativeStakingDeposit] })
override type = TransactionInfoType.NativeStakingDeposit;

@ApiProperty({ enum: StakingDepositStatus })
status: StakingDepositStatus;
@ApiProperty({ enum: StakingStatus })
status: StakingStatus;

@ApiProperty()
estimatedEntryTime: number;
Expand Down Expand Up @@ -65,7 +62,7 @@ export class NativeStakingDepositTransactionInfo
tokenInfo: TokenInfo;

constructor(args: {
status: StakingDepositStatus;
status: StakingStatus;
estimatedEntryTime: number;
estimatedExitTime: number;
estimatedWithdrawalTime: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,20 @@ import {
Baseline,
DecodedType,
} from '@/routes/transactions/entities/confirmation-view/confirmation-view.entity';
import {
StakingValidatorsExitInfo,
StakingValidatorsExitStatus,
} from '@/routes/transactions/entities/staking/staking.entity';
import { StakingStatus } from '@/routes/transactions/entities/staking/staking.entity';
import { TokenInfo } from '@/routes/transactions/entities/swaps/token-info.entity';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';

export class NativeStakingValidatorsExitConfirmationView
implements Baseline, StakingValidatorsExitInfo
{
export class NativeStakingValidatorsExitConfirmationView implements Baseline {
@ApiProperty({
enum: [DecodedType.KilnNativeStakingValidatorsExit],
})
type = DecodedType.KilnNativeStakingValidatorsExit;

@ApiProperty({
enum: StakingValidatorsExitStatus,
enum: StakingStatus,
})
status: StakingValidatorsExitStatus;
status: StakingStatus;

@ApiProperty()
method: string;
Expand All @@ -47,7 +42,7 @@ export class NativeStakingValidatorsExitConfirmationView
constructor(args: {
method: string;
parameters: DataDecodedParameter[] | null;
status: StakingValidatorsExitStatus;
status: StakingStatus;
estimatedExitTime: number;
estimatedWithdrawalTime: number;
value: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { StakingValidatorsExitStatus } from '@/routes/transactions/entities/staking/staking.entity';
import { StakingStatus } from '@/routes/transactions/entities/staking/staking.entity';
import { TokenInfo } from '@/routes/transactions/entities/swaps/token-info.entity';
import {
TransactionInfo,
Expand All @@ -10,8 +10,8 @@ export class NativeStakingValidatorsExitTransactionInfo extends TransactionInfo
@ApiProperty({ enum: [TransactionInfoType.NativeStakingValidatorsExit] })
override type = TransactionInfoType.NativeStakingValidatorsExit;

@ApiProperty({ enum: StakingValidatorsExitStatus })
status: StakingValidatorsExitStatus;
@ApiProperty({ enum: StakingStatus })
status: StakingStatus;

@ApiProperty()
estimatedExitTime: number;
Expand All @@ -29,7 +29,7 @@ export class NativeStakingValidatorsExitTransactionInfo extends TransactionInfo
tokenInfo: TokenInfo;

constructor(args: {
status: StakingValidatorsExitStatus;
status: StakingStatus;
estimatedExitTime: number;
estimatedWithdrawalTime: number;
value: string;
Expand Down
29 changes: 9 additions & 20 deletions src/routes/transactions/entities/staking/staking.entity.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,15 @@
// Present in all calls for native/pooled/defi staking
export enum StakingDepositStatus {
AwaitingEntry = 'AWAITING_ENTRY',
AwaitingExecution = 'AWAITING_EXECUTION',
SignatureNeeded = 'SIGNATURE_NEEDED',
ValidationStarted = 'VALIDATION_STARTED',
export enum StakingStatus {
NotStaked = 'NOT_STAKED',
Activating = 'ACTIVATING',
DepositInProgress = 'DEPOSIT_IN_PROGRESS',
Active = 'ACTIVE',
ExitRequested = 'EXIT_REQUESTED',
Exiting = 'EXITING',
Exited = 'EXITED',
Slashed = 'SLASHED',
}

export type StakingDepositStatusInfo = {
status: StakingDepositStatus;
};

export enum StakingValidatorsExitStatus {
AwaitingExecution = 'AWAITING_EXECUTION',
ReadyToWithdraw = 'READY_TO_WITHDRAW',
RequestPending = 'REQUEST_PENDING',
SignatureNeeded = 'SIGNATURE_NEEDED',
}

export type StakingValidatorsExitInfo = {
status: StakingValidatorsExitStatus;
};

// Present in all deposit calls for native/pooled staking
export type StakingTimeInfo = {
estimatedEntryTime: number;
Expand Down
Loading

0 comments on commit fa3c730

Please sign in to comment.