189 lines
4.4 KiB
TypeScript
189 lines
4.4 KiB
TypeScript
import {Buffer} from 'buffer';
|
|
import * as BufferLayout from '@solana/buffer-layout';
|
|
|
|
import {VoteAuthorizeWithSeedArgs} from './programs/vote';
|
|
|
|
/**
|
|
* Layout for a public key
|
|
*/
|
|
export const publicKey = (property: string = 'publicKey') => {
|
|
return BufferLayout.blob(32, property);
|
|
};
|
|
|
|
/**
|
|
* Layout for a signature
|
|
*/
|
|
export const signature = (property: string = 'signature') => {
|
|
return BufferLayout.blob(64, property);
|
|
};
|
|
|
|
/**
|
|
* Layout for a 64bit unsigned value
|
|
*/
|
|
export const uint64 = (property: string = 'uint64') => {
|
|
return BufferLayout.blob(8, property);
|
|
};
|
|
|
|
interface IRustStringShim
|
|
extends Omit<
|
|
BufferLayout.Structure<
|
|
Readonly<{
|
|
length: number;
|
|
lengthPadding: number;
|
|
chars: Uint8Array;
|
|
}>
|
|
>,
|
|
'decode' | 'encode' | 'replicate'
|
|
> {
|
|
alloc: (str: string) => number;
|
|
decode: (b: Uint8Array, offset?: number) => string;
|
|
encode: (str: string, b: Uint8Array, offset?: number) => number;
|
|
replicate: (property: string) => this;
|
|
}
|
|
|
|
/**
|
|
* Layout for a Rust String type
|
|
*/
|
|
export const rustString = (
|
|
property: string = 'string',
|
|
): BufferLayout.Layout<string> => {
|
|
const rsl = BufferLayout.struct<
|
|
Readonly<{
|
|
length?: number;
|
|
lengthPadding?: number;
|
|
chars: Uint8Array;
|
|
}>
|
|
>(
|
|
[
|
|
BufferLayout.u32('length'),
|
|
BufferLayout.u32('lengthPadding'),
|
|
BufferLayout.blob(BufferLayout.offset(BufferLayout.u32(), -8), 'chars'),
|
|
],
|
|
property,
|
|
);
|
|
const _decode = rsl.decode.bind(rsl);
|
|
const _encode = rsl.encode.bind(rsl);
|
|
|
|
const rslShim = rsl as unknown as IRustStringShim;
|
|
|
|
rslShim.decode = (b: Uint8Array, offset?: number) => {
|
|
const data = _decode(b, offset);
|
|
return data['chars'].toString();
|
|
};
|
|
|
|
rslShim.encode = (str: string, b: Uint8Array, offset?: number) => {
|
|
const data = {
|
|
chars: Buffer.from(str, 'utf8'),
|
|
};
|
|
return _encode(data, b, offset);
|
|
};
|
|
|
|
rslShim.alloc = (str: string) => {
|
|
return (
|
|
BufferLayout.u32().span +
|
|
BufferLayout.u32().span +
|
|
Buffer.from(str, 'utf8').length
|
|
);
|
|
};
|
|
|
|
return rslShim;
|
|
};
|
|
|
|
/**
|
|
* Layout for an Authorized object
|
|
*/
|
|
export const authorized = (property: string = 'authorized') => {
|
|
return BufferLayout.struct<
|
|
Readonly<{
|
|
staker: Uint8Array;
|
|
withdrawer: Uint8Array;
|
|
}>
|
|
>([publicKey('staker'), publicKey('withdrawer')], property);
|
|
};
|
|
|
|
/**
|
|
* Layout for a Lockup object
|
|
*/
|
|
export const lockup = (property: string = 'lockup') => {
|
|
return BufferLayout.struct<
|
|
Readonly<{
|
|
custodian: Uint8Array;
|
|
epoch: number;
|
|
unixTimestamp: number;
|
|
}>
|
|
>(
|
|
[
|
|
BufferLayout.ns64('unixTimestamp'),
|
|
BufferLayout.ns64('epoch'),
|
|
publicKey('custodian'),
|
|
],
|
|
property,
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Layout for a VoteInit object
|
|
*/
|
|
export const voteInit = (property: string = 'voteInit') => {
|
|
return BufferLayout.struct<
|
|
Readonly<{
|
|
authorizedVoter: Uint8Array;
|
|
authorizedWithdrawer: Uint8Array;
|
|
commission: number;
|
|
nodePubkey: Uint8Array;
|
|
}>
|
|
>(
|
|
[
|
|
publicKey('nodePubkey'),
|
|
publicKey('authorizedVoter'),
|
|
publicKey('authorizedWithdrawer'),
|
|
BufferLayout.u8('commission'),
|
|
],
|
|
property,
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Layout for a VoteAuthorizeWithSeedArgs object
|
|
*/
|
|
export const voteAuthorizeWithSeedArgs = (
|
|
property: string = 'voteAuthorizeWithSeedArgs',
|
|
) => {
|
|
return BufferLayout.struct<VoteAuthorizeWithSeedArgs>(
|
|
[
|
|
BufferLayout.u32('voteAuthorizationType'),
|
|
publicKey('currentAuthorityDerivedKeyOwnerPubkey'),
|
|
rustString('currentAuthorityDerivedKeySeed'),
|
|
publicKey('newAuthorized'),
|
|
],
|
|
property,
|
|
);
|
|
};
|
|
|
|
export function getAlloc(type: any, fields: any): number {
|
|
const getItemAlloc = (item: any): number => {
|
|
if (item.span >= 0) {
|
|
return item.span;
|
|
} else if (typeof item.alloc === 'function') {
|
|
return item.alloc(fields[item.property]);
|
|
} else if ('count' in item && 'elementLayout' in item) {
|
|
const field = fields[item.property];
|
|
if (Array.isArray(field)) {
|
|
return field.length * getItemAlloc(item.elementLayout);
|
|
}
|
|
} else if ('fields' in item) {
|
|
// This is a `Structure` whose size needs to be recursively measured.
|
|
return getAlloc({layout: item}, fields[item.property]);
|
|
}
|
|
// Couldn't determine allocated size of layout
|
|
return 0;
|
|
};
|
|
|
|
let alloc = 0;
|
|
type.layout.fields.forEach((item: any) => {
|
|
alloc += getItemAlloc(item);
|
|
});
|
|
|
|
return alloc;
|
|
}
|