Skip to content

Commit dd575b2

Browse files
rymncxgreenx
andauthored
chore: add method to get fn selector (#1686)
use can use it like this ```rs abigen!(Contract( name = "MyContract", abi = "e2e/sway/contracts/contract_name/out/release/contract_name-abi.json" )); fn main() { let methods = MyContract::METHODS; let burn_coins_fn_selector = methods.burn_coins().fn_selector(); } ``` the method descriptor is now known at compile time, the fn_selector and name are generated with the macro --------- Co-authored-by: Green Baneling <[email protected]>
1 parent 3eb9458 commit dd575b2

File tree

7 files changed

+135
-12
lines changed

7 files changed

+135
-12
lines changed

e2e/tests/contracts.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2839,3 +2839,44 @@ async fn multicall_tx_input_output() -> Result<()> {
28392839

28402840
Ok(())
28412841
}
2842+
2843+
#[tokio::test]
2844+
async fn test_returned_method_descriptors_are_valid() -> Result<()> {
2845+
abigen!(Contract(
2846+
name = "MyContract",
2847+
abi = "e2e/sway/contracts/token_ops/out/release/token_ops-abi.json"
2848+
));
2849+
2850+
assert_eq!(
2851+
MyContract::METHODS.burn_coins().fn_selector(),
2852+
encode_fn_selector("burn_coins")
2853+
);
2854+
assert_eq!(
2855+
MyContract::METHODS.get_balance().fn_selector(),
2856+
encode_fn_selector("get_balance")
2857+
);
2858+
assert_eq!(
2859+
MyContract::METHODS.get_msg_amount().fn_selector(),
2860+
encode_fn_selector("get_msg_amount")
2861+
);
2862+
assert_eq!(
2863+
MyContract::METHODS.mint_coins().fn_selector(),
2864+
encode_fn_selector("mint_coins")
2865+
);
2866+
assert_eq!(
2867+
MyContract::METHODS.mint_to_addresses().fn_selector(),
2868+
encode_fn_selector("mint_to_addresses")
2869+
);
2870+
assert_eq!(
2871+
MyContract::METHODS.send_message().fn_selector(),
2872+
encode_fn_selector("send_message")
2873+
);
2874+
assert_eq!(
2875+
MyContract::METHODS.transfer().fn_selector(),
2876+
encode_fn_selector("transfer")
2877+
);
2878+
2879+
assert_eq!(MyContract::METHODS.iter().len(), 7);
2880+
2881+
Ok(())
2882+
}

packages/fuels-code-gen/src/program_bindings/abigen/bindings/contract.rs

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,29 @@ pub(crate) fn contract_bindings(
3232
let error_codes = quote! {::std::collections::HashMap::from([#(#error_codes),*])};
3333

3434
let methods_name = ident(&format!("{name}Methods"));
35+
let contract_methods_name = ident(&format!("{name}MethodVariants"));
3536

3637
let contract_functions = expand_functions(&abi.functions)?;
38+
let constant_methods_code =
39+
generate_constant_methods_pattern(&abi.functions, &contract_methods_name)?;
3740

3841
let configuration_struct_name = ident(&format!("{name}Configurables"));
3942
let constant_configuration_code =
4043
generate_code_for_configurable_constants(&configuration_struct_name, &abi.configurables)?;
4144

4245
let code = quote! {
4346
#[derive(Debug, Clone)]
44-
pub struct #name<A> {
47+
pub struct #name<A = ()> {
4548
contract_id: ::fuels::types::ContractId,
4649
account: A,
4750
log_decoder: ::fuels::core::codec::LogDecoder,
4851
encoder_config: ::fuels::core::codec::EncoderConfig,
4952
}
5053

54+
impl #name {
55+
pub const METHODS: #contract_methods_name = #contract_methods_name;
56+
}
57+
5158
impl<A> #name<A>
5259
{
5360
pub fn new(
@@ -126,13 +133,20 @@ pub(crate) fn contract_bindings(
126133
}
127134

128135
#constant_configuration_code
136+
137+
#constant_methods_code
129138
};
130139

131140
// All publicly available types generated above should be listed here.
132-
let type_paths = [name, &methods_name, &configuration_struct_name]
133-
.map(|type_name| TypePath::new(type_name).expect("We know the given types are not empty"))
134-
.into_iter()
135-
.collect();
141+
let type_paths = [
142+
name,
143+
&methods_name,
144+
&configuration_struct_name,
145+
&contract_methods_name,
146+
]
147+
.map(|type_name| TypePath::new(type_name).expect("We know the given types are not empty"))
148+
.into_iter()
149+
.collect();
136150

137151
Ok(GeneratedCode::new(code, type_paths, no_std))
138152
}
@@ -179,6 +193,49 @@ pub(crate) fn expand_fn(abi_fun: &FullABIFunction) -> Result<TokenStream> {
179193
Ok(generator.generate())
180194
}
181195

196+
fn generate_constant_methods_pattern(
197+
functions: &[FullABIFunction],
198+
contract_methods_name: &Ident,
199+
) -> Result<TokenStream> {
200+
let method_descriptors = functions.iter().map(|func| {
201+
let method_name = ident(func.name());
202+
let fn_name = func.name();
203+
let fn_selector =
204+
proc_macro2::Literal::byte_string(&crate::utils::encode_fn_selector(fn_name));
205+
206+
quote! {
207+
pub const fn #method_name(&self) -> ::fuels::types::MethodDescriptor {
208+
::fuels::types::MethodDescriptor {
209+
name: #fn_name,
210+
fn_selector: #fn_selector,
211+
}
212+
}
213+
}
214+
});
215+
216+
let all_methods = functions.iter().map(|func| {
217+
let method_name = ident(func.name());
218+
quote! { Self.#method_name() }
219+
});
220+
221+
let method_count = functions.len();
222+
223+
let code = quote! {
224+
#[derive(Debug, Clone, Copy)]
225+
pub struct #contract_methods_name;
226+
227+
impl #contract_methods_name {
228+
#(#method_descriptors)*
229+
230+
pub const fn iter(&self) -> [::fuels::types::MethodDescriptor; #method_count] {
231+
[#(#all_methods),*]
232+
}
233+
}
234+
};
235+
236+
Ok(code)
237+
}
238+
182239
#[cfg(test)]
183240
mod tests {
184241
use std::collections::HashMap;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,8 @@
11
pub use fuel_abi_types::utils::{TypePath, ident, safe_ident};
2+
3+
pub fn encode_fn_selector(name: &str) -> Vec<u8> {
4+
let bytes = name.as_bytes().to_vec();
5+
let len = bytes.len() as u64;
6+
7+
[len.to_be_bytes().to_vec(), bytes].concat()
8+
}

packages/fuels-core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ async-trait = { workspace = true, default-features = false }
1414
chrono = { workspace = true }
1515
fuel-abi-types = { workspace = true }
1616
fuel-asm = { workspace = true }
17+
fuels-code-gen = { workspace = true }
1718
fuel-core-chain-config = { workspace = true }
1819
fuel-core-client = { workspace = true, optional = true }
1920
fuel-core-types = { workspace = true }

packages/fuels-core/src/codec/function_selector.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
pub fn encode_fn_selector(name: &str) -> Vec<u8> {
2-
let bytes = name.as_bytes().to_vec();
3-
let len = bytes.len() as u64;
4-
5-
[len.to_be_bytes().to_vec(), bytes].concat()
6-
}
1+
pub use fuels_code_gen::utils::encode_fn_selector;
72

83
/// This uses the default `EncoderConfig` configuration.
94
#[macro_export]

packages/fuels-core/src/types.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pub use fuel_types::{
44
MessageId, Nonce, Salt, SubAssetId, Word,
55
};
66

7-
pub use crate::types::{core::*, token::*, wrappers::*};
7+
pub use crate::types::{core::*, method_descriptor::*, token::*, wrappers::*};
88
use crate::{error, types::errors::Result};
99

1010
mod core;
@@ -18,6 +18,7 @@ pub mod tx_status;
1818
mod wrappers;
1919
pub use dry_runner::*;
2020
pub mod checksum_address;
21+
pub mod method_descriptor;
2122

2223
pub type ByteArray = [u8; 8];
2324
pub type Selector = Vec<u8>;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/// This type is used to specify the fn_selector and name
2+
/// of methods on contracts at compile time, exported by the abigen! macro
3+
#[derive(Debug, Clone, Copy)]
4+
pub struct MethodDescriptor {
5+
/// The name of the method.
6+
pub name: &'static str,
7+
/// The function selector of the method.
8+
pub fn_selector: &'static [u8],
9+
}
10+
11+
impl MethodDescriptor {
12+
/// Returns the function selector of the method.
13+
pub const fn fn_selector(&self) -> &'static [u8] {
14+
self.fn_selector
15+
}
16+
17+
/// Returns the name of the method.
18+
pub const fn name(&self) -> &'static str {
19+
self.name
20+
}
21+
}

0 commit comments

Comments
 (0)