@honeycomb-protocol/solita
Version:
Generates SDK API from solana contract IDL.
284 lines • 9.81 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.adaptIdl = void 0;
const assert_1 = __importDefault(require("assert"));
const types_1 = require("./types");
const premitiveTypes = {
bool: 'bool',
pubkey: 'publicKey',
publickey: 'publicKey',
string: 'string',
u8: 'u8',
u16: 'u16',
u32: 'u32',
u64: 'u64',
u128: 'u128',
f64: {
array: ['u8', 8],
},
};
let idlTypeFallback = (strType, resolveType) => null;
const parseGenericType = (input) => {
let result = {
type: '',
parameters: [],
};
let depth = 0, param = '', typeName = '';
for (let char of input) {
if (char === '<') {
if (depth++ === 0)
result.type = typeName.trim();
else
param += char;
}
else if (char === '>') {
if (--depth === 0) {
result.parameters.push(param.trim());
param = '';
}
else
param += char;
}
else if (char === ',' && depth === 1) {
result.parameters.push(param.trim());
param = '';
}
else if (depth > 0)
param += char;
else
typeName += char;
}
return result;
};
const mapMapper = (strType, resolveType) => {
const parsed = parseGenericType(strType);
if (parsed && parsed.type.endsWith('Map')) {
const [inner1, inner2] = parsed.parameters;
const innerTy1 = resolveType(inner1);
const innerTy2 = resolveType(inner2);
if (parsed.type === 'BTreeMap') {
const map = { bTreeMap: [innerTy1, innerTy2] };
return map;
}
else if (parsed.type === 'VecMap') {
return {
vec: resolveType('(' + strType.slice(7, -1) + ')'), // Adjust slicing based on "VecMap"
};
}
else {
const map = { hashMap: [innerTy1, innerTy2] };
return map;
}
}
return null;
};
const tuppleMapper = (strType, resolveType) => {
if (strType.startsWith('(') && strType.endsWith(')')) {
const items = strType
.slice(1, -1)
.split(/\s*,\s*/)
.map(resolveType);
return {
tuple: items,
};
}
return null;
};
const vecMapper = (strType, resolveType) => {
if (strType.startsWith('Vec<')) {
return {
vec: resolveType(strType.slice(4, -1)),
};
}
return null;
};
const optionMapper = (strType, resolveType) => {
if (strType.startsWith('Option<')) {
return {
option: resolveType(strType.slice(7, -1)),
};
}
return null;
};
const nodeMapper = (strType, resolveType) => {
if (strType === 'Node') {
return {
array: ['u8', 32],
};
}
return null;
};
const customTypeMappers = [
mapMapper,
vecMapper,
tuppleMapper,
optionMapper,
nodeMapper,
];
const generatedTypes = new Map();
/**
* When anchor doesn't understand a type it just assumes it is a user defined one.
* This includes HashMaps and BTreeMaps. However it doesn't check if that type
* is actually defined somewhere.
* Thus we end up with invalid types here like `HashMap<String,DataItem>` which
* is basically just the type definition copied from the Rust code.
*
* This function attempts to fix this. At this point only top level struct
* fields are supported.
*
* Whenever more cases of incorrect types are encountered this transformer needs
* to be updated to handle them.
*/
function adaptIdl(idl, config) {
// Apply Idl hook if provided
if (config.idlHook != null) {
assert_1.default.equal(typeof config.idlHook, 'function', `idlHook needs to be a function of the type: (idl: Idl) => idl, but is of type ${typeof config.idlHook}`);
idl = config.idlHook(idl);
}
if (config.customTypeMappers != null) {
if (!Array.isArray(config.customTypeMappers))
throw new Error(`customTypeMappers needs to be array of the type: (strType: string, resolveType: (strType: string) => IdlType, registerGeneratedType: (def: IdlDefinedTypeDefinition) => void) => IdlType | null, but is of type ${typeof config.customTypeMappers}`);
config.customTypeMappers.forEach((mapper) => {
assert_1.default.equal(typeof mapper, 'function', `customTypeMappers items needs to be of the type: (strType: string, resolveType: (strType: string) => IdlType, registerGeneratedType: (def: IdlDefinedTypeDefinition) => void) => IdlType | null, but one of them is of type ${typeof mapper}`);
customTypeMappers.push(mapper);
});
}
// Set Idl type fallback if provided
if (config.idlTypeFallback != null) {
assert_1.default.equal(typeof config.idlTypeFallback, 'function', `idlTypeFallback needs to be a function of the type: (strType: string, resolveType: (strType: string) => IdlType) => IdlType | null, but is of type ${typeof config.idlTypeFallback}`);
idlTypeFallback = config.idlTypeFallback;
}
if ((0, types_1.isShankIdl)(idl))
return idl;
const typeCache = new Map();
if (idl.types != null) {
for (let i = 0; i < idl.types.length; i++) {
typeCache.set(idl.types[i].name, transformDefinition(idl.types[i]));
}
}
if (idl.accounts != null) {
for (let i = 0; i < idl.accounts.length; i++) {
const accountType = typeCache.get(idl.accounts[i].name);
typeCache.delete(idl.accounts[i].name);
const account = {
name: idl.accounts[i].name,
discriminator: idl.accounts[i].discriminator,
type: accountType.type,
};
idl.accounts[i] = account;
}
}
idl.types = Array.from(typeCache.values());
for (let ix of idl.instructions) {
ix.name = snakeToCamel(ix.name);
for (let account of ix.accounts) {
account.name = snakeToCamel(account.name);
}
for (let i = 0; i < ix.args.length; i++) {
ix.args[i].name = snakeToCamel(ix.args[i].name);
ix.args[i].type = transformType(ix.args[i].type);
}
}
if (generatedTypes.size > 0) {
if (idl.types == null)
idl.types = [];
idl.types.push(...Array.from(generatedTypes.values()));
}
if (config.idlHookPostAdaption != null) {
assert_1.default.equal(typeof config.idlHookPostAdaption, 'function', `idlHookPostAdaption needs to be a function of the type: (idl: Idl) => idl, but is of type ${typeof config.idlHookPostAdaption}`);
idl = config.idlHookPostAdaption(idl);
}
return idl;
}
exports.adaptIdl = adaptIdl;
// -----------------
// Types
// -----------------
function transformDefinition(def) {
if (def.name == 'Number')
debugger;
const ty = def.type;
if ((0, types_1.isFieldsType)(ty)) {
for (const f of ty.fields) {
f.name = snakeToCamel(f.name);
f.type = transformType(f.type);
}
}
else if ((0, types_1.isIdlTypeDataEnum)(ty)) {
for (const v of ty.variants) {
if ((0, types_1.isDataEnumVariantWithNamedFields)(v)) {
for (const f of v.fields) {
f.name = snakeToCamel(f.name);
f.type = transformType(f.type);
}
}
else if ((0, types_1.isDataEnumVariantWithUnnamedFields)(v)) {
for (const f in v.fields) {
v.fields[f] = transformType(v.fields[f]);
}
}
}
}
return def;
}
const snakeToCamel = (str) => str
.toLowerCase()
.replace(/([-_][a-z0-9])/g, (group) => group.toUpperCase().replace('-', '').replace('_', ''));
function transformType(ty) {
var _a, _b;
if ((0, types_1.isIdlTypeOption)(ty)) {
const option = {
option: transformType(ty.option),
};
return option;
}
if ((0, types_1.isIdlTypeVec)(ty)) {
const vec = {
vec: transformType(ty.vec),
};
return vec;
}
if ((0, types_1.isIdlTypeDefined)(ty)) {
const resolvedType = resolveType(ty.defined.name);
if ((0, types_1.isIdlTypeDefined)(resolvedType) && ((_a = ty.defined.generics) === null || _a === void 0 ? void 0 : _a.length)) {
const mapped = {
defined: {
...resolvedType.defined,
generics: (_b = ty.defined.generics) === null || _b === void 0 ? void 0 : _b.map((t) => ({
kind: 'type',
type: transformType(t.type),
})),
},
};
return mapped;
}
return resolvedType;
}
if (typeof ty === 'string') {
let k = ty.toLocaleLowerCase();
if (k in premitiveTypes) {
// @ts-ignore
return premitiveTypes[k];
}
}
return ty;
}
const resolveType = (strType) => {
if (strType.toLocaleLowerCase() in premitiveTypes) {
// @ts-ignore
return premitiveTypes[strType.toLocaleLowerCase()];
}
for (let mapper of customTypeMappers) {
const type = mapper(strType, resolveType, (def) => generatedTypes.set(def.name, def));
if (type)
return type;
}
return idlTypeFallback(strType, resolveType) || { defined: { name: strType } };
};
// -----------------
// Instruction
// -----------------
//# sourceMappingURL=transform-type.js.map