Socket
Socket
Sign inDemoInstall

@solana/options

Package Overview
Dependencies
5
Maintainers
15
Versions
777
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

@solana/options


Version published
Maintainers
15
Install size
3.04 MB
Created

Readme

Source

npm npm-downloads semantic-release
code-style-prettier

@solana/options

This package allows us to manage and serialize Rust-like Option types in JavaScript. It can be used standalone, but it is also exported as part of the Solana JavaScript SDK @solana/web3.js@experimental.

This package is also part of the @solana/codecs package which acts as an entry point for all codec packages as well as for their documentation.

Creating options

In Rust, we define optional values as an Option<T> type which can either be Some(T) or None. This is usually represented as T | null in the JavaScript world. The issue with this approach is it doesn't work with nested options. For instance, an Option<Option<T>> in Rust would become a T | null | null in JavaScript which is equivalent to T | null. That means, there is no way for us to represent the Some(None) value in JavaScript or any other nested option.

To solve this issue, this library provides an Option<T> union type that works very similarly to the Rust Option<T> type. It is defined as follows:

type Option<T> = Some<T> | None;
type Some<T> = { __option: 'Some'; value: T };
type None = { __option: 'None' };

To improve the developer experience, helper functions are available to help you create options. The type T of the option can either be inferred by TypeScript or explicitly provided.

// Create an option with a value.
some('Hello World');
some<number | string>(123);

// Create an empty option.
none();
none<number | string>();

Option helpers

This library also provides helper functions to help us identify and manage Option types.

For instance, you can use the isSome and isNone type guards to check whether a given Option is of the desired type.

isSome(some('Hello World')); // true
isSome(none()); // false

isNone(some('Hello World')); // false
isNone(none()); // true

If you are given a type T | null, you may also use the wrapNullable helper function to transform it into an Option<T> type.

wrapNullable('Hello world'); // Some<string>
wrapNullable(null); // None

Unwrapping options

Several helpers are available to help you unwrap your options and access their potential value. For instance, the unwrapOption function transforms an Option<T> type into T if the value exits and null otherwise.

unwrapOption(some('Hello World')); // "Hello World"
unwrapOption(none()); // null

If null isn’t the value you want to use for None options, you may provide a custom fallback function as the second argument. Its return value will be assigned to None options.

unwrapOption(some('Hello World'), () => 'Default'); // "Hello World"
unwrapOption(none(), () => 'Default'); // "Default"

Note that this unwrapOption function does not recursively unwrap nested options. You may use the unwrapOptionRecursively function for that purpose instead.

unwrapOptionRecursively(some(some(some('Hello World')))); // "Hello World"
unwrapOptionRecursively(some(some(none<string>()))); // null

The unwrapOptionRecursively function also walks any object and array it encounters and recursively unwraps any option it identifies in its journey without mutating any object or array.

unwrapOptionRecursively({
    a: 'hello',
    b: none(),
    c: [{ c1: some(42) }, { c2: none() }],
});
// { a: "hello", b: null, c: [{ c1: 42 }, { c2: null }] }

The unwrapOptionRecursively also accepts a fallback function as a second argument to provide custom values for None options.

unwrapOptionRecursively(
    {
        a: 'hello',
        b: none(),
        c: [{ c1: some(42) }, { c2: none() }],
    },
    () => 'Default',
);
// { a: "hello", b: "Default", c: [{ c1: 42 }, { c2: "Default" }] }

Option codec

The getOptionCodec function behaves exactly the same as the getNullableCodec except that it encodes Option<T> types instead of T | null types.

Namely, it accepts a codec of type T and returns a codec of type Option<T>. It stores whether or not the item exists as a boolean prefix using a u8 by default.

const optionStringCodec = getOptionCodec(addCodecSizePrefix(getUtf8Codec(), getU32Codec()));

optionStringCodec.encode(some('Hi'));
// 0x01020000004869
//   | |       └-- utf8 string content ("Hi").
//   | └-- u32 string prefix (2 characters).
//   └-- 1-byte prefix (Some).

optionStringCodec.encode(none());
// 0x00
//   └-- 1-byte prefix (None).

You may provide a number codec as the prefix option of the getOptionCodec function to configure how to store the boolean prefix.

const u32OptionStringCodec = getOptionCodec(addCodecSizePrefix(getUtf8Codec(), getU32Codec()), {
    prefix: getU32Codec(),
});

u32OptionStringCodec.encode(some('Hi'));
// 0x01000000020000004869
//   └------┘ 4-byte prefix (Some).

u32OptionStringCodec.encode(none());
// 0x00000000
//   └------┘ 4-byte prefix (None).

Additionally, if the item is a FixedSizeCodec, you may set the fixed option to true to also make the returned option codec a FixedSizeCodec. To do so, it will pad null values with zeroes to match the length of existing values.

const fixedOptionStringCodec = getOptionCodec(
    fixCodecSize(getUtf8Codec(), 8), // Only works with fixed-size items.
    { fixed: true },
);

fixedOptionStringCodec.encode(some('Hi'));
// 0x014869000000000000
//   | └-- 8-byte utf8 string content ("Hi").
//   └-- 1-byte prefix (Some).

fixedOptionStringCodec.encode(none());
// 0x000000000000000000
//   | └-- 8-byte of padding to make a fixed-size codec.
//   └-- 1-byte prefix (None).

Separate getOptionEncoder and getOptionDecoder functions are also available.

const bytes = getOptionEncoder(getU32Encoder()).encode(some(42));
const value = getOptionDecoder(getU32Decoder()).decode(bytes);

Zeroable option codec

Similarly to the getOptionCodec function, The getZeroableOptionCodec function accepts a codec of type T and returns a codec of type Option<T>. However, instead of relying on a boolean prefix to determine whether the item exists, it uses a zero value to represent None. This means, you may only use this codec with fixed-size items.

const codec = getZeroableOptionCodec(getU16Codec());
codec.encode(some(42)); // 0x2a00
codec.encode(none()); // 0x0000
codec.decode(new Uint8Array([42, 0])); // Some<42>
codec.encode(new Uint8Array([0, 0])); // None

As you can see, by default, it uses a Uint8Array of zeroes to represent None. However, you may provide a custom zero value that will be used to encode/decode None values. Note that this zero value must be a Uint8Array of the same length as the codec's fixed size.

const codec = getZeroableOptionCodec(getU16Codec(), {
    zeroValue: new Uint8Array([255, 255]),
});
codec.encode(some(42)); // 0x2a00
codec.encode(none()); // 0xfffff
codec.encode(new Uint8Array([0, 0])); // Some<0>
codec.decode(new Uint8Array([42, 0])); // Some<42>
codec.decode(new Uint8Array([255, 255])); // None

Separate getZeroableOptionEncoder and getZeroableOptionDecoder functions are also available.

const bytes = getZeroableOptionEncoder(getU16Encoder()).encode(some(42));
const value = getZeroableOptionDecoder(getU16Decoder()).decode(bytes);

To read more about the available codecs and how to use them, check out the documentation of the main @solana/codecs package.

Keywords

FAQs

Last updated on 25 Apr 2024

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc