acheron 2 жил өмнө
parent
commit
d1e32674d5

+ 6 - 1
CHANGELOG.md

@@ -18,12 +18,16 @@ The minor version will be incremented upon a breaking change and the patch versi
 - cli, lang: Add IDL generation through compilation. `anchor build` still uses parsing method to generate IDLs, use `anchor idl build` to generate IDLs with the build method ([#2011](https://github.com/coral-xyz/anchor/pull/2011)).
 - 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)).
+- bench: Add benchmarking for program binary size ([#2591](https://github.com/coral-xyz/anchor/pull/2591)).
 - 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)).
 - cli: Add program template with multiple files for instructions, state... ([#2602](https://github.com/coral-xyz/anchor/pull/2602)).
+- bench: Add benchmarking for stack memory usage ([#2617](https://github.com/coral-xyz/anchor/pull/2617)).
 - lang: `Box` the inner enums of `anchor_lang::error::Error` to optimize `anchor_lang::Result` ([#2600](https://github.com/coral-xyz/anchor/pull/2600)).
 - ts: Add strong type support for `Program.addEventListener` method ([#2627](https://github.com/coral-xyz/anchor/pull/2627)).
+- syn: Add `IdlBuild` trait to implement IDL support for custom types ([#2629](https://github.com/coral-xyz/anchor/pull/2629)).
+- spl: Add `idl-build` feature. IDL build method will not work without enabling this feature when using `anchor-spl` ([#2629](https://github.com/coral-xyz/anchor/pull/2629)).
 
 ### Fixes
 
@@ -41,7 +45,8 @@ The minor version will be incremented upon a breaking change and the patch versi
 
 - syn: `idl` feature has been replaced with `idl-build`, `idl-parse` and `idl-types` features ([#2011](https://github.com/coral-xyz/anchor/pull/2011)).
 - syn: IDL `parse` method now returns `Result<Idl>` instead of `Result<Option<Idl>>` ([#2582](https://github.com/coral-xyz/anchor/pull/2582)).
-- spl: Update Token Metadata dependency to use the client SDK instead of the program crate ([#2632](https://github.com/coral-xyz/anchor/pull/2632))
+- spl: Update `mpl-token-metadata` dependency to use the client SDK instead of the program crate ([#2632](https://github.com/coral-xyz/anchor/pull/2632)).
+- ts: Remove `base64-js` dependency ([#2635](https://github.com/coral-xyz/anchor/pull/2635)).
 
 ## [0.28.0] - 2023-06-09
 

+ 16 - 12
cli/src/lib.rs

@@ -2626,7 +2626,7 @@ fn account(
     let mut data_view = &data[8..];
 
     let deserialized_json =
-        deserialize_idl_struct_to_json(&idl, account_type_name, &mut data_view)?;
+        deserialize_idl_defined_type_to_json(&idl, account_type_name, &mut data_view)?;
 
     println!(
         "{}",
@@ -2636,26 +2636,25 @@ fn account(
     Ok(())
 }
 
-// Deserializes a user defined IDL struct/enum by munching the account data.
-// Recursively deserializes elements one by one
-fn deserialize_idl_struct_to_json(
+// Deserializes user defined IDL types by munching the account data(recursively).
+fn deserialize_idl_defined_type_to_json(
     idl: &Idl,
-    account_type_name: &str,
+    defined_type_name: &str,
     data: &mut &[u8],
 ) -> Result<JsonValue, anyhow::Error> {
-    let account_type = &idl
-        .accounts
+    let defined_type = &idl
+        .types
         .iter()
-        .chain(idl.types.iter())
-        .find(|account_type| account_type.name == account_type_name)
+        .chain(idl.accounts.iter())
+        .find(|defined_type| defined_type.name == defined_type_name)
         .ok_or_else(|| {
-            anyhow::anyhow!("Struct/Enum named {} not found in IDL.", account_type_name)
+            anyhow::anyhow!("Struct/Enum named {} not found in IDL.", defined_type_name)
         })?
         .ty;
 
     let mut deserialized_fields = Map::new();
 
-    match account_type {
+    match defined_type {
         IdlTypeDefinitionTy::Struct { fields } => {
             for field in fields {
                 deserialized_fields.insert(
@@ -2701,6 +2700,9 @@ fn deserialize_idl_struct_to_json(
 
             deserialized_fields.insert(variant.name.clone(), value);
         }
+        IdlTypeDefinitionTy::Alias { value } => {
+            return deserialize_idl_type_to_json(value, data, idl);
+        }
     }
 
     Ok(JsonValue::Object(deserialized_fields))
@@ -2764,7 +2766,9 @@ fn deserialize_idl_type_to_json(
         IdlType::PublicKey => {
             json!(<Pubkey as AnchorDeserialize>::deserialize(data)?.to_string())
         }
-        IdlType::Defined(type_name) => deserialize_idl_struct_to_json(parent_idl, type_name, data)?,
+        IdlType::Defined(type_name) => {
+            deserialize_idl_defined_type_to_json(parent_idl, type_name, data)?
+        }
         IdlType::Option(ty) => {
             let is_present = <u8 as AnchorDeserialize>::deserialize(data)?;
 

+ 26 - 3
lang/syn/src/idl/parse/file.rs

@@ -9,6 +9,7 @@ use heck::MixedCase;
 use quote::ToTokens;
 use std::collections::{HashMap, HashSet};
 use std::path::Path;
+use std::str::FromStr;
 use syn::{
     Expr, ExprLit, ItemConst,
     Lit::{Byte, ByteStr},
@@ -354,10 +355,10 @@ fn parse_ty_defs(ctx: &CrateContext, no_docs: bool) -> Result<Vec<IdlTypeDefinit
         })
         .chain(ctx.enums().filter_map(|enm| {
             // Only take public types
-            match &enm.vis {
-                syn::Visibility::Public(_) => (),
-                _ => return None,
+            if !matches!(&enm.vis, syn::Visibility::Public(_)) {
+                return None;
             }
+
             let name = enm.ident.to_string();
             let doc = if !no_docs {
                 docs::parse(&enm.attrs)
@@ -405,6 +406,7 @@ fn parse_ty_defs(ctx: &CrateContext, no_docs: bool) -> Result<Vec<IdlTypeDefinit
                     IdlEnumVariant { name, fields }
                 })
                 .collect::<Vec<IdlEnumVariant>>();
+
             Some(Ok(IdlTypeDefinition {
                 name,
                 generics: None,
@@ -412,6 +414,27 @@ fn parse_ty_defs(ctx: &CrateContext, no_docs: bool) -> Result<Vec<IdlTypeDefinit
                 ty: IdlTypeDefinitionTy::Enum { variants },
             }))
         }))
+        .chain(ctx.type_aliases().filter_map(|alias| {
+            // Only take public types
+            if !matches!(&alias.vis, syn::Visibility::Public(_)) {
+                return None;
+            }
+
+            let name = alias.ident.to_string();
+            let doc = if !no_docs {
+                docs::parse(&alias.attrs)
+            } else {
+                None
+            };
+            let value = IdlType::from_str(&alias.ty.to_token_stream().to_string()).unwrap();
+
+            Some(Ok(IdlTypeDefinition {
+                name,
+                generics: None,
+                docs: doc,
+                ty: IdlTypeDefinitionTy::Alias { value },
+            }))
+        }))
         .collect()
 }
 

+ 1 - 0
lang/syn/src/idl/types.rs

@@ -166,6 +166,7 @@ pub struct IdlTypeDefinition {
 pub enum IdlTypeDefinitionTy {
     Struct { fields: Vec<IdlField> },
     Enum { variants: Vec<IdlEnumVariant> },
+    Alias { value: IdlType },
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]

+ 11 - 0
lang/syn/src/parser/context.rs

@@ -28,6 +28,10 @@ impl CrateContext {
         self.modules.iter().flat_map(|(_, ctx)| ctx.enums())
     }
 
+    pub fn type_aliases(&self) -> impl Iterator<Item = &syn::ItemType> {
+        self.modules.iter().flat_map(|(_, ctx)| ctx.type_aliases())
+    }
+
     pub fn modules(&self) -> impl Iterator<Item = ModuleContext> {
         self.modules.values().map(|detail| ModuleContext { detail })
     }
@@ -293,4 +297,11 @@ impl ParsedModule {
             })
             .flatten()
     }
+
+    fn type_aliases(&self) -> impl Iterator<Item = &syn::ItemType> {
+        self.items.iter().filter_map(|i| match i {
+            syn::Item::Type(item) => Some(item),
+            _ => None,
+        })
+    }
 }

+ 29 - 0
tests/idl/programs/client-interactions/src/lib.rs

@@ -35,6 +35,18 @@ pub mod client_interactions {
         ctx.accounts.account.enum_field = enum_arg;
         Ok(())
     }
+
+    pub fn type_alias(
+        ctx: Context<TypeAlias>,
+        type_alias_u8: TypeAliasU8,
+        type_alias_u8_array: TypeAliasU8Array,
+        type_alias_struct: TypeAliasStruct,
+    ) -> Result<()> {
+        ctx.accounts.account.type_alias_u8 = type_alias_u8;
+        ctx.accounts.account.type_alias_u8_array = type_alias_u8_array;
+        ctx.accounts.account.type_alias_struct = type_alias_struct;
+        Ok(())
+    }
 }
 
 #[derive(Accounts)]
@@ -93,3 +105,20 @@ pub struct MyStruct {
     pub u32: u32,
     pub u64: u64,
 }
+
+#[derive(Accounts)]
+pub struct TypeAlias<'info> {
+    #[account(zero)]
+    pub account: Account<'info, TypeAliasAccount>,
+}
+
+#[account]
+pub struct TypeAliasAccount {
+    pub type_alias_u8: TypeAliasU8,
+    pub type_alias_u8_array: TypeAliasU8Array,
+    pub type_alias_struct: TypeAliasStruct,
+}
+
+pub type TypeAliasU8 = u8;
+pub type TypeAliasU8Array = [TypeAliasU8; 8];
+pub type TypeAliasStruct = MyStruct;

+ 28 - 0
tests/idl/tests/client-interactions.ts

@@ -118,4 +118,32 @@ describe("Client interactions", () => {
       unnamedStruct.enumField.unnamedStruct[0].u64.eq(tupleStructArg[0].u64)
     );
   });
+
+  it("Can use type aliases", async () => {
+    const kp = anchor.web3.Keypair.generate();
+
+    const typeAliasU8 = 42;
+    const typeAliasU8Array = [1, 2, 3, 4, 5, 6, 7, 8];
+    const typeAliasStruct = {
+      u8: 1,
+      u16: 2,
+      u32: 3,
+      u64: new anchor.BN(4),
+    };
+
+    await program.methods
+      .typeAlias(typeAliasU8, typeAliasU8Array, typeAliasStruct)
+      .accounts({ account: kp.publicKey })
+      .signers([kp])
+      .preInstructions([await program.account.intAccount.createInstruction(kp)])
+      .rpc();
+
+    const account = await program.account.typeAliasAccount.fetch(kp.publicKey);
+    assert.strictEqual(account.typeAliasU8, typeAliasU8);
+    assert.deepEqual(account.typeAliasU8Array, typeAliasU8Array);
+    assert.strictEqual(typeAliasStruct.u8, 1);
+    assert.strictEqual(typeAliasStruct.u16, 2);
+    assert.strictEqual(typeAliasStruct.u32, 3);
+    assert(typeAliasStruct.u64.eq(typeAliasStruct.u64));
+  });
 });

+ 40 - 33
ts/packages/anchor/src/coder/borsh/idl.ts

@@ -90,11 +90,12 @@ export class IdlCoder {
             fieldName
           );
         } else if ("defined" in field.type) {
-          const defined = field.type.defined;
           // User defined type.
-          if (types === undefined) {
+          if (!types) {
             throw new IdlError("User defined types not provided");
           }
+
+          const defined = field.type.defined;
           const filtered = types.filter((t) => t.name === defined);
           if (filtered.length !== 1) {
             throw new IdlError(`Type not found: ${JSON.stringify(field)}`);
@@ -123,45 +124,51 @@ export class IdlCoder {
     types: IdlTypeDef[] = [],
     name?: string
   ): Layout {
-    if (typeDef.type.kind === "struct") {
-      const fieldLayouts = typeDef.type.fields.map((field) => {
-        const x = IdlCoder.fieldLayout(field, types);
-        return x;
-      });
-      return borsh.struct(fieldLayouts, name);
-    } else if (typeDef.type.kind === "enum") {
-      let variants = typeDef.type.variants.map((variant: IdlEnumVariant) => {
-        const name = camelCase(variant.name);
-        if (variant.fields === undefined) {
-          return borsh.struct([], name);
-        }
-        const fieldLayouts = variant.fields.map(
-          (f: IdlField | IdlType, i: number) => {
-            if (!f.hasOwnProperty("name")) {
+    switch (typeDef.type.kind) {
+      case "struct": {
+        const fieldLayouts = typeDef.type.fields.map((field) => {
+          return IdlCoder.fieldLayout(field, types);
+        });
+        return borsh.struct(fieldLayouts, name);
+      }
+
+      case "enum": {
+        let variants = typeDef.type.variants.map((variant: IdlEnumVariant) => {
+          const name = camelCase(variant.name);
+          if (!variant.fields) {
+            return borsh.struct([], name);
+          }
+
+          const fieldLayouts = variant.fields.map(
+            (f: IdlField | IdlType, i: number) => {
+              if ((f as IdlField)?.name) {
+                return IdlCoder.fieldLayout(f as IdlField, types);
+              }
+
               return IdlCoder.fieldLayout(
                 { type: f as IdlType, name: i.toString() },
                 types
               );
             }
-            // this typescript conversion is ok
-            // because if f were of type IdlType
-            // (that does not have a name property)
-            // the check before would've errored
-            return IdlCoder.fieldLayout(f as IdlField, types);
-          }
-        );
-        return borsh.struct(fieldLayouts, name);
-      });
+          );
+          return borsh.struct(fieldLayouts, name);
+        });
 
-      if (name !== undefined) {
-        // Buffer-layout lib requires the name to be null (on construction)
-        // when used as a field.
-        return borsh.rustEnum(variants).replicate(name);
+        if (name !== undefined) {
+          // Buffer-layout lib requires the name to be null (on construction)
+          // when used as a field.
+          return borsh.rustEnum(variants).replicate(name);
+        }
+
+        return borsh.rustEnum(variants, name);
       }
 
-      return borsh.rustEnum(variants, name);
-    } else {
-      throw new Error(`Unknown type kint: ${typeDef}`);
+      case "alias": {
+        return IdlCoder.fieldLayout(
+          { type: typeDef.type.value, name: typeDef.name },
+          types
+        );
+      }
     }
   }
 }

+ 51 - 41
ts/packages/anchor/src/coder/borsh/instruction.ts

@@ -264,56 +264,66 @@ class InstructionFormatter {
     data: Object,
     types: IdlTypeDef[]
   ): string {
-    if (typeDef.type.kind === "struct") {
-      const struct: IdlTypeDefTyStruct = typeDef.type;
-      const fields = Object.keys(data)
-        .map((k) => {
-          const f = struct.fields.filter((f) => f.name === k)[0];
-          if (f === undefined) {
-            throw new Error("Unable to find type");
-          }
-          return (
-            k + ": " + InstructionFormatter.formatIdlData(f, data[k], types)
-          );
-        })
-        .join(", ");
-      return "{ " + fields + " }";
-    } else {
-      if (typeDef.type.variants.length === 0) {
-        return "{}";
-      }
-      // Struct enum.
-      if (typeDef.type.variants[0].name) {
-        const variants = typeDef.type.variants;
-        const variant = Object.keys(data)[0];
-        const enumType = data[variant];
-        const namedFields = Object.keys(enumType)
-          .map((f) => {
-            const fieldData = enumType[f];
-            const idlField = variants[variant]?.filter(
-              (v: IdlField) => v.name === f
-            )[0];
-            if (idlField === undefined) {
-              throw new Error("Unable to find variant");
+    switch (typeDef.type.kind) {
+      case "struct": {
+        const struct: IdlTypeDefTyStruct = typeDef.type;
+        const fields = Object.keys(data)
+          .map((k) => {
+            const field = struct.fields.find((f) => f.name === k);
+            if (!field) {
+              throw new Error("Unable to find type");
             }
             return (
-              f +
+              k +
               ": " +
-              InstructionFormatter.formatIdlData(idlField, fieldData, types)
+              InstructionFormatter.formatIdlData(field, data[k], types)
             );
           })
           .join(", ");
+        return "{ " + fields + " }";
+      }
+
+      case "enum": {
+        if (typeDef.type.variants.length === 0) {
+          return "{}";
+        }
+        // Struct enum.
+        if (typeDef.type.variants[0].name) {
+          const variants = typeDef.type.variants;
+          const variant = Object.keys(data)[0];
+          const enumType = data[variant];
+          const namedFields = Object.keys(enumType)
+            .map((f) => {
+              const fieldData = enumType[f];
+              const idlField = variants[variant]?.find(
+                (v: IdlField) => v.name === f
+              );
+              if (!idlField) {
+                throw new Error("Unable to find variant");
+              }
+              return (
+                f +
+                ": " +
+                InstructionFormatter.formatIdlData(idlField, fieldData, types)
+              );
+            })
+            .join(", ");
 
-        const variantName = camelCase(variant, { pascalCase: true });
-        if (namedFields.length === 0) {
-          return variantName;
+          const variantName = camelCase(variant, { pascalCase: true });
+          if (namedFields.length === 0) {
+            return variantName;
+          }
+          return `${variantName} { ${namedFields} }`;
+        }
+        // Tuple enum.
+        else {
+          // TODO.
+          return "Tuple formatting not yet implemented";
         }
-        return `${variantName} { ${namedFields} }`;
       }
-      // Tuple enum.
-      else {
-        // TODO.
-        return "Tuple formatting not yet implemented";
+
+      case "alias": {
+        return InstructionFormatter.formatIdlType(typeDef.type.value);
       }
     }
   }

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

@@ -2,31 +2,39 @@ import { Idl, IdlField, IdlTypeDef, IdlType } from "../idl.js";
 import { IdlError } from "../error.js";
 
 export function accountSize(idl: Idl, idlAccount: IdlTypeDef) {
-  if (idlAccount.type.kind === "enum") {
-    const variantSizes = idlAccount.type.variants.map((variant) => {
-      if (!variant.fields) {
-        return 0;
-      }
+  switch (idlAccount.type.kind) {
+    case "struct": {
+      return idlAccount.type.fields
+        .map((f) => typeSize(idl, f.type))
+        .reduce((acc, size) => acc + size, 0);
+    }
 
-      return variant.fields
-        .map((f: IdlField | IdlType) => {
-          // Unnamed enum variant
-          if (!(typeof f === "object" && "name" in f)) {
-            return typeSize(idl, f);
-          }
+    case "enum": {
+      const variantSizes = idlAccount.type.variants.map((variant) => {
+        if (!variant.fields) {
+          return 0;
+        }
 
-          // Named enum variant
-          return typeSize(idl, f.type);
-        })
-        .reduce((acc, size) => acc + size, 0);
-    });
+        return variant.fields
+          .map((f: IdlField | IdlType) => {
+            // Unnamed enum variant
+            if (!(typeof f === "object" && "name" in f)) {
+              return typeSize(idl, f);
+            }
 
-    return Math.max(...variantSizes) + 1;
-  }
+            // Named enum variant
+            return typeSize(idl, f.type);
+          })
+          .reduce((acc, size) => acc + size, 0);
+      });
+
+      return Math.max(...variantSizes) + 1;
+    }
 
-  return idlAccount.type.fields
-    .map((f) => typeSize(idl, f.type))
-    .reduce((acc, size) => acc + size, 0);
+    case "alias": {
+      return typeSize(idl, idlAccount.type.value);
+    }
+  }
 }
 
 // Returns the size of the type in bytes. For variable length types, just return

+ 9 - 1
ts/packages/anchor/src/idl.ts

@@ -104,7 +104,15 @@ export type IdlTypeDefTyEnum = {
   variants: IdlEnumVariant[];
 };
 
-export type IdlTypeDefTy = IdlTypeDefTyEnum | IdlTypeDefTyStruct;
+export type IdlTypeDefTyAlias = {
+  kind: "alias";
+  value: IdlType;
+};
+
+export type IdlTypeDefTy =
+  | IdlTypeDefTyEnum
+  | IdlTypeDefTyStruct
+  | IdlTypeDefTyAlias;
 
 export type IdlTypeDefStruct = Array<IdlField>;
 

+ 8 - 0
ts/packages/anchor/src/program/namespace/types.ts

@@ -11,6 +11,7 @@ import {
   IdlInstruction,
   IdlType,
   IdlTypeDef,
+  IdlTypeDefTyAlias,
   IdlTypeDefTyEnum,
   IdlTypeDefTyStruct,
 } from "../../idl";
@@ -214,6 +215,11 @@ type DecodeStruct<I extends IdlTypeDefTyStruct, Defined> = {
   [F in I["fields"][number] as F["name"]]: DecodeType<F["type"], Defined>;
 };
 
+type DecodeAlias<I extends IdlTypeDefTyAlias, Defined> = DecodeType<
+  I["value"],
+  Defined
+>;
+
 export type TypeDef<
   I extends IdlTypeDef,
   Defined
@@ -221,6 +227,8 @@ export type TypeDef<
   ? DecodeEnum<I["type"], Defined>
   : I["type"] extends IdlTypeDefTyStruct
   ? DecodeStruct<I["type"], Defined>
+  : I["type"] extends IdlTypeDefTyAlias
+  ? DecodeAlias<I["type"], Defined>
   : never;
 
 type TypeDefDictionary<T extends IdlTypeDef[], Defined> = {