General
Створення SDK з Codama

Створення SDK з Codama

Створіть свій Codama IDL з нуля

Створення Codama IDL з нуля означає побудову повного дерева вузлів для вашої програми. Щоб зробити це ефективно, розглянемо типи вузлів, доступні для використання.

Типи вузлів

Вузол значення

Для передачі конкретних значень у вузол ми використовуємо ValueNode. Цей тип представляє всі доступні вузли значень, які можуть містити різні типи даних.

Тут ви можете знайти детальну документацію щодо всіх доступних значень.

ValueNode є псевдонімом типу і не може використовуватися безпосередньо як вузол. Коли потрібен ValueNode, використовуйте натомість один із конкретних типів вузлів значень.

Вузол типу

Для визначення структури та форми даних ми використовуємо TypeNode. Ці вузли описують, який тип даних очікується: числа, рядки, структури, масиви або користувацькі типи.

Вони визначають схему без фактичних значень.

Тут ви можете знайти детальну документацію щодо всіх доступних типів.

TypeNode є псевдонімом типу і не може використовуватися безпосередньо як вузол. Коли потрібен TypeNode, використовуйте натомість один із конкретних типів вузлів типу.

Вузол дискримінатора

Щоб розрізняти між обліковими записами та інструкціями в нашій програмі, ми використовуємо дискримінатори. Існують різні методи для досягнення цього, і DiscriminatorNode надає всі доступні варіанти:

  • ConstantDiscriminatorNode: Використовується для опису константного значення на заданому зміщенні. Приймає ConstantValueNode як константу та number як зміщення:

    ts
    const discriminatorNode = constantDiscriminatorNode(constantValueNodeFromString('utf8', 'Hello'), 64);
  • FieldDiscriminatorNode: Використовується для опису значення за замовчуванням поля структури на заданому зміщенні. Приймає CamelCaseString як ім'я поля та number як зміщення:

    ts
    const discriminatorNode = fieldDiscriminatorNode('accountState', 64);

    Поле має бути доступним у даних облікового запису або аргументах інструкції, і воно повинно мати значення за замовчуванням. Наприклад:

    ts
    accountNode({
        data: structTypeNode([
            structFieldTypeNode({
                name: 'discriminator',
                type: numberTypeNode('u32'),
                defaultValue: numberValueNode(42),
                defaultValueStrategy: 'omitted',
            }),
            // ...
        ]),
        discriminators: [fieldDiscriminatorNode('discriminator')],
        // ...
    });
  • SizeDiscriminatorNode: Використовується для розрізнення облікових записів або інструкцій на основі розміру їхніх даних. Приймає number як параметр розміру:

    ts
    const discriminatorNode = sizeDiscriminatorNode(165);

DiscriminatorNode є псевдонімом типу і не може використовуватися безпосередньо як вузол. Коли потрібен DiscriminatorNode, використовуйте натомість один із конкретних типів вузлів дискримінатора.

Вузол насіння PDA

Для визначення насіння для програмно похідних адрес (PDA) ми використовуємо PdaSeedNode. Ці вузли визначають, як мають бути отримані адреси PDA, або з постійних значень, або зі змінних вхідних даних. PdaSeedNode надає різні методи для визначення насіння PDA:

  • ConstantPdaSeedNode: Використовується для опису постійного насіння для програмно похідної адреси. Приймає TypeNode та ValueNode у комбінації:

    ts
    const pdaSeedNode = constantPdaSeedNode(stringTypeNode('utf8'), stringValueNode('auth'));

    Також можна використовувати допоміжний засіб constantPdaSeedNodeFromString для легшого визначення констант на основі рядків. Наприклад, наступний приклад еквівалентний наведеному вище:

    ts
    const pdaSeedNode = constantPdaSeedNodeFromString('utf8', 'auth');
  • VariablePdaSeedNode: Використовується для опису змінного насіння для програмно похідної адреси. Приймає ім'я та TypeNode:

    ts
    const pdaSeedNode = variablePdaSeedNode('authority', publicKeyTypeNode())

Ось як вони використовуються в pdaNode:

ts
const counterPda = pdaNode({
    name: 'counter',
    seeds: [
        constantPdaSeedNodeFromString('utf8', 'counter'),
        variablePdaSeedNode('authority', publicKeyTypeNode()),
    ],
});

PdaSeedNode є псевдонімом типу і не може використовуватися безпосередньо як вузол. Коли потрібен PdaSeedNode, використовуйте натомість один із конкретних типів вузлів насіння pda.

Написання Codama IDL

Тепер, коли ми розглянули найважливіші вузли, доступні в програмі, давайте дізнаємося, як створити Codama IDL з нуля.

Кореневий вузол

Для створення основи вашого Codama IDL ми використовуємо RootNode. Цей вузол служить контейнером верхнього рівня, який містить вашу основну ProgramNode, а також будь-які додаткові програми, на які може посилатися основна програма.

ts
const node = rootNode(programNode({ ... }));

Вузол програми

Для визначення цілої програми в блокчейні ми використовуємо ProgramNode. Цей вузол представляє повну програму, розгорнуту в блокчейні, і визначає всі мінімально необхідні елементи, такі як облікові записи, інструкції, PDA та помилки.

Окрім цих основних елементів, цей вузол приймає назву програми, версію, публічний ключ розгортання та документацію у форматі markdown:

ts
const node = programNode({
    name: 'counter',
    publicKey: '22222222222222222222222222222222222222222222',
    version: '0.0.1',
    docs: [],
    accounts: [],
    instructions: [],
    definedTypes: [],
    pdas: [],
    errors: [],
});

У наступних розділах ми детально розглянемо всі ці елементи.

Вузол облікового запису

Для визначення облікових записів у блокчейні ми використовуємо AccountNode. Цей вузол характеризується своєю назвою, структурою даних та додатковими атрибутами, такими як визначення PDA та дискримінатори облікових записів.

Він представляє облікові записи, які зазвичай відповідають файлу state.rs вашої програми.

Поле docs можна використовувати для додавання документації, яка пояснює, що цей обліковий запис виконує в програмі:

ts
const node = accountNode({
    name: 'token',
    data: structTypeNode([
        structFieldTypeNode({ name: 'mint', type: publicKeyTypeNode() }),
        structFieldTypeNode({ name: 'owner', type: publicKeyTypeNode() }),
        structFieldTypeNode({ name: 'amount', type: numberTypeNode('u64') }),
    ]),
    discriminators: [sizeDiscriminatorNode(72)],
    size: 72,
    pda: pdaLinkNode("associatedTokenAccount"),
});

AccountNode не зберігає масив PdaNode, а лише одне опціональне посилання на PdaNode через PdaLinkNode, тому якщо PdaNode визначено всередині ProgramNode, ці два елементи можуть бути пов'язані тут.

Вузол інструкції

Для визначення інструкцій програми ми використовуємо InstructionNode. Цей вузол представляє інструкцію в програмі і дозволяє вам вказати дискримінатор, необхідні облікові записи та дані інструкції без ускладнень.

Крім того, ви можете включити опціональні облікові записи. Залежно від дизайну вашої програми, ці опціональні облікові записи можуть бути вирішені двома способами: шляхом їх пропуску або передачі ідентифікатора програми як облікового запису. Ви можете вибрати метод вирішення за допомогою поля optionalAccountStrategy: "omitted" | "programId".

Коли optionalAccountStrategy не вказано, за замовчуванням приймається стратегія programId.

Поле docs можна використовувати для додавання документації, яка пояснює, що ця інструкція виконує в програмі.

ts
const node = instructionNode({
    name: 'increment',
    discriminators: [fieldDiscriminatorNode('discriminator')],
    arguments: [
        instructionArgumentNode({
            name: 'discriminator',
            type: numberTypeNode('u8'),
            defaultValue: numberValueNode(1),
            defaultValueStrategy: 'omitted',
        }),
    ],
    accounts: [
        instructionAccountNode({ name: 'counter', isWritable: true, isSigner: true }),
        instructionAccountNode({ name: 'authority', isWritable: false, isSigner: false }),
    ],
    remainingAccounts: [instructionRemainingAccountsNode(argumentValueNode('authorities'), { isSigner: true })],
    optionalAccountStrategy: 'omitted',
});

У instructionAccountNode ви можете вказати опціональність облікового запису за допомогою поля isOptional: boolean або надати defaultValue, який перетворюється на ValueNode.

Функціональність defaultValue також працює для instructionArgumentNode.

Параметр defaultValueStrategy в instructionArgumentNode визначає, як обробляються значення за замовчуванням: "optional" означає, що значення за замовчуванням аргументу може бути перевизначене наданим аргументом, або "omitted", що означає, що аргумент не повинен бути наданий, і завжди має використовуватися значення за замовчуванням

Якщо не вказано, стратегія за замовчуванням — "optional".

Вузол помилки

Для визначення помилок, які може повертати програма, ми використовуємо ErrorNode. Цей вузол характеризується назвою, числовим кодом, який буде повернуто, та пов'язаним читабельним повідомленням для налагодження.

ts
const node = errorNode({
    name: 'invalidAmountArgument',
    code: 1,
    message: 'The amount argument is invalid.',
});

Вузол PDA

Для надання визначень конкретних Program-Derived Addresses (PDA), ми використовуємо PdaNode. Цей вузол характеризується назвою та списком сідів, які можуть бути константними або змінними, що дозволяє гнучко генерувати PDA.

ts
const node = pdaNode({
    name: 'counter',
    seeds: [variablePdaSeedNode('authority', publicKeyTypeNode())],
    docs: ['The counter PDA derived from its authority.'],
});

Якщо ідентифікатор програми відрізняється від найближчого предка ProgramNode, вам потрібно вказати його за допомогою поля programId.

Це можна використовувати для зв'язування PDA як defaultValue у instructionAccountNode за допомогою pdaValueNode.

Blueshift © 2025Commit: 6d01265