Skip to content

Commit

Permalink
js-v1: Various fixups (#11)
Browse files Browse the repository at this point in the history
* js-v1: Various fixups

#### Problem

The web3.js v1 package is inconsistent with the v2 package.

#### Summary of changes

A few things:

* Rename web3js-1.0 -> js-v1
* Add testing to CI
* Format the code
* Use eslint correctly
* Fix stake account discriminants

* js-v1: Bump to v1.0.1 for release
  • Loading branch information
joncinque authored Oct 11, 2024
1 parent 669ed92 commit 0c92b1f
Show file tree
Hide file tree
Showing 17 changed files with 518 additions and 158 deletions.
43 changes: 43 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,49 @@ jobs:
- name: Run tests
run: cd js && pnpm test

format_and_lint_js_v1:
name: Format & Lint JS (web3.js v1)
runs-on: ubuntu-latest
steps:
- name: Git Checkout
uses: actions/checkout@v4

- name: Setup Environment
uses: ./.github/actions/setup
with:
js: true

- name: Install Dependencies
run: cd js-v1 && pnpm install --frozen-lockfile

- name: Format
run: cd js-v1 && pnpm format

- name: Lint
run: cd js-v1 && pnpm lint

test_js_v1:
name: Test JS (web3.js v1)
runs-on: ubuntu-latest
needs: format_and_lint_js
steps:
- name: Git Checkout
uses: actions/checkout@v4

- name: Setup Environment
uses: ./.github/actions/setup
with:
js: true

- name: Install Dependencies
run: cd js-v1 && pnpm install --frozen-lockfile

- name: Build code
run: cd js-v1 && pnpm build

- name: Run tests
run: cd js-v1 && pnpm test

format_and_lint_rust:
name: Format & Lint Rust
runs-on: ubuntu-latest
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
3 changes: 2 additions & 1 deletion web3js-1.0/package.json → js-v1/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@anza-xyz/solana-rpc-get-stake-activation",
"version": "1.0.0",
"version": "1.0.1",
"description": "Client-side replica of Solana RPC's getStakeActivation",
"sideEffects": false,
"module": "./dist/src/index.mjs",
Expand Down Expand Up @@ -52,6 +52,7 @@
},
"devDependencies": {
"@ava/typescript": "^4.1.0",
"@solana/eslint-config-solana": "^3.0.3",
"@types/node": "^20",
"@typescript-eslint/eslint-plugin": "^7.16.1",
"@typescript-eslint/parser": "^7.16.1",
Expand Down
266 changes: 266 additions & 0 deletions web3js-1.0/pnpm-lock.yaml → js-v1/pnpm-lock.yaml

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions web3js-1.0/src/delegation.ts → js-v1/src/delegation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Delegation, StakeHistoryEntry } from "./stake";
import { Delegation, StakeHistoryEntry } from './stake';

export interface StakeActivatingAndDeactivating {
effective: bigint;
Expand Down Expand Up @@ -36,7 +36,7 @@ export function getStakeAndActivating(
effective: BigInt(0),
activating: BigInt(0),
};
} else if (targetEpoch === delegation.activationEpoch) {
} else if (targetEpoch === delegation.activationEpoch) {
// all is activating
return {
effective: BigInt(0),
Expand Down Expand Up @@ -172,4 +172,4 @@ export function getStakeActivatingAndDeactivating(
deactivating: BigInt(0),
};
}
}
}
File renamed without changes.
74 changes: 74 additions & 0 deletions js-v1/src/rpc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { Connection, PublicKey } from '@solana/web3.js';
import { getStakeAccount, getStakeHistory } from './stake';
import { getStakeActivatingAndDeactivating } from './delegation';

export interface StakeActivation {
status: string;
active: bigint;
inactive: bigint;
}

export async function getStakeActivation(
connection: Connection,
stakeAddress: PublicKey
): Promise<StakeActivation> {
const SYSVAR_STAKE_HISTORY_ADDRESS = new PublicKey(
'SysvarStakeHistory1111111111111111111111111'
);
const [epochInfo, { stakeAccount, stakeAccountLamports }, stakeHistory] =
await Promise.all([
connection.getEpochInfo(),
(async () => {
const stakeAccountParsed =
await connection.getParsedAccountInfo(stakeAddress);
if (stakeAccountParsed === null || stakeAccountParsed.value === null) {
throw new Error('Account not found');
}
const stakeAccount = getStakeAccount(stakeAccountParsed);
const stakeAccountLamports = stakeAccountParsed.value.lamports;
return { stakeAccount, stakeAccountLamports };
})(),
(async () => {
const stakeHistoryParsed = await connection.getParsedAccountInfo(
SYSVAR_STAKE_HISTORY_ADDRESS
);
if (stakeHistoryParsed === null) {
throw new Error('StakeHistory not found');
}
return getStakeHistory(stakeHistoryParsed);
})(),
]);

const { effective, activating, deactivating } = stakeAccount.stake
? getStakeActivatingAndDeactivating(
stakeAccount.stake.delegation,
BigInt(epochInfo.epoch),
stakeHistory
)
: {
effective: BigInt(0),
activating: BigInt(0),
deactivating: BigInt(0),
};

let status;
if (deactivating > 0) {
status = 'deactivating';
} else if (activating > 0) {
status = 'activating';
} else if (effective > 0) {
status = 'active';
} else {
status = 'inactive';
}
const inactive =
BigInt(stakeAccountLamports) -
effective -
stakeAccount.meta.rentExemptReserve;

return {
status,
active: effective,
inactive,
};
}
130 changes: 130 additions & 0 deletions js-v1/src/stake.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import {
AccountInfo,
ParsedAccountData,
RpcResponseAndContext,
} from '@solana/web3.js';

export type StakeHistoryEntry = {
epoch: bigint;
effective: bigint;
activating: bigint;
deactivating: bigint;
};

export type Delegation = {
voterPubkey: Uint8Array;
stake: bigint;
activationEpoch: bigint;
deactivationEpoch: bigint;
};

export type StakeAccount = {
discriminant: bigint;
meta: {
rentExemptReserve: bigint;
authorized: {
staker: Uint8Array;
withdrawer: Uint8Array;
};
lockup: {
unixTimestamp: bigint;
epoch: bigint;
custodian: Uint8Array;
};
};
stake: {
delegation: Delegation;
creditsObserved: bigint;
} | null;
};

type StakeHistoryEntryRaw = {
epoch: number;
stakeHistory: {
effective: number;
activating: number;
deactivating: number;
};
};

export const getStakeHistory = function (
parsedData: RpcResponseAndContext<AccountInfo<
ParsedAccountData | Buffer
> | null>
): StakeHistoryEntry[] {
if (parsedData.value === null || parsedData.value.data instanceof Buffer) {
throw new Error('Account not found');
}

const stakeHistory: StakeHistoryEntry[] = [];

parsedData.value.data.parsed.info.forEach((entry: StakeHistoryEntryRaw) => {
stakeHistory.push({
epoch: BigInt(entry.epoch),
effective: BigInt(entry.stakeHistory.effective),
activating: BigInt(entry.stakeHistory.activating),
deactivating: BigInt(entry.stakeHistory.deactivating),
});
});

return stakeHistory;
};

export const getStakeAccount = function (
parsedData: RpcResponseAndContext<AccountInfo<
ParsedAccountData | Buffer
> | null>
): StakeAccount {
if (parsedData.value === null || parsedData.value.data instanceof Buffer) {
throw new Error('Account not found');
}

let discriminant = BigInt(0);
if (parsedData.value.data.parsed.type === 'initialized') {
discriminant = BigInt(1);
} else if (parsedData.value.data.parsed.type === 'delegated') {
discriminant = BigInt(2);
}

return {
discriminant: discriminant,
meta: {
rentExemptReserve: BigInt(
parsedData.value.data.parsed.info.meta.rentExemptReserve
),
authorized: {
staker: parsedData.value.data.parsed.info.meta.authorized.staker,
withdrawer:
parsedData.value.data.parsed.info.meta.authorized.withdrawer,
},
lockup: {
unixTimestamp: BigInt(
parsedData.value.data.parsed.info.meta.lockup.unixTimestamp
),
epoch: BigInt(parsedData.value.data.parsed.info.meta.lockup.epoch),
custodian: parsedData.value.data.parsed.info.meta.lockup.custodian,
},
},
stake: parsedData.value.data.parsed.info.stake
? {
delegation: {
voterPubkey:
parsedData.value.data.parsed.info.stake.delegation.voterPubkey,
stake: BigInt(
parsedData.value.data.parsed.info.stake.delegation.stake
),
activationEpoch: BigInt(
parsedData.value.data.parsed.info.stake.delegation.activationEpoch
),
deactivationEpoch: BigInt(
parsedData.value.data.parsed.info.stake.delegation
.deactivationEpoch
),
},
creditsObserved: BigInt(
parsedData.value.data.parsed.info.stake.creditsObserved
),
}
: null,
};
};
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
64 changes: 0 additions & 64 deletions web3js-1.0/src/rpc.ts

This file was deleted.

Loading

0 comments on commit 0c92b1f

Please sign in to comment.