1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
#![allow(clippy::too_many_lines, clippy::unnecessary_wraps)]
//! Macros for near-sdk-contract-tools.
use darling::{ast::NestedMeta, FromDeriveInput, FromMeta};
use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput, Item};
mod approval;
mod escrow;
mod migrate;
mod owner;
mod pause;
mod rbac;
mod rename;
mod standard;
mod upgrade;
fn default_crate_name() -> syn::Path {
syn::parse_str("::near_sdk_contract_tools").unwrap()
}
fn default_macros() -> syn::Path {
syn::parse_str("::near_sdk_contract_tools").unwrap()
}
fn default_near_sdk() -> syn::Path {
syn::parse_str("::near_sdk").unwrap()
}
fn default_serde() -> syn::Path {
syn::parse_str("::near_sdk::serde").unwrap()
}
fn unitify(ty: Option<syn::Type>) -> syn::Type {
ty.unwrap_or_else(|| syn::parse_quote! { () })
}
fn make_derive<T>(
input: TokenStream,
expand: fn(T) -> Result<proc_macro2::TokenStream, darling::Error>,
) -> TokenStream
where
T: FromDeriveInput,
{
let input = parse_macro_input!(input as DeriveInput);
FromDeriveInput::from_derive_input(&input)
.and_then(expand)
.map_or_else(|e| e.write_errors().into(), Into::into)
}
/// Use on a struct to emit NEP-297 event strings.
///
/// Specify event standard parameters: `#[nep297(standard = "...", version = "...")]`
///
/// Optional: `#[nep297(name = "...")]`
///
/// Rename strategy for all variants (default: unchanged): `#[event(rename = "<strategy>")]`
/// Options for `<strategy>`:
/// - `UpperCamelCase`
/// - `lowerCamelCase`
/// - `snake_case`
/// - `kebab-case`
/// - `SHOUTY_SNAKE_CASE`
/// - `SHOUTY-KEBAB-CASE`
/// - `Title Case`
///
/// # Warning
///
/// Rename strategies are provided for convenience, and the actual string
/// transformation is delegated to the [`heck`](https://crates.io/crates/heck)
/// crate. It _is_ possible that unexpected name transformations or collisions
/// may occur, but it is _extremely unlikely_ if reasonable Rust naming
/// conventions are followed.
///
/// For example, `"HelloWorld"`, `"hello_world"`, and `"hello__world"`, when
/// snake-case-ified, are `"hello_world"`, so if you happened to name distinct
/// events thusly, the transformed names would collide.
#[proc_macro_derive(Nep297, attributes(nep297))]
pub fn derive_nep297(input: TokenStream) -> TokenStream {
make_derive(input, standard::nep297::expand)
}
/// Creates a managed, lazily-loaded `Owner` implementation for the targeted
/// `#[near(contract_state)]` struct.
///
/// The storage key prefix for the fields can be optionally specified (default:
/// `"~o"`) using `#[owner(storage_key = "<expression>")]`.
#[proc_macro_derive(Owner, attributes(owner))]
pub fn derive_owner(input: TokenStream) -> TokenStream {
make_derive(input, owner::expand)
}
/// Makes a contract pausable. Provides an implementation of the `Pause` trait.
///
/// The storage key prefix for the fields can be optionally specified (default:
/// `"~p"`) using `#[pause(storage_key = "<expression>")]`.
#[proc_macro_derive(Pause, attributes(pause))]
pub fn derive_pause(input: TokenStream) -> TokenStream {
make_derive(input, pause::expand)
}
/// Adds role-based access control. No external methods are exposed.
///
/// The roles prefix can be specified using `#[rbac(roles = "MyRoles")]`.
/// Typically `"MyRoles"` is an enum and its variants are the different role
/// names.
///
/// The storage key prefix for the fields can be optionally specified (default:
/// `"~r"`) using `#[rbac(storage_key = "<expression>")]`.
#[proc_macro_derive(Rbac, attributes(rbac))]
pub fn derive_rbac(input: TokenStream) -> TokenStream {
make_derive(input, rbac::expand)
}
/// Adds NEP-141 fungible token core functionality to a contract. Exposes
/// `ft_*` functions to the public blockchain, implements internal controller
/// and receiver functionality.
///
/// The storage key prefix for the fields can be optionally specified (default:
/// `"~$141"`) using `#[nep141(storage_key = "<expression>")]`.
#[proc_macro_derive(Nep141, attributes(nep141))]
pub fn derive_nep141(input: TokenStream) -> TokenStream {
make_derive(input, standard::nep141::expand)
}
/// Adds NEP-145 fungible token core functionality to a contract. Exposes
/// `storage_*` functions to the public blockchain, implements internal
/// controller functionality.
///
/// The storage key prefix for the fields can be optionally specified (default:
/// `"~$145"`) using `#[nep145(storage_key = "<expression>")]`.
#[proc_macro_derive(Nep145, attributes(nep145))]
pub fn derive_nep145(input: TokenStream) -> TokenStream {
make_derive(input, standard::nep145::expand)
}
/// Adds NEP-148 fungible token metadata functionality to a contract. Metadata
/// must be initialized during contract creation using `Nep148Controller::set_metadata`.
///
/// The storage key prefix for the fields can be optionally specified (default:
/// `"~$148"`) using `#[nep148(storage_key = "<expression>")]`.
#[proc_macro_derive(Nep148, attributes(nep148))]
pub fn derive_nep148(input: TokenStream) -> TokenStream {
make_derive(input, standard::nep148::expand)
}
/// Implements NEP-141, NEP-145, and NEP-148 functionality, like
/// `#[derive(Nep141, Nep145, Nep148)]`. This is the recommended way to
/// implement a fungible token, as it also ensures that all of the standards
/// integrate with each other correctly.
///
/// Attributes are generally the union of those from the constituent derive
/// macros.
/// Specify attributes with `#[fungible_token(...)]`.
#[proc_macro_derive(FungibleToken, attributes(fungible_token))]
pub fn derive_fungible_token(input: TokenStream) -> TokenStream {
make_derive(input, standard::fungible_token::expand)
}
/// Adds NEP-171 non-fungible token core functionality to a contract. Exposes
/// `nft_*` functions to the public blockchain, implements internal controller
/// and receiver functionality.
///
/// The storage key prefix for the fields can be optionally specified (default:
/// `"~$171"`) using `#[nep171(storage_key = "<expression>")]`.
///
/// Fields:
/// - `no_hooks`: Flag. Removes the requirement for the contract to implement
/// transfer hooks.
/// - `token_data`: specify the token metadata loading extensions invoked by
/// `nft_token`.
#[proc_macro_derive(Nep171, attributes(nep171))]
pub fn derive_nep171(input: TokenStream) -> TokenStream {
make_derive(input, standard::nep171::expand)
}
/// Adds NEP-177 non-fungible token metadata functionality to a contract.
///
/// The storage key prefix for the fields can be optionally specified (default:
/// `"~$177"`) using `#[nep177(storage_key = "<expression>")]`.
#[proc_macro_derive(Nep177, attributes(nep177))]
pub fn derive_nep177(input: TokenStream) -> TokenStream {
make_derive(input, standard::nep177::expand)
}
/// Adds NEP-178 non-fungible token approvals functionality to a contract.
///
/// The storage key prefix for the fields can be optionally specified (default:
/// `"~$178"`) using `#[nep178(storage_key = "<expression>")]`.
#[proc_macro_derive(Nep178, attributes(nep178))]
pub fn derive_nep178(input: TokenStream) -> TokenStream {
make_derive(input, standard::nep178::expand)
}
/// Adds NEP-181 non-fungible token enumeration functionality to a contract.
///
/// The storage key prefix for the fields can be optionally specified (default:
/// `"~$181"`) using `#[nep181(storage_key = "<expression>")]`.
#[proc_macro_derive(Nep181, attributes(nep181))]
pub fn derive_nep181(input: TokenStream) -> TokenStream {
make_derive(input, standard::nep181::expand)
}
/// Implements all NFT functionality at once, like `#[derive(Nep171, Nep177, Nep178, Nep181)]`.
#[proc_macro_derive(NonFungibleToken, attributes(non_fungible_token))]
pub fn derive_non_fungible_token(input: TokenStream) -> TokenStream {
make_derive(input, standard::non_fungible_token::expand)
}
/// Migrate a contract's default struct from one schema to another.
///
/// Fields may be specified in the `#[migrate(...)]` attribute.
///
/// Fields include:
/// - `from` Old default struct type to convert from. (required)
/// - `to` New default struct type to convert into. (optional, default: `Self`)
/// - `convert` Identifier of a function that converts from the old schema to
/// the new schema. Mutually exclusive with `convert_with_args`. (optional,
/// default: `<Self::NewSchema as From<Self::OldSchema>>::from`)
/// - `convert_with_args` Identifier of a function that converts from the old
/// schema to the new schema and accepts a single `String` argument.
/// Mutually exclusive with `convert`. (optional)
/// - `allow` Expression to evaluate before allowing
#[proc_macro_derive(Migrate, attributes(migrate))]
pub fn derive_migrate(input: TokenStream) -> TokenStream {
make_derive(input, migrate::expand)
}
/// Create a simple multisig component. Does not expose any functions to the
/// blockchain. Creates implementations for `ApprovalManager` and
/// `AccountApprover` for the target contract struct.
///
/// Fields may be specified in the `#[simple_multisig(...)]` attribute.
///
/// Fields include:
/// - `storage_key` Storage prefix for multisig data (optional, default: `b"~sm"`)
/// - `action` What sort of approval `Action` can be approved by the multisig
/// component?
/// - `role` Approving accounts are required to have this `Rbac` role.
#[proc_macro_derive(SimpleMultisig, attributes(simple_multisig))]
pub fn derive_simple_multisig(input: TokenStream) -> TokenStream {
make_derive(input, approval::simple_multisig::expand)
}
/// Smart `#[event]` macro.
///
/// See documentation on the [`derive@Nep297`] derive macro for more details.
#[proc_macro_attribute]
pub fn event(attr: TokenStream, item: TokenStream) -> TokenStream {
let attr = match NestedMeta::parse_meta_list(attr.into()) {
Ok(v) => v,
Err(e) => {
return TokenStream::from(darling::Error::from(e).write_errors());
}
};
let item = parse_macro_input!(item as Item);
standard::event::EventAttributeMeta::from_list(&attr)
.and_then(|meta| standard::event::event_attribute(meta, &item))
.map_or_else(|e| e.write_errors().into(), Into::into)
}
/// Create an upgrade component. Does not expose any functions to the
/// blockchain.
///
/// Fields may be specified in the `#[upgrade(...)]` attribute.
///
/// Fields include:
/// - `hook` - If included, provides an implementation of `UpgradeHook`. An implementation must be explicity provided otherwise. Options include:
/// - `"none"` - Empty upgrade hook.
/// - `"owner"` - The upgrade function may only be called by the owner of the contract as specified by an `Owner` implementation.
/// - `"role(r)"` - The upgrade function may only be called by an account that has been assigned the role `r` as determined by an `Rbac` implementation.
/// - `serializer` - `"borsh"` or `"jsonbase64"` (default). Indicates the serialization format of code the `upgrade` function will accept.
/// - `migrate_method_name` - The name of the method to call after the upgrade. Default `"migrate"`.
/// - `migrate_method_args` - The input to send to the migrate function. Default empty vector.
/// - `migrate_minimum_gas` - How much gas to guarantee the migrate function, otherwise reject. Default 15T.
#[proc_macro_derive(Upgrade, attributes(upgrade))]
pub fn derive_upgrade(input: TokenStream) -> TokenStream {
make_derive(input, upgrade::expand)
}
/// Creates a managed, lazily-loaded `Escrow` implementation for the targeted
/// `#[near(contract_state)]` struct.
///
/// Fields include:
/// - `id` - the type required for id, must be `borsh::BorshSerialize` & `serde::Serialize`, for events
/// - `state` - the type required for id, must be `borsh::BorshSerialize` & `borsh::BorshSerialize`
/// - `storage_key` Storage prefix for escrow data (optional, default: `b"~es"`)
#[proc_macro_derive(Escrow, attributes(escrow))]
pub fn derive_escrow(input: TokenStream) -> TokenStream {
make_derive(input, escrow::expand)
}