Browse Source

idl: Fix using `address` constraint with non-const expressions (#3216)

acheron 1 year ago
parent
commit
467ae43621

+ 1 - 0
CHANGELOG.md

@@ -65,6 +65,7 @@ The minor version will be incremented upon a breaking change and the patch versi
 - idl: Fix panicking on tests ([#3197](https://github.com/coral-xyz/anchor/pull/3197)).
 - idl: Fix panicking on tests ([#3197](https://github.com/coral-xyz/anchor/pull/3197)).
 - lang: Remove `arrayref` dependency ([#3201](https://github.com/coral-xyz/anchor/pull/3201)).
 - lang: Remove `arrayref` dependency ([#3201](https://github.com/coral-xyz/anchor/pull/3201)).
 - cli: Fix template code shouldn't escape ([#3210](https://github.com/coral-xyz/anchor/pull/3210)).
 - cli: Fix template code shouldn't escape ([#3210](https://github.com/coral-xyz/anchor/pull/3210)).
+- idl: Fix using `address` constraint with non-const expressions ([#3216](https://github.com/coral-xyz/anchor/pull/3216)).
 
 
 ### Breaking
 ### Breaking
 
 

+ 19 - 1
lang/syn/src/idl/accounts.rs

@@ -161,7 +161,25 @@ fn get_address(acc: &Field) -> TokenStream {
             .address
             .address
             .as_ref()
             .as_ref()
             .map(|constraint| &constraint.address)
             .map(|constraint| &constraint.address)
-            .filter(|address| !matches!(address, syn::Expr::Field(_)))
+            .filter(|address| {
+                match address {
+                    // Allow constants (assume the identifier follows the Rust naming convention)
+                    // e.g. `crate::ID`
+                    syn::Expr::Path(expr) => expr
+                        .path
+                        .segments
+                        .last()
+                        .unwrap()
+                        .ident
+                        .to_string()
+                        .chars()
+                        .all(|c| c.is_uppercase() || c == '_'),
+                    // Allow `const fn`s (assume any stand-alone function call without an argument)
+                    // e.g. `crate::id()`
+                    syn::Expr::Call(expr) => expr.args.is_empty(),
+                    _ => false,
+                }
+            })
             .map(|address| quote! { Some(#address.to_string()) })
             .map(|address| quote! { Some(#address.to_string()) })
             .unwrap_or_else(|| quote! { None }),
             .unwrap_or_else(|| quote! { None }),
     }
     }

+ 20 - 3
tests/relations-derivation/programs/relations-derivation/src/lib.rs

@@ -19,7 +19,7 @@ pub mod relations_derivation {
         Ok(())
         Ok(())
     }
     }
 
 
-    pub fn test_address_relation(_ctx: Context<TestAddressRelation>) -> Result<()> {
+    pub fn test_address(_ctx: Context<TestAddress>) -> Result<()> {
         Ok(())
         Ok(())
     }
     }
 }
 }
@@ -66,9 +66,20 @@ pub struct TestRelation<'info> {
 }
 }
 
 
 #[derive(Accounts)]
 #[derive(Accounts)]
-pub struct TestAddressRelation<'info> {
+pub struct TestAddress<'info> {
+    // Included wit the `address` field in IDL
+    // It's actually `static` but it doesn't matter for our purposes
+    #[account(address = crate::ID)]
+    constant: UncheckedAccount<'info>,
+    #[account(address = crate::id())]
+    const_fn: UncheckedAccount<'info>,
+
+    // Not included with the `address` field in IDL
     #[account(address = my_account.my_account)]
     #[account(address = my_account.my_account)]
-    account: UncheckedAccount<'info>,
+    field: UncheckedAccount<'info>,
+    #[account(address = my_account.my_account())]
+    method: UncheckedAccount<'info>,
+
     #[account(seeds = [b"seed"], bump = my_account.bump)]
     #[account(seeds = [b"seed"], bump = my_account.bump)]
     my_account: Account<'info, MyAccount>,
     my_account: Account<'info, MyAccount>,
 }
 }
@@ -78,3 +89,9 @@ pub struct MyAccount {
     pub my_account: Pubkey,
     pub my_account: Pubkey,
     pub bump: u8,
     pub bump: u8,
 }
 }
+
+impl MyAccount {
+    pub fn my_account(&self) -> Pubkey {
+        self.my_account
+    }
+}

+ 8 - 3
tests/relations-derivation/tests/typescript.spec.ts

@@ -41,8 +41,13 @@ describe("typescript", () => {
     await tx.rpc();
     await tx.rpc();
   });
   });
 
 
-  it("Can use relations derivation with `address` constraint", () => {
-    // Only compile test for now since the IDL spec doesn't currently support field access
-    // expressions for the `address` constraint
+  it("Can use `address` constraint", () => {
+    const ix = program.idl.instructions.find(
+      (ix) => ix.name === "testAddress"
+    )!;
+    expect(ix.accounts.find((acc) => acc.name === "constant")!.address).to.not
+      .be.undefined;
+    expect(ix.accounts.find((acc) => acc.name === "constFn")!.address).to.not.be
+      .undefined;
   });
   });
 });
 });