Membangun Codama IDL Anda dari awal
Membangun Codama IDL dari awal berarti membuat pohon node lengkap untuk program Anda. Untuk melakukan ini secara efisien, mari kita periksa jenis-jenis node yang tersedia untuk digunakan.
Jenis Node
Node Nilai
Untuk memasukkan nilai spesifik ke dalam node, kita menggunakan ValueNode
. Jenis ini mewakili semua node nilai yang tersedia yang dapat menyimpan berbagai jenis data.
Di sini Anda dapat menemukan dokumentasi detail tentang semua nilai yang tersedia.
Node Tipe
Untuk mendefinisikan struktur dan bentuk data, kita menggunakan TypeNode
. Node-node ini menjelaskan jenis data apa yang diharapkan: seperti angka, string, struct, array, atau tipe kustom.
Mereka mendefinisikan skema tanpa berisi nilai sebenarnya.
Di sini Anda dapat menemukan dokumentasi detail tentang semua tipe yang tersedia.
Node Diskriminator
Untuk membedakan antara akun dan instruksi dalam program kita, kita menggunakan diskriminator. Ada berbagai metode untuk mencapai ini, dan DiscriminatorNode
menyediakan semua opsi yang tersedia:
-
ConstantDiscriminatorNode
: Digunakan untuk mendeskripsikan nilai konstan pada offset tertentu. Ini mengambilConstantValueNode
sebagai konstanta dannumber
sebagai offset:tsconst discriminatorNode = constantDiscriminatorNode(constantValueNodeFromString('utf8', 'Hello'), 64);
-
FieldDiscriminatorNode
: Digunakan untuk mendeskripsikan nilai default dari field struct pada offset tertentu. Ini mengambilCamelCaseString
sebagai nama field dannumber
sebagai offset:tsconst discriminatorNode = fieldDiscriminatorNode('accountState', 64);
tsaccountNode({ data: structTypeNode([ structFieldTypeNode({ name: 'discriminator', type: numberTypeNode('u32'), defaultValue: numberValueNode(42), defaultValueStrategy: 'omitted', }), // ... ]), discriminators: [fieldDiscriminatorNode('discriminator')], // ... });
-
SizeDiscriminatorNode
: Digunakan untuk membedakan akun atau instruksi berdasarkan ukuran datanya. Ini mengambilnumber
sebagai parameter ukuran:tsconst discriminatorNode = sizeDiscriminatorNode(165);
Node Pda Seed
Untuk mendefinisikan seed untuk Program Derived Addresses (PDA), kita menggunakan PdaSeedNode
. Node-node ini menentukan bagaimana alamat PDA harus diturunkan, baik dari nilai konstan atau input variabel. PdaSeedNode
menyediakan berbagai metode untuk mendefinisikan seed PDA:
-
ConstantPdaSeedNode
: Digunakan untuk mendeskripsikan seed konstan untuk alamat program-derived. Ini mengambilTypeNode
danValueNode
secara bersamaan:tsconst pdaSeedNode = constantPdaSeedNode(stringTypeNode('utf8'), stringValueNode('auth'));
tsconst pdaSeedNode = constantPdaSeedNodeFromString('utf8', 'auth');
-
VariablePdaSeedNode
: Digunakan untuk mendeskripsikan seed variabel untuk alamat program-derived. Ini mengambil nama danTypeNode
:tsconst pdaSeedNode = variablePdaSeedNode('authority', publicKeyTypeNode())
Berikut cara penggunaannya dalam pdaNode
:
const counterPda = pdaNode({
name: 'counter',
seeds: [
constantPdaSeedNodeFromString('utf8', 'counter'),
variablePdaSeedNode('authority', publicKeyTypeNode()),
],
});
Writing a Codama IDL
Sekarang kita telah memeriksa node-node terpenting yang tersedia dalam program, mari kita pelajari cara membuat Codama IDL dari awal.
Root Node
Untuk membuat dasar Codama IDL Anda, kita menggunakan RootNode
. Node ini berfungsi sebagai wadah tingkat atas yang menampung ProgramNode
utama Anda serta program tambahan lainnya yang mungkin direferensikan oleh program utama.
const node = rootNode(programNode({ ... }));
Program Node
Untuk mendefinisikan seluruh program on-chain, kita menggunakan ProgramNode
. Node ini mewakili program lengkap yang di-deploy pada blockchain dan mendefinisikan semua elemen minimum yang diperlukan seperti akun, instruksi, PDA, dan error.
Selain elemen-elemen inti tersebut, node ini menerima nama program, versi, kunci publik deployment, dan dokumentasi markdown:
const node = programNode({
name: 'counter',
publicKey: '22222222222222222222222222222222222222222222',
version: '0.0.1',
docs: [],
accounts: [],
instructions: [],
definedTypes: [],
pdas: [],
errors: [],
});
Pada bagian selanjutnya kita akan memeriksa semua elemen ini secara detail.
Account Node
Untuk mendefinisikan akun on-chain, kita menggunakan AccountNode
. Node ini dicirikan oleh nama, struktur data, dan atribut opsional seperti definisi PDA dan diskriminator akun.
Ini mewakili akun yang biasanya sesuai dengan file state.rs
dari program Anda.
Bidang docs dapat digunakan untuk menambahkan dokumentasi yang menjelaskan apa yang dicapai akun ini dalam program:
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"),
});
Instruction Node
Untuk mendefinisikan instruksi program, kita menggunakan InstructionNode
. Node ini mewakili instruksi dalam program dan memungkinkan Anda menentukan diskriminator, akun yang diperlukan, dan data instruksi tanpa komplikasi.
Selain itu, Anda dapat menyertakan akun opsional. Berdasarkan desain program Anda, akun opsional ini dapat diselesaikan dengan dua cara: dengan dihilangkan atau dengan meneruskan ID program sebagai akun. Anda dapat memilih metode penyelesaian menggunakan bidang optionalAccountStrategy: "omitted" | "programId"
.
Bidang docs dapat digunakan untuk menambahkan dokumentasi yang menjelaskan apa yang dicapai instruksi ini dalam program.
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',
});
Dalam instructionAccountNode
, Anda dapat menentukan opsionalitas akun menggunakan bidang isOptional: boolean
, atau menyediakan defaultValue
yang mengarah ke ValueNode
.
DefaultValueStrategy dalam instructionArgumentNode menentukan bagaimana nilai default ditangani: "optional" berarti nilai default argumen dapat diganti oleh argumen yang disediakan atau "omitted" yang berarti tidak ada argumen yang harus disediakan dan nilai default harus selalu digunakan
Strategi ini default ke "optional" jika tidak ditentukan.
Error Node
Untuk mendefinisikan error yang dapat dikembalikan oleh program, kita menggunakan ErrorNode
. Node ini dicirikan oleh nama, kode numerik yang akan dikembalikan, dan pesan yang dapat dibaca terkait untuk debugging.
const node = errorNode({
name: 'invalidAmountArgument',
code: 1,
message: 'The amount argument is invalid.',
});
PDA Node
Untuk menyediakan definisi untuk Program-Derived Addresses tertentu, kita menggunakan PdaNode
. Node ini dicirikan oleh nama dan daftar seed yang bisa konstan atau variabel, memungkinkan pembuatan PDA yang fleksibel.
const node = pdaNode({
name: 'counter',
seeds: [variablePdaSeedNode('authority', publicKeyTypeNode())],
docs: ['The counter PDA derived from its authority.'],
});
Ini dapat digunakan untuk menghubungkan PDA sebagai defaultValue
dalam instructionAccountNode
menggunakan pdaValueNode
.