solid_core/
builder.rs

1use crate::{
2    encode::Encode,
3    into_type::IntoType,
4    selector::Selector,
5};
6
7/// Function call builder
8///
9/// Builds a function signature along with encode parameters to can be used to
10/// call a Solidity function
11pub struct Builder<'a> {
12    name: Option<&'a str>,
13    selector: Selector,
14    pub(super) params: Vec<(bool, Vec<u8>)>,
15}
16
17impl<'a> Builder<'a> {
18    pub fn new() -> Self {
19        Self {
20            name: None,
21            selector: Selector::new(),
22            params: Vec::new(),
23        }
24    }
25
26    /// Set the name of the function
27    pub fn name(mut self, name: &'a str) -> Self {
28        self.name = Some(name);
29        self
30    }
31
32    /// Push an argument to the functions argument list
33    ///
34    /// Each argument is used to determine the function signature.
35    pub fn push<F: Encode + IntoType>(mut self, value: F) -> Self {
36        self.selector = self.selector.push::<F>();
37        self.params.push((F::is_dynamic(), value.encode()));
38        self
39    }
40
41    /// Build the function call
42    ///
43    /// If a name was set the a function selector will be used. Otherwise only the
44    /// parameters will be encoded. A function name must not be set if a Solidity
45    /// contract constructor is to be called.
46    pub fn build(self) -> Vec<u8> {
47        let name_offset = if let Some(_) = self.name { 4 } else { 0 };
48
49        let sig = if let Some(name) = self.name {
50            Some(self.selector.build(name))
51        } else {
52            None
53        };
54
55        let total_len = self.params.iter().map(|param| param.1.len()).sum::<usize>()
56            + self
57                .params
58                .iter()
59                .map(|param| param.0)
60                .filter(|&param| param == true)
61                .count()
62                * 32;
63
64        let mut buf: Vec<u8> = vec![0; total_len + name_offset];
65
66        let mut offset: usize = self.params.len() * 32 + name_offset;
67
68        for (index, (dynamic, bytes)) in self.params.into_iter().enumerate() {
69            if dynamic {
70                buf[index * 32 + 24 + name_offset..(index + 1) * 32 + name_offset]
71                    .copy_from_slice(&(offset as u64).to_be_bytes());
72                buf[offset..offset + bytes.len()].copy_from_slice(&bytes);
73                offset += bytes.len()
74            } else {
75                buf[index * 32 + name_offset..(index + 1) * 32 + name_offset]
76                    .copy_from_slice(&bytes);
77            }
78        }
79
80        if let Some(sig) = sig {
81            buf[0..4].copy_from_slice(&sig)
82        }
83
84        buf
85    }
86}