diff --git a/ts/packages/anchor-upgrade/README.md b/ts/packages/anchor-upgrade/README.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ts/packages/anchor-upgrade/jest.config.js b/ts/packages/anchor-upgrade/jest.config.js new file mode 100644 index 0000000000..502cbf0df0 --- /dev/null +++ b/ts/packages/anchor-upgrade/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + preset: 'ts-jest/presets/default', + testEnvironment: 'node', + testTimeout: 90000, + resolver: "ts-jest-resolver", +}; diff --git a/ts/packages/anchor-upgrade/package.json b/ts/packages/anchor-upgrade/package.json new file mode 100644 index 0000000000..e9e58968f4 --- /dev/null +++ b/ts/packages/anchor-upgrade/package.json @@ -0,0 +1,72 @@ +{ + "name": "@anchor-lang/core-upgrade", + "version": "0.1.0", + "description": "Anchor upgraded client", + "module": "./dist/esm/index.js", + "main": "./dist/cjs/index.js", + "browser": "./dist/browser/index.js", + "license": "(MIT OR Apache-2.0)", + "types": "dist/cjs/index.d.ts", + "homepage": "https://github.com/coral-xyz/anchor#readme", + "bugs": { + "url": "https://github.com/coral-xyz/anchor/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/coral-xyz/anchor.git" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=17" + }, + "scripts": { + "build": "rimraf dist/ && yarn build:node && yarn build:browser", + "build:node": "tsc && tsc -p tsconfig.cjs.json", + "build:browser": "rollup --config", + "lint:fix": "prettier src/** tests/** -w", + "lint": "prettier src/** tests/** --check", + "watch": "tsc -p tsconfig.cjs.json --watch", + "prepublishOnly": "yarn build", + "docs": "typedoc --excludePrivate --includeVersion --out ../../../docs/src/.vuepress/dist/ts/ --readme none src/index.ts", + "test": "jest tests --detectOpenHandles" + }, + "dependencies": { + "@solana/kit": "6.1.0", + "camelcase": "^6.3.0", + "pako": "^2.0.3", + "toml": "^3.0.0" + }, + "devDependencies": { + "@commitlint/cli": "^11.0.0", + "@commitlint/config-conventional": "^11.0.0", + "@rollup/plugin-commonjs": "^21.0.1", + "@rollup/plugin-node-resolve": "^13.0.6", + "@rollup/plugin-replace": "^3.0.0", + "@rollup/plugin-typescript": "^8.3.0", + "@types/jest": "^27.4.1", + "@types/pako": "^1.0.1", + "@typescript-eslint/eslint-plugin": "^4.6.0", + "@typescript-eslint/parser": "^4.6.0", + "eslint": "^7.12.1", + "eslint-config-prettier": "^6.15.0", + "husky": "^4.3.0", + "jest": "27.3.1", + "jest-config": "27.3.1", + "lint-staged": "^10.5.0", + "prettier": "^2.1.2", + "rimraf": "^3.0.2", + "rollup": "^2.60.2", + "ts-jest": "^27.0.7", + "ts-jest-resolver": "^2.0.0", + "ts-node": "^9.0.0", + "tslib": "^2.3.1", + "typedoc": "^0.22.10", + "typescript": "^5.5.4" + }, + "files": [ + "dist", + "types" + ] +} diff --git a/ts/packages/anchor-upgrade/rollup.config.ts b/ts/packages/anchor-upgrade/rollup.config.ts new file mode 100644 index 0000000000..ef0d9f7fda --- /dev/null +++ b/ts/packages/anchor-upgrade/rollup.config.ts @@ -0,0 +1,37 @@ +import typescript from "@rollup/plugin-typescript"; +import replace from "@rollup/plugin-replace"; +import commonjs from "@rollup/plugin-commonjs"; + +const env = process.env.NODE_ENV; + +export default { + input: "src/index.ts", + plugins: [ + commonjs(), + typescript({ + tsconfig: "./tsconfig.base.json", + moduleResolution: "node", + outDir: "types", + target: "es2019", + outputToFilesystem: false, + }), + replace({ + preventAssignment: true, + values: { + "process.env.NODE_ENV": JSON.stringify(env), + "process.env.ANCHOR_BROWSER": JSON.stringify(true), + }, + }), + ], + external: [ + "@solana/kit", + "camelcase", + "pako", + "toml", + ], + output: { + file: "dist/browser/index.js", + format: "es", + sourcemap: true, + }, +}; diff --git a/ts/packages/anchor-upgrade/src/clients/account-client.ts b/ts/packages/anchor-upgrade/src/clients/account-client.ts new file mode 100644 index 0000000000..405c249ab1 --- /dev/null +++ b/ts/packages/anchor-upgrade/src/clients/account-client.ts @@ -0,0 +1,25 @@ +import { AccountCodec } from '../codec/account-codec'; +import type { AccountByName, AccountName, DecodedAccount, Idl } from '../types'; + +export class AccountClient> { + private readonly codec: AccountCodec>; + + constructor( + private readonly idl: IDL, + private readonly accountDef: AccountByName, + ) { + this.codec = new AccountCodec>(this.idl, this.accountDef); + } + + get discriminator(): Uint8Array { + return Uint8Array.from(this.accountDef.discriminator); + } + + encode(data: DecodedAccount): Uint8Array { + return this.codec.encode(data); + } + + decode(data: Uint8Array): DecodedAccount { + return this.codec.decode(data); + } +} diff --git a/ts/packages/anchor-upgrade/src/clients/constant-client.ts b/ts/packages/anchor-upgrade/src/clients/constant-client.ts new file mode 100644 index 0000000000..e24f470adf --- /dev/null +++ b/ts/packages/anchor-upgrade/src/clients/constant-client.ts @@ -0,0 +1,17 @@ +import { decodeIdlConstValue } from '../codec/type-codec'; +import type { ConstantByName, ConstantName, DecodedConstant, Idl } from '../types'; + +export class ConstantClient> { + constructor( + private readonly idl: IDL, + private readonly constantDef: ConstantByName, + ) {} + + get value() { + return decodeIdlConstValue( + this.constantDef.type, + this.constantDef.value, + this.idl.types ?? [], + ) as DecodedConstant; + } +} diff --git a/ts/packages/anchor-upgrade/src/clients/event-client.ts b/ts/packages/anchor-upgrade/src/clients/event-client.ts new file mode 100644 index 0000000000..d956abf1f4 --- /dev/null +++ b/ts/packages/anchor-upgrade/src/clients/event-client.ts @@ -0,0 +1,16 @@ +import { EventCodec } from '../codec/event-codec'; +import type { DecodedEvent, EventByName, EventName, Idl } from '../types'; + +export class EventClient> { + private readonly codec: EventCodec>; + constructor( + private readonly idl: IDL, + private readonly eventDef: EventByName, + ) { + this.codec = new EventCodec>(this.idl, this.eventDef); + } + + decode(base64Log: string): DecodedEvent { + return this.codec.decode(base64Log); + } +} diff --git a/ts/packages/anchor-upgrade/src/clients/index.ts b/ts/packages/anchor-upgrade/src/clients/index.ts new file mode 100644 index 0000000000..8c22173f93 --- /dev/null +++ b/ts/packages/anchor-upgrade/src/clients/index.ts @@ -0,0 +1,4 @@ +export * from './account-client'; +export * from './constant-client'; +export * from './event-client'; +export * from './instruction-client'; diff --git a/ts/packages/anchor-upgrade/src/clients/instruction-client.ts b/ts/packages/anchor-upgrade/src/clients/instruction-client.ts new file mode 100644 index 0000000000..282507b046 --- /dev/null +++ b/ts/packages/anchor-upgrade/src/clients/instruction-client.ts @@ -0,0 +1,60 @@ +import { type Address, getAddressDecoder } from '@solana/kit'; + +import { InstructionCodec } from '../codec/instruction-codec'; +import { IdlError } from '../error'; +import type { + Idl, + InstructionAccountName, + InstructionArgs, + InstructionByName, + InstructionName, +} from '../types'; +import { flattenInstructionAccounts } from '../utils/instruction-accounts'; + +export class InstructionClient> { + private readonly codec: InstructionCodec>; + private readonly accountIndexByName: Map; + + constructor( + private readonly idl: IDL, + private readonly instructionDef: InstructionByName, + ) { + if (instructionDef.name === '_inner') { + throw new IdlError('The _inner name is reserved'); + } + + this.accountIndexByName = new Map( + flattenInstructionAccounts(instructionDef.accounts).map((account, index) => [ + account.name, + index, + ]), + ); + + this.codec = new InstructionCodec>(this.idl, this.instructionDef); + } + + get discriminator(): Uint8Array { + return Uint8Array.from(this.instructionDef.discriminator); + } + + encode(data: InstructionArgs): Uint8Array { + return this.codec.encode(data); + } + + decode(data: Uint8Array): InstructionArgs { + return this.codec.decode(data); + } + + getAccount( + accountName: InstructionAccountName, + instructionAccounts: Uint8Array[], + ): Address { + const acc = instructionAccounts[this.accountIndexByName.get(accountName)!]; + + if (!acc) { + throw new IdlError('Account not found'); + } + + return getAddressDecoder().decode(acc); + } +} diff --git a/ts/packages/anchor-upgrade/src/codec/account-codec.ts b/ts/packages/anchor-upgrade/src/codec/account-codec.ts new file mode 100644 index 0000000000..fa8db146d1 --- /dev/null +++ b/ts/packages/anchor-upgrade/src/codec/account-codec.ts @@ -0,0 +1,34 @@ +import type { Idl, IdlDiscriminator, NullableIdlAccount } from '../types'; +import type { IdlCodec } from './type-codec'; +import { + assertDiscriminator, + getTypeDefLayoutByName, + stripDiscriminator, +} from './type-codec/helpers'; + +export class AccountCodec { + private readonly discriminator: IdlDiscriminator; + private readonly layout: IdlCodec; + + constructor(idl: Idl, accountDef: NullableIdlAccount) { + this.discriminator = accountDef.discriminator; + this.layout = getTypeDefLayoutByName(idl, accountDef.name, 'account').layout; + } + + encode(accountData: T): Uint8Array { + const encodedRaw = this.layout.encode(accountData); + const encoded = new Uint8Array( + encodedRaw.buffer, + encodedRaw.byteOffset, + encodedRaw.byteLength + ); + + return Uint8Array.from([...this.discriminator, ...encoded]) + } + + decode(encodedData: Uint8Array): T { + assertDiscriminator(encodedData, this.discriminator, 'Invalid account discriminator'); + + return this.layout.decode(stripDiscriminator(encodedData, this.discriminator)) as T; + } +} diff --git a/ts/packages/anchor-upgrade/src/codec/event-codec.ts b/ts/packages/anchor-upgrade/src/codec/event-codec.ts new file mode 100644 index 0000000000..3b43ac8811 --- /dev/null +++ b/ts/packages/anchor-upgrade/src/codec/event-codec.ts @@ -0,0 +1,25 @@ +import type { Idl, IdlDiscriminator, NullableIdlEvent } from '../types'; +import type { IdlCodec } from './type-codec'; +import { + assertDiscriminator, + getTypeDefLayoutByName, + stripDiscriminator, +} from './type-codec/helpers'; + +export class EventCodec { + private readonly discriminator: IdlDiscriminator; + private readonly layout: IdlCodec; + + constructor(idl: Idl, eventDef: NullableIdlEvent) { + this.discriminator = eventDef.discriminator; + this.layout = getTypeDefLayoutByName(idl, eventDef.name, 'event').layout; + } + + decode(base64Log: string): T { + const encodedData = Uint8Array.from(Buffer.from(base64Log, 'base64')); + + assertDiscriminator(encodedData, this.discriminator, 'Invalid event discriminator'); + + return this.layout.decode(stripDiscriminator(encodedData, this.discriminator)) as T; + } +} diff --git a/ts/packages/anchor-upgrade/src/codec/index.ts b/ts/packages/anchor-upgrade/src/codec/index.ts new file mode 100644 index 0000000000..f71799fd4f --- /dev/null +++ b/ts/packages/anchor-upgrade/src/codec/index.ts @@ -0,0 +1,3 @@ +export * from './account-codec'; +export * from './event-codec'; +export * from './instruction-codec'; diff --git a/ts/packages/anchor-upgrade/src/codec/instruction-codec.ts b/ts/packages/anchor-upgrade/src/codec/instruction-codec.ts new file mode 100644 index 0000000000..ace65993ea --- /dev/null +++ b/ts/packages/anchor-upgrade/src/codec/instruction-codec.ts @@ -0,0 +1,36 @@ +import { getStructCodec } from '@solana/kit'; + +import type { Idl, IdlDiscriminator, IdlInstruction } from '../types'; +import { fieldLayout, type IdlCodec, type IdlNamedCodec } from './type-codec'; +import { assertDiscriminator, stripDiscriminator } from './type-codec/helpers'; + +export class InstructionCodec { + private readonly discriminator: IdlDiscriminator; + private readonly layout: IdlCodec; + + constructor(idl: Idl, instructionDef: IdlInstruction) { + const fieldCodecs = instructionDef.args.map( + (arg) => fieldLayout(arg, idl.types) as IdlNamedCodec, + ); + + this.discriminator = instructionDef.discriminator; + this.layout = getStructCodec(fieldCodecs); + } + + encode(instructionData: T): Uint8Array { + const encodedRaw = this.layout.encode(instructionData); + const encoded = new Uint8Array( + encodedRaw.buffer, + encodedRaw.byteOffset, + encodedRaw.byteLength + ); + + return Uint8Array.from([...this.discriminator, ...encoded]) + } + + decode(encodedData: Uint8Array): T { + assertDiscriminator(encodedData, this.discriminator, 'Invalid instruction discriminator'); + + return this.layout.decode(stripDiscriminator(encodedData, this.discriminator)) as T; + } +} diff --git a/ts/packages/anchor-upgrade/src/codec/type-codec/helpers.ts b/ts/packages/anchor-upgrade/src/codec/type-codec/helpers.ts new file mode 100644 index 0000000000..dbc8a617c4 --- /dev/null +++ b/ts/packages/anchor-upgrade/src/codec/type-codec/helpers.ts @@ -0,0 +1,46 @@ +import { IdlError } from '../../error'; +import type { Idl, IdlDiscriminator } from '../../types'; +import { type IdlCodec, typeDefLayout } from './idl'; + +export function getTypeDefLayoutByName( + idl: Idl, + name: string, + kind: 'account' | 'event', +): { discriminator?: IdlDiscriminator; layout: IdlCodec } { + const { types } = idl; + + if (!types) { + throw new IdlError(`${capitalize(kind)}s require \`idl.types\``); + } + + const typeDef = types.find((item) => item.name === name); + + if (!typeDef) { + throw new IdlError(`${capitalize(kind)} not found: ${name}`); + } + + return { + layout: typeDefLayout({ typeDef, types }), + }; +} + +export function assertDiscriminator( + actualData: Uint8Array, + expectedDiscriminator: IdlDiscriminator, + errorMessage: string, +) { + const expected = Buffer.from(expectedDiscriminator); + const actual = actualData.subarray(0, expected.length); + + if (expected.compare(actual) !== 0) { + throw new IdlError(errorMessage); + } +} + +export function stripDiscriminator(data: Uint8Array, discriminator: IdlDiscriminator): Uint8Array { + return data.subarray(discriminator.length); +} + +function capitalize(value: string): string { + return value.charAt(0).toUpperCase() + value.slice(1); +} diff --git a/ts/packages/anchor-upgrade/src/codec/type-codec/idl-constant.ts b/ts/packages/anchor-upgrade/src/codec/type-codec/idl-constant.ts new file mode 100644 index 0000000000..95a292aaba --- /dev/null +++ b/ts/packages/anchor-upgrade/src/codec/type-codec/idl-constant.ts @@ -0,0 +1,228 @@ +import { IdlError } from '../../error'; +import type { IdlField, IdlType, IdlTypeDef } from '../../types'; + +export function decodeIdlConstValue(type: IdlType, rawValue: string, types: IdlTypeDef[]): unknown { + rawValue = rawValue.trim(); + + switch (type) { + case 'string': + case 'pubkey': + return rawValue; + + case 'bool': + return parseBoolean(rawValue); + + case 'u8': + case 'i8': + case 'u16': + case 'i16': + case 'u32': + case 'i32': + case 'f32': + case 'f64': + return Number(rawValue); + + case 'u64': + case 'i64': + case 'u128': + case 'i128': + case 'u256': + case 'i256': + return rawValue.at(0) === '-' ? Number(rawValue) : BigInt(rawValue); + + case 'bytes': + return Uint8Array.from(parseJson(rawValue)); + + default: + if ('option' in type) { + return decodeOptionalValue(type.option, rawValue, types); + } + + if ('coption' in type) { + return decodeOptionalValue(type.coption, rawValue, types); + } + + if ('vec' in type) { + return decodeArrayValues(type.vec, rawValue, types); + } + + if ('array' in type) { + const [elementType] = type.array; + return decodeArrayValues(elementType, rawValue, types); + } + + if ('defined' in type) { + return decodeTypeDefValue(requireTypeDef(types, type.defined.name), rawValue, types); + } + + if ('generic' in type) { + throw new IdlError(`Generic constants are not supported: ${type.generic}`); + } + + throw new IdlError(`Constant decoding is not implemented for type: ${JSON.stringify(type)}`); + } +} + +function decodeOptionalValue(innerType: IdlType, rawValue: string, types: IdlTypeDef[]): unknown { + if (rawValue === 'null' || rawValue === 'None') { + return null; + } + + const someMatch = rawValue.match(/^Some\((.*)\)$/s); + + return decodeIdlConstValue(innerType, someMatch ? someMatch[1]! : rawValue, types); +} + +function decodeArrayValues(elementType: IdlType, rawValue: string, types: IdlTypeDef[]): unknown[] { + return expectArray(parseJson(rawValue), rawValue).map((value) => + decodeFromUnknown(elementType, value, types), + ); +} + +function decodeTypeDefValue(typeDef: IdlTypeDef, rawValue: string, types: IdlTypeDef[]): unknown { + switch (typeDef.type.kind) { + case 'type': + return decodeIdlConstValue(typeDef.type.alias, rawValue, types); + + case 'struct': { + const fields = typeDef.type.fields; + + if (!fields) { + return {}; + } + + const parsed = parseJson(rawValue); + + if (isNamedFields(fields)) { + const record = expectRecord(parsed, rawValue); + + return Object.fromEntries( + fields.map((field) => [ + field.name, + decodeFromUnknown(field.type, record[field.name], types), + ]), + ); + } + + const tupleValues = expectArray(parsed, rawValue); + + return fields.map((fieldType, index) => + decodeFromUnknown(fieldType, tupleValues[index], types), + ); + } + + case 'enum': { + const parsed = parseJson(rawValue); + + if (typeof parsed === 'string') { + const variant = requireEnumVariant(typeDef, parsed); + + if (variant.fields) { + throw new IdlError(`Enum variant ${parsed} in ${typeDef.name} requires payload`); + } + + return { [parsed]: {} }; + } + + const record = expectRecord(parsed, rawValue); + const entries = Object.entries(record); + + if (entries.length !== 1) { + throw new IdlError(`Enum ${typeDef.name} must contain exactly one variant`); + } + + const [variantName, variantPayload] = entries[0]!; + const variant = requireEnumVariant(typeDef, variantName); + + if (!variant.fields) { + return { [variantName]: {} }; + } + + if (isNamedFields(variant.fields)) { + const payloadRecord = expectRecord(variantPayload, JSON.stringify(variantPayload)); + + return { + [variantName]: Object.fromEntries( + variant.fields.map((field) => [ + field.name, + decodeFromUnknown(field.type, payloadRecord[field.name], types), + ]), + ), + }; + } + + const tuplePayload = expectArray(variantPayload, JSON.stringify(variantPayload)); + + return { + [variantName]: variant.fields.map((fieldType, index) => + decodeFromUnknown(fieldType, tuplePayload[index], types), + ), + }; + } + } +} + +function decodeFromUnknown(type: IdlType, value: unknown, types: IdlTypeDef[]): unknown { + return decodeIdlConstValue(type, JSON.stringify(value), types); +} + +function requireTypeDef(types: IdlTypeDef[], name: string): IdlTypeDef { + const typeDef = types.find((item) => item.name === name); + + if (!typeDef) { + throw new IdlError(`Type not found: ${name}`); + } + + return typeDef; +} + +function requireEnumVariant(typeDef: IdlTypeDef, variantName: string) { + if (typeDef.type.kind !== 'enum') { + throw new IdlError(`Type ${typeDef.name} is not an enum`); + } + + const variant = typeDef.type.variants.find((item) => item.name === variantName); + + if (!variant) { + throw new IdlError(`Variant not found in ${typeDef.name}: ${variantName}`); + } + + return variant; +} + +function parseJson(rawValue: string): T { + try { + return JSON.parse(rawValue) as T; + } catch { + throw new IdlError(`Invalid JSON constant value: ${rawValue}`); + } +} + +function parseBoolean(rawValue: string): boolean { + if (rawValue === 'true') return true; + if (rawValue === 'false') return false; + + throw new IdlError(`Invalid bool constant: ${rawValue}`); +} + +function expectArray(value: unknown, rawValue: string): unknown[] { + if (!Array.isArray(value)) { + throw new IdlError(`Expected array constant: ${rawValue}`); + } + + return value; +} + +function expectRecord(value: unknown, rawValue: string): Record { + if (value === null || typeof value !== 'object' || Array.isArray(value)) { + throw new IdlError(`Expected object constant: ${rawValue}`); + } + + return value as Record; +} + +function isNamedFields(fields: readonly unknown[]): fields is readonly IdlField[] { + return fields.every( + (field) => typeof field === 'object' && field !== null && 'name' in field && 'type' in field, + ); +} diff --git a/ts/packages/anchor-upgrade/src/codec/type-codec/idl.ts b/ts/packages/anchor-upgrade/src/codec/type-codec/idl.ts new file mode 100644 index 0000000000..9bf3d90579 --- /dev/null +++ b/ts/packages/anchor-upgrade/src/codec/type-codec/idl.ts @@ -0,0 +1,402 @@ +import { + addCodecSizePrefix, + type Codec, + getAddressCodec, + getArrayCodec, + getBooleanCodec, + getDiscriminatedUnionCodec, + getF32Codec, + getF64Codec, + getI8Codec, + getI16Codec, + getI32Codec, + getI64Codec, + getI128Codec, + getOptionCodec, + getStructCodec, + getU8Codec, + getU16Codec, + getU32Codec, + getU64Codec, + getU128Codec, + getUnitCodec, + getUtf8Codec, + type Option, + transformCodec, +} from '@solana/kit'; + +import { IdlError } from '../../error'; +import type { + IdlArrayLen, + IdlField, + IdlGenericArg, + IdlGenericArgConst, + IdlType, + IdlTypeDef, +} from '../../types'; +import { handleDefinedFields } from '../../utils'; + +export type IdlCodec = Codec; +export type IdlNamedCodec = [string, IdlCodec]; + +type PartialField = { name?: string } & Pick; + +function withFieldName(codec: IdlCodec, fieldName?: string): IdlNamedCodec | IdlCodec { + return fieldName ? [fieldName, codec] : codec; +} + +function getStringCodec(): Codec { + return addCodecSizePrefix(getUtf8Codec(), getU32Codec()); +} + +function getU256Codec(): Codec { + throw new IdlError('u256 is not provided by @solana/kit; implement a custom 32-byte LE codec'); +} + +function getI256Codec(): Codec { + throw new IdlError('i256 is not provided by @solana/kit; implement a custom 32-byte LE codec'); +} + +function resolveArrayLen(len: IdlArrayLen, genericArgs?: IdlGenericArg[] | null): number { + if (typeof len === 'number') return len; + + if (genericArgs) { + const genericLen = genericArgs.find((g) => g.kind === 'const'); + if (genericLen?.kind === 'const') { + len = +genericLen.value; + } + } + + if (typeof len !== 'number') { + throw new IdlError('Generic array length did not resolve'); + } + + return len; +} + +function getNullableOptionCodec(innerCodec: IdlCodec): IdlCodec { + return transformCodec( + getOptionCodec(innerCodec), + (value: unknown | null) => + value === null ? { __option: 'None' } : { __option: 'Some', value }, + (value: Option) => (value.__option === 'Some' ? value.value : null), + ); +} + +function resolveGenericArgs({ + type, + typeDef, + genericArgs, + isDefined, +}: { + type: IdlType; + typeDef: IdlTypeDef; + genericArgs: IdlGenericArg[]; + isDefined?: boolean | undefined; +}): IdlGenericArg[] | null { + if (typeof type !== 'object') return null; + + const defGenerics = typeDef.generics ?? []; + + for (const [index, defGeneric] of defGenerics.entries()) { + if ('generic' in type && defGeneric.name === type.generic) { + return [genericArgs[index]!]; + } + + if ('option' in type) { + const args = resolveGenericArgs({ + type: type.option, + typeDef, + genericArgs, + isDefined, + }); + + if (!args || !isDefined) return args; + + if (args[0]?.kind === 'type') { + return [{ kind: 'type', type: { option: args[0].type } }]; + } + } + + if ('vec' in type) { + const args = resolveGenericArgs({ + type: type.vec, + typeDef, + genericArgs, + isDefined, + }); + + if (!args || !isDefined) return args; + + if (args[0]?.kind === 'type') { + return [{ kind: 'type', type: { vec: args[0].type } }]; + } + } + + if ('array' in type) { + const [elTy, len] = type.array; + const isGenericLen = typeof len === 'object'; + + const args = + resolveGenericArgs({ + type: elTy, + typeDef, + genericArgs, + isDefined, + }) ?? []; + + if (isGenericLen) { + const matchingGeneric = defGenerics.findIndex((g) => g.name === len.generic); + if (matchingGeneric !== -1) { + args.push(genericArgs[matchingGeneric]!); + } + } + + if (args.length > 0) { + if (!isDefined) return args; + + if (args[0]?.kind === 'type' && args[1]?.kind === 'const') { + return [ + { + kind: 'type', + type: { array: [args[0].type, +args[1].value] }, + }, + ]; + } + } + + if (isGenericLen && defGeneric.name === len.generic) { + const arg = genericArgs[index]!; + if (!isDefined) return [arg]; + + return [ + { + kind: 'type', + type: { array: [elTy, +(arg as IdlGenericArgConst).value] }, + }, + ]; + } + + return null; + } + + if ('defined' in type) { + if (!type.defined.generics) return null; + + return type.defined.generics + .flatMap((g) => { + switch (g.kind) { + case 'type': + return ( + resolveGenericArgs({ + type: g.type, + typeDef, + genericArgs, + isDefined: true, + }) ?? [] + ); + case 'const': + return [g]; + + default: + return []; + } + }) + .filter(Boolean) as IdlGenericArg[]; + } + } + + return null; +} + +export function fieldCodec( + type: IdlType, + types: IdlTypeDef[] = [], + genericArgs?: IdlGenericArg[] | null, +): IdlCodec { + switch (type) { + case 'bool': + return getBooleanCodec(); + case 'u8': + return getU8Codec(); + case 'i8': + return getI8Codec(); + case 'u16': + return getU16Codec(); + case 'i16': + return getI16Codec(); + case 'u32': + return getU32Codec(); + case 'i32': + return getI32Codec(); + case 'f32': + return getF32Codec(); + case 'u64': + return getU64Codec(); + case 'i64': + return getI64Codec(); + case 'f64': + return getF64Codec(); + case 'u128': + return getU128Codec(); + case 'i128': + return getI128Codec(); + case 'u256': + return getU256Codec(); + case 'i256': + return getI256Codec(); + case 'bytes': + return getArrayCodec(getU8Codec(), { size: getU32Codec() }); + case 'string': + return getStringCodec(); + case 'pubkey': + return getAddressCodec(); + default: { + // if ('сoption' in type) { + // return getNullableOptionCodec(fieldCodec(type.сoption, types, genericArgs)); + // } + + if ('option' in type) { + return getNullableOptionCodec(fieldCodec(type.option, types, genericArgs)); + } + + if ('vec' in type) { + return getArrayCodec(fieldCodec(type.vec, types, genericArgs), { + size: getU32Codec(), + }); + } + + if ('array' in type) { + const [innerType, len] = type.array; + return getArrayCodec(fieldCodec(innerType, types, genericArgs), { + size: resolveArrayLen(len, genericArgs), + }); + } + + if ('defined' in type) { + const typeDef = types.find((t) => t.name === type.defined.name); + if (!typeDef) { + throw new IdlError(`Type not found: ${type.defined.name}`); + } + + return typeDefLayout({ + typeDef, + types, + genericArgs: genericArgs ?? type.defined.generics ?? null, + }); + } + + if ('generic' in type) { + const genericArg = genericArgs?.at(0); + if (genericArg?.kind !== 'type') { + throw new IdlError(`Invalid generic field: ${type.generic}`); + } + return fieldCodec(genericArg.type, types); + } + + throw new IdlError(`Not yet implemented: ${JSON.stringify(type)}`); + } + } +} + +export function fieldLayout( + field: PartialField, + types: IdlTypeDef[] = [], + genericArgs?: IdlGenericArg[] | null, +): IdlNamedCodec | IdlCodec { + return withFieldName(fieldCodec(field.type, types, genericArgs), field.name); +} + +export function typeDefLayout({ + typeDef, + types, + genericArgs, +}: { + typeDef: IdlTypeDef; + types: IdlTypeDef[]; + genericArgs?: IdlGenericArg[] | null; +}): IdlCodec { + switch (typeDef.type.kind) { + case 'struct': { + const fieldLayouts = handleDefinedFields( + typeDef.type.fields, + () => [], + (fields) => + fields.map((f) => { + const genArgs = genericArgs + ? resolveGenericArgs({ + type: f.type, + typeDef, + genericArgs, + }) + : genericArgs; + return fieldLayout(f, types, genArgs) as IdlNamedCodec; + }), + (fields) => + fields.map((f, i) => { + const genArgs = genericArgs + ? resolveGenericArgs({ + type: f, + typeDef, + genericArgs, + }) + : genericArgs; + return fieldLayout({ name: i.toString(), type: f }, types, genArgs) as IdlNamedCodec; + }), + ); + + return getStructCodec(fieldLayouts); + } + + case 'enum': { + const variants = typeDef.type.variants.map((variant) => { + const variantCodec = handleDefinedFields( + variant.fields, + () => getUnitCodec(), + (fields) => + getStructCodec( + fields.map((f) => { + const genArgs = genericArgs + ? resolveGenericArgs({ + type: f.type, + typeDef, + genericArgs, + }) + : genericArgs; + + return fieldLayout(f, types, genArgs) as IdlNamedCodec; + }), + ), + (fields) => + getStructCodec( + fields.map((f, i) => { + const genArgs = genericArgs + ? resolveGenericArgs({ + type: f, + typeDef, + genericArgs, + }) + : genericArgs; + + return fieldLayout( + { name: i.toString(), type: f }, + types, + genArgs, + ) as IdlNamedCodec; + }), + ), + ); + + return [variant.name, variantCodec] as const; + }); + + return getDiscriminatedUnionCodec(variants as never, { + discriminator: '__kind', + size: getU8Codec(), + }) as IdlCodec; + } + + case 'type': + return fieldCodec(typeDef.type.alias, types, genericArgs); + } +} diff --git a/ts/packages/anchor-upgrade/src/codec/type-codec/index.ts b/ts/packages/anchor-upgrade/src/codec/type-codec/index.ts new file mode 100644 index 0000000000..d95e8ad47a --- /dev/null +++ b/ts/packages/anchor-upgrade/src/codec/type-codec/index.ts @@ -0,0 +1,2 @@ +export * from './idl'; +export * from './idl-constant'; diff --git a/ts/packages/anchor-upgrade/src/define-idl.ts b/ts/packages/anchor-upgrade/src/define-idl.ts new file mode 100644 index 0000000000..1b38f23375 --- /dev/null +++ b/ts/packages/anchor-upgrade/src/define-idl.ts @@ -0,0 +1,21 @@ +import type { AnyFn, Idl, Primitive } from './types'; + +type DeepReadonly = T extends Primitive | AnyFn + ? T + : T extends readonly [unknown, ...unknown[]] + ? { readonly [K in keyof T]: DeepReadonly } + : T extends readonly (infer U)[] + ? readonly DeepReadonly[] + : { readonly [K in keyof T]: DeepReadonly }; + +type DeepMutable = T extends Primitive | AnyFn + ? T + : T extends readonly [unknown, ...unknown[]] + ? { -readonly [K in keyof T]: DeepMutable } + : T extends readonly (infer U)[] + ? DeepMutable[] + : { -readonly [K in keyof T]: DeepMutable }; + +export function defineIdl>(idl: T): DeepMutable { + return idl as DeepMutable; +} diff --git a/ts/packages/anchor-upgrade/src/error.ts b/ts/packages/anchor-upgrade/src/error.ts new file mode 100644 index 0000000000..0ec5f696f0 --- /dev/null +++ b/ts/packages/anchor-upgrade/src/error.ts @@ -0,0 +1,6 @@ +export class IdlError extends Error { + constructor(message: string) { + super(message); + this.name = 'IdlError'; + } +} diff --git a/ts/packages/anchor-upgrade/src/index.ts b/ts/packages/anchor-upgrade/src/index.ts new file mode 100644 index 0000000000..08874b76b2 --- /dev/null +++ b/ts/packages/anchor-upgrade/src/index.ts @@ -0,0 +1,2 @@ +export * from './define-idl'; +export * from './parser'; diff --git a/ts/packages/anchor-upgrade/src/parser.ts b/ts/packages/anchor-upgrade/src/parser.ts new file mode 100644 index 0000000000..dd482c0a6e --- /dev/null +++ b/ts/packages/anchor-upgrade/src/parser.ts @@ -0,0 +1,69 @@ +import { AccountClient, ConstantClient, EventClient, InstructionClient } from './clients'; +import type { + AccountName, + CamelizedIdl, + ConstantName, + EventName, + Idl, + InstructionName, +} from './types'; +import { convertIdlToCamelCase } from './utils'; + +type AccountRegistry = { + [N in AccountName]: AccountClient; +}; + +type InstructionRegistry = { + [N in InstructionName]: InstructionClient; +}; + +type EventRegistry = { + [N in EventName]: EventClient; +}; + +type ConstantRegistry = { + [N in ConstantName]: ConstantClient; +}; + +type NamedItem = { name: string }; + +export class IdlParser { + readonly idl: CamelizedIdl; + + readonly accounts: AccountRegistry>; + readonly instructions: InstructionRegistry>; + readonly events: EventRegistry>; + readonly constants: ConstantRegistry>; + + constructor(rawIdl: RawIDL) { + this.idl = convertIdlToCamelCase(rawIdl); + + this.accounts = this.buildRegistry( + this.idl.accounts, + (df) => new AccountClient(this.idl, df as never), + ); + + this.instructions = this.buildRegistry( + this.idl.instructions, + (df) => new InstructionClient(this.idl, df as never), + ); + + this.events = this.buildRegistry( + this.idl.events, + (df) => new EventClient(this.idl, df as never), + ); + + this.constants = this.buildRegistry( + this.idl.constants, + (df) => new ConstantClient(this.idl, df as never), + ); + } + + private buildRegistry>( + items: readonly TItem[] | undefined, + createValue: (item: TItem) => TRegistry[keyof TRegistry], + ): TRegistry { + const entries = (items ?? []).map((item) => [item.name, createValue(item)] as const); + return Object.fromEntries(entries) as TRegistry; + } +} diff --git a/ts/packages/anchor-upgrade/src/types/camelize-idl.ts b/ts/packages/anchor-upgrade/src/types/camelize-idl.ts new file mode 100644 index 0000000000..680205fe93 --- /dev/null +++ b/ts/packages/anchor-upgrade/src/types/camelize-idl.ts @@ -0,0 +1,31 @@ +import type { Primitive } from './common'; + +type TargetKey = 'relations' | 'account' | 'path' | 'name' | 'generic'; + +type CamelTail = S extends `${infer Head}_${infer Tail}` + ? `${Capitalize>}${CamelTail}` + : Capitalize>; + +type CamelCase = S extends `_${infer Rest}` + ? `_${CamelCase}` + : S extends `${infer Head}_${infer Tail}` + ? `${Lowercase}${CamelTail}` + : Uncapitalize; + +type WalkField = K extends TargetKey + ? V extends string + ? CamelCase + : RecursiveWalk + : V extends Primitive + ? V + : RecursiveWalk; + +type RecursiveWalk = T extends Primitive + ? T + : T extends readonly unknown[] + ? { [I in keyof T]: RecursiveWalk } + : T extends object + ? { [K in keyof T]: WalkField } + : T; + +export type CamelizedIdl = RecursiveWalk; diff --git a/ts/packages/anchor-upgrade/src/types/common.ts b/ts/packages/anchor-upgrade/src/types/common.ts new file mode 100644 index 0000000000..db97812f2d --- /dev/null +++ b/ts/packages/anchor-upgrade/src/types/common.ts @@ -0,0 +1,2 @@ +export type Primitive = string | number | boolean | bigint | symbol | null | undefined; +export type AnyFn = (...args: never[]) => unknown; diff --git a/ts/packages/anchor-upgrade/src/types/idl.ts b/ts/packages/anchor-upgrade/src/types/idl.ts new file mode 100644 index 0000000000..adf5c74319 --- /dev/null +++ b/ts/packages/anchor-upgrade/src/types/idl.ts @@ -0,0 +1,252 @@ +export type Idl = { + address: string; + metadata: IdlMetadata; + docs?: string[]; + instructions: IdlInstruction[]; + accounts?: IdlAccount[]; + events?: IdlEvent[]; + errors?: IdlErrorCode[]; + types?: IdlTypeDef[]; + constants?: IdlConst[]; +}; + +export type IdlMetadata = { + name: string; + version: string; + spec: string; + description?: string; + repository?: string; + dependencies?: IdlDependency[]; + contact?: string; + deployments?: IdlDeployments; +}; + +export type IdlDependency = { + name: string; + version: string; +}; + +export type IdlDeployments = { + mainnet?: string; + testnet?: string; + devnet?: string; + localnet?: string; +}; + +export type IdlInstruction = { + name: string; + docs?: string[]; + discriminator: IdlDiscriminator; + accounts: IdlInstructionAccountItem[]; + args: IdlField[]; + returns?: IdlType; +}; + +export type IdlInstructionAccountItem = IdlInstructionAccount | IdlInstructionAccounts; + +export type IdlInstructionAccount = { + name: string; + docs?: string[]; + writable?: boolean; + signer?: boolean; + optional?: boolean; + address?: string; + pda?: IdlPda; + relations?: string[]; +}; + +export type IdlInstructionAccounts = { + name: string; + accounts: IdlInstructionAccount[]; +}; + +export type IdlPda = { + seeds: IdlSeed[]; + program?: IdlSeed; +}; + +export type IdlSeed = IdlSeedConst | IdlSeedArg | IdlSeedAccount; + +export type IdlSeedConst = { + kind: 'const'; + value: number[]; +}; + +export type IdlSeedArg = { + kind: 'arg'; + path: string; +}; + +export type IdlSeedAccount = { + kind: 'account'; + path: string; + account?: string; +}; + +export type IdlAccount = { + name: string; + discriminator: IdlDiscriminator; +}; + +export type IdlEvent = { + name: string; + discriminator: IdlDiscriminator; +}; + +export type IdlConst = { + name: string; + type: IdlType; + value: string; +}; + +export type IdlErrorCode = { + name: string; + code: number; + msg?: string; +}; + +export type IdlField = { + name: string; + docs?: string[]; + type: IdlType; +}; + +export type IdlTypeDef = { + name: string; + docs?: string[]; + serialization?: IdlSerialization; + repr?: IdlRepr; + generics?: IdlTypeDefGeneric[]; + type: IdlTypeDefTy; +}; + +export type IdlSerialization = 'borsh' | 'bytemuck' | 'bytemuckunsafe' | { custom: string }; + +export type IdlRepr = IdlReprRust | IdlReprC | IdlReprTransparent; + +export type IdlReprRust = { + kind: 'rust'; +} & IdlReprModifier; + +export type IdlReprC = { + kind: 'c'; +} & IdlReprModifier; + +export type IdlReprTransparent = { + kind: 'transparent'; +}; + +export type IdlReprModifier = { + packed?: boolean; + align?: number; +}; + +export type IdlTypeDefGeneric = IdlTypeDefGenericType | IdlTypeDefGenericConst; + +export type IdlTypeDefGenericType = { + kind: 'type'; + name: string; +}; + +export type IdlTypeDefGenericConst = { + kind: 'const'; + name: string; + type: string; +}; + +export type IdlTypeDefTy = IdlTypeDefTyEnum | IdlTypeDefTyStruct | IdlTypeDefTyType; + +export type IdlTypeDefTyStruct = { + kind: 'struct'; + fields?: IdlDefinedFields; +}; + +export type IdlTypeDefTyEnum = { + kind: 'enum'; + variants: IdlEnumVariant[]; +}; + +export type IdlTypeDefTyType = { + kind: 'type'; + alias: IdlType; +}; + +export type IdlEnumVariant = { + name: string; + fields?: IdlDefinedFields; +}; + +export type IdlDefinedFields = IdlDefinedFieldsNamed | IdlDefinedFieldsTuple; + +export type IdlDefinedFieldsNamed = IdlField[]; + +export type IdlDefinedFieldsTuple = IdlType[]; + +export type IdlArrayLen = IdlArrayLenGeneric | IdlArrayLenValue; + +export type IdlArrayLenGeneric = { + generic: string; +}; + +export type IdlArrayLenValue = number; + +export type IdlGenericArg = IdlGenericArgType | IdlGenericArgConst; + +export type IdlGenericArgType = { kind: 'type'; type: IdlType }; + +export type IdlGenericArgConst = { kind: 'const'; value: string }; + +export type IdlType = + | 'bool' + | 'u8' + | 'i8' + | 'u16' + | 'i16' + | 'u32' + | 'i32' + | 'f32' + | 'u64' + | 'i64' + | 'f64' + | 'u128' + | 'i128' + | 'u256' + | 'i256' + | 'bytes' + | 'string' + | 'pubkey' + | IdlTypeOption + | IdlTypeCOption + | IdlTypeVec + | IdlTypeArray + | IdlTypeDefined + | IdlTypeGeneric; + +export type IdlTypeOption = { + option: IdlType; +}; + +export type IdlTypeCOption = { + coption: IdlType; +}; + +export type IdlTypeVec = { + vec: IdlType; +}; + +export type IdlTypeArray = { + array: [idlType: IdlType, size: IdlArrayLen]; +}; + +export type IdlTypeDefined = { + defined: { + name: string; + generics?: IdlGenericArg[]; + }; +}; + +export type IdlTypeGeneric = { + generic: string; +}; + +export type IdlDiscriminator = number[]; diff --git a/ts/packages/anchor-upgrade/src/types/index.ts b/ts/packages/anchor-upgrade/src/types/index.ts new file mode 100644 index 0000000000..2bae3fb1b0 --- /dev/null +++ b/ts/packages/anchor-upgrade/src/types/index.ts @@ -0,0 +1,5 @@ +export * from './camelize-idl'; +export * from './common'; +export * from './idl'; +export * from './lookup'; +export * from './type-def'; diff --git a/ts/packages/anchor-upgrade/src/types/lookup.ts b/ts/packages/anchor-upgrade/src/types/lookup.ts new file mode 100644 index 0000000000..c892269219 --- /dev/null +++ b/ts/packages/anchor-upgrade/src/types/lookup.ts @@ -0,0 +1,83 @@ +import type { Idl, IdlInstructionAccountItem, IdlInstructionAccounts, IdlTypeDef } from './idl'; +import type { + AllAccounts, + AllEvents, + AllInstructions, + DecodeType, + IdlTypes, + NullableIdlAccount, + NullableIdlConst, + NullableIdlEvent, + TypeDef, +} from './type-def'; + +type ByName = { + [K in T['name']]: Extract; +}; + +export type AccountName = NullableIdlAccount['name']; +export type EventName = NullableIdlEvent['name']; +export type InstructionName = AllInstructions['name']; +export type ConstantName = NullableIdlConst['name']; + +export type AccountByName> = Extract< + NullableIdlAccount, + { name: N } +>; + +export type EventByName> = Extract< + NullableIdlEvent, + { name: N } +>; + +export type InstructionByName> = Extract< + AllInstructions, + { name: N } +>; + +export type AllAccountsMap = ByName>; +export type AllEventsMap = ByName>; +export type AllConstantsMap = ByName>; +export type AllInstructionsMap = ByName>; + +type DecodeNamedTypeDef = TypeDef< + Extract, + Defined +>; + +export type DecodedAccount> = DecodeNamedTypeDef< + AllAccounts, + N, + IdlTypes +>; + +export type DecodedEvent> = DecodeNamedTypeDef< + AllEvents, + N, + IdlTypes +>; + +export type InstructionArgs> = { + [F in InstructionByName['args'][number] as F['name']]: DecodeType< + F['type'], + IdlTypes + >; +}; + +type FlattenInstructionAccountItem = + A extends IdlInstructionAccounts ? FlattenInstructionAccountItem : A; + +export type InstructionAccountName< + IDL extends Idl, + N extends InstructionName, +> = FlattenInstructionAccountItem['accounts'][number]>['name']; + +export type ConstantByName> = Extract< + NullableIdlConst, + { name: N } +>; + +export type DecodedConstant> = DecodeType< + ConstantByName['type'], + IdlTypes +>; diff --git a/ts/packages/anchor-upgrade/src/types/type-def.ts b/ts/packages/anchor-upgrade/src/types/type-def.ts new file mode 100644 index 0000000000..daf0c50602 --- /dev/null +++ b/ts/packages/anchor-upgrade/src/types/type-def.ts @@ -0,0 +1,159 @@ +import type { Address } from '@solana/kit'; + +import type { + Idl, + IdlAccount, + IdlArrayLen, + IdlConst, + IdlDefinedFields, + IdlDefinedFieldsNamed, + IdlDefinedFieldsTuple, + IdlEvent, + IdlType, + IdlTypeDef, + IdlTypeDefTyEnum, + IdlTypeDefTyStruct, + IdlTypeDefTyType, +} from './idl'; + +type IdlPointerSection = keyof Pick; + +type FilterTuple = T extends [infer Head, ...infer Tail] + ? [Head] extends [F] + ? [Head, ...FilterTuple] + : FilterTuple + : []; + +type ResolveIdlTypePointer = FilterTuple< + NonNullable, + { name: NonNullable[number]['name'] } +>; + +type UnboxToUnion = T extends (infer U)[] + ? UnboxToUnion + : T extends Record // empty object, eg: named enum variant without fields + ? '__empty_object__' + : T extends Record // object with props, eg: struct + ? UnboxToUnion + : T; + +type DecodedHelper = { + [D in T[number] as D['name']]: TypeDef; +}; + +type UnknownType = '__unknown_defined_type__'; + +type EmptyDefined = Record; + +type RecursiveDepth2< + T extends IdlTypeDef[], + Defined = EmptyDefined, + Decoded = DecodedHelper, +> = UnknownType extends UnboxToUnion + ? RecursiveDepth3> + : Decoded; + +type RecursiveDepth3< + T extends IdlTypeDef[], + Defined = EmptyDefined, + Decoded = DecodedHelper, +> = UnknownType extends UnboxToUnion + ? RecursiveDepth4> + : Decoded; + +type RecursiveDepth4 = DecodedHelper; + +type RecursiveTypes< + T extends IdlTypeDef[], + Defined = EmptyDefined, + Decoded = DecodedHelper, +> = UnknownType extends UnboxToUnion + ? RecursiveDepth2> + : Decoded; + +export type IdlTypes = RecursiveTypes>; + +type TypeMap = { + pubkey: Address; + bool: boolean; + string: string; + bytes: Buffer; +} & { + [K in 'u8' | 'i8' | 'u16' | 'i16' | 'u32' | 'i32' | 'f32' | 'f64']: number; +} & { + [K in 'u64' | 'i64' | 'u128' | 'i128' | 'u256' | 'i256']: bigint; +}; + +export type DecodeType = IdlType extends T + ? unknown + : T extends keyof TypeMap + ? TypeMap[T] + : T extends { defined: { name: keyof Defined } } + ? Defined[T['defined']['name']] + : T extends { option: IdlType } + ? DecodeType | null + : T extends { coption: IdlType } + ? DecodeType | null + : T extends { vec: IdlType } + ? DecodeType[] + : T extends { array: [defined: IdlType, size: IdlArrayLen] } + ? DecodeType[] + : unknown; + +type DecodeDefinedField = F extends IdlType ? DecodeType : never; + +type DecodeDefinedFields = F extends IdlDefinedFieldsNamed + ? { + [F2 in F[number] as F2['name']]: DecodeDefinedField; + } + : F extends IdlDefinedFieldsTuple + ? { + [F3 in keyof F as Exclude]: DecodeDefinedField; + } + : Record; + +type DecodeEnumVariants = { + [V in I['variants'][number] as V['name']]: DecodeDefinedFields, Defined>; +}; + +type ValueOf = T[keyof T]; +type XorEnumVariants> = ValueOf<{ + [K1 in keyof T]: { + [K2 in Exclude]?: never; + } & { [K2 in K1]: T[K2] }; +}>; + +type DecodeEnum = XorEnumVariants< + DecodeEnumVariants +>; + +type DecodeStruct = DecodeDefinedFields< + NonNullable, + Defined +>; + +type DecodeAlias = DecodeType; + +export type TypeDef = I['type'] extends IdlTypeDefTyEnum + ? DecodeEnum + : I['type'] extends IdlTypeDefTyStruct + ? DecodeStruct + : I['type'] extends IdlTypeDefTyType + ? DecodeAlias + : never; + +export type NullableIdlAccount = IDL['accounts'] extends undefined + ? IdlAccount + : NonNullable[number]; + +export type NullableIdlEvent = IDL['events'] extends undefined + ? IdlEvent + : NonNullable[number]; + +export type NullableIdlConst = IDL['constants'] extends undefined + ? IdlConst + : NonNullable[number]; + +export type AllEvents = ResolveIdlTypePointer; +export type AllAccounts = ResolveIdlTypePointer; +export type AllInstructions = I['instructions'][number]; diff --git a/ts/packages/anchor-upgrade/src/utils/idl.ts b/ts/packages/anchor-upgrade/src/utils/idl.ts new file mode 100644 index 0000000000..21375c957f --- /dev/null +++ b/ts/packages/anchor-upgrade/src/utils/idl.ts @@ -0,0 +1,63 @@ +import camelCase from 'camelcase'; + +import type { + CamelizedIdl, + Idl, + IdlDefinedFields, + IdlDefinedFieldsNamed, + IdlDefinedFieldsTuple, +} from '../types'; + +const KEYS_TO_CONVERT = ['name', 'path', 'account', 'relations', 'generic'] as const; + +const toCamelCase = (s: string): string => + s + .split('.') + .map((part) => camelCase(part, { locale: false })) + .join('.'); + +const convertNamesToCamelCase = (obj: Record): void => { + for (const key in obj) { + const val = obj[key]; + + if ((KEYS_TO_CONVERT as readonly string[]).includes(key)) { + obj[key] = Array.isArray(val) + ? val.map((item) => toCamelCase(item as string)) + : toCamelCase(val as string); + } else if (typeof val === 'object' && val !== null) { + convertNamesToCamelCase(val as Record); + } + } +}; + +export function convertIdlToCamelCase(idl: T): CamelizedIdl { + const camelCasedIdl = structuredClone(idl); + + if (typeof camelCasedIdl === 'object' && camelCasedIdl !== null) { + convertNamesToCamelCase(camelCasedIdl as Record); + } + + return camelCasedIdl as CamelizedIdl; +} + +function isNamedFields(fields: IdlDefinedFields): fields is IdlDefinedFieldsNamed { + return Boolean(fields[0] && typeof fields[0] === 'object' && 'name' in fields[0]); +} + +export function handleDefinedFields( + fields: IdlDefinedFields | undefined, + unitCb: () => U, + namedCb: (fields: IdlDefinedFieldsNamed) => N, + tupleCb: (fields: IdlDefinedFieldsTuple) => T, +): U | N | T { + // Unit + if (!fields?.length) return unitCb(); + + // Named + if (isNamedFields(fields)) { + return namedCb(fields); + } + + // Tuple + return tupleCb(fields); +} diff --git a/ts/packages/anchor-upgrade/src/utils/index.ts b/ts/packages/anchor-upgrade/src/utils/index.ts new file mode 100644 index 0000000000..bb66ab1ee2 --- /dev/null +++ b/ts/packages/anchor-upgrade/src/utils/index.ts @@ -0,0 +1,2 @@ +export * from './idl'; +export * from './instruction-accounts'; diff --git a/ts/packages/anchor-upgrade/src/utils/instruction-accounts.ts b/ts/packages/anchor-upgrade/src/utils/instruction-accounts.ts new file mode 100644 index 0000000000..1ca89cc667 --- /dev/null +++ b/ts/packages/anchor-upgrade/src/utils/instruction-accounts.ts @@ -0,0 +1,23 @@ +import type { IdlInstructionAccountItem, IdlInstructionAccounts } from '../types'; + +export type FlatInstructionAccount = { + name: string; +}; + +export function isCompositeAccounts( + accountItem: IdlInstructionAccountItem, +): accountItem is IdlInstructionAccounts { + return 'accounts' in accountItem; +} + +export function flattenInstructionAccounts( + accounts: IdlInstructionAccountItem[], +): FlatInstructionAccount[] { + return accounts.flatMap((accountItem) => { + if (isCompositeAccounts(accountItem)) { + return flattenInstructionAccounts(accountItem.accounts); + } + + return [{ name: accountItem.name }]; + }); +} diff --git a/ts/packages/anchor-upgrade/tsconfig.base.json b/ts/packages/anchor-upgrade/tsconfig.base.json new file mode 100644 index 0000000000..c4e5a21025 --- /dev/null +++ b/ts/packages/anchor-upgrade/tsconfig.base.json @@ -0,0 +1,28 @@ +{ + "include": [ + "./src/**/*" + ], + "compilerOptions": { + "sourceMap": true, + "declaration": true, + "declarationMap": true, + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "noImplicitAny": false, + "strictNullChecks": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "composite": true, + "baseUrl": ".", + "typeRoots": [ + "types/", + "../../node_modules/@types" + ], + "paths": { + "@solana/web3.js": [ + "../../node_modules/@solana/kit" + ] + } + } +} diff --git a/ts/packages/anchor-upgrade/tsconfig.cjs.json b/ts/packages/anchor-upgrade/tsconfig.cjs.json new file mode 100644 index 0000000000..fe927d5a36 --- /dev/null +++ b/ts/packages/anchor-upgrade/tsconfig.cjs.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "target": "es2019", + "outDir": "dist/cjs/", + "rootDir": "./src" + } +} diff --git a/ts/packages/anchor-upgrade/tsconfig.json b/ts/packages/anchor-upgrade/tsconfig.json new file mode 100644 index 0000000000..d618d26b5c --- /dev/null +++ b/ts/packages/anchor-upgrade/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "moduleResolution": "node", + "module": "es2022", + "target": "es2019", + "outDir": "dist/esm/", + "rootDir": "./src", + } +} diff --git a/ts/yarn.lock b/ts/yarn.lock index af0b205be3..2c2a7f9678 100644 --- a/ts/yarn.lock +++ b/ts/yarn.lock @@ -968,6 +968,36 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@solana/accounts@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/accounts/-/accounts-6.1.0.tgz#0093a0fbdc4aa066ea51a022255c99e1a37c6188" + integrity sha512-0jhmhSSS71ClLtBQIDrLlhkiNER4M9RIXTl1eJ1yJoFlE608JaKHTjNWsdVKdke7uBD6exdjNZkIVmouQPHMcA== + dependencies: + "@solana/addresses" "6.1.0" + "@solana/codecs-core" "6.1.0" + "@solana/codecs-strings" "6.1.0" + "@solana/errors" "6.1.0" + "@solana/rpc-spec" "6.1.0" + "@solana/rpc-types" "6.1.0" + +"@solana/addresses@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/addresses/-/addresses-6.1.0.tgz#1ef6705dd564fb8b5f69b86f4bfc26a378ed327e" + integrity sha512-QT04Vie4iICaalQQRJFMGj/P56IxXiwFtVuZHu1qjZUNmuGTOvX6G98b27RaGtLzpJ3NIku/6OtKxLUBqAKAyQ== + dependencies: + "@solana/assertions" "6.1.0" + "@solana/codecs-core" "6.1.0" + "@solana/codecs-strings" "6.1.0" + "@solana/errors" "6.1.0" + "@solana/nominal-types" "6.1.0" + +"@solana/assertions@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/assertions/-/assertions-6.1.0.tgz#1358ee2d749ebe58747c8407b3da28d8b0b94452" + integrity sha512-pLgxB2xxTk2QfTaWpnRpSMYgaPkKYDQgptRvbwmuDQnOW1Zopg+42MT2UrDGd3UFMML1uOFPxIwKM6m51H0uXw== + dependencies: + "@solana/errors" "6.1.0" + "@solana/buffer-layout-utils@=0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@solana/buffer-layout-utils/-/buffer-layout-utils-0.2.0.tgz#b45a6cab3293a2eb7597cceb474f229889d875ca" @@ -985,6 +1015,420 @@ dependencies: buffer "~6.0.3" +"@solana/codecs-core@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/codecs-core/-/codecs-core-6.1.0.tgz#e2548cdab130483f88dca0361d3fa9bf21f05c3e" + integrity sha512-5rNnDOOm2GRFMJbd9imYCPNvGOrQ+TZ53NCkFFWbbB7f+L9KkLeuuAsDMFN1lCziJFlymvN785YtDnMeWj2W+g== + dependencies: + "@solana/errors" "6.1.0" + +"@solana/codecs-data-structures@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/codecs-data-structures/-/codecs-data-structures-6.1.0.tgz#3a434f541d6f28bb2644badf39a225307aa0f15d" + integrity sha512-1cb9g5hrrucTuGkGxqVVq7dCwSMnn4YqwTe365iKkK8HBpLBmUl8XATf1MUs5UtDun1g9eNWOL72Psr8mIUqTQ== + dependencies: + "@solana/codecs-core" "6.1.0" + "@solana/codecs-numbers" "6.1.0" + "@solana/errors" "6.1.0" + +"@solana/codecs-numbers@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/codecs-numbers/-/codecs-numbers-6.1.0.tgz#63431694c9957c3c0bbf10aafc4b55f5e8377df4" + integrity sha512-YPQwwl6LE3igH23ah+d8kgpyE5xFcPbuwhxCDsLWqY/ESrvO/0YQSbsgIXahbhZxN59ZC4uq1LnHhBNbpCSVQg== + dependencies: + "@solana/codecs-core" "6.1.0" + "@solana/errors" "6.1.0" + +"@solana/codecs-strings@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/codecs-strings/-/codecs-strings-6.1.0.tgz#196878bf063cad7db34545adb19dc1e70ec3c16e" + integrity sha512-pRH5uAn4VCFUs2rYiDITyWsRnpvs3Uh/nhSc6OSP/kusghcCcCJcUzHBIjT4x08MVacXmGUlSLe/9qPQO+QK3Q== + dependencies: + "@solana/codecs-core" "6.1.0" + "@solana/codecs-numbers" "6.1.0" + "@solana/errors" "6.1.0" + +"@solana/codecs@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/codecs/-/codecs-6.1.0.tgz#f70e61e0cf6f376854ace18a74a7ab862ed8ace9" + integrity sha512-VHBS3t8fyVjE0Nqo6b4TUnzdwdRaVo+B5ufHhPLbbjkEXzz8HB4E/OBjgasn+zWGlfScfQAiBFOsfZjbVWu4XA== + dependencies: + "@solana/codecs-core" "6.1.0" + "@solana/codecs-data-structures" "6.1.0" + "@solana/codecs-numbers" "6.1.0" + "@solana/codecs-strings" "6.1.0" + "@solana/options" "6.1.0" + +"@solana/errors@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/errors/-/errors-6.1.0.tgz#1b5dd154b7be024766cbddd335d66f873696ca45" + integrity sha512-cqSwcw3Rmn85UR7PyF5nKPdlQsRYBkx7YGRvFaJ6Sal1PM+bfolhL5iT7STQoXxdhXGYwHMPg7kZYxmMdjwnJA== + dependencies: + chalk "5.6.2" + commander "14.0.3" + +"@solana/fast-stable-stringify@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/fast-stable-stringify/-/fast-stable-stringify-6.1.0.tgz#ea9bf7fb911cce99bf3878d5232ae6110a633536" + integrity sha512-QXUfDFaJCFeARsxJgScWmJ153Tit7Cimk9y0UWWreNBr2Aphi67Nlcj/tr7UABTO0Qaw/0gwrK76zz3m1t3nIw== + +"@solana/functional@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/functional/-/functional-6.1.0.tgz#04a53f89e8c11367d5ea6c3d64dfb86190cf6d88" + integrity sha512-+Sm8ldVxSTHIKaZDvcBu81FPjknXx6OMPlakkKmXjKxPgVLl86ruqMo2yEwoDUHV7DysLrLLcRNn13rfulomRw== + +"@solana/instruction-plans@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/instruction-plans/-/instruction-plans-6.1.0.tgz#c4f6d47c9f5cc1268d781b5a06e7e28df05fe689" + integrity sha512-zcsHg544t1zn7LLOVUxOWYlsKn9gvT7R+pL3cTiP2wFNoUN0h9En87H6nVqkZ8LWw23asgW0uM5uJGwfBx2h1Q== + dependencies: + "@solana/errors" "6.1.0" + "@solana/instructions" "6.1.0" + "@solana/keys" "6.1.0" + "@solana/promises" "6.1.0" + "@solana/transaction-messages" "6.1.0" + "@solana/transactions" "6.1.0" + +"@solana/instructions@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/instructions/-/instructions-6.1.0.tgz#3fb9a6ad6ad13ec9dc6de5001aa4b426261477c5" + integrity sha512-w1LdbJ3yanESckNTYC5KPckgN/25FyGCm07WWrs+dCnnpRNeLiVHIytXCPmArOVAXVkOYidXzhWmqCzqKUjYaA== + dependencies: + "@solana/codecs-core" "6.1.0" + "@solana/errors" "6.1.0" + +"@solana/keys@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/keys/-/keys-6.1.0.tgz#85c41ab9d0a1dd3939b17bb3b9f4e38431052714" + integrity sha512-C/SGCl3VOgBQZ0mLrMxCcJYnMsGpgE8wbx29jqRY+R91m5YhS1f/GfXJPR1lN/h7QGrJ6YDm8eI0Y3AZ7goKHg== + dependencies: + "@solana/assertions" "6.1.0" + "@solana/codecs-core" "6.1.0" + "@solana/codecs-strings" "6.1.0" + "@solana/errors" "6.1.0" + "@solana/nominal-types" "6.1.0" + +"@solana/kit@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/kit/-/kit-6.1.0.tgz#2ffa31c2a5515955bfd21c3b86257936f15d0a9b" + integrity sha512-24exn11BPonquufyCkGgypVtmN4JOsdGMsbF3EZ4kFyk7ZNryCn/N8eELr1FCVrHWRXoc0xy/HFaESBULTMf6g== + dependencies: + "@solana/accounts" "6.1.0" + "@solana/addresses" "6.1.0" + "@solana/codecs" "6.1.0" + "@solana/errors" "6.1.0" + "@solana/functional" "6.1.0" + "@solana/instruction-plans" "6.1.0" + "@solana/instructions" "6.1.0" + "@solana/keys" "6.1.0" + "@solana/offchain-messages" "6.1.0" + "@solana/plugin-core" "6.1.0" + "@solana/plugin-interfaces" "6.1.0" + "@solana/program-client-core" "6.1.0" + "@solana/programs" "6.1.0" + "@solana/rpc" "6.1.0" + "@solana/rpc-api" "6.1.0" + "@solana/rpc-parsed-types" "6.1.0" + "@solana/rpc-spec-types" "6.1.0" + "@solana/rpc-subscriptions" "6.1.0" + "@solana/rpc-types" "6.1.0" + "@solana/signers" "6.1.0" + "@solana/sysvars" "6.1.0" + "@solana/transaction-confirmation" "6.1.0" + "@solana/transaction-messages" "6.1.0" + "@solana/transactions" "6.1.0" + +"@solana/nominal-types@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/nominal-types/-/nominal-types-6.1.0.tgz#663d6f8de554c3f8337cf8cc22f03501b3e49660" + integrity sha512-+skHjN0arNNB9TLsGqA94VCx7euyGURI+qG6wck6E4D7hH6i6DxGiVrtKRghx+smJkkLtTm9BvdVKGoeNQYr7Q== + +"@solana/offchain-messages@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/offchain-messages/-/offchain-messages-6.1.0.tgz#001eaefe33308f9b5c673e462e030cf23237f36f" + integrity sha512-jrUb7HGUnRA+k44upcqKeevtEdqMxYRSlFdE0JTctZunGlP3GCcTl12tFOpbnFHvBLt8RwS62+nyeES8zzNwXA== + dependencies: + "@solana/addresses" "6.1.0" + "@solana/codecs-core" "6.1.0" + "@solana/codecs-data-structures" "6.1.0" + "@solana/codecs-numbers" "6.1.0" + "@solana/codecs-strings" "6.1.0" + "@solana/errors" "6.1.0" + "@solana/keys" "6.1.0" + "@solana/nominal-types" "6.1.0" + +"@solana/options@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/options/-/options-6.1.0.tgz#f4294596da0172f069d389daf0468630fcb6e6ba" + integrity sha512-/4FtVfR6nkHkMCumyh7/lJ6jMqyES6tKUbOJRa6gJxcIUWeRDu+XrHTHLf3gRNUqDAbFvW8FMIrQm7PdreZgRA== + dependencies: + "@solana/codecs-core" "6.1.0" + "@solana/codecs-data-structures" "6.1.0" + "@solana/codecs-numbers" "6.1.0" + "@solana/codecs-strings" "6.1.0" + "@solana/errors" "6.1.0" + +"@solana/plugin-core@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/plugin-core/-/plugin-core-6.1.0.tgz#fb97ef5142f9a1e46154171fa1c51df0c7d3cff4" + integrity sha512-2nmNCPa6B1QArqpAZHWUkK6K7UXLTrekfcfJm2V//ATEtLpKEBlv0c3mrhOYwNAKP2TpNuvEV33InXWKst9oXQ== + +"@solana/plugin-interfaces@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/plugin-interfaces/-/plugin-interfaces-6.1.0.tgz#97188afe195c7729ef8394a3d4dd95cb31a21425" + integrity sha512-eWSzfOuwtHUp8vljf5V24Tkz3WxqxiV0vD/BJZBNRZMdYRw3Cw3oeWcvEqHHxGUOie6AjIK8GrKggi8F73ZXbg== + dependencies: + "@solana/addresses" "6.1.0" + "@solana/instruction-plans" "6.1.0" + "@solana/keys" "6.1.0" + "@solana/rpc-spec" "6.1.0" + "@solana/rpc-subscriptions-spec" "6.1.0" + "@solana/rpc-types" "6.1.0" + "@solana/signers" "6.1.0" + +"@solana/program-client-core@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/program-client-core/-/program-client-core-6.1.0.tgz#29419cce60d43e5968ba69dc821f1d7dc2b3f735" + integrity sha512-5Apka+ulWNfLNLYNR63pLnr5XvkXTQWeaftWED93iTWTZrZv9SyFWcmIsaes6eqGXMQ3RhlebnrWODtKuAA62g== + dependencies: + "@solana/accounts" "6.1.0" + "@solana/addresses" "6.1.0" + "@solana/codecs-core" "6.1.0" + "@solana/errors" "6.1.0" + "@solana/instruction-plans" "6.1.0" + "@solana/instructions" "6.1.0" + "@solana/plugin-interfaces" "6.1.0" + "@solana/rpc-api" "6.1.0" + "@solana/signers" "6.1.0" + +"@solana/programs@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/programs/-/programs-6.1.0.tgz#21db6988f8de89de5b6992366e32631a8c987ff3" + integrity sha512-i4L4gSlIHDsdYRt3/YKVKMIN3UuYSKHRqK9B+AejcIc0y6Y/AXnHqzmpBRXEhvTXz18nt59MLXpVU4wu7ASjJA== + dependencies: + "@solana/addresses" "6.1.0" + "@solana/errors" "6.1.0" + +"@solana/promises@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/promises/-/promises-6.1.0.tgz#f7a9cd29c1cf0da1eca4d32112aed483674ae042" + integrity sha512-/mUW6peXQiEOaylLpGv4vtkvPzQvSbfhX9j5PNIK/ry4S3SHRQ3j3W/oGy4y3LR5alwo7NcVbubrkh4e4xwcww== + +"@solana/rpc-api@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-api/-/rpc-api-6.1.0.tgz#df28bdc4df5224aab11cff6bf92646b47b25c04f" + integrity sha512-+hO5+kZjJHuUNATUQxlJ1+ztXFkgn1j46zRwt3X7kF+VHkW3wsQ7up0JTS+Xsacmkrj1WKfymQweq8JTrsAG8A== + dependencies: + "@solana/addresses" "6.1.0" + "@solana/codecs-core" "6.1.0" + "@solana/codecs-strings" "6.1.0" + "@solana/errors" "6.1.0" + "@solana/keys" "6.1.0" + "@solana/rpc-parsed-types" "6.1.0" + "@solana/rpc-spec" "6.1.0" + "@solana/rpc-transformers" "6.1.0" + "@solana/rpc-types" "6.1.0" + "@solana/transaction-messages" "6.1.0" + "@solana/transactions" "6.1.0" + +"@solana/rpc-parsed-types@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-parsed-types/-/rpc-parsed-types-6.1.0.tgz#ce70c498f95aac18671966da9a63baac46ff2345" + integrity sha512-YKccynVgWt/gbs0tBYstNw6BSVuOeWdeAldTB2OgH95o2Q04DpO4v97X1MZDysA4SvSZM30Ek5Ni5ss3kskgdw== + +"@solana/rpc-spec-types@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-spec-types/-/rpc-spec-types-6.1.0.tgz#f22773e0a45c68119a80c2238b358754d11c47f1" + integrity sha512-tldMv1b6VGcvcRrY5MDWKlsyEKH6K96zE7gAIpKDX2G4T47ZOV+OMA3nh6xQpRgtyCUBsej0t80qmvTBDX/5IQ== + +"@solana/rpc-spec@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-spec/-/rpc-spec-6.1.0.tgz#f0023f821046ef8d1f37361618756618f961c01d" + integrity sha512-RxpkIGizCYhXGUcap7npV2S/rAXZ7P/liozY/ExjMmCxYTDwGIW33kp/uH/JRxuzrL8+f8FqY76VsqqIe+2VZw== + dependencies: + "@solana/errors" "6.1.0" + "@solana/rpc-spec-types" "6.1.0" + +"@solana/rpc-subscriptions-api@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-subscriptions-api/-/rpc-subscriptions-api-6.1.0.tgz#88678fa858b045f2e0771d4586350ee46891779d" + integrity sha512-I6J+3VU0dda6EySKbDyd+1urC7RGIRPRp0DcWRVcy68NOLbq0I5C40Dn9O2Zf8iCdK4PbQ7JKdCvZ/bDd45hdg== + dependencies: + "@solana/addresses" "6.1.0" + "@solana/keys" "6.1.0" + "@solana/rpc-subscriptions-spec" "6.1.0" + "@solana/rpc-transformers" "6.1.0" + "@solana/rpc-types" "6.1.0" + "@solana/transaction-messages" "6.1.0" + "@solana/transactions" "6.1.0" + +"@solana/rpc-subscriptions-channel-websocket@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-subscriptions-channel-websocket/-/rpc-subscriptions-channel-websocket-6.1.0.tgz#e20ac2fd7af5e4fae75186b05d9b5400101a7d67" + integrity sha512-vsx9b+uyCr9L3giao/BTiBFA8DxV5+gDNFq0t5uL21uQ17JXzBektwzHuHoth9IjkvXV/h+IhwXfuLE9Qm4GQg== + dependencies: + "@solana/errors" "6.1.0" + "@solana/functional" "6.1.0" + "@solana/rpc-subscriptions-spec" "6.1.0" + "@solana/subscribable" "6.1.0" + ws "^8.19.0" + +"@solana/rpc-subscriptions-spec@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-subscriptions-spec/-/rpc-subscriptions-spec-6.1.0.tgz#1ed0742e247087eb7f982ca8715514be7dae2704" + integrity sha512-P06jhqzHpZGaLeJmIQkpDeMDD1xUp53ARpmXMsduMC+U5ZKQt29CLo+JrR18boNtls6WfttjVMEbzF25/4UPVA== + dependencies: + "@solana/errors" "6.1.0" + "@solana/promises" "6.1.0" + "@solana/rpc-spec-types" "6.1.0" + "@solana/subscribable" "6.1.0" + +"@solana/rpc-subscriptions@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-subscriptions/-/rpc-subscriptions-6.1.0.tgz#ea6947597e17f1c911c0e3c02268e0a41fa19405" + integrity sha512-sqwj+cQinWcZ7M/9+cudKxMPTkTQyGP73980vPCWM7vCpPkp2qzgrEie4DdgDGo+NMwIjeFgu2kdUuLHI3GD/g== + dependencies: + "@solana/errors" "6.1.0" + "@solana/fast-stable-stringify" "6.1.0" + "@solana/functional" "6.1.0" + "@solana/promises" "6.1.0" + "@solana/rpc-spec-types" "6.1.0" + "@solana/rpc-subscriptions-api" "6.1.0" + "@solana/rpc-subscriptions-channel-websocket" "6.1.0" + "@solana/rpc-subscriptions-spec" "6.1.0" + "@solana/rpc-transformers" "6.1.0" + "@solana/rpc-types" "6.1.0" + "@solana/subscribable" "6.1.0" + +"@solana/rpc-transformers@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-transformers/-/rpc-transformers-6.1.0.tgz#93466d0c6b8f07a821c72daf693fd27c0e311f6d" + integrity sha512-OsSuuRPmsmS02eR9Zz+4iTsr+21hvEMEex5vwbwN6LAGPFlQ4ohqGkxgZCwmYd+Q5HWpnn9Uuf1MDTLLrKQkig== + dependencies: + "@solana/errors" "6.1.0" + "@solana/functional" "6.1.0" + "@solana/nominal-types" "6.1.0" + "@solana/rpc-spec-types" "6.1.0" + "@solana/rpc-types" "6.1.0" + +"@solana/rpc-transport-http@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-transport-http/-/rpc-transport-http-6.1.0.tgz#b59699a742762d6b5f4126f70a43158a63937eb3" + integrity sha512-3ebaTYuglLJagaXtjwDPVI7SQeeeFN2fpetpGKsuMAiti4fzYqEkNN8FIo+nXBzqqG/cVc2421xKjXl6sO1k/g== + dependencies: + "@solana/errors" "6.1.0" + "@solana/rpc-spec" "6.1.0" + "@solana/rpc-spec-types" "6.1.0" + undici-types "^7.21.0" + +"@solana/rpc-types@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-types/-/rpc-types-6.1.0.tgz#7c24de40b76f1cdc3cda2938e33416f58f6c349e" + integrity sha512-lR+Cb3v5Rpl49HsXWASy++TSE1AD86eRKabY+iuWnbBMYVGI4MamAvYwgBiygsCNc30nyO2TFNj9STMeSD/gAg== + dependencies: + "@solana/addresses" "6.1.0" + "@solana/codecs-core" "6.1.0" + "@solana/codecs-numbers" "6.1.0" + "@solana/codecs-strings" "6.1.0" + "@solana/errors" "6.1.0" + "@solana/nominal-types" "6.1.0" + +"@solana/rpc@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/rpc/-/rpc-6.1.0.tgz#a0df78a831976ed393f9156b84cff14c25f684e2" + integrity sha512-R3y5PklW9mPy5Y34hsXj40R28zN2N7AGLnHqYJVkXkllwVub/QCNpSdDxAnbbS5EGOYGoUOW8s5LFoXwMSr1LQ== + dependencies: + "@solana/errors" "6.1.0" + "@solana/fast-stable-stringify" "6.1.0" + "@solana/functional" "6.1.0" + "@solana/rpc-api" "6.1.0" + "@solana/rpc-spec" "6.1.0" + "@solana/rpc-spec-types" "6.1.0" + "@solana/rpc-transformers" "6.1.0" + "@solana/rpc-transport-http" "6.1.0" + "@solana/rpc-types" "6.1.0" + +"@solana/signers@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/signers/-/signers-6.1.0.tgz#398b0b747cf341fe9a77af17f453ad06dbc5b6e9" + integrity sha512-WDPGZJr6jIe2dEChv/2KQBnaga8dqOjd6ceBj/HcDHxnCudo66t7GlyZ9+9jMO40AgOOb7EDE5FDqPMrHMg5Yw== + dependencies: + "@solana/addresses" "6.1.0" + "@solana/codecs-core" "6.1.0" + "@solana/errors" "6.1.0" + "@solana/instructions" "6.1.0" + "@solana/keys" "6.1.0" + "@solana/nominal-types" "6.1.0" + "@solana/offchain-messages" "6.1.0" + "@solana/transaction-messages" "6.1.0" + "@solana/transactions" "6.1.0" + +"@solana/subscribable@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/subscribable/-/subscribable-6.1.0.tgz#6c0b579b1b4260ce7b69183ced526701980576c4" + integrity sha512-HiUfkxN7638uxPmY4t0gI4+yqnFLZYJKFaT9EpWIuGrOB1d9n+uOHNs3NU7cVMwWXgfZUbztTCKyCVTbcwesNg== + dependencies: + "@solana/errors" "6.1.0" + +"@solana/sysvars@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/sysvars/-/sysvars-6.1.0.tgz#38932ae2a511b41b26fb959c6d53285f71a53e69" + integrity sha512-KwJyBBrAOx0BgkiZqOKAaySDb/0JrUFSBQL9/O1kSKGy9TCRX55Ytr1HxNTcTPppWNpbM6JZVK+yW3Ruey0HRw== + dependencies: + "@solana/accounts" "6.1.0" + "@solana/codecs" "6.1.0" + "@solana/errors" "6.1.0" + "@solana/rpc-types" "6.1.0" + +"@solana/transaction-confirmation@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/transaction-confirmation/-/transaction-confirmation-6.1.0.tgz#3d5c798a3aa381d51b5bd523306815c4eb03ef97" + integrity sha512-akSjcqAMOGPFvKctFDSzhjcRc/45WbEVdVQ9mjgH6OYo7B11WZZZaeGPlzAw5KyuG34Px941xmICkBmNqEH47Q== + dependencies: + "@solana/addresses" "6.1.0" + "@solana/codecs-strings" "6.1.0" + "@solana/errors" "6.1.0" + "@solana/keys" "6.1.0" + "@solana/promises" "6.1.0" + "@solana/rpc" "6.1.0" + "@solana/rpc-subscriptions" "6.1.0" + "@solana/rpc-types" "6.1.0" + "@solana/transaction-messages" "6.1.0" + "@solana/transactions" "6.1.0" + +"@solana/transaction-messages@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/transaction-messages/-/transaction-messages-6.1.0.tgz#0af36669733432423c56e24081811646fb9e0f91" + integrity sha512-Dpv54LRVcfFbFEa/uB53LaY/TRfKuPGMKR7Z4F290zBgkj9xkpZkI+WLiJBiSloI7Qo2KZqXj3514BIeZvJLcg== + dependencies: + "@solana/addresses" "6.1.0" + "@solana/codecs-core" "6.1.0" + "@solana/codecs-data-structures" "6.1.0" + "@solana/codecs-numbers" "6.1.0" + "@solana/errors" "6.1.0" + "@solana/functional" "6.1.0" + "@solana/instructions" "6.1.0" + "@solana/nominal-types" "6.1.0" + "@solana/rpc-types" "6.1.0" + +"@solana/transactions@6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@solana/transactions/-/transactions-6.1.0.tgz#99d3f9514f6055afa859bd48001c0fc88e8d1222" + integrity sha512-1dkiNJcTtlHm4Fvs5VohNVpv7RbvbUYYKV7lYXMPIskoLF1eZp0tVlEqD/cRl91RNz7HEysfHqBAwlcJcRmrRg== + dependencies: + "@solana/addresses" "6.1.0" + "@solana/codecs-core" "6.1.0" + "@solana/codecs-data-structures" "6.1.0" + "@solana/codecs-numbers" "6.1.0" + "@solana/codecs-strings" "6.1.0" + "@solana/errors" "6.1.0" + "@solana/functional" "6.1.0" + "@solana/instructions" "6.1.0" + "@solana/keys" "6.1.0" + "@solana/nominal-types" "6.1.0" + "@solana/rpc-types" "6.1.0" + "@solana/transaction-messages" "6.1.0" + "@solana/web3.js@^1.32.0", "@solana/web3.js@^1.69.0": version "1.69.0" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.69.0.tgz#1756b1a26087172291c0b5163d3b44d24eef8aa7" @@ -1693,6 +2137,11 @@ chalk@4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@5.6.2: + version "5.6.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.6.2.tgz#b1238b6e23ea337af71c7f8a295db5af0c158aea" + integrity sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA== + chalk@^2.0.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -1814,6 +2263,11 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" +commander@14.0.3: + version "14.0.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-14.0.3.tgz#425d79b48f9af82fcd9e4fc1ea8af6c5ec07bbc2" + integrity sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw== + commander@^2.20.0, commander@^2.20.3: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -4787,6 +5241,11 @@ typescript@^5.5.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== +undici-types@^7.21.0: + version "7.24.2" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.24.2.tgz#6cb3b057576283dac54d731676bbb6e4c05f120b" + integrity sha512-WZZjYxf4yelEyde+mBI0OYvcPlNWeGSlWTmzoIntzWKELcCSISOFWNB0XfHvmJlAtC+Ao2LQP0WYYr7iVk0yZg== + universalify@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" @@ -4999,6 +5458,11 @@ ws@^7.4.5, ws@^7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== +ws@^8.19.0: + version "8.19.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.19.0.tgz#ddc2bdfa5b9ad860204f5a72a4863a8895fd8c8b" + integrity sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg== + ws@^8.5.0: version "8.11.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143"