Browse Source

ts: Add support for unnamed(tuple) enum in accounts (#2601)

acheron 2 years ago
parent
commit
454f1dd044

+ 2 - 0
CHANGELOG.md

@@ -19,6 +19,8 @@ The minor version will be incremented upon a breaking change and the patch versi
 - avm: Add support for the `.anchorversion` file to facilitate switching between different versions of the `anchor-cli` ([#2553](https://github.com/coral-xyz/anchor/pull/2553)).
 - ts: Add ability to access workspace programs independent of the casing used, e.g. `anchor.workspace.myProgram`, `anchor.workspace.MyProgram`... ([#2579](https://github.com/coral-xyz/anchor/pull/2579)).
 - spl: Export `mpl-token-metadata` crate ([#2583](https://github.com/coral-xyz/anchor/pull/2583)).
+- spl: Add `TokenRecordAccount` for pNFTs ([#2597](https://github.com/coral-xyz/anchor/pull/2597)).
+- ts: Add support for unnamed(tuple) enum in accounts([#2601](https://github.com/coral-xyz/anchor/pull/2601)).
 
 ### Fixes
 

+ 22 - 0
tests/misc/programs/misc-optional/src/account.rs

@@ -36,6 +36,12 @@ pub struct DataI16 {
 }
 size!(DataI16, 2);
 
+#[account]
+pub struct DataEnum {
+    pub data: TestEnum, // 1 + 16
+}
+size!(DataEnum, 17);
+
 #[account(zero_copy)]
 pub struct DataZeroCopy {
     pub data: u16,    // 2
@@ -73,3 +79,19 @@ pub struct DataConstCastArraySize {
 pub struct DataMultidimensionalArrayConstSizes {
     pub data: [[u8; MAX_SIZE_U8 as usize]; MAX_SIZE],
 }
+
+#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Eq)]
+pub enum TestEnum {
+    First,
+    Second { x: u64, y: u64 },
+    TupleTest(u8, u8, u16, u16),
+    TupleStructTest(TestStruct),
+}
+
+#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Eq)]
+pub struct TestStruct {
+    pub data1: u8,
+    pub data2: u16,
+    pub data3: u32,
+    pub data4: u64,
+}

+ 8 - 0
tests/misc/programs/misc-optional/src/context.rs

@@ -208,6 +208,14 @@ pub struct TestI16<'info> {
 #[derive(Accounts)]
 pub struct TestSimulate {}
 
+#[derive(Accounts)]
+pub struct TestAccountEnum<'info> {
+    #[account(init, payer = payer.as_ref().unwrap(), space = 8+ DataEnum::LEN )]
+    pub data: Option<Account<'info, DataEnum>>,
+    pub payer: Option<Signer<'info>>,
+    pub system_program: Option<Program<'info, System>>,
+}
+
 #[derive(Accounts)]
 pub struct TestI8<'info> {
     #[account(zero)]

+ 2 - 16
tests/misc/programs/misc-optional/src/event.rs

@@ -1,5 +1,7 @@
 use anchor_lang::prelude::*;
 
+use crate::account::*;
+
 pub const MAX_EVENT_SIZE: usize = 10;
 pub const MAX_EVENT_SIZE_U8: u8 = 11;
 
@@ -33,22 +35,6 @@ pub struct E6 {
     pub data: [u8; MAX_EVENT_SIZE_U8 as usize],
 }
 
-#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Eq)]
-pub struct TestStruct {
-    pub data1: u8,
-    pub data2: u16,
-    pub data3: u32,
-    pub data4: u64,
-}
-
-#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Eq)]
-pub enum TestEnum {
-    First,
-    Second { x: u64, y: u64 },
-    TupleTest(u8, u8, u16, u16),
-    TupleStructTest(TestStruct),
-}
-
 #[event]
 pub struct E7 {
     pub data: TestEnum,

+ 26 - 10
tests/misc/programs/misc-optional/src/lib.rs

@@ -1,7 +1,7 @@
 //! Misc optional example is a catchall program for testing unrelated features.
 //! It's not too instructive/coherent by itself, so please see other examples.
 
-use account::MAX_SIZE;
+use account::*;
 use anchor_lang::prelude::*;
 use context::*;
 use event::*;
@@ -62,11 +62,16 @@ pub mod misc_optional {
         Ok(())
     }
 
-    pub fn test_input_enum(ctx: Context<TestSimulate>, data: TestEnum) -> Result<()> {
+    pub fn test_input_enum(_ctx: Context<TestSimulate>, data: TestEnum) -> Result<()> {
         emit!(E7 { data: data });
         Ok(())
     }
 
+    pub fn test_account_enum(ctx: Context<TestAccountEnum>, data: TestEnum) -> Result<()> {
+        ctx.accounts.data.as_mut().unwrap().data = data;
+        Ok(())
+    }
+
     pub fn test_i8(ctx: Context<TestI8>, data: i8) -> Result<()> {
         ctx.accounts.data.as_mut().unwrap().data = data;
         Ok(())
@@ -171,7 +176,9 @@ pub mod misc_optional {
         Ok(())
     }
 
-    pub fn test_init_mint_with_token_program(_ctx: Context<TestInitMintWithTokenProgram>) -> Result<()> {
+    pub fn test_init_mint_with_token_program(
+        _ctx: Context<TestInitMintWithTokenProgram>,
+    ) -> Result<()> {
         Ok(())
     }
 
@@ -182,11 +189,12 @@ pub mod misc_optional {
         Ok(())
     }
 
-    pub fn test_init_token_with_token_program(_ctx: Context<TestInitTokenWithTokenProgram>) -> Result<()> {
+    pub fn test_init_token_with_token_program(
+        _ctx: Context<TestInitTokenWithTokenProgram>,
+    ) -> Result<()> {
         Ok(())
     }
 
-
     pub fn test_composite_payer(ctx: Context<TestCompositePayer>) -> Result<()> {
         ctx.accounts.composite.data.as_mut().unwrap().data = 1;
         ctx.accounts.data.as_mut().unwrap().udata = 2;
@@ -201,7 +209,9 @@ pub mod misc_optional {
         Ok(())
     }
 
-    pub fn test_init_associated_token_with_token_program(ctx: Context<TestInitAssociatedTokenWithTokenProgram>) -> Result<()> {
+    pub fn test_init_associated_token_with_token_program(
+        _ctx: Context<TestInitAssociatedTokenWithTokenProgram>,
+    ) -> Result<()> {
         Ok(())
     }
 
@@ -261,7 +271,9 @@ pub mod misc_optional {
         Ok(())
     }
 
-    pub fn test_init_token_if_needed_with_token_program(_ctx: Context<TestInitTokenIfNeededWithTokenProgram>) -> Result<()> {
+    pub fn test_init_token_if_needed_with_token_program(
+        _ctx: Context<TestInitTokenIfNeededWithTokenProgram>,
+    ) -> Result<()> {
         Ok(())
     }
 
@@ -277,7 +289,7 @@ pub mod misc_optional {
         Ok(())
     }
 
-    pub fn init_with_space(_ctx: Context<InitWithSpace>, data: u16) -> Result<()> {
+    pub fn init_with_space(_ctx: Context<InitWithSpace>, _data: u16) -> Result<()> {
         Ok(())
     }
 
@@ -357,7 +369,9 @@ pub mod misc_optional {
         Ok(())
     }
 
-    pub fn test_only_token_program_constraint(_ctx: Context<TestOnlyTokenProgramConstraint>) -> Result<()> {
+    pub fn test_only_token_program_constraint(
+        _ctx: Context<TestOnlyTokenProgramConstraint>,
+    ) -> Result<()> {
         Ok(())
     }
 
@@ -401,7 +415,9 @@ pub mod misc_optional {
         Ok(())
     }
 
-    pub fn test_associated_token_with_token_program_constraint(_ctx: Context<TestAssociatedTokenWithTokenProgramConstraint>) -> Result<()> {
+    pub fn test_associated_token_with_token_program_constraint(
+        _ctx: Context<TestAssociatedTokenWithTokenProgramConstraint>,
+    ) -> Result<()> {
         Ok(())
     }
 }

+ 22 - 0
tests/misc/programs/misc/src/account.rs

@@ -36,6 +36,12 @@ pub struct DataI16 {
 }
 size!(DataI16, 2);
 
+#[account]
+pub struct DataEnum {
+    pub data: TestEnum, // 1 + 16
+}
+size!(DataEnum, 17);
+
 #[account(zero_copy)]
 pub struct DataZeroCopy {
     pub data: u16,    // 2
@@ -94,3 +100,19 @@ pub enum CoolEnum {
         some_slot: u64,
     },
 }
+
+#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Eq)]
+pub enum TestEnum {
+    First,
+    Second { x: u64, y: u64 },
+    TupleTest(u8, u8, u16, u16),
+    TupleStructTest(TestStruct),
+}
+
+#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Eq)]
+pub struct TestStruct {
+    pub data1: u8,
+    pub data2: u16,
+    pub data3: u32,
+    pub data4: u64,
+}

+ 10 - 1
tests/misc/programs/misc/src/context.rs

@@ -215,6 +215,15 @@ pub struct TestI16<'info> {
 #[derive(Accounts)]
 pub struct TestSimulate {}
 
+#[derive(Accounts)]
+pub struct TestAccountEnum<'info> {
+    #[account(init, payer = payer, space = 8 + DataEnum::LEN)]
+    pub data: Account<'info, DataEnum>,
+    #[account(mut)]
+    pub payer: Signer<'info>,
+    pub system_program: Program<'info, System>,
+}
+
 #[derive(Accounts)]
 pub struct TestI8<'info> {
     #[account(zero)]
@@ -767,4 +776,4 @@ pub struct TestUsedIdentifiers<'info> {
     )]
     /// CHECK: ignore
     pub test4: AccountInfo<'info>,
-}
+}

+ 4 - 18
tests/misc/programs/misc/src/event.rs

@@ -1,5 +1,7 @@
 use anchor_lang::prelude::*;
 
+use crate::account::*;
+
 pub const MAX_EVENT_SIZE: usize = 10;
 pub const MAX_EVENT_SIZE_U8: u8 = 11;
 
@@ -33,23 +35,7 @@ pub struct E6 {
     pub data: [u8; MAX_EVENT_SIZE_U8 as usize],
 }
 
-#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Eq)]
-pub struct TestStruct {
-    pub data1: u8,
-    pub data2: u16,
-    pub data3: u32,
-    pub data4: u64,
-}
-
-#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Eq)]
-pub enum TestEnum {
-    First,
-    Second {x: u64, y: u64},
-    TupleTest (u8, u8, u16, u16),
-    TupleStructTest (TestStruct),
-}
-
-#[event] 
+#[event]
 pub struct E7 {
     pub data: TestEnum,
-}
+}

+ 26 - 9
tests/misc/programs/misc/src/lib.rs

@@ -1,7 +1,7 @@
 //! Misc example is a catchall program for testing unrelated features.
 //! It's not too instructive/coherent by itself, so please see other examples.
 
-use account::MAX_SIZE;
+use account::*;
 use anchor_lang::prelude::*;
 use context::*;
 use event::*;
@@ -71,6 +71,11 @@ pub mod misc {
         Ok(())
     }
 
+    pub fn test_account_enum(ctx: Context<TestAccountEnum>, data: TestEnum) -> Result<()> {
+        ctx.accounts.data.data = data;
+        Ok(())
+    }
+
     pub fn test_i8(ctx: Context<TestI8>, data: i8) -> Result<()> {
         ctx.accounts.data.data = data;
         Ok(())
@@ -175,7 +180,9 @@ pub mod misc {
         Ok(())
     }
 
-    pub fn test_init_mint_with_token_program(_ctx: Context<TestInitMintWithTokenProgram>) -> Result<()> {
+    pub fn test_init_mint_with_token_program(
+        _ctx: Context<TestInitMintWithTokenProgram>,
+    ) -> Result<()> {
         Ok(())
     }
 
@@ -184,7 +191,9 @@ pub mod misc {
         Ok(())
     }
 
-    pub fn test_init_token_with_token_program(_ctx: Context<TestInitTokenWithTokenProgram>) -> Result<()> {
+    pub fn test_init_token_with_token_program(
+        _ctx: Context<TestInitTokenWithTokenProgram>,
+    ) -> Result<()> {
         Ok(())
     }
 
@@ -200,7 +209,9 @@ pub mod misc {
         Ok(())
     }
 
-    pub fn test_init_associated_token_with_token_program(_ctx: Context<TestInitAssociatedTokenWithTokenProgram>) -> Result<()> {
+    pub fn test_init_associated_token_with_token_program(
+        _ctx: Context<TestInitAssociatedTokenWithTokenProgram>,
+    ) -> Result<()> {
         Ok(())
     }
 
@@ -250,7 +261,7 @@ pub mod misc {
     }
 
     pub fn test_init_mint_if_needed_with_token_program(
-        _ctx: Context<TestInitMintIfNeededWithTokenProgram>
+        _ctx: Context<TestInitMintIfNeededWithTokenProgram>,
     ) -> Result<()> {
         Ok(())
     }
@@ -259,7 +270,9 @@ pub mod misc {
         Ok(())
     }
 
-    pub fn test_init_token_if_needed_with_token_program(_ctx: Context<TestInitTokenIfNeededWithTokenProgram>) -> Result<()> {
+    pub fn test_init_token_if_needed_with_token_program(
+        _ctx: Context<TestInitTokenIfNeededWithTokenProgram>,
+    ) -> Result<()> {
         Ok(())
     }
 
@@ -345,7 +358,9 @@ pub mod misc {
         Ok(())
     }
 
-    pub fn test_only_token_program_constraint(_ctx: Context<TestOnlyTokenProgramConstraint>) -> Result<()> {
+    pub fn test_only_token_program_constraint(
+        _ctx: Context<TestOnlyTokenProgramConstraint>,
+    ) -> Result<()> {
         Ok(())
     }
 
@@ -389,7 +404,9 @@ pub mod misc {
         Ok(())
     }
 
-    pub fn test_associated_token_with_token_program_constraint(_ctx: Context<TestAssociatedTokenWithTokenProgramConstraint>) -> Result<()> {
+    pub fn test_associated_token_with_token_program_constraint(
+        _ctx: Context<TestAssociatedTokenWithTokenProgramConstraint>,
+    ) -> Result<()> {
         Ok(())
     }
 
@@ -399,7 +416,7 @@ pub mod misc {
         program_id: u8,
         accounts: u8,
         ix_data: u8,
-        remaining_accounts: u8
+        remaining_accounts: u8,
     ) -> Result<()> {
         Ok(())
     }

+ 49 - 0
tests/misc/tests/misc/misc.ts

@@ -336,6 +336,55 @@ const miscTest = (
       assert.strictEqual(event4.data.tupleTest[3], 4);
     });
 
+    it("Can use enum in accounts", async () => {
+      const testAccountEnum = async (
+        ...args: Parameters<typeof program["methods"]["testAccountEnum"]>
+      ) => {
+        const dataKp = anchor.web3.Keypair.generate();
+        const txHash = await program.methods
+          .testAccountEnum(...(args as any))
+          .accounts({
+            data: dataKp.publicKey,
+            payer: program.provider.publicKey,
+          })
+          .signers([dataKp])
+          .rpc();
+        await program.provider.connection.confirmTransaction(txHash);
+        return await program.account.dataEnum.fetch(dataKp.publicKey);
+      };
+
+      // Unit
+      const unit = await testAccountEnum({ first: {} });
+      assert.deepEqual(unit.data.first, {});
+
+      // Named
+      const named = await testAccountEnum({
+        second: { x: new BN(1), y: new BN(2) },
+      });
+      assert.isTrue(new BN(1).eq(named.data.second.x));
+      assert.isTrue(new BN(2).eq(named.data.second.y));
+
+      // Unnamed
+      const unnamed = await testAccountEnum({ tupleTest: [1, 2, 3, 4] });
+      assert.strictEqual(unnamed.data.tupleTest[0], 1);
+      assert.strictEqual(unnamed.data.tupleTest[1], 2);
+      assert.strictEqual(unnamed.data.tupleTest[2], 3);
+      assert.strictEqual(unnamed.data.tupleTest[3], 4);
+
+      // Unnamed struct
+      const unnamedStruct = await testAccountEnum({
+        tupleStructTest: [
+          { data1: 1, data2: 11, data3: 111, data4: new BN(1111) },
+        ],
+      });
+      assert.strictEqual(unnamedStruct.data.tupleStructTest[0].data1, 1);
+      assert.strictEqual(unnamedStruct.data.tupleStructTest[0].data2, 11);
+      assert.strictEqual(unnamedStruct.data.tupleStructTest[0].data3, 111);
+      assert.isTrue(
+        unnamedStruct.data.tupleStructTest[0].data4.eq(new BN(1111))
+      );
+    });
+
     let dataI8;
 
     it("Can use i8 in the idl", async () => {

+ 21 - 20
ts/packages/anchor/src/coder/common.ts

@@ -1,31 +1,32 @@
-import { Idl, IdlField, IdlTypeDef, IdlEnumVariant, IdlType } from "../idl.js";
+import { Idl, IdlField, IdlTypeDef, IdlType } from "../idl.js";
 import { IdlError } from "../error.js";
 
-export function accountSize(idl: Idl, idlAccount: IdlTypeDef): number {
+export function accountSize(idl: Idl, idlAccount: IdlTypeDef) {
   if (idlAccount.type.kind === "enum") {
-    let variantSizes = idlAccount.type.variants.map(
-      (variant: IdlEnumVariant) => {
-        if (variant.fields === undefined) {
-          return 0;
-        }
-        return variant.fields
-          .map((f: IdlField | IdlType) => {
-            if (!(typeof f === "object" && "name" in f)) {
-              throw new Error("Tuple enum variants not yet implemented.");
-            }
-            return typeSize(idl, f.type);
-          })
-          .reduce((a: number, b: number) => a + b);
+    const variantSizes = idlAccount.type.variants.map((variant) => {
+      if (!variant.fields) {
+        return 0;
       }
-    );
+
+      return variant.fields
+        .map((f: IdlField | IdlType) => {
+          // Unnamed enum variant
+          if (!(typeof f === "object" && "name" in f)) {
+            return typeSize(idl, f);
+          }
+
+          // Named enum variant
+          return typeSize(idl, f.type);
+        })
+        .reduce((acc, size) => acc + size, 0);
+    });
+
     return Math.max(...variantSizes) + 1;
   }
-  if (idlAccount.type.fields === undefined) {
-    return 0;
-  }
+
   return idlAccount.type.fields
     .map((f) => typeSize(idl, f.type))
-    .reduce((a, b) => a + b, 0);
+    .reduce((acc, size) => acc + size, 0);
 }
 
 // Returns the size of the type in bytes. For variable length types, just return