Skip to content

Commit 894eb8d

Browse files
idl: Exclude external accounts (solana-foundation#4197)
1 parent 7dba200 commit 894eb8d

File tree

6 files changed

+69
-23
lines changed

6 files changed

+69
-23
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ The minor version will be incremented upon a breaking change and the patch versi
3737
- lang: Rename `utils` module of `declare_program!` to `parsers` ([#4151](https://github.com/solana-foundation/anchor/pull/4151)).
3838
- lang: Remove the `interface-instructions` feature and the `#[interface]` attribute ([#4156](https://github.com/solana-foundation/anchor/pull/4156)).
3939
- cli: Remove the `login` command ([#4182](https://github.com/solana-foundation/anchor/pull/4182)).
40+
- idl: Exclude external accounts ([#4197](https://github.com/solana-foundation/anchor/pull/4197)).
4041

4142
## [0.32.1] - 2025-10-09
4243

lang/syn/src/idl/accounts.rs

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ pub fn gen_idl_build_impl_accounts_struct(accounts: &AccountsStruct) -> TokenStr
4343
(quote! { None }, quote! { None }, quote! { vec![] })
4444
};
4545

46-
let acc_type_path = match &acc.ty {
46+
let defined = match &acc.ty {
4747
Ty::Account(ty)
4848
// Skip `UpgradeableLoaderState` type for now until `bincode` serialization
4949
// is supported.
@@ -57,11 +57,21 @@ pub fn gen_idl_build_impl_accounts_struct(accounts: &AccountsStruct) -> TokenStr
5757
.to_string()
5858
.contains("UpgradeableLoaderState") =>
5959
{
60-
Some(&ty.account_type_path)
60+
let defined = &ty.account_type_path;
61+
Some((defined, quote! { <#defined>::owner() == crate::ID }))
6162
}
62-
Ty::LazyAccount(ty) => Some(&ty.account_type_path),
63-
Ty::AccountLoader(ty) => Some(&ty.account_type_path),
64-
Ty::InterfaceAccount(ty) => Some(&ty.account_type_path),
63+
Ty::LazyAccount(ty) => {
64+
let defined = &ty.account_type_path;
65+
Some((defined, quote! { <#defined>::owner() == crate::ID }))
66+
},
67+
Ty::AccountLoader(ty) => {
68+
let defined = &ty.account_type_path;
69+
Some((defined, quote! { <#defined>::owner() == crate::ID }))
70+
},
71+
Ty::InterfaceAccount(ty) => {
72+
let defined = &ty.account_type_path;
73+
Some((defined, quote! { <#defined>::owners().contains(&crate::ID) }))
74+
},
6575
_ => None,
6676
};
6777

@@ -78,7 +88,7 @@ pub fn gen_idl_build_impl_accounts_struct(accounts: &AccountsStruct) -> TokenStr
7888
relations: #relations,
7989
})
8090
},
81-
acc_type_path,
91+
defined,
8292
)
8393
}
8494
AccountField::CompositeField(comp_f) => {
@@ -116,7 +126,10 @@ pub fn gen_idl_build_impl_accounts_struct(accounts: &AccountsStruct) -> TokenStr
116126
}
117127
})
118128
.unzip::<_, _, Vec<_>, Vec<_>>();
119-
let defined = defined.into_iter().flatten().collect::<Vec<_>>();
129+
let (defined, is_owner) = defined
130+
.into_iter()
131+
.flatten()
132+
.unzip::<_, _, Vec<_>, Vec<_>>();
120133

121134
quote! {
122135
impl #impl_generics #ident #ty_generics #where_clause {
@@ -126,11 +139,14 @@ pub fn gen_idl_build_impl_accounts_struct(accounts: &AccountsStruct) -> TokenStr
126139
) -> Vec<#idl::IdlInstructionAccountItem> {
127140
#(
128141
if let Some(ty) = <#defined>::create_type() {
129-
let account = #idl::IdlAccount {
130-
name: ty.name.clone(),
131-
discriminator: <#defined>::DISCRIMINATOR.into(),
132-
};
133-
accounts.insert(account.name.clone(), account);
142+
if #is_owner {
143+
let account = #idl::IdlAccount {
144+
name: ty.name.clone(),
145+
discriminator: <#defined>::DISCRIMINATOR.into(),
146+
};
147+
accounts.insert(account.name.clone(), account);
148+
}
149+
134150
types.insert(ty.name.clone(), ty);
135151
<#defined>::insert_types(types);
136152
}

tests/idl/idls/idl.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,9 @@
596596
}
597597
]
598598
},
599+
{
600+
"name": "external"
601+
},
599602
{
600603
"name": "token_account"
601604
},
@@ -1356,6 +1359,13 @@
13561359
]
13571360
}
13581361
},
1362+
{
1363+
"name": "MyAccount",
1364+
"type": {
1365+
"kind": "struct",
1366+
"fields": []
1367+
}
1368+
},
13591369
{
13601370
"name": "MyStruct",
13611371
"type": {

tests/idl/programs/external/src/lib.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,19 @@ declare_id!("Externa1111111111111111111111111111111111111");
66
pub mod external {
77
use super::*;
88

9-
pub fn initialize(_ctx: Context<Initialize>) -> Result<()> {
9+
pub fn test_compilation(_ctx: Context<TestCompilation>) -> Result<()> {
1010
Ok(())
1111
}
1212
}
1313

14+
#[derive(Accounts)]
15+
pub struct TestCompilation<'info> {
16+
account: Account<'info, MyAccount>,
17+
}
18+
19+
#[account]
20+
pub struct MyAccount {}
21+
1422
#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
1523
pub struct MyStruct {
1624
some_field: u8,
@@ -22,10 +30,3 @@ pub enum MyEnum {
2230
Named { name: String },
2331
Tuple(String),
2432
}
25-
26-
pub struct NonBorshStruct {
27-
pub data: i32,
28-
}
29-
30-
#[derive(Accounts)]
31-
pub struct Initialize {}

tests/idl/programs/idl/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,8 @@ pub struct TestCompilation<'info> {
541541

542542
composite: Composite<'info>,
543543

544+
external: Account<'info, external::MyAccount>,
545+
544546
token_account: Account<'info, token::TokenAccount>,
545547
mint_account: Account<'info, token::Mint>,
546548
token_interface_account: InterfaceAccount<'info, token_interface::TokenAccount>,

tests/idl/tests/idl.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as anchor from "@anchor-lang/core";
22
import BN from "bn.js";
33
import { assert } from "chai";
44

5+
import type { External } from "../target/types/external";
56
import type { Idl } from "../target/types/idl";
67

78
describe("IDL", () => {
@@ -547,7 +548,7 @@ describe("IDL", () => {
547548
program.idl.accounts.find((acc) => acc.name === "zcAccount")
548549
);
549550
const zcAccount = program.idl.types.find((ty) => ty.name === "zcAccount");
550-
if (!zcAccount) throw new Error("Zero copy accout not found");
551+
if (!zcAccount) throw new Error("`zcAccount` not found");
551552

552553
assert.strictEqual(zcAccount.serialization, "bytemuck");
553554
assert.deepEqual(zcAccount.repr, { kind: "c" });
@@ -560,13 +561,28 @@ describe("IDL", () => {
560561
const zcUnsafeAccount = program.idl.types.find(
561562
(ty) => ty.name === "zcUnsafeAccount"
562563
);
563-
if (!zcUnsafeAccount)
564-
throw new Error("Unsafe zero copy accout not found");
564+
if (!zcUnsafeAccount) throw new Error("`zcUnsafeAccount` not found");
565565

566566
assert.strictEqual(zcUnsafeAccount.serialization, "bytemuckunsafe");
567567
assert.deepEqual(zcUnsafeAccount.repr, { kind: "rust", packed: true });
568568
});
569569

570+
it("Does not include external accounts", () => {
571+
const external: anchor.Program<External> = anchor.workspace.external;
572+
assert.isDefined(
573+
external.idl.accounts.find((acc) => acc.name === "myAccount")
574+
);
575+
assert.isDefined(
576+
external.idl.types.find((ty) => ty.name === "myAccount")
577+
);
578+
579+
assert.isUndefined(
580+
// @ts-expect-error
581+
program.idl.accounts.find((acc) => acc.name === "myAccount")
582+
);
583+
assert.isDefined(program.idl.types.find((ty) => ty.name === "myAccount"));
584+
});
585+
570586
it("Includes constants marked with `#[constant]`", () => {
571587
const checkDefined = (
572588
cb: (constant: typeof program["idl"]["constants"][number]) => boolean

0 commit comments

Comments
 (0)