Quellcode durchsuchen

add borsh + u256 changes + update to v0.2.6 (#2290)

Co-authored-by: henrye <henry@notanemail>
Henry-E vor 2 Jahren
Ursprung
Commit
35f4e26ef8
3 geänderte Dateien mit 405 neuen und 0 gelöschten Zeilen
  1. 33 0
      ts/packages/borsh/package.json
  2. 350 0
      ts/packages/borsh/src/index.ts
  3. 22 0
      ts/packages/borsh/tsconfig.json

+ 33 - 0
ts/packages/borsh/package.json

@@ -0,0 +1,33 @@
+{
+  "name": "@coral-xyz/borsh",
+  "version": "0.2.6",
+  "description": "Anchor Borsh",
+  "main": "dist/lib/index.js",
+  "types": "dist/lib/index.d.ts",
+  "exports": {
+    ".": "./dist/lib/index.js"
+  },
+  "license": "Apache-2.0",
+  "publishConfig": {
+    "access": "public"
+  },
+  "engines": {
+    "node": ">=10"
+  },
+  "scripts": {
+    "build": "tsc",
+    "test": "",
+    "clean": "rm -rf dist",
+    "prepare": "run-s clean build"
+  },
+  "dependencies": {
+    "bn.js": "^5.1.2",
+    "buffer-layout": "^1.2.0"
+  },
+  "peerDependencies": {
+    "@solana/web3.js": "^1.2.0"
+  },
+  "files": [
+    "dist"
+  ]
+}

+ 350 - 0
ts/packages/borsh/src/index.ts

@@ -0,0 +1,350 @@
+import {
+  blob,
+  Layout as LayoutCls,
+  offset,
+  seq,
+  struct,
+  u32,
+  u8,
+  union,
+} from "buffer-layout";
+import { PublicKey } from "@solana/web3.js";
+import BN from "bn.js";
+export {
+  u8,
+  s8 as i8,
+  u16,
+  s16 as i16,
+  u32,
+  s32 as i32,
+  f32,
+  f64,
+  struct,
+} from "buffer-layout";
+
+export interface Layout<T> {
+  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<BN> {
+  blob: Layout<Buffer>;
+  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<BN> {
+  return new BNLayout(8, false, property);
+}
+
+export function i64(property?: string): Layout<BN> {
+  return new BNLayout(8, true, property);
+}
+
+export function u128(property?: string): Layout<BN> {
+  return new BNLayout(16, false, property);
+}
+
+export function i128(property?: string): Layout<BN> {
+  return new BNLayout(16, true, property);
+}
+
+export function u256(property?: string): Layout<BN> {
+  return new BNLayout(32, false, property);
+}
+
+export function i256(property?: string): Layout<BN> {
+  return new BNLayout(32, true, property);
+}
+
+class WrappedLayout<T, U> extends LayoutCls<U> {
+  layout: Layout<T>;
+  decoder: (data: T) => U;
+  encoder: (src: U) => T;
+
+  constructor(
+    layout: Layout<T>,
+    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<PublicKey> {
+  return new WrappedLayout(
+    blob(32),
+    (b: Buffer) => new PublicKey(b),
+    (key: PublicKey) => key.toBuffer(),
+    property
+  );
+}
+
+class OptionLayout<T> extends LayoutCls<T | null> {
+  layout: Layout<T>;
+  discriminator: Layout<number>;
+
+  constructor(layout: Layout<T>, 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<T>(
+  layout: Layout<T>,
+  property?: string
+): Layout<T | null> {
+  return new OptionLayout<T>(layout, property);
+}
+
+export function bool(property?: string): Layout<boolean> {
+  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<T>(
+  elementLayout: Layout<T>,
+  property?: string
+): Layout<T[]> {
+  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
+  );
+}
+
+export function tagged<T>(
+  tag: BN,
+  layout: Layout<T>,
+  property?: string
+): Layout<T> {
+  const wrappedLayout: Layout<{ tag: BN; data: T }> = struct([
+    u64("tag"),
+    layout.replicate("data"),
+  ]);
+
+  function decodeTag({ tag: receivedTag, data }: { tag: BN; data: T }) {
+    if (!receivedTag.eq(tag)) {
+      throw new Error(
+        "Invalid tag, expected: " +
+          tag.toString("hex") +
+          ", got: " +
+          receivedTag.toString("hex")
+      );
+    }
+    return data;
+  }
+
+  return new WrappedLayout(
+    wrappedLayout,
+    decodeTag,
+    (data) => ({ tag, data }),
+    property
+  );
+}
+
+export function vecU8(property?: string): Layout<Buffer> {
+  const length = u32("length");
+  const layout: Layout<{ data: Buffer }> = struct([
+    length,
+    blob(offset(length, -length.span), "data"),
+  ]);
+  return new WrappedLayout(
+    layout,
+    ({ data }) => data,
+    (data) => ({ data }),
+    property
+  );
+}
+
+export function str(property?: string): Layout<string> {
+  return new WrappedLayout(
+    vecU8(),
+    (data) => data.toString("utf-8"),
+    (s) => Buffer.from(s, "utf-8"),
+    property
+  );
+}
+
+export interface EnumLayout<T> extends Layout<T> {
+  registry: Record<string, Layout<any>>;
+}
+
+export function rustEnum<T>(
+  variants: Layout<any>[],
+  property?: string,
+  discriminant?: Layout<any>
+): EnumLayout<T> {
+  const unionLayout = union(discriminant ?? u8(), property);
+  variants.forEach((variant, index) =>
+    unionLayout.addVariant(index, variant, variant.property)
+  );
+  return unionLayout;
+}
+
+export function array<T>(
+  elementLayout: Layout<T>,
+  length: number,
+  property?: string
+): Layout<T[]> {
+  const layout: Layout<{ values: T[] }> = struct([
+    seq(elementLayout, length, "values"),
+  ]);
+  return new WrappedLayout(
+    layout,
+    ({ values }) => values,
+    (values) => ({ values }),
+    property
+  );
+}
+
+class MapEntryLayout<K, V> extends LayoutCls<[K, V]> {
+  keyLayout: Layout<K>;
+  valueLayout: Layout<V>;
+
+  constructor(keyLayout: Layout<K>, valueLayout: Layout<V>, property?: string) {
+    super(keyLayout.span + valueLayout.span, property);
+    this.keyLayout = keyLayout;
+    this.valueLayout = valueLayout;
+  }
+
+  decode(b: Buffer, offset?: number): [K, V] {
+    offset = offset || 0;
+    const key = this.keyLayout.decode(b, offset);
+    const value = this.valueLayout.decode(
+      b,
+      offset + this.keyLayout.getSpan(b, offset)
+    );
+    return [key, value];
+  }
+
+  encode(src: [K, V], b: Buffer, offset?: number): number {
+    offset = offset || 0;
+    const keyBytes = this.keyLayout.encode(src[0], b, offset);
+    const valueBytes = this.valueLayout.encode(src[1], b, offset + keyBytes);
+    return keyBytes + valueBytes;
+  }
+
+  getSpan(b: Buffer, offset?: number): number {
+    return (
+      this.keyLayout.getSpan(b, offset) + this.valueLayout.getSpan(b, offset)
+    );
+  }
+}
+
+export function map<K, V>(
+  keyLayout: Layout<K>,
+  valueLayout: Layout<V>,
+  property?: string
+): Layout<Map<K, V>> {
+  const length = u32("length");
+  const layout: Layout<{ values: [K, V][] }> = struct([
+    length,
+    seq(
+      new MapEntryLayout(keyLayout, valueLayout),
+      offset(length, -length.span),
+      "values"
+    ),
+  ]);
+  return new WrappedLayout(
+    layout,
+    ({ values }) => new Map(values),
+    (values) => ({ values: Array.from(values.entries()) }),
+    property
+  );
+}

+ 22 - 0
ts/packages/borsh/tsconfig.json

@@ -0,0 +1,22 @@
+{
+  "compilerOptions": {
+    "module": "commonjs",
+    "target": "es2019",
+
+    "outDir": "./dist/lib",
+    "rootDir": "./src",
+
+    "composite": true,
+    "declaration": true,
+    "declarationMap": true,
+    "sourceMap": true,
+
+    "strict": true,
+    "esModuleInterop": true,
+    "skipLibCheck": true,
+    "forceConsistentCasingInFileNames": true,
+    "typeRoots": ["../../types/", "../../node_modules/@types"]
+  },
+  "include": ["src/**/*"],
+  "exclude": ["src/**/*.test.ts", "**/node_modules"]
+}