從零開始建立你的 Codama IDL
從零開始建立 Codama IDL 意味著為你的程式創建完整的節點樹。為了高效完成這項工作,讓我們來檢視可用的節點類型。
節點類型
值節點
要將特定值傳遞到節點中,我們使用 ValueNode。此類型代表所有可用的值節點,這些節點可以容納不同類型的數據。
這裡可以找到所有可用值的詳細文檔。
類型節點
要定義數據的結構和形狀,我們使用 TypeNode。這些節點描述了期望的數據類型,例如數字、字符串、結構、數組或自定義類型。
它們定義了架構而不包含實際值。
這裡可以找到所有可用類型的詳細文檔。
判別器節點
為了區分我們程式中的帳戶和指令,我們使用判別器。有不同的方法可以實現這一點,而 DiscriminatorNode 提供了所有可用的選項:
ConstantDiscriminatorNode:用於描述在給定偏移量處的常量值。它接受一個ConstantValueNode作為常量,以及一個number作為偏移量:tsconst discriminatorNode = constantDiscriminatorNode(constantValueNodeFromString('utf8', 'Hello'), 64);FieldDiscriminatorNode:用於描述在給定偏移量處的結構字段的默認值。它接受一個CamelCaseString作為字段名稱,以及一個number作為偏移量:tsconst discriminatorNode = fieldDiscriminatorNode('accountState', 64);tsaccountNode({ data: structTypeNode([ structFieldTypeNode({ name: 'discriminator', type: numberTypeNode('u32'), defaultValue: numberValueNode(42), defaultValueStrategy: 'omitted', }), // ... ]), discriminators: [fieldDiscriminatorNode('discriminator')], // ... });SizeDiscriminatorNode:用於根據數據大小區分帳戶或指令。它以number作為大小參數:tsconst discriminatorNode = sizeDiscriminatorNode(165);
Pda Seed Node
要為程式衍生地址(PDA)定義種子,我們使用 PdaSeedNode。這些節點指定了 PDA 地址應如何衍生,可以來自常量值或變量輸入。PdaSeedNode 提供了不同的方法來定義 PDA 種子:
ConstantPdaSeedNode:用於描述程式衍生地址的常量種子。它結合了TypeNode和ValueNode:tsconst pdaSeedNode = constantPdaSeedNode(stringTypeNode('utf8'), stringValueNode('auth'));tsconst pdaSeedNode = constantPdaSeedNodeFromString('utf8', 'auth');VariablePdaSeedNode:用於描述程式衍生地址的變量種子。它需要一個名稱和一個TypeNode:tsconst pdaSeedNode = variablePdaSeedNode('authority', publicKeyTypeNode())
以下是它們在 pdaNode 中的使用方式:
const counterPda = pdaNode({
name: 'counter',
seeds: [
constantPdaSeedNodeFromString('utf8', 'counter'),
variablePdaSeedNode('authority', publicKeyTypeNode()),
],
});撰寫 Codama IDL
現在我們已經檢視了程式中可用的最重要節點,讓我們來了解如何從零開始創建一個 Codama IDL。
根節點
要建立您的 Codama IDL 的基礎,我們使用 RootNode。此節點作為頂層容器,包含您的主要 ProgramNode 以及任何主要程式可能引用的其他程式。
const node = rootNode(programNode({ ... }));程式節點
要定義整個鏈上程式,我們使用 ProgramNode。此節點代表部署在鏈上的完整程式,並定義所有最低可行元素,例如帳戶、指令、PDA 和錯誤。
除了這些核心元素外,此節點還接受程式的名稱、版本、部署公鑰和 Markdown 文件:
const node = programNode({
name: 'counter',
publicKey: '22222222222222222222222222222222222222222222',
version: '0.0.1',
docs: [],
accounts: [],
instructions: [],
definedTypes: [],
pdas: [],
errors: [],
});在接下來的部分中,我們將詳細檢視所有這些元素。
帳戶節點
要定義鏈上帳戶,我們使用 AccountNode。此節點的特點是其名稱、數據結構以及可選屬性,例如 PDA 定義和帳戶識別符。
它代表通常對應於程式的 state.rs 文件的帳戶。
docs 欄位可用於添加文件,解釋此帳戶在程式中的作用:
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"),
});指令節點
要定義程式指令,我們使用 InstructionNode。此節點代表程式中的一個指令,並允許您輕鬆指定識別符、所需帳戶和指令數據。
此外,您可以包含可選帳戶。根據程式的設計,這些可選帳戶可以通過兩種方式解決:省略它們或將程式 ID 作為帳戶傳遞。您可以使用 optionalAccountStrategy: "omitted" | "programId" 欄位選擇解決方法。
docs欄位可用於添加文件,解釋此指令在程式中完成的功能。
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。
instructionArgumentNode中的defaultValueStrategy決定了如何處理預設值:「optional」表示參數的預設值可以被提供的參數覆蓋;「omitted」表示不應提供任何參數,並且應始終使用預設值。
如果未指定,策略預設為「optional」。
錯誤節點
為了定義程式可能返回的錯誤,我們使用ErrorNode。此節點的特徵包括名稱、將返回的數字代碼以及用於調試的相關可讀訊息。
const node = errorNode({
name: 'invalidAmountArgument',
code: 1,
message: 'The amount argument is invalid.',
});PDA節點
為了提供特定程式衍生地址(Program-Derived Addresses, PDA)的定義,我們使用PdaNode。此節點的特徵包括名稱和一個種子列表,這些種子可以是常量或變量,從而允許靈活的PDA生成。
const node = pdaNode({
name: 'counter',
seeds: [variablePdaSeedNode('authority', publicKeyTypeNode())],
docs: ['The counter PDA derived from its authority.'],
});這可以用於將PDA作為defaultValue連結到instructionAccountNode中,使用pdaValueNode。