Bladeren bron

ts: Fix parsing IDL with multiple const generics (#3665)

cryptopapi997 5 maanden geleden
bovenliggende
commit
5913906130
3 gewijzigde bestanden met toevoegingen van 202 en 16 verwijderingen
  1. 1 0
      CHANGELOG.md
  2. 17 16
      ts/packages/anchor/src/coder/borsh/idl.ts
  3. 184 0
      ts/packages/anchor/tests/coder-accounts.spec.ts

+ 1 - 0
CHANGELOG.md

@@ -16,6 +16,7 @@ The minor version will be incremented upon a breaking change and the patch versi
 ### Fixes
 
 - idl: Update `proc-macro2` usage for latest nightly ([#3663](https://github.com/solana-foundation/anchor/pull/3663))
+- ts: Fix parsing IDL with multiple const generics ([#3665](https://github.com/solana-foundation/anchor/pull/3665))
 - cli, docker: Replace `backpackapp/build` Docker image with `solanafoundation/anchor` ([#3619](https://github.com/coral-xyz/anchor/pull/3619)).
 
 ### Breaking

+ 17 - 16
ts/packages/anchor/src/coder/borsh/idl.ts

@@ -449,24 +449,25 @@ export class IdlCoder {
         const [elTy, len] = type.array;
         const isGenericLen = typeof len === "object";
 
-        const args = IdlCoder.resolveGenericArgs({
-          type: elTy,
-          typeDef,
-          genericArgs,
-          isDefined,
-        });
-        if (args) {
-          // Has generic type, also check for generic length
-          for (const i in typeDef.generics.slice(+index)) {
-            const curIndex = +index + +i;
-            if (
-              isGenericLen &&
-              typeDef.generics[curIndex].name === len.generic
-            ) {
-              args.push(genericArgs[curIndex]);
-            }
+        const args =
+          IdlCoder.resolveGenericArgs({
+            type: elTy,
+            typeDef,
+            genericArgs,
+            isDefined,
+          }) || [];
+
+        // Check all generics for matching const generic length
+        if (isGenericLen) {
+          const matchingGeneric = typeDef.generics.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") {

+ 184 - 0
ts/packages/anchor/tests/coder-accounts.spec.ts

@@ -49,4 +49,188 @@ describe("coder.accounts", () => {
       assert.deepEqual(coder.accounts.decode("MemberDAO", encoded), memberDAO);
     });
   });
+
+  test("Can encode and decode user-defined accounts, including those with more nested & multiple const generics", () => {
+    const idl: Idl = {
+      address: "EQoYLkj17hXm8yc9qLq5Cm7FgCqujRVHd2ZhdEfAmrMF",
+      metadata: {
+        name: "gen_idl",
+        version: "0.1.0",
+        spec: "0.1.0",
+        description: "Created with Anchor",
+      },
+      instructions: [
+        {
+          name: "initialize",
+          discriminator: [175, 175, 109, 31, 13, 152, 155, 237],
+          accounts: [
+            {
+              name: "my_acc",
+              pda: {
+                seeds: [
+                  {
+                    kind: "const",
+                    value: [
+                      115, 109, 97, 108, 108, 95, 109, 101, 109, 112, 111, 111,
+                      108,
+                    ],
+                  },
+                ],
+              },
+            },
+          ],
+          args: [],
+        },
+      ],
+      accounts: [
+        {
+          name: "MyAcc",
+          discriminator: [123, 153, 151, 118, 126, 71, 73, 92],
+        },
+      ],
+      types: [
+        {
+          name: "MaxHeap",
+          serialization: "bytemuckunsafe",
+          repr: {
+            kind: "c",
+          },
+          generics: [
+            {
+              kind: "type",
+              name: "T",
+            },
+            {
+              kind: "const",
+              name: "SIZE",
+              type: "usize",
+            },
+            {
+              kind: "const",
+              name: "PADDING",
+              type: "usize",
+            },
+          ],
+          type: {
+            kind: "struct",
+            fields: [
+              {
+                name: "entries",
+                type: {
+                  array: [
+                    {
+                      generic: "T",
+                    },
+                    {
+                      generic: "SIZE",
+                    },
+                  ],
+                },
+              },
+              {
+                name: "count",
+                type: "u16",
+              },
+              {
+                name: "padding",
+                type: {
+                  array: [
+                    "u8",
+                    {
+                      generic: "PADDING",
+                    },
+                  ],
+                },
+              },
+            ],
+          },
+        },
+        {
+          name: "MyStruct",
+          serialization: "bytemuck",
+          repr: {
+            kind: "c",
+          },
+          type: {
+            kind: "struct",
+            fields: [
+              {
+                name: "a",
+                type: "u16",
+              },
+              {
+                name: "b",
+                type: "u16",
+              },
+            ],
+          },
+        },
+        {
+          name: "MyAcc",
+          serialization: "bytemuck",
+          repr: {
+            kind: "transparent",
+          },
+          type: {
+            kind: "struct",
+            fields: [
+              {
+                name: "inner",
+                type: {
+                  defined: {
+                    name: "MaxHeap",
+                    generics: [
+                      {
+                        kind: "type",
+                        type: {
+                          defined: {
+                            name: "MyStruct",
+                          },
+                        },
+                      },
+                      {
+                        kind: "const",
+                        value: "3",
+                      },
+                      {
+                        kind: "const",
+                        value: "10",
+                      },
+                    ],
+                  },
+                },
+              },
+            ],
+          },
+        },
+      ],
+    };
+
+    const coder = new BorshCoder(idl);
+
+    const myAcc = {
+      inner: {
+        entries: [
+          {
+            a: 1,
+            b: 2,
+          },
+          {
+            a: 3,
+            b: 4,
+          },
+          {
+            a: 5,
+            b: 6,
+          },
+        ],
+        count: 2,
+        padding: new Array(10).fill(0),
+      },
+    };
+
+    coder.accounts.encode("MyAcc", myAcc).then((encoded) => {
+      assert.deepEqual(coder.accounts.decode("MyAcc", encoded), myAcc);
+    });
+  });
 });