Skip to content

Commit c4c05c2

Browse files
committed
Add support for events and indexed fields.
This adds two new attributes: `#[event]` and `#[indexed]`. The `#[event]` attribute marks a struct or enum as an event that can be emitted by a contract. The `#[indexed]` attribute can be applied to fields within structs that are attributed with `#[event]`. This is particularly useful for event structs, allowing for efficient filtering and searching of emitted events based on the values of these fields. When using this attribute, the indexed fields must be applied sequentially to the initial set of fields in a struct. This attribute can only be applied to fields whose type is an exact size ABI type. The exact size ABI types include: - `bool` - `u8`, `u16`, `u32`, `u64`, `u256` - `numeric` - `b256` - `str[N]` - Tuples containing only exact size types - Structs containing only exact size types - Arrays of exact size types with a literal length - Type aliases to exact size types Additionally it causes the event types to be included in the JSON ABI representation for the contract. ```sway #[event] struct MyEventStruct { #[indexed] id: u64, sender: Identity, } #[event] enum MyEventEnum { A: (), B: (), } ``` Additionally this updates the JSON ABI to emit an `offset` for indexed fields.
1 parent ec8d86b commit c4c05c2

File tree

48 files changed

+872
-125
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+872
-125
lines changed

Cargo.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ members = [
2929
"sway-utils",
3030
"swayfmt",
3131
"test",
32+
"test/src/sdk-harness",
3233
]
3334
exclude = ["examples/*", "swayfmt/test_macros", "forc-test/test_data"]
3435

@@ -82,7 +83,7 @@ sway-ir-macros = { path = "sway-ir/sway-ir-macros", version = "0.68.2" }
8283
#
8384

8485
# Dependencies from the `fuel-abi-types` repository:
85-
fuel-abi-types = "0.8"
86+
fuel-abi-types = { path = "fuel-abi-types/", version = "0.9.0" }
8687

8788
# Dependencies from the `fuel-core` repository:
8889
#
@@ -94,9 +95,9 @@ fuel-core-types = { version = "0.43", default-features = false }
9495

9596
# Dependencies from the `fuels-rs` repository:
9697

97-
fuels = "0.72"
98-
fuels-core = "0.72"
99-
fuels-accounts = "0.72"
98+
fuels = { path = "../fuels-rs/packages/fuels", version = "0.72" }
99+
fuels-core = { path = "../fuels-rs/packages/fuels-core", version = "0.72" }
100+
fuels-accounts = { path = "../fuels-rs/packages/fuels-accounts", version = "0.72" }
100101

101102
# Dependencies from the `fuel-vm` repository:
102103
fuel-asm = "0.60"

docs/book/src/reference/attributes.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ Below is the list of attributes supported by the Sway compiler, ordered alphabet
1010
- [Deprecated](#deprecated)
1111
- [Error](#error)
1212
- [Error Type](#error-type)
13+
- [Event](#event--indexed)
1314
- [Fallback](#fallback)
15+
- [Indexed](#event--indexed)
1416
- [Inline](#inline)
1517
- [Payable](#payable)
1618
- [Storage](#payable)
@@ -122,6 +124,43 @@ All variants of an error type enum must be annotated with the [`#[error]` attrib
122124

123125
> **Note**: Error types are still an experimental feature. For more info, see the [tracking issue for "Error types"](https://github.com/FuelLabs/sway/issues/6765).
124126
127+
## Event / Indexed
128+
129+
The `#[event]` attribute marks a struct or enum as an event that can be emitted by a contract.
130+
131+
The `#[indexed]` attribute can be applied to fields within structs that are attributed with `#[event]`. This is particularly useful for event structs, allowing for efficient filtering and searching of emitted events based on the values of these fields.
132+
133+
When using this attribute, the indexed fields must be applied sequentially to the initial set of fields in a struct.
134+
135+
This attribute can only be applied to fields whose type is an exact size ABI type. The exact size ABI types include:
136+
137+
- `bool`
138+
- `u8`, `u16`, `u32`, `u64`, `u256`
139+
- `numeric`
140+
- `b256`
141+
- `str[N]`
142+
- Tuples containing only exact size types
143+
- Structs containing only exact size types
144+
- Arrays of exact size types with a literal length
145+
- Type aliases to exact size types
146+
147+
Additionally it causes the event types to be included in the JSON ABI representation for the contract.
148+
149+
```sway
150+
#[event]
151+
struct MyEventStruct {
152+
#[indexed]
153+
id: u64,
154+
sender: Identity,
155+
}
156+
157+
#[event]
158+
enum MyEventEnum {
159+
A: (),
160+
B: (),
161+
}
162+
```
163+
125164
## Fallback
126165

127166
The `#[fallback]` attribute makes the compiler use the marked function as the contract call fallback function. This means that, when a contract method is called, and the contract method selection fails, the fallback function will be called instead.

sway-ast/src/attribute.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ pub const ERROR_M_ARG_NAME: &str = "m";
5656
pub const ABI_NAME_ATTRIBUTE_NAME: &str = "abi_name";
5757
pub const ABI_NAME_NAME_ARG_NAME: &str = "name";
5858

59+
pub const EVENT_ATTRIBUTE_NAME: &str = "event";
60+
pub const INDEXED_ATTRIBUTE_NAME: &str = "indexed";
61+
5962
pub const KNOWN_ATTRIBUTE_NAMES: &[&str] = &[
6063
STORAGE_ATTRIBUTE_NAME,
6164
DOC_COMMENT_ATTRIBUTE_NAME,
@@ -67,6 +70,8 @@ pub const KNOWN_ATTRIBUTE_NAMES: &[&str] = &[
6770
DEPRECATED_ATTRIBUTE_NAME,
6871
FALLBACK_ATTRIBUTE_NAME,
6972
ABI_NAME_ATTRIBUTE_NAME,
73+
EVENT_ATTRIBUTE_NAME,
74+
INDEXED_ATTRIBUTE_NAME,
7075
];
7176

7277
/// An attribute declaration. Attribute declaration

sway-core/src/abi_generation/fuel_abi.rs

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::{
1414
ast_elements::type_parameter::GenericTypeParameter,
1515
language::ty::{TyFunctionDecl, TyProgram, TyProgramKind},
1616
transform::Attributes,
17-
Engines, TypeId, TypeInfo,
17+
AbiEncodeSizeHint, Engines, TypeId, TypeInfo,
1818
};
1919

2020
use super::abi_str::AbiStrContext;
@@ -644,6 +644,7 @@ fn generate_configurables(
644644
.iter()
645645
.map(|decl| {
646646
Ok(program_abi::Configurable {
647+
indirect: false,
647648
name: decl.call_path.suffix.to_string(),
648649
concrete_type_id: generate_concrete_type_declaration(
649650
handler,
@@ -725,15 +726,15 @@ impl TypeId {
725726

726727
let mut new_metadata_types_to_add =
727728
Vec::<program_abi::TypeMetadataDeclaration>::new();
728-
for x in decl.variants.iter() {
729+
for variant in decl.variants.iter() {
729730
generate_type_metadata_declaration(
730731
handler,
731732
ctx,
732733
engines,
733734
metadata_types,
734735
concrete_types,
735-
x.type_argument.initial_type_id(),
736-
x.type_argument.type_id(),
736+
variant.type_argument.initial_type_id(),
737+
variant.type_argument.type_id(),
737738
&mut new_metadata_types_to_add,
738739
)?;
739740
}
@@ -743,13 +744,14 @@ impl TypeId {
743744
let components = decl
744745
.variants
745746
.iter()
746-
.map(|x| {
747+
.map(|variant| {
747748
Ok(program_abi::TypeApplication {
748-
name: x.name.to_string(),
749+
name: variant.name.to_string(),
749750
type_id: program_abi::TypeId::Metadata(MetadataTypeId(
750-
x.type_argument.initial_type_id().index(),
751+
variant.type_argument.initial_type_id().index(),
751752
)),
752-
type_arguments: x
753+
offset: None,
754+
type_arguments: variant
753755
.type_argument
754756
.initial_type_id()
755757
.get_abi_type_arguments(
@@ -758,7 +760,7 @@ impl TypeId {
758760
engines,
759761
metadata_types,
760762
concrete_types,
761-
x.type_argument.type_id(),
763+
variant.type_argument.type_id(),
762764
&mut new_metadata_types_to_add,
763765
)?,
764766
})
@@ -777,15 +779,15 @@ impl TypeId {
777779

778780
let mut new_metadata_types_to_add =
779781
Vec::<program_abi::TypeMetadataDeclaration>::new();
780-
for x in decl.fields.iter() {
782+
for field in decl.fields.iter() {
781783
generate_type_metadata_declaration(
782784
handler,
783785
ctx,
784786
engines,
785787
metadata_types,
786788
concrete_types,
787-
x.type_argument.initial_type_id(),
788-
x.type_argument.type_id(),
789+
field.type_argument.initial_type_id(),
790+
field.type_argument.type_id(),
789791
&mut new_metadata_types_to_add,
790792
)?;
791793
}
@@ -795,13 +797,31 @@ impl TypeId {
795797
let components = decl
796798
.fields
797799
.iter()
798-
.map(|x| {
800+
.scan(0u16, |field_offset, field| {
801+
if field.attributes.indexed().is_none() {
802+
return None;
803+
}
804+
let hint = engines
805+
.te()
806+
.get(field.type_argument.type_id())
807+
.abi_encode_size_hint(engines);
808+
let size = if let AbiEncodeSizeHint::Exact(sz) = hint {
809+
sz as u16
810+
} else {
811+
0
812+
};
813+
let offset = if size > 0 { Some(*field_offset) } else { None };
814+
*field_offset += size;
815+
Some((field, offset))
816+
})
817+
.map(|(field, offset)| {
799818
Ok(program_abi::TypeApplication {
800-
name: x.name.to_string(),
819+
name: field.name.to_string(),
801820
type_id: program_abi::TypeId::Metadata(MetadataTypeId(
802-
x.type_argument.initial_type_id().index(),
821+
field.type_argument.initial_type_id().index(),
803822
)),
804-
type_arguments: x
823+
offset,
824+
type_arguments: field
805825
.type_argument
806826
.initial_type_id()
807827
.get_abi_type_arguments(
@@ -810,7 +830,7 @@ impl TypeId {
810830
engines,
811831
metadata_types,
812832
concrete_types,
813-
x.type_argument.type_id(),
833+
field.type_argument.type_id(),
814834
&mut new_metadata_types_to_add,
815835
)?,
816836
})
@@ -844,6 +864,7 @@ impl TypeId {
844864
type_id: program_abi::TypeId::Metadata(MetadataTypeId(
845865
elem_ty.initial_type_id().index(),
846866
)),
867+
offset: None,
847868
type_arguments: elem_ty.initial_type_id().get_abi_type_arguments(
848869
handler,
849870
ctx,
@@ -878,6 +899,7 @@ impl TypeId {
878899
type_id: program_abi::TypeId::Metadata(MetadataTypeId(
879900
elem_ty.initial_type_id().index(),
880901
)),
902+
offset: None,
881903
type_arguments: elem_ty.initial_type_id().get_abi_type_arguments(
882904
handler,
883905
ctx,
@@ -919,6 +941,7 @@ impl TypeId {
919941
type_id: program_abi::TypeId::Metadata(MetadataTypeId(
920942
x.initial_type_id().index(),
921943
)),
944+
offset: None,
922945
type_arguments: x.initial_type_id().get_abi_type_arguments(
923946
handler,
924947
ctx,
@@ -1064,6 +1087,7 @@ impl TypeId {
10641087
type_id: program_abi::TypeId::Metadata(MetadataTypeId(
10651088
arg.initial_type_id().index(),
10661089
)),
1090+
offset: None,
10671091
type_arguments: arg.initial_type_id().get_abi_type_arguments(
10681092
handler,
10691093
ctx,
@@ -1110,6 +1134,7 @@ impl TypeId {
11101134
type_id: program_abi::TypeId::Metadata(MetadataTypeId(
11111135
p.type_id.index(),
11121136
)),
1137+
offset: None,
11131138
type_arguments: p.type_id.get_abi_type_arguments(
11141139
handler,
11151140
ctx,
@@ -1164,6 +1189,7 @@ impl TypeId {
11641189
type_id: program_abi::TypeId::Metadata(MetadataTypeId(
11651190
p.type_id.index(),
11661191
)),
1192+
offset: None,
11671193
type_arguments: p.type_id.get_abi_type_arguments(
11681194
handler,
11691195
ctx,

sway-core/src/semantic_analysis/ast_node/declaration/struct.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ use crate::{
66
Engines,
77
};
88
use ast_elements::type_parameter::GenericTypeParameter;
9-
use sway_error::handler::{ErrorEmitted, Handler};
9+
use sway_error::{
10+
error::CompileError,
11+
handler::{ErrorEmitted, Handler},
12+
};
1013
use sway_types::Spanned;
1114
use symbol_collection_context::SymbolCollectionContext;
1215

@@ -59,7 +62,32 @@ impl ty::TyStructDecl {
5962
// type check the fields
6063
let mut new_fields = vec![];
6164
for field in fields.into_iter() {
62-
new_fields.push(ty::TyStructField::type_check(handler, ctx.by_ref(), field)?);
65+
let ty_field = ty::TyStructField::type_check(handler, ctx.by_ref(), field)?;
66+
if ty_field.attributes.indexed().is_some() && attributes.event().is_none() {
67+
return Err(
68+
handler.emit_err(CompileError::IndexedFieldInNonEventStruct {
69+
field_name: ty_field.name.into(),
70+
struct_name: name.into(),
71+
}),
72+
);
73+
}
74+
75+
let abi_size_hint = ctx
76+
.engines()
77+
.te()
78+
.get(ty_field.type_argument.type_id())
79+
.abi_encode_size_hint(ctx.engines());
80+
if ty_field.attributes.indexed().is_some()
81+
&& !matches!(abi_size_hint, AbiEncodeSizeHint::Exact(_))
82+
{
83+
return Err(handler.emit_err(
84+
CompileError::IndexedFieldIsNotFixedSizeABIType {
85+
field_name: ty_field.name.into(),
86+
},
87+
));
88+
}
89+
90+
new_fields.push(ty_field);
6391
}
6492

6593
let path = CallPath::ident_to_fullpath(name, ctx.namespace());

0 commit comments

Comments
 (0)