1#![recursion_limit = "128"]
2extern crate proc_macro;
3
4mod core_impl;
5
6use core_impl::{ext::generate_ext_structs, metadata::generate_contract_metadata_method};
7
8use proc_macro::TokenStream;
9
10use self::core_impl::*;
11use darling::ast::NestedMeta;
12use darling::{Error, FromMeta};
13use proc_macro2::{Ident, Span};
14use quote::{quote, ToTokens};
15use syn::{parse_quote, Expr, ImplItem, ItemEnum, ItemImpl, ItemStruct, ItemTrait, WhereClause};
16
17#[derive(Debug, Clone)]
18struct Serializers {
19 vec: Vec<Expr>,
20}
21
22impl FromMeta for Serializers {
23 fn from_expr(expr: &syn::Expr) -> Result<Self, darling::Error> {
24 match expr {
25 syn::Expr::Array(expr_array) => Ok(Serializers {
26 vec: expr_array
27 .elems
28 .iter()
29 .map(<Expr as FromMeta>::from_expr)
30 .map(|x| x.unwrap())
31 .collect::<Vec<_>>(),
32 }),
33 _ => Err(Error::unexpected_expr_type(expr)),
34 }
35 }
36}
37
38#[derive(FromMeta)]
39struct NearMacroArgs {
40 serializers: Option<Serializers>,
41 contract_state: Option<bool>,
42 contract_metadata: Option<core_impl::ContractMetadata>,
43 inside_nearsdk: Option<bool>,
44}
45
46fn has_nested_near_macros(item: TokenStream) -> bool {
47 syn::parse::<syn::Item>(item)
48 .ok()
49 .and_then(|item_ast| {
50 let attrs = match item_ast {
51 syn::Item::Struct(s) => s.attrs,
52 syn::Item::Enum(e) => e.attrs,
53 syn::Item::Impl(i) => i.attrs,
54 _ => vec![], };
56
57 attrs.into_iter().find(|attr| {
58 let path_str = attr.path().to_token_stream().to_string();
59 path_str == "near" || path_str == "near_bindgen"
60 })
61 })
62 .is_some()
63}
64
65#[proc_macro_attribute]
66pub fn near(attr: TokenStream, item: TokenStream) -> TokenStream {
67 if attr.to_string().contains("event_json") {
68 return core_impl::near_events(attr, item);
69 }
70
71 let meta_list = match NestedMeta::parse_meta_list(attr.into()) {
72 Ok(v) => v,
73 Err(e) => {
74 return TokenStream::from(Error::from(e).write_errors());
75 }
76 };
77
78 let near_macro_args = match NearMacroArgs::from_list(&meta_list) {
79 Ok(v) => v,
80 Err(e) => {
81 return TokenStream::from(e.write_errors());
82 }
83 };
84
85 let near_sdk_crate = if near_macro_args.inside_nearsdk.unwrap_or(false) {
86 quote! {crate}
87 } else {
88 quote! {::near_sdk}
89 };
90
91 if has_nested_near_macros(item.clone()) {
93 return TokenStream::from(
94 syn::Error::new(
95 Span::call_site(),
96 "#[near] or #[near_bindgen] attributes are not allowed to be nested inside of the outmost #[near] attribute. Only a single #[near] attribute is allowed",
97 )
98 .to_compile_error(),
99 );
100 }
101 let string_borsh_crate = quote! {#near_sdk_crate::borsh}.to_string();
102 let string_serde_crate = quote! {#near_sdk_crate::serde}.to_string();
103
104 let mut expanded: proc_macro2::TokenStream = quote! {};
105
106 if near_macro_args.contract_state.unwrap_or(false) {
107 if let Some(metadata) = near_macro_args.contract_metadata {
108 expanded = quote! {#[#near_sdk_crate::near_bindgen(#metadata)]}
109 } else {
110 expanded = quote! {#[#near_sdk_crate::near_bindgen]}
111 }
112 };
113
114 let mut has_borsh = false;
115 let mut has_json = false;
116
117 let mut borsh_attr = quote! {};
118
119 match near_macro_args.serializers {
120 Some(serializers) => {
121 let attr2 = serializers.clone();
122
123 attr2.vec.iter().for_each(|old_expr| {
124 let new_expr = &mut old_expr.clone();
125 match &mut *new_expr {
126 Expr::Call(ref mut call_expr) => {
127 if let Expr::Path(ref mut path) = &mut *call_expr.func {
128 if let Some(ident) = path.path.get_ident() {
129 if *ident == "json" {
130 has_json = true;
131 path.path =
132 syn::Path::from(Ident::new("serde", Span::call_site()));
133 call_expr.args.push(parse_quote! {crate=#string_serde_crate});
134 } else if *ident == "borsh" {
135 has_borsh = true;
136 call_expr.args.push(parse_quote! {crate=#string_borsh_crate});
137 }
138 }
139 }
140 borsh_attr = quote! {#[#new_expr]};
141 }
142 Expr::Path(ref mut path_expr) => {
143 if let Some(ident) = path_expr.path.get_ident() {
144 if *ident == "json" {
145 has_json = true;
146 }
147 if *ident == "borsh" {
148 has_borsh = true;
149 borsh_attr = quote! {#[borsh(crate=#string_borsh_crate)]};
150 }
151 }
152 }
153 _ => {}
154 }
155 });
156 }
157 None => {
158 has_borsh = true;
159 borsh_attr = quote! {#[borsh(crate = #string_borsh_crate)]};
160 }
161 }
162
163 #[cfg(feature = "abi")]
164 {
165 let schema_derive: proc_macro2::TokenStream =
166 get_schema_derive(has_json, has_borsh, near_sdk_crate.clone(), false);
167 expanded = quote! {
168 #expanded
169 #schema_derive
170 };
171 }
172
173 if has_borsh {
174 expanded = quote! {
175 #expanded
176 #[derive(#near_sdk_crate::borsh::BorshSerialize, #near_sdk_crate::borsh::BorshDeserialize)]
177 #borsh_attr
178 };
179 }
180
181 if has_json {
182 expanded = quote! {
183 #expanded
184 #[derive(#near_sdk_crate::serde::Serialize, #near_sdk_crate::serde::Deserialize)]
185 #[serde(crate = #string_serde_crate)]
186 };
187 }
188
189 if let Ok(input) = syn::parse::<ItemStruct>(item.clone()) {
190 expanded = quote! {
191 #expanded
192 #input
193 };
194 } else if let Ok(input) = syn::parse::<ItemEnum>(item.clone()) {
195 expanded = quote! {
196 #expanded
197 #input
198 };
199 } else if let Ok(input) = syn::parse::<ItemImpl>(item) {
200 expanded = quote! {
201 #[#near_sdk_crate::near_bindgen]
202 #input
203 };
204 } else {
205 return TokenStream::from(
206 syn::Error::new(
207 Span::call_site(),
208 "near macro can only be used on struct or enum definition and impl sections.",
209 )
210 .to_compile_error(),
211 );
212 }
213
214 TokenStream::from(expanded)
215}
216
217#[proc_macro_attribute]
218pub fn near_bindgen(attr: TokenStream, item: TokenStream) -> TokenStream {
219 if attr.to_string().contains("event_json") {
220 return core_impl::near_events(attr, item);
221 }
222
223 let generate_metadata = |ident: &Ident,
224 generics: &syn::Generics|
225 -> Result<proc_macro2::TokenStream, proc_macro2::TokenStream> {
226 let metadata_impl_gen = generate_contract_metadata_method(ident, generics).into();
227
228 let metadata_impl_gen = syn::parse::<ItemImpl>(metadata_impl_gen)
229 .expect("failed to generate contract metadata");
230 process_impl_block(metadata_impl_gen)
231 };
232
233 if let Ok(input) = syn::parse::<ItemStruct>(item.clone()) {
234 let metadata = core_impl::contract_source_metadata_const(attr);
235
236 let metadata_impl_gen = generate_metadata(&input.ident, &input.generics);
237
238 let metadata_impl_gen = match metadata_impl_gen {
239 Ok(metadata) => metadata,
240 Err(err) => return err.into(),
241 };
242
243 let ext_gen = generate_ext_structs(&input.ident, Some(&input.generics));
244 #[cfg(feature = "__abi-embed-checked")]
245 let abi_embedded = abi::embed();
246 #[cfg(not(feature = "__abi-embed-checked"))]
247 let abi_embedded = quote! {};
248 TokenStream::from(quote! {
249 #input
250 #ext_gen
251 #abi_embedded
252 #metadata
253 #metadata_impl_gen
254 })
255 } else if let Ok(input) = syn::parse::<ItemEnum>(item.clone()) {
256 let metadata = core_impl::contract_source_metadata_const(attr);
257 let metadata_impl_gen = generate_metadata(&input.ident, &input.generics);
258
259 let metadata_impl_gen = match metadata_impl_gen {
260 Ok(metadata) => metadata,
261 Err(err) => return err.into(),
262 };
263
264 let ext_gen = generate_ext_structs(&input.ident, Some(&input.generics));
265 #[cfg(feature = "__abi-embed-checked")]
266 let abi_embedded = abi::embed();
267 #[cfg(not(feature = "__abi-embed-checked"))]
268 let abi_embedded = quote! {};
269 TokenStream::from(quote! {
270 #input
271 #ext_gen
272 #abi_embedded
273 #metadata
274 #metadata_impl_gen
275 })
276 } else if let Ok(input) = syn::parse::<ItemImpl>(item) {
277 for method in &input.items {
278 if let ImplItem::Fn(m) = method {
279 let ident = &m.sig.ident;
280 if ident.eq("__contract_abi") || ident.eq("contract_source_metadata") {
281 return TokenStream::from(
282 syn::Error::new_spanned(
283 ident.to_token_stream(),
284 "use of reserved contract method",
285 )
286 .to_compile_error(),
287 );
288 }
289 }
290 }
291 match process_impl_block(input) {
292 Ok(output) => output,
293 Err(output) => output,
294 }
295 .into()
296 } else {
297 TokenStream::from(
298 syn::Error::new(
299 Span::call_site(),
300 "near_bindgen can only be used on struct or enum definition and impl sections.",
301 )
302 .to_compile_error(),
303 )
304 }
305}
306
307fn process_impl_block(
314 mut input: ItemImpl,
315) -> Result<proc_macro2::TokenStream, proc_macro2::TokenStream> {
316 let item_impl_info = match ItemImplInfo::new(&mut input) {
317 Ok(x) => x,
318 Err(err) => return Err(err.to_compile_error()),
319 };
320
321 #[cfg(not(feature = "__abi-generate"))]
322 let abi_generated = quote! {};
323 #[cfg(feature = "__abi-generate")]
324 let abi_generated = abi::generate(&item_impl_info);
325
326 let generated_code = item_impl_info.wrapper_code();
327
328 let ext_generated_code = item_impl_info.generate_ext_wrapper_code();
330
331 Ok(TokenStream::from(quote! {
332 #ext_generated_code
333 #input
334 #generated_code
335 #abi_generated
336 })
337 .into())
338}
339
340#[proc_macro_attribute]
341pub fn ext_contract(attr: TokenStream, item: TokenStream) -> TokenStream {
342 if let Ok(mut input) = syn::parse::<ItemTrait>(item) {
343 let mod_name: Option<proc_macro2::Ident> = if attr.is_empty() {
344 None
345 } else {
346 match syn::parse(attr) {
347 Ok(x) => x,
348 Err(err) => {
349 return TokenStream::from(
350 syn::Error::new(
351 Span::call_site(),
352 format!("Failed to parse mod name for ext_contract: {err}"),
353 )
354 .to_compile_error(),
355 )
356 }
357 }
358 };
359 let item_trait_info = match ItemTraitInfo::new(&mut input, mod_name) {
360 Ok(x) => x,
361 Err(err) => return TokenStream::from(err.to_compile_error()),
362 };
363 let ext_api = item_trait_info.wrap_trait_ext();
364
365 TokenStream::from(quote! {
366 #input
367 #ext_api
368 })
369 } else {
370 TokenStream::from(
371 syn::Error::new(Span::call_site(), "ext_contract can only be used on traits")
372 .to_compile_error(),
373 )
374 }
375}
376
377#[cfg(feature = "abi")]
378#[derive(darling::FromDeriveInput, Debug)]
379#[darling(attributes(abi), forward_attrs(serde, borsh_skip, schemars, validate))]
380struct DeriveNearSchema {
381 attrs: Vec<syn::Attribute>,
382 json: Option<bool>,
383 borsh: Option<bool>,
384}
385
386#[proc_macro_derive(NearSchema, attributes(abi, serde, borsh, schemars, validate, inside_nearsdk))]
387pub fn derive_near_schema(#[allow(unused)] input: TokenStream) -> TokenStream {
388 #[cfg(not(feature = "abi"))]
389 {
390 TokenStream::from(quote! {})
391 }
392
393 #[cfg(feature = "abi")]
394 {
395 use darling::FromDeriveInput;
396
397 let derive_input = syn::parse_macro_input!(input as syn::DeriveInput);
398 let generics = derive_input.generics.clone();
399 let args = match DeriveNearSchema::from_derive_input(&derive_input) {
400 Ok(v) => v,
401 Err(e) => {
402 return TokenStream::from(e.write_errors());
403 }
404 };
405
406 if args.borsh.is_none()
407 && args.json.is_none()
408 && derive_input.clone().attrs.iter().any(|attr| attr.path().is_ident("abi"))
409 {
410 return TokenStream::from(
411 syn::Error::new_spanned(
412 derive_input.to_token_stream(),
413 "At least one of `json` or `borsh` inside of `#[abi(...)]` must be specified",
414 )
415 .to_compile_error(),
416 );
417 }
418
419 let (json_schema, borsh_schema) = (args.json.unwrap_or(false), args.borsh.unwrap_or(false));
421 let mut input = derive_input.clone();
422 input.attrs = args.attrs;
423
424 let strip_unknown_attr = |attrs: &mut Vec<syn::Attribute>| {
425 attrs.retain(|attr| {
426 ["serde", "schemars", "validate", "borsh"]
427 .iter()
428 .any(|&path| attr.path().is_ident(path))
429 });
430 };
431
432 match &mut input.data {
433 syn::Data::Struct(data) => {
434 for field in &mut data.fields {
435 strip_unknown_attr(&mut field.attrs);
436 }
437 }
438 syn::Data::Enum(data) => {
439 for variant in &mut data.variants {
440 strip_unknown_attr(&mut variant.attrs);
441 for field in &mut variant.fields {
442 strip_unknown_attr(&mut field.attrs);
443 }
444 }
445 }
446 syn::Data::Union(_) => {
447 return TokenStream::from(
448 syn::Error::new_spanned(
449 input.to_token_stream(),
450 "`NearSchema` does not support derive for unions",
451 )
452 .to_compile_error(),
453 )
454 }
455 }
456
457 let near_sdk_crate =
458 if derive_input.attrs.iter().any(|attr| attr.path().is_ident("inside_nearsdk")) {
459 quote! {crate}
460 } else {
461 quote! {::near_sdk}
462 };
463
464 let json_schema = json_schema || !borsh_schema;
466
467 let derive = get_schema_derive(json_schema, borsh_schema, near_sdk_crate.clone(), true);
468
469 let input_ident = &input.ident;
470
471 let input_ident_proxy = quote::format_ident!("{}__NEAR_SCHEMA_PROXY", input_ident);
472
473 let json_impl = if json_schema {
474 let where_clause = get_where_clause(
475 &generics,
476 input_ident,
477 quote! {#near_sdk_crate::schemars::JsonSchema},
478 );
479 quote! {
480 #[automatically_derived]
481 impl #generics #near_sdk_crate::schemars::JsonSchema for #input_ident_proxy #generics #where_clause {
482 fn schema_name() -> ::std::string::String {
483 <#input_ident #generics as #near_sdk_crate::schemars::JsonSchema>::schema_name()
484 }
485
486 fn json_schema(gen: &mut #near_sdk_crate::schemars::gen::SchemaGenerator) -> #near_sdk_crate::schemars::schema::Schema {
487 <#input_ident #generics as #near_sdk_crate::schemars::JsonSchema>::json_schema(gen)
488 }
489 }
490 }
491 } else {
492 quote! {}
493 };
494
495 let borsh_impl = if borsh_schema {
496 let where_clause = get_where_clause(
497 &generics,
498 input_ident,
499 quote! {#near_sdk_crate::borsh::BorshSchema},
500 );
501 quote! {
502 #[automatically_derived]
503 impl #generics #near_sdk_crate::borsh::BorshSchema for #input_ident_proxy #generics #where_clause {
504 fn declaration() -> #near_sdk_crate::borsh::schema::Declaration {
505 <#input_ident #generics as #near_sdk_crate::borsh::BorshSchema>::declaration()
506 }
507
508 fn add_definitions_recursively(
509 definitions: &mut #near_sdk_crate::borsh::__private::maybestd::collections::BTreeMap<
510 #near_sdk_crate::borsh::schema::Declaration,
511 #near_sdk_crate::borsh::schema::Definition
512 >,
513 ) {
514 <#input_ident #generics as #near_sdk_crate::borsh::BorshSchema>::add_definitions_recursively(definitions);
515 }
516 }
517 }
518 } else {
519 quote! {}
520 };
521
522 TokenStream::from(quote! {
523 #[cfg(not(target_arch = "wasm32"))]
524 const _: () = {
525 #[allow(non_camel_case_types)]
526 type #input_ident_proxy #generics = #input_ident #generics;
527 {
528 #derive
529 #[allow(dead_code)]
530 #input
531
532 #json_impl
533 #borsh_impl
534 };
535 };
536 })
537 }
538}
539
540#[allow(dead_code)]
541fn get_schema_derive(
542 json_schema: bool,
543 borsh_schema: bool,
544 near_sdk_crate: proc_macro2::TokenStream,
545 need_borsh_crate: bool,
546) -> proc_macro2::TokenStream {
547 let string_borsh_crate = quote! {#near_sdk_crate::borsh}.to_string();
548 let string_schemars_crate = quote! {#near_sdk_crate::schemars}.to_string();
549
550 let mut derive = quote! {};
551 if borsh_schema {
552 derive = quote! {
553 #[cfg_attr(not(target_arch = "wasm32"), derive(#near_sdk_crate::borsh::BorshSchema))]
554 };
555 if need_borsh_crate {
556 derive = quote! {
557 #derive
558 #[cfg_attr(not(target_arch = "wasm32"), borsh(crate = #string_borsh_crate))]
559 };
560 }
561 }
562 if json_schema {
563 derive = quote! {
564 #derive
565 #[cfg_attr(not(target_arch = "wasm32"), derive(#near_sdk_crate::schemars::JsonSchema))]
566 #[cfg_attr(not(target_arch = "wasm32"), schemars(crate = #string_schemars_crate))]
567 };
568 }
569 derive
570}
571
572#[cfg(feature = "abi")]
573fn get_where_clause(
574 generics: &syn::Generics,
575 input_ident: &syn::Ident,
576 trait_name: proc_macro2::TokenStream,
577) -> WhereClause {
578 let (_, ty_generics, where_clause) = generics.split_for_impl();
579
580 let predicate = parse_quote!(#input_ident #ty_generics: #trait_name);
581
582 let where_clause: WhereClause = if let Some(mut w) = where_clause.cloned() {
583 w.predicates.push(predicate);
584 w
585 } else {
586 parse_quote!(where #predicate)
587 };
588 where_clause
589}
590
591#[proc_macro_derive(PanicOnDefault)]
592pub fn derive_no_default(item: TokenStream) -> TokenStream {
593 if let Ok(input) = syn::parse::<ItemStruct>(item) {
594 let name = &input.ident;
595 TokenStream::from(quote! {
596 impl ::std::default::Default for #name {
597 fn default() -> Self {
598 ::near_sdk::env::panic_str("The contract is not initialized");
599 }
600 }
601 })
602 } else {
603 TokenStream::from(
604 syn::Error::new(
605 Span::call_site(),
606 "PanicOnDefault can only be used on type declarations sections.",
607 )
608 .to_compile_error(),
609 )
610 }
611}
612
613#[proc_macro_derive(BorshStorageKey)]
614pub fn borsh_storage_key(item: TokenStream) -> TokenStream {
615 let (name, generics) = if let Ok(input) = syn::parse::<ItemEnum>(item.clone()) {
616 (input.ident, input.generics)
617 } else if let Ok(input) = syn::parse::<ItemStruct>(item) {
618 (input.ident, input.generics)
619 } else {
620 return TokenStream::from(
621 syn::Error::new(
622 Span::call_site(),
623 "BorshStorageKey can only be used as a derive on enums or structs.",
624 )
625 .to_compile_error(),
626 );
627 };
628 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
629 let predicate = parse_quote!(#name #ty_generics: ::near_sdk::borsh::BorshSerialize);
630 let where_clause: WhereClause = if let Some(mut w) = where_clause.cloned() {
631 w.predicates.push(predicate);
632 w
633 } else {
634 parse_quote!(where #predicate)
635 };
636 TokenStream::from(quote! {
637 impl #impl_generics ::near_sdk::__private::BorshIntoStorageKey for #name #ty_generics #where_clause {}
638 })
639}
640
641#[proc_macro_derive(FunctionError)]
642pub fn function_error(item: TokenStream) -> TokenStream {
643 let name = if let Ok(input) = syn::parse::<ItemEnum>(item.clone()) {
644 input.ident
645 } else if let Ok(input) = syn::parse::<ItemStruct>(item) {
646 input.ident
647 } else {
648 return TokenStream::from(
649 syn::Error::new(
650 Span::call_site(),
651 "FunctionError can only be used as a derive on enums or structs.",
652 )
653 .to_compile_error(),
654 );
655 };
656 TokenStream::from(quote! {
657 impl ::near_sdk::FunctionError for #name {
658 fn panic(&self) -> ! {
659 ::near_sdk::env::panic_str(&::std::string::ToString::to_string(&self))
660 }
661 }
662 })
663}
664
665#[proc_macro_derive(EventMetadata, attributes(event_version))]
666pub fn derive_event_attributes(item: TokenStream) -> TokenStream {
667 if let Ok(input) = syn::parse::<ItemEnum>(item) {
668 let name = &input.ident;
669 let standard_name = format!("{name}_event_standard");
671 let standard_ident = syn::Ident::new(&standard_name, Span::call_site());
672 let mut event_meta: Vec<proc_macro2::TokenStream> = vec![];
674 for var in &input.variants {
675 if let Some(version) = core_impl::get_event_version(var) {
676 let var_ident = &var.ident;
677 event_meta.push(quote! {
678 #name::#var_ident { .. } => {(::std::string::ToString::to_string(&#standard_ident), ::std::string::ToString::to_string(#version))}
679 })
680 } else {
681 return TokenStream::from(
682 syn::Error::new(
683 Span::call_site(),
684 "Near events must have `event_version`. Must have a single string literal value.",
685 )
686 .to_compile_error(),
687 );
688 }
689 }
690
691 let (impl_generics, type_generics, where_clause) = &input.generics.split_for_impl();
693 let mut generics = input.generics.clone();
695 let event_lifetime = syn::Lifetime::new("'near_event", Span::call_site());
696 generics.params.insert(
697 0,
698 syn::GenericParam::Lifetime(syn::LifetimeParam::new(event_lifetime.clone())),
699 );
700 let (custom_impl_generics, ..) = generics.split_for_impl();
701
702 TokenStream::from(quote! {
703 impl #impl_generics #name #type_generics #where_clause {
704 pub fn emit(&self) {
705 use ::std::string::String;
706
707 let (standard, version): (String, String) = match self {
708 #(#event_meta),*
709 };
710
711 #[derive(::near_sdk::serde::Serialize)]
712 #[serde(crate="::near_sdk::serde")]
713 #[serde(rename_all="snake_case")]
714 struct EventBuilder #custom_impl_generics #where_clause {
715 standard: String,
716 version: String,
717 #[serde(flatten)]
718 event_data: &#event_lifetime #name #type_generics
719 }
720 let event = EventBuilder { standard, version, event_data: self };
721 let json = ::near_sdk::serde_json::to_string(&event)
722 .unwrap_or_else(|_| ::near_sdk::env::abort());
723 ::near_sdk::env::log_str(&::std::format!("EVENT_JSON:{}", json));
724 }
725
726 pub fn to_json(&self) -> ::near_sdk::serde_json::Value {
727 use ::std::string::String;
728
729 let (standard, version): (String, String) = match self {
730 #(#event_meta),*
731 };
732
733 #[derive(::near_sdk::serde::Serialize)]
734 #[serde(crate="::near_sdk::serde")]
735 #[serde(rename_all="snake_case")]
736 struct EventBuilder #custom_impl_generics #where_clause {
737 standard: String,
738 version: String,
739 #[serde(flatten)]
740 event_data: &#event_lifetime #name #type_generics
741 }
742 let event = EventBuilder { standard, version, event_data: self };
743 ::near_sdk::serde_json::to_value(&event)
744 .unwrap_or_else(|_| ::near_sdk::env::abort())
745 }
746 }
747 })
748 } else {
749 TokenStream::from(
750 syn::Error::new(
751 Span::call_site(),
752 "EventMetadata can only be used as a derive on enums.",
753 )
754 .to_compile_error(),
755 )
756 }
757}