-
Notifications
You must be signed in to change notification settings - Fork 305
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor ClusterProvider to remove legacy web3js dependency (#267)
This provider makes 3 requests, and makes their responses available to child components: - `getFirstAvailableBlock` - `getEpochInfo` - `getEpochSchedule` The first 2 are reasonably straightforward, we have basically the same structure as the legacy web3js, with just number/bigint changes. `getEpochSchedule` is a bit more complex. The existing API exposes a class with a bunch of functionality for finding the epoch for a slot, and the first/last slot for an epoch. None of this is RPC functionality, it's all baked into the legacy web3js code. Since the experimental web3js doesn't do any of that, I've copied these functions into the Explorer codebase, as pure functions that take an `EpochSchedule` (pure data returned by the new RPC method) and a slot/epoch (bigint). See existing web3js code here: https://github.com/solana-labs/solana-web3.js/blob/9232d2b1019dc50f852ad70aa81624e751d76161/packages/library-legacy/src/epoch-schedule.ts Also note that I hit a bug in experimental web3js where some of these functions are incorrectly typed as unknown: solana-labs/solana-web3.js#1389 This was easy enough to work around for now I've also moved `localStorageIsAvailable` from `utils/index.ts` to its own `utils/local-storage`. This lets us import it without pulling in the web3js dependency in `utils/index.ts` The result of this PR is that the `ClusterProvider` in the root layout no longer pulls in the legacy web3js dependency.
- Loading branch information
1 parent
080f1d8
commit a292352
Showing
14 changed files
with
240 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { EpochSchedule, getEpochForSlot, getFirstSlotInEpoch, getLastSlotInEpoch } from "../epoch-schedule" | ||
|
||
describe('getEpoch', () => { | ||
it('returns the correct epoch for a slot after `firstNormalSlot`', () => { | ||
const schedule: EpochSchedule = { | ||
firstNormalEpoch: 0n, | ||
firstNormalSlot: 0n, | ||
slotsPerEpoch: 432_000n | ||
} | ||
|
||
expect(getEpochForSlot(schedule, 1n)).toEqual(0n); | ||
expect(getEpochForSlot(schedule, 431_999n)).toEqual(0n); | ||
expect(getEpochForSlot(schedule, 432_000n)).toEqual(1n); | ||
expect(getEpochForSlot(schedule, 500_000n)).toEqual(1n); | ||
expect(getEpochForSlot(schedule, 228_605_332n)).toEqual(529n); | ||
}) | ||
|
||
it('returns the correct epoch for a slot before `firstNormalSlot`', () => { | ||
const schedule: EpochSchedule = { | ||
firstNormalEpoch: 100n, | ||
firstNormalSlot: 3_200n, | ||
slotsPerEpoch: 432_000n | ||
}; | ||
|
||
expect(getEpochForSlot(schedule, 1n)).toEqual(0n); | ||
expect(getEpochForSlot(schedule, 31n)).toEqual(0n); | ||
expect(getEpochForSlot(schedule, 32n)).toEqual(1n); | ||
}) | ||
}) | ||
|
||
describe('getFirstSlotInEpoch', () => { | ||
it('returns the first slot for an epoch after `firstNormalEpoch`', () => { | ||
const schedule: EpochSchedule = { | ||
firstNormalEpoch: 0n, | ||
firstNormalSlot: 0n, | ||
slotsPerEpoch: 100n | ||
} | ||
|
||
expect(getFirstSlotInEpoch(schedule, 1n)).toEqual(100n); | ||
expect(getFirstSlotInEpoch(schedule, 2n)).toEqual(200n); | ||
expect(getFirstSlotInEpoch(schedule, 10n)).toEqual(1000n); | ||
}) | ||
|
||
it('returns the first slot for an epoch before `firstNormalEpoch`', () => { | ||
const schedule: EpochSchedule = { | ||
firstNormalEpoch: 100n, | ||
firstNormalSlot: 100_000n, | ||
slotsPerEpoch: 100n | ||
}; | ||
|
||
expect(getFirstSlotInEpoch(schedule, 0n)).toEqual(0n); | ||
expect(getFirstSlotInEpoch(schedule, 1n)).toEqual(32n); | ||
expect(getFirstSlotInEpoch(schedule, 2n)).toEqual(96n); | ||
expect(getFirstSlotInEpoch(schedule, 10n)).toEqual(32_736n); | ||
}) | ||
}) | ||
|
||
describe('getLastSlotInEpoch', () => { | ||
it('returns the last slot for an epoch after `firstNormalEpoch`', () => { | ||
const schedule: EpochSchedule = { | ||
firstNormalEpoch: 0n, | ||
firstNormalSlot: 0n, | ||
slotsPerEpoch: 100n | ||
} | ||
|
||
expect(getLastSlotInEpoch(schedule, 1n)).toEqual(199n); | ||
expect(getLastSlotInEpoch(schedule, 2n)).toEqual(299n); | ||
expect(getLastSlotInEpoch(schedule, 10n)).toEqual(1099n); | ||
}) | ||
|
||
it('returns the first slot for an epoch before `firstNormalEpoch`', () => { | ||
const schedule: EpochSchedule = { | ||
firstNormalEpoch: 100n, | ||
firstNormalSlot: 100_000n, | ||
slotsPerEpoch: 100n | ||
}; | ||
|
||
expect(getLastSlotInEpoch(schedule, 0n)).toEqual(31n); | ||
expect(getLastSlotInEpoch(schedule, 1n)).toEqual(95n); | ||
expect(getLastSlotInEpoch(schedule, 2n)).toEqual(223n); | ||
expect(getLastSlotInEpoch(schedule, 10n)).toEqual(65_503n); | ||
}) | ||
}) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
const MINIMUM_SLOT_PER_EPOCH = BigInt(32); | ||
|
||
export interface EpochSchedule { | ||
/** The maximum number of slots in each epoch */ | ||
slotsPerEpoch: bigint, | ||
/** The first epoch with `slotsPerEpoch` slots */ | ||
firstNormalEpoch: bigint, | ||
/** The first slot of `firstNormalEpoch` */ | ||
firstNormalSlot: bigint | ||
} | ||
|
||
// Returns the number of trailing zeros in the binary representation of n | ||
function trailingZeros(n: bigint): number { | ||
let trailingZeros = 0; | ||
while (n > 1) { | ||
n /= 2n; | ||
trailingZeros++; | ||
} | ||
return trailingZeros; | ||
} | ||
|
||
// Returns the smallest power of two greater than or equal to n | ||
function nextPowerOfTwo(n: bigint): bigint { | ||
if (n === 0n) return 1n; | ||
n--; | ||
n |= n >> 1n | ||
n |= n >> 2n | ||
n |= n >> 4n | ||
n |= n >> 8n | ||
n |= n >> 16n | ||
n |= n >> 32n | ||
return n + 1n | ||
} | ||
|
||
/** | ||
* Get the epoch number for a given slot | ||
* @param epochSchedule Epoch schedule information | ||
* @param slot The slot to get the epoch number for | ||
* @returns The epoch number that contains or will contain the given slot | ||
*/ | ||
export function getEpochForSlot( | ||
epochSchedule: EpochSchedule, | ||
slot: bigint, | ||
): bigint { | ||
if (slot < epochSchedule.firstNormalSlot) { | ||
const epoch = | ||
trailingZeros(nextPowerOfTwo(slot + MINIMUM_SLOT_PER_EPOCH + BigInt(1))) - | ||
trailingZeros(MINIMUM_SLOT_PER_EPOCH) - | ||
1; | ||
|
||
return BigInt(epoch); | ||
} else { | ||
const normalSlotIndex = slot - epochSchedule.firstNormalSlot; | ||
const normalEpochIndex = normalSlotIndex / epochSchedule.slotsPerEpoch; | ||
const epoch = epochSchedule.firstNormalEpoch + normalEpochIndex; | ||
return epoch; | ||
} | ||
} | ||
|
||
/** | ||
* Get the first slot in a given epoch | ||
* @param epochSchedule Epoch schedule information | ||
* @param epoch Epoch to get the first slot for | ||
* @returns First slot in the epoch | ||
*/ | ||
export function getFirstSlotInEpoch( | ||
epochSchedule: EpochSchedule, | ||
epoch: bigint | ||
): bigint { | ||
if (epoch <= epochSchedule.firstNormalEpoch) { | ||
return ((2n ** epoch) - 1n) * MINIMUM_SLOT_PER_EPOCH; | ||
} else { | ||
return ( | ||
(epoch - epochSchedule.firstNormalEpoch) * epochSchedule.slotsPerEpoch + | ||
epochSchedule.firstNormalSlot | ||
); | ||
} | ||
} | ||
|
||
/** | ||
* Get the last slot in a given epoch | ||
* @param epochSchedule Epoch schedule information | ||
* @param epoch Epoch to get the last slot for | ||
* @returns Last slot in the epoch | ||
*/ | ||
export function getLastSlotInEpoch( | ||
epochSchedule: EpochSchedule, | ||
epoch: bigint | ||
): bigint { | ||
return getFirstSlotInEpoch(epochSchedule, epoch + 1n) - 1n; | ||
} |
Oops, something went wrong.
a292352
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
explorer – ./
explorer-git-master-solana-labs.vercel.app
explorer-solana-labs.vercel.app
explorer.solana.com