project-serum / serum-ts

Project Serum TypeScript monorepo
https://projectserum.com
Apache License 2.0
270 stars 245 forks source link

Support COption Layout #243

Open smehraein opened 2 years ago

smehraein commented 2 years ago

Would like to add support for Coption encoding/decoding for interacting with accounts using fields of that type. Requires a new layout class in borsh/src/index.ts

Code would look like this:

class CoptionLayout<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 = u32();
  }

  encode(src: T | null, b: Buffer, offset = 0): number {
    if (src === null || src === undefined) {
      return this.discriminator.encode(0, b, offset);
    }
    this.discriminator.encode(4, b, offset);
    return this.layout.encode(src, b, offset + 4) + 4;
  }

  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 + 4);
    }
    throw new Error('Invalid coption ' + this.property);
  }

  getSpan(b: Buffer, offset = 0): number {
    const discriminator = this.discriminator.decode(b, offset);
    if (discriminator === 0) {
      return 4;
    } else if (discriminator >= 1) {
      return this.layout.getSpan(b, offset + 4) + 4;
    }
    throw new Error('Invalid coption ' + this.property);
  }
}

export function coption<T>(
  layout: Layout<T>,
  property?: string
): Layout<T | null> {
  return new CoptionLayout<T>(layout, property);
}

Happy to create the PR myself. Just let me know if the team supports adding this. Already using a jerry-rigged version of this for my own codebase.

Only question is around else if (discriminator >= 1). I believe that the coption would also evaluate to 1 if it's set, but I'm not 100% sure.