Search code examples
rustanchorsolana

Anchor `declare_id!` with Environment Variable - Environment Variables in Macros


In anchor the "top" of any program features a declare_id!() statement. The input to that statement is a Pubkey, typically in the form of a hard coded string. 12Factor Methodology typically dictates that hard coded configuration values like this should be avoided. However trying to not hard code the value has me pulling out my hair.

declare_id!(std::env::var("VARIABLE_NAME"));

Does not work because the env::var call executes at runtime while the macro executes at compile time.

declare_id!(env!("VARIABLE_NAME"));

Does not work because env! returns a &str.

declare_id!(Pubkey::from_str(env!("VARIABLE_NAME")));

Does not work because Pubkey::from_str can fail and as such returns a Result

declare_id!(Pubkey::from_str(env!("VARIABLE_NAME")).unwrap());

Does not work because declare_id! requires a constant and constants cannot be made from unwrap (Its probably more nuanced than that, but I'm new to rust) and ? fails for the same reason.

  1. How would I go about defining an environment variable within a macro?
  2. Given the lack of resources on the topic, I'm presuming an environment variable in this case is not a best practice. Why should one not use an environment variable in this case?
  3. How can one accomplish the injection of program id into an anchor application if environment variables are not the way to do so?

Bonus points:

env::var() returns a Result<&str>

env::var_os() returns a Result<&OsStr>

env!() returns a &str

env_os!() does not exist in core

How do you handle an OsStr environment variable at build time? Why would you not need to be able to?


Solution

  • Working with env vars seems troublesome, since most of the ways of creating a Pubkey aren't const fn. To me, this seems like an issue with the anchor-lang crate, usually env vars aren't this troublesome.

    That said, you could write the key to a config file somewhere, and read that in using include_bytes, and pass that to Pubkey::new_from_array, which is const:

    // this works because concat! expands to a string literal
    const BYTES: &[u8; 32] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/key"));
    anchor_lang::declare_id!(Pubkey::new_from_array(*BYTES));
    

    If you don't want to store the file in the repo, you can point include_bytes!() at any location, even one read from an env var. For example, you could have:

    const BYTES: &[u8; 32] = include_bytes!(env!("ANCHOR_KEY_LOCATION"));
    anchor_lang::declare_id!(Pubkey::new_from_array(*BYTES));
    

    which will read the key bytes from a file at $ANCHOR_KEY_LOCATION