From 8a90cf71f251179ad8c05a22ca8cb7aa69fbb5b9 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Tue, 4 Jun 2024 20:16:12 +0000 Subject: [PATCH 1/2] stake-pool-js: Remove borsh to fix downstream usage --- pnpm-lock.yaml | 14 -- stake-pool/js/package.json | 1 - stake-pool/js/rollup.config.mjs | 4 +- stake-pool/js/src/codecs.ts | 159 +++++++++++++++++++++ stake-pool/js/src/layouts.ts | 8 +- stake-pool/js/src/types/buffer-layout.d.ts | 7 + 6 files changed, 172 insertions(+), 21 deletions(-) create mode 100644 stake-pool/js/src/codecs.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 76e233ce6f4..433fe821777 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -391,9 +391,6 @@ importers: stake-pool/js: dependencies: - '@coral-xyz/borsh': - specifier: ^0.30.0 - version: 0.30.0(@solana/web3.js@1.91.8) '@solana/buffer-layout': specifier: ^4.0.1 version: 4.0.1 @@ -1227,17 +1224,6 @@ packages: buffer-layout: 1.2.2 dev: true - /@coral-xyz/borsh@0.30.0(@solana/web3.js@1.91.8): - resolution: {integrity: sha512-OrcV+7N10cChhgDRUxM4iEIuwxUHHs52XD85R8cFCUqE0vbLYrcoPPPs+VF6kZ9DhdJGVW2I6DHJOp5TykyZog==} - engines: {node: '>=10'} - peerDependencies: - '@solana/web3.js': ^1.68.0 - dependencies: - '@solana/web3.js': 1.91.8 - bn.js: 5.2.1 - buffer-layout: 1.2.2 - dev: false - /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} diff --git a/stake-pool/js/package.json b/stake-pool/js/package.json index 862006f1403..639d26125c5 100644 --- a/stake-pool/js/package.json +++ b/stake-pool/js/package.json @@ -43,7 +43,6 @@ ], "license": "ISC", "dependencies": { - "@coral-xyz/borsh": "^0.30.0", "@solana/buffer-layout": "^4.0.1", "@solana/spl-token": "0.4.6", "@solana/web3.js": "^1.91.8", diff --git a/stake-pool/js/rollup.config.mjs b/stake-pool/js/rollup.config.mjs index 1742dcd152c..f1ceb7e4904 100644 --- a/stake-pool/js/rollup.config.mjs +++ b/stake-pool/js/rollup.config.mjs @@ -66,12 +66,12 @@ function generateConfig(configType, format) { // Prevent dependencies from being bundled config.external = [ - '@coral-xyz/borsh', '@solana/buffer-layout', '@solana/spl-token', '@solana/web3.js', 'bn.js', 'buffer', + 'buffer-layout', ]; } @@ -94,12 +94,12 @@ function generateConfig(configType, format) { // Prevent dependencies from being bundled config.external = [ - '@coral-xyz/borsh', '@solana/buffer-layout', '@solana/spl-token', '@solana/web3.js', 'bn.js', 'buffer', + 'buffer-layout', ]; } diff --git a/stake-pool/js/src/codecs.ts b/stake-pool/js/src/codecs.ts new file mode 100644 index 00000000000..0dbbde55ac8 --- /dev/null +++ b/stake-pool/js/src/codecs.ts @@ -0,0 +1,159 @@ +import { blob, Layout as LayoutCls, offset, seq, struct, u32, u8 } from 'buffer-layout'; +import { PublicKey } from '@solana/web3.js'; +import BN from 'bn.js'; + +export interface Layout { + span: number; + property?: string; + + decode(b: Buffer, offset?: number): T; + + encode(src: T, b: Buffer, offset?: number): number; + + getSpan(b: Buffer, offset?: number): number; + + replicate(name: string): this; +} + +class BNLayout extends LayoutCls { + blob: Layout; + signed: boolean; + + constructor(span: number, signed: boolean, property?: string) { + super(span, property); + this.blob = blob(span); + this.signed = signed; + } + + decode(b: Buffer, offset = 0) { + const num = new BN(this.blob.decode(b, offset), 10, 'le'); + if (this.signed) { + return num.fromTwos(this.span * 8).clone(); + } + return num; + } + + encode(src: BN, b: Buffer, offset = 0) { + if (this.signed) { + src = src.toTwos(this.span * 8); + } + return this.blob.encode(src.toArrayLike(Buffer, 'le', this.span), b, offset); + } +} + +export function u64(property?: string): Layout { + return new BNLayout(8, false, property); +} + +class WrappedLayout extends LayoutCls { + layout: Layout; + decoder: (data: T) => U; + encoder: (src: U) => T; + + constructor( + layout: Layout, + decoder: (data: T) => U, + encoder: (src: U) => T, + property?: string, + ) { + super(layout.span, property); + this.layout = layout; + this.decoder = decoder; + this.encoder = encoder; + } + + decode(b: Buffer, offset?: number): U { + return this.decoder(this.layout.decode(b, offset)); + } + + encode(src: U, b: Buffer, offset?: number): number { + return this.layout.encode(this.encoder(src), b, offset); + } + + getSpan(b: Buffer, offset?: number): number { + return this.layout.getSpan(b, offset); + } +} + +export function publicKey(property?: string): Layout { + return new WrappedLayout( + blob(32), + (b: Buffer) => new PublicKey(b), + (key: PublicKey) => key.toBuffer(), + property, + ); +} + +class OptionLayout extends LayoutCls { + layout: Layout; + discriminator: Layout; + + constructor(layout: Layout, property?: string) { + super(-1, property); + this.layout = layout; + this.discriminator = u8(); + } + + encode(src: T | null, b: Buffer, offset = 0): number { + if (src === null || src === undefined) { + return this.discriminator.encode(0, b, offset); + } + this.discriminator.encode(1, b, offset); + return this.layout.encode(src, b, offset + 1) + 1; + } + + decode(b: Buffer, offset = 0): T | null { + const discriminator = this.discriminator.decode(b, offset); + if (discriminator === 0) { + return null; + } else if (discriminator === 1) { + return this.layout.decode(b, offset + 1); + } + throw new Error('Invalid option ' + this.property); + } + + getSpan(b: Buffer, offset = 0): number { + const discriminator = this.discriminator.decode(b, offset); + if (discriminator === 0) { + return 1; + } else if (discriminator === 1) { + return this.layout.getSpan(b, offset + 1) + 1; + } + throw new Error('Invalid option ' + this.property); + } +} + +export function option(layout: Layout, property?: string): Layout { + return new OptionLayout(layout, property); +} + +export function bool(property?: string): Layout { + return new WrappedLayout(u8(), decodeBool, encodeBool, property); +} + +function decodeBool(value: number): boolean { + if (value === 0) { + return false; + } else if (value === 1) { + return true; + } + throw new Error('Invalid bool: ' + value); +} + +function encodeBool(value: boolean): number { + return value ? 1 : 0; +} + +export function vec(elementLayout: Layout, property?: string): Layout { + const length = u32('length'); + const layout: Layout<{ values: T[] }> = struct([ + length, + seq(elementLayout, offset(length, -length.span), 'values'), + ]); + return new WrappedLayout( + layout, + ({ values }) => values, + (values) => ({ values }), + property, + ); +} diff --git a/stake-pool/js/src/layouts.ts b/stake-pool/js/src/layouts.ts index 86feebdd5c1..bc6c3cd3138 100644 --- a/stake-pool/js/src/layouts.ts +++ b/stake-pool/js/src/layouts.ts @@ -1,5 +1,5 @@ -import { Layout, publicKey, struct, u32, u64, u8, option, vec } from '@coral-xyz/borsh'; -import { Layout as LayoutCls, u8 as u8Cls } from 'buffer-layout'; +import { Layout, publicKey, u64, option, vec } from './codecs'; +import { struct, Layout as LayoutCls, u8, u32 } from 'buffer-layout'; import { PublicKey } from '@solana/web3.js'; import BN from 'bn.js'; import { @@ -45,7 +45,7 @@ export class FutureEpochLayout extends LayoutCls { constructor(layout: Layout, property?: string) { super(-1, property); this.layout = layout; - this.discriminator = u8Cls(); + this.discriminator = u8(); } encode(src: T | null, b: Buffer, offset = 0): number { @@ -78,7 +78,7 @@ export class FutureEpochLayout extends LayoutCls { } } -export function futureEpoch(layout: Layout, property?: string): Layout { +export function futureEpoch(layout: Layout, property?: string): LayoutCls { return new FutureEpochLayout(layout, property); } diff --git a/stake-pool/js/src/types/buffer-layout.d.ts b/stake-pool/js/src/types/buffer-layout.d.ts index 90538bf0fdc..5ef8ba4c87e 100644 --- a/stake-pool/js/src/types/buffer-layout.d.ts +++ b/stake-pool/js/src/types/buffer-layout.d.ts @@ -13,6 +13,13 @@ declare module 'buffer-layout' { property?: string, decodePrefixes?: boolean, ): Layout; + export function seq( + elementLayout: Layout, + count: number | Layout, + property?: string, + ): Layout; + export function offset(layout: Layout, offset?: number, property?: string): Layout; + export function blob(length: number | Layout, property?: string): Layout; export function s32(property?: string): Layout; export function u32(property?: string): Layout; export function s16(property?: string): Layout; From 0471498619fce022c7e7265af154e78eef991de9 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Tue, 4 Jun 2024 20:17:01 +0000 Subject: [PATCH 2/2] Bump version to 1.1.5 --- stake-pool/js/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stake-pool/js/package.json b/stake-pool/js/package.json index 639d26125c5..9883e57a64b 100644 --- a/stake-pool/js/package.json +++ b/stake-pool/js/package.json @@ -1,6 +1,6 @@ { "name": "@solana/spl-stake-pool", - "version": "1.1.4", + "version": "1.1.5", "description": "SPL Stake Pool Program JS API", "scripts": { "build": "tsc && cross-env NODE_ENV=production rollup -c",