git.delta.rocks / unique-network / refs/commits / c0989353baf4

difftreelog

doc(evm-coder): document public api

Yaroslav Bolyukin2022-07-12parent: #f0dde16.patch.diff
in: master

9 files changed

modifiedcrates/evm-coder/Cargo.tomldiffbeforeafterboth
5edition = "2021"5edition = "2021"
66
7[dependencies]7[dependencies]
8# evm-coder reexports those proc-macro
8evm-coder-macros = { path = "../evm-coder-macros" }9evm-coder-procedural = { path = "./procedural" }
10# Evm uses primitive-types for H160, H256 and others
9primitive-types = { version = "0.11.1", default-features = false }11primitive-types = { version = "0.11.1", default-features = false }
10hex-literal = "0.3.3"12# Evm doesn't have reexports for log and others
11ethereum = { version = "0.12.0", default-features = false }13ethereum = { version = "0.12.0", default-features = false }
14# Error types for execution
12evm-core = { default-features = false, git = "https://github.com/uniquenetwork/evm", branch = "unique-polkadot-v0.9.27" }15evm-core = { default-features = false, git = "https://github.com/uniquenetwork/evm", branch = "unique-polkadot-v0.9.27" }
16# We have tuple-heavy code in solidity.rs
13impl-trait-for-tuples = "0.2.1"17impl-trait-for-tuples = "0.2.2"
1418
15[dev-dependencies]19[dev-dependencies]
20# We want to assert some large binary blobs equality in tests
16hex = "0.4.3"21hex = "0.4.3"
22hex-literal = "0.3.4"
1723
18[features]24[features]
19default = ["std"]25default = ["std"]
addedcrates/evm-coder/README.mddiffbeforeafterboth

no changes

modifiedcrates/evm-coder/procedural/Cargo.tomldiffbeforeafterboth
8proc-macro = true8proc-macro = true
99
10[dependencies]10[dependencies]
11# Ethereum uses keccak (=sha3) for selectors
11sha3 = "0.10.1"12sha3 = "0.10.1"
13# Value formatting
14hex = "0.4.3"
15Inflector = "0.11.4"
16# General proc-macro utilities
12quote = "1.0"17quote = "1.0"
13proc-macro2 = "1.0"18proc-macro2 = "1.0"
14syn = { version = "1.0", features = ["full"] }19syn = { version = "1.0", features = ["full"] }
15hex = "0.4.3"
16Inflector = "0.11.4"
1720
modifiedcrates/evm-coder/procedural/src/lib.rsdiffbeforeafterboth
199 Ident::new(&name, ident.span())199 Ident::new(&name, ident.span())
200}200}
201201
202/// Derives call enum implementing [`evm_coder::Callable`], [`evm_coder::Weighted`]202/// See documentation for this proc-macro reexported in `evm-coder` crate
203/// and [`evm_coder::Call`] from impl block
204///
205/// ## Macro syntax
206///
207/// `#[solidity_interface(name, is, inline_is, events)]`
208/// - *name*: used in generated code, and for Call enum name
209/// - *is*: used to provide call inheritance, not found methods will be delegated to all contracts
210/// specified in is/inline_is
211/// - *inline_is*: same as is, but selectors for passed contracts will be used by derived ERC165
212/// implementation
213///
214/// `#[weight(value)]`
215/// Can be added to every method of impl block, used for deriving [`evm_coder::Weighted`], which
216/// is used by substrate bridge
217/// - *value*: expression, which evaluates to weight required to call this method.
218/// This expression can use call arguments to calculate non-constant execution time.
219/// This expression should evaluate faster than actual execution does, and may provide worser case
220/// than one is called
221///
222/// `#[solidity_interface(rename_selector)]`
223/// - *rename_selector*: by default, selector name will be generated by transforming method name
224/// from snake_case to camelCase. Use this option, if other naming convention is required.
225/// I.e: method `token_uri` will be automatically renamed to `tokenUri` in selector, but name
226/// required by ERC721 standard is `tokenURI`, thus we need to specify `rename_selector = "tokenURI"`
227/// explicitly
228///
229/// Also, any contract method may have doc comments, which will be automatically added to generated
230/// solidity interface definitions
231///
232/// ## Example
233///
234/// ```ignore
235/// struct SuperContract;
236/// struct InlineContract;
237/// struct Contract;
238///
239/// #[derive(ToLog)]
240/// enum ContractEvents {
241/// Event(#[indexed] uint32),
242/// }
243///
244/// #[solidity_interface(name = MyContract, is(SuperContract), inline_is(InlineContract))]
245/// impl Contract {
246/// /// Multiply two numbers
247/// #[weight(200 + a + b)]
248/// #[solidity_interface(rename_selector = "mul")]
249/// fn mul(&mut self, a: uint32, b: uint32) -> Result<uint32> {
250/// Ok(a.checked_mul(b).ok_or("overflow")?)
251/// }
252/// }
253/// ```
254#[proc_macro_attribute]203#[proc_macro_attribute]
255pub fn solidity_interface(args: TokenStream, stream: TokenStream) -> TokenStream {204pub fn solidity_interface(args: TokenStream, stream: TokenStream) -> TokenStream {
256 let args = parse_macro_input!(args as solidity_interface::InterfaceInfo);205 let args = parse_macro_input!(args as solidity_interface::InterfaceInfo);
282 stream231 stream
283}232}
284233
285/// ## Syntax234/// See documentation for this proc-macro reexported in `evm-coder` crate
286///
287/// `#[indexed]`
288/// Marks this field as indexed, so it will appear in [`ethereum::Log`] topics instead of data
289#[proc_macro_derive(ToLog, attributes(indexed))]235#[proc_macro_derive(ToLog, attributes(indexed))]
290pub fn to_log(value: TokenStream) -> TokenStream {236pub fn to_log(value: TokenStream) -> TokenStream {
291 let input = parse_macro_input!(value as DeriveInput);237 let input = parse_macro_input!(value as DeriveInput);
modifiedcrates/evm-coder/src/abi.rsdiffbeforeafterboth
14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17//! TODO: I misunterstood therminology, abi IS rlp encoded, so17//! Implementation of EVM RLP reader/writer
18//! this module should be replaced with rlp crate
1918
20#![allow(dead_code)]19#![allow(dead_code)]
2120
3231
33const ABI_ALIGNMENT: usize = 32;32const ABI_ALIGNMENT: usize = 32;
3433
34/// View into RLP data, which provides method to read typed items from it
35#[derive(Clone)]35#[derive(Clone)]
36pub struct AbiReader<'i> {36pub struct AbiReader<'i> {
37 buf: &'i [u8],37 buf: &'i [u8],
38 subresult_offset: usize,38 subresult_offset: usize,
39 offset: usize,39 offset: usize,
40}40}
41impl<'i> AbiReader<'i> {41impl<'i> AbiReader<'i> {
42 /// Start reading RLP buffer, assuming there is no padding bytes
42 pub fn new(buf: &'i [u8]) -> Self {43 pub fn new(buf: &'i [u8]) -> Self {
43 Self {44 Self {
44 buf,45 buf,
45 subresult_offset: 0,46 subresult_offset: 0,
46 offset: 0,47 offset: 0,
47 }48 }
48 }49 }
50 /// Start reading RLP buffer, parsing first 4 bytes as selector
49 pub fn new_call(buf: &'i [u8]) -> Result<(types::bytes4, Self)> {51 pub fn new_call(buf: &'i [u8]) -> Result<(types::bytes4, Self)> {
50 if buf.len() < 4 {52 if buf.len() < 4 {
51 return Err(Error::Error(ExitError::OutOfOffset));53 return Err(Error::Error(ExitError::OutOfOffset));
109 )111 )
110 }112 }
111113
114 /// Read [`H160`] at current position, then advance
112 pub fn address(&mut self) -> Result<H160> {115 pub fn address(&mut self) -> Result<H160> {
113 Ok(H160(self.read_padleft()?))116 Ok(H160(self.read_padleft()?))
114 }117 }
115118
119 /// Read [`bool`] at current position, then advance
116 pub fn bool(&mut self) -> Result<bool> {120 pub fn bool(&mut self) -> Result<bool> {
117 let data: [u8; 1] = self.read_padleft()?;121 let data: [u8; 1] = self.read_padleft()?;
118 match data[0] {122 match data[0] {
122 }126 }
123 }127 }
124128
129 /// Read [`[u8; 4]`] at current position, then advance
125 pub fn bytes4(&mut self) -> Result<[u8; 4]> {130 pub fn bytes4(&mut self) -> Result<[u8; 4]> {
126 self.read_padright()131 self.read_padright()
127 }132 }
128133
134 /// Read [`Vec<u8>`] at current position, then advance
129 pub fn bytes(&mut self) -> Result<Vec<u8>> {135 pub fn bytes(&mut self) -> Result<Vec<u8>> {
130 let mut subresult = self.subresult()?;136 let mut subresult = self.subresult()?;
131 let length = subresult.read_usize()?;137 let length = subresult.uint32()? as usize;
132 if subresult.buf.len() < subresult.offset + length {138 if subresult.buf.len() < subresult.offset + length {
133 return Err(Error::Error(ExitError::OutOfOffset));139 return Err(Error::Error(ExitError::OutOfOffset));
134 }140 }
135 Ok(subresult.buf[subresult.offset..subresult.offset + length].into())141 Ok(subresult.buf[subresult.offset..subresult.offset + length].into())
136 }142 }
143
144 /// Read [`string`] at current position, then advance
137 pub fn string(&mut self) -> Result<string> {145 pub fn string(&mut self) -> Result<string> {
138 string::from_utf8(self.bytes()?).map_err(|_| Error::Error(ExitError::InvalidRange))146 string::from_utf8(self.bytes()?).map_err(|_| Error::Error(ExitError::InvalidRange))
139 }147 }
140148
149 /// Read [`u8`] at current position, then advance
141 pub fn uint8(&mut self) -> Result<u8> {150 pub fn uint8(&mut self) -> Result<u8> {
142 Ok(self.read_padleft::<1>()?[0])151 Ok(self.read_padleft::<1>()?[0])
143 }152 }
144153
154 /// Read [`u32`] at current position, then advance
145 pub fn uint32(&mut self) -> Result<u32> {155 pub fn uint32(&mut self) -> Result<u32> {
146 Ok(u32::from_be_bytes(self.read_padleft()?))156 Ok(u32::from_be_bytes(self.read_padleft()?))
147 }157 }
148158
159 /// Read [`u128`] at current position, then advance
149 pub fn uint128(&mut self) -> Result<u128> {160 pub fn uint128(&mut self) -> Result<u128> {
150 Ok(u128::from_be_bytes(self.read_padleft()?))161 Ok(u128::from_be_bytes(self.read_padleft()?))
151 }162 }
152163
164 /// Read [`U256`] at current position, then advance
153 pub fn uint256(&mut self) -> Result<U256> {165 pub fn uint256(&mut self) -> Result<U256> {
154 let buf: [u8; 32] = self.read_padleft()?;166 let buf: [u8; 32] = self.read_padleft()?;
155 Ok(U256::from_big_endian(&buf))167 Ok(U256::from_big_endian(&buf))
156 }168 }
157169
170 /// Read [`u64`] at current position, then advance
158 pub fn uint64(&mut self) -> Result<u64> {171 pub fn uint64(&mut self) -> Result<u64> {
159 Ok(u64::from_be_bytes(self.read_padleft()?))172 Ok(u64::from_be_bytes(self.read_padleft()?))
160 }173 }
161174
175 /// Read [`usize`] at current position, then advance
176 #[deprecated = "dangerous, as usize may have different width in wasm and native execution"]
162 pub fn read_usize(&mut self) -> Result<usize> {177 pub fn read_usize(&mut self) -> Result<usize> {
163 Ok(usize::from_be_bytes(self.read_padleft()?))178 Ok(usize::from_be_bytes(self.read_padleft()?))
164 }179 }
165180
181 /// Slice recursive buffer, advance one word for buffer offset
166 fn subresult(&mut self) -> Result<AbiReader<'i>> {182 fn subresult(&mut self) -> Result<AbiReader<'i>> {
167 let offset = self.read_usize()?;183 let offset = self.uint32()? as usize;
168 if offset + self.subresult_offset > self.buf.len() {184 if offset + self.subresult_offset > self.buf.len() {
169 return Err(Error::Error(ExitError::InvalidRange));185 return Err(Error::Error(ExitError::InvalidRange));
170 }186 }
175 })191 })
176 }192 }
177193
194 /// Is this parser reached end of buffer?
178 pub fn is_finished(&self) -> bool {195 pub fn is_finished(&self) -> bool {
179 self.buf.len() == self.offset196 self.buf.len() == self.offset
180 }197 }
181}198}
182199
200/// Writer for RLP encoded data
183#[derive(Default)]201#[derive(Default)]
184pub struct AbiWriter {202pub struct AbiWriter {
185 static_part: Vec<u8>,203 static_part: Vec<u8>,
186 dynamic_part: Vec<(usize, AbiWriter)>,204 dynamic_part: Vec<(usize, AbiWriter)>,
187 had_call: bool,205 had_call: bool,
188}206}
189impl AbiWriter {207impl AbiWriter {
208 /// Initialize internal buffers for output data, assuming no padding required
190 pub fn new() -> Self {209 pub fn new() -> Self {
191 Self::default()210 Self::default()
192 }211 }
212 /// Initialize internal buffers, inserting method selector at beginning
193 pub fn new_call(method_id: u32) -> Self {213 pub fn new_call(method_id: u32) -> Self {
194 let mut val = Self::new();214 let mut val = Self::new();
195 val.static_part.extend(&method_id.to_be_bytes());215 val.static_part.extend(&method_id.to_be_bytes());
211 .extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - bytes.len()]);231 .extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - bytes.len()]);
212 }232 }
213233
234 /// Write [`H160`] to end of buffer
214 pub fn address(&mut self, address: &H160) {235 pub fn address(&mut self, address: &H160) {
215 self.write_padleft(&address.0)236 self.write_padleft(&address.0)
216 }237 }
217238
239 /// Write [`bool`] to end of buffer
218 pub fn bool(&mut self, value: &bool) {240 pub fn bool(&mut self, value: &bool) {
219 self.write_padleft(&[if *value { 1 } else { 0 }])241 self.write_padleft(&[if *value { 1 } else { 0 }])
220 }242 }
221243
244 /// Write [`u8`] to end of buffer
222 pub fn uint8(&mut self, value: &u8) {245 pub fn uint8(&mut self, value: &u8) {
223 self.write_padleft(&[*value])246 self.write_padleft(&[*value])
224 }247 }
225248
249 /// Write [`u32`] to end of buffer
226 pub fn uint32(&mut self, value: &u32) {250 pub fn uint32(&mut self, value: &u32) {
227 self.write_padleft(&u32::to_be_bytes(*value))251 self.write_padleft(&u32::to_be_bytes(*value))
228 }252 }
229253
254 /// Write [`u128`] to end of buffer
230 pub fn uint128(&mut self, value: &u128) {255 pub fn uint128(&mut self, value: &u128) {
231 self.write_padleft(&u128::to_be_bytes(*value))256 self.write_padleft(&u128::to_be_bytes(*value))
232 }257 }
233258
259 /// Write [`U256`] to end of buffer
234 pub fn uint256(&mut self, value: &U256) {260 pub fn uint256(&mut self, value: &U256) {
235 let mut out = [0; 32];261 let mut out = [0; 32];
236 value.to_big_endian(&mut out);262 value.to_big_endian(&mut out);
237 self.write_padleft(&out)263 self.write_padleft(&out)
238 }264 }
239265
266 /// Write [`usize`] to end of buffer
267 #[deprecated = "dangerous, as usize may have different width in wasm and native execution"]
240 pub fn write_usize(&mut self, value: &usize) {268 pub fn write_usize(&mut self, value: &usize) {
241 self.write_padleft(&usize::to_be_bytes(*value))269 self.write_padleft(&usize::to_be_bytes(*value))
242 }270 }
243271
272 /// Append recursive data, writing pending offset at end of buffer
244 pub fn write_subresult(&mut self, result: Self) {273 pub fn write_subresult(&mut self, result: Self) {
245 self.dynamic_part.push((self.static_part.len(), result));274 self.dynamic_part.push((self.static_part.len(), result));
246 // Empty block, to be filled later275 // Empty block, to be filled later
247 self.write_padleft(&[]);276 self.write_padleft(&[]);
248 }277 }
249278
250 pub fn memory(&mut self, value: &[u8]) {279 fn memory(&mut self, value: &[u8]) {
251 let mut sub = Self::new();280 let mut sub = Self::new();
252 sub.write_usize(&value.len());281 sub.uint32(&(value.len() as u32));
253 for chunk in value.chunks(ABI_ALIGNMENT) {282 for chunk in value.chunks(ABI_ALIGNMENT) {
254 sub.write_padright(chunk);283 sub.write_padright(chunk);
255 }284 }
256 self.write_subresult(sub);285 self.write_subresult(sub);
257 }286 }
258287
288 /// Append recursive [`str`] at end of buffer
259 pub fn string(&mut self, value: &str) {289 pub fn string(&mut self, value: &str) {
260 self.memory(value.as_bytes())290 self.memory(value.as_bytes())
261 }291 }
262292
293 /// Append recursive [`[u8]`] at end of buffer
263 pub fn bytes(&mut self, value: &[u8]) {294 pub fn bytes(&mut self, value: &[u8]) {
264 self.memory(value)295 self.memory(value)
265 }296 }
266297
298 /// Finish writer, concatenating all internal buffers
267 pub fn finish(mut self) -> Vec<u8> {299 pub fn finish(mut self) -> Vec<u8> {
268 for (static_offset, part) in self.dynamic_part {300 for (static_offset, part) in self.dynamic_part {
269 let part_offset = self.static_part.len() - self.had_call.then(|| 4).unwrap_or(0);301 let part_offset = self.static_part.len() - self.had_call.then(|| 4).unwrap_or(0);
278 }310 }
279}311}
280312
313/// [`AbiReader`] implements reading of many types, but it should
314/// be limited to types defined in spec
315///
316/// As this trait can't be made sealed,
317/// instead of having `impl AbiRead for T`, we have `impl AbiRead<T> for AbiReader`
281pub trait AbiRead<T> {318pub trait AbiRead<T> {
319 /// Read item from current position, advanding decoder
282 fn abi_read(&mut self) -> Result<T>;320 fn abi_read(&mut self) -> Result<T>;
283}321}
284322
318{356{
319 fn abi_read(&mut self) -> Result<Vec<R>> {357 fn abi_read(&mut self) -> Result<Vec<R>> {
320 let mut sub = self.subresult()?;358 let mut sub = self.subresult()?;
321 let size = sub.read_usize()?;359 let size = sub.uint32()? as usize;
322 sub.subresult_offset = sub.offset;360 sub.subresult_offset = sub.offset;
323 let mut out = Vec::with_capacity(size);361 let mut out = Vec::with_capacity(size);
324 for _ in 0..size {362 for _ in 0..size {
366impl_tuples! {A B C D E F G H I}404impl_tuples! {A B C D E F G H I}
367impl_tuples! {A B C D E F G H I J}405impl_tuples! {A B C D E F G H I J}
368406
407/// For questions about inability to provide custom implementations,
408/// see [`AbiRead`]
369pub trait AbiWrite {409pub trait AbiWrite {
410 /// Write value to end of specified encoder
370 fn abi_write(&self, writer: &mut AbiWriter);411 fn abi_write(&self, writer: &mut AbiWriter);
412 /// Specialization for [`crate::solidity_interface`] implementation,
413 /// see comment in `impl AbiWrite for ResultWithPostInfo`
371 fn to_result(&self) -> ResultWithPostInfo<AbiWriter> {414 fn to_result(&self) -> ResultWithPostInfo<AbiWriter> {
372 let mut writer = AbiWriter::new();415 let mut writer = AbiWriter::new();
373 self.abi_write(&mut writer);416 self.abi_write(&mut writer);
374 Ok(writer.into())417 Ok(writer.into())
375 }418 }
376}419}
377420
421/// This particular AbiWrite implementation should be split to another trait,
422/// which only implements `to_result`, but due to lack of specialization feature
423/// in stable Rust, we can't have blanket impl of this trait `for T where T: AbiWrite`,
424/// so here we abusing default trait methods for it
378impl<T: AbiWrite> AbiWrite for ResultWithPostInfo<T> {425impl<T: AbiWrite> AbiWrite for ResultWithPostInfo<T> {
379 // this particular AbiWrite implementation should be split to another trait,
380 // which only implements [`to_result`]
381 //
382 // But due to lack of specialization feature in stable Rust, we can't have
383 // blanket impl of this trait `for T where T: AbiWrite`, so here we abusing
384 // default trait methods for it
385 fn abi_write(&self, _writer: &mut AbiWriter) {426 fn abi_write(&self, _writer: &mut AbiWriter) {
386 debug_assert!(false, "shouldn't be called, see comment")427 debug_assert!(false, "shouldn't be called, see comment")
387 }428 }
432 fn abi_write(&self, _writer: &mut AbiWriter) {}473 fn abi_write(&self, _writer: &mut AbiWriter) {}
433}474}
434475
476/// Helper macros to parse reader into variables
477#[deprecated]
435#[macro_export]478#[macro_export]
436macro_rules! abi_decode {479macro_rules! abi_decode {
437 ($reader:expr, $($name:ident: $typ:ident),+ $(,)?) => {480 ($reader:expr, $($name:ident: $typ:ident),+ $(,)?) => {
441 }484 }
442}485}
486
487/// Helper macros to construct RLP-encoded buffer
488#[deprecated]
443#[macro_export]489#[macro_export]
444macro_rules! abi_encode {490macro_rules! abi_encode {
445 ($($typ:ident($value:expr)),* $(,)?) => {{491 ($($typ:ident($value:expr)),* $(,)?) => {{
modifiedcrates/evm-coder/src/events.rsdiffbeforeafterboth
1919
20use crate::types::*;20use crate::types::*;
2121
22/// Implementation of this trait should not be written manually,
23/// instead use [`crate::ToLog`] proc macros.
24///
25/// See also [`evm_coder_procedural::ToLog`], [solidity docs on events](https://docs.soliditylang.org/en/develop/contracts.html#events)
22pub trait ToLog {26pub trait ToLog {
27 /// Convert event to [`ethereum::Log`].
28 /// Because event by itself doesn't contains current contract
29 /// address, it should be specified manually.
23 fn to_log(&self, contract: H160) -> Log;30 fn to_log(&self, contract: H160) -> Log;
24}31}
2532
33/// Only items implementing `ToTopic` may be used as `#[indexed]` field
34/// in [`crate::ToLog`] macro usage.
35///
36/// See also (solidity docs on events)[<https://docs.soliditylang.org/en/develop/contracts.html#events>]
26pub trait ToTopic {37pub trait ToTopic {
38 /// Convert value to topic to be used in [`ethereum::Log`]
27 fn to_topic(&self) -> H256;39 fn to_topic(&self) -> H256;
28}40}
2941
modifiedcrates/evm-coder/src/execution.rsdiffbeforeafterboth
14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
16
17//! Contract execution related types
1618
17#[cfg(not(feature = "std"))]19#[cfg(not(feature = "std"))]
18use alloc::string::{String, ToString};20use alloc::string::{String, ToString};
2224
23use crate::Weight;25use crate::Weight;
2426
27/// Execution error, should be convertible between EVM and Substrate.
25#[derive(Debug, Clone)]28#[derive(Debug, Clone)]
26pub enum Error {29pub enum Error {
30 /// Non-fatal contract error occured
27 Revert(String),31 Revert(String),
32 /// EVM fatal error
28 Fatal(ExitFatal),33 Fatal(ExitFatal),
34 /// EVM normal error
29 Error(ExitError),35 Error(ExitError),
30}36}
3137
38 }44 }
39}45}
4046
47/// To be used in [`crate::solidity_interface`] implementation.
41pub type Result<T> = core::result::Result<T, Error>;48pub type Result<T> = core::result::Result<T, Error>;
4249
50/// Static information collected from [`crate::weight`].
43pub struct DispatchInfo {51pub struct DispatchInfo {
52 /// Statically predicted call weight
44 pub weight: Weight,53 pub weight: Weight,
45}54}
4655
55 }64 }
56}65}
5766
67/// Weight information that is only available post dispatch.
68/// Note: This can only be used to reduce the weight or fee, not increase it.
58#[derive(Default, Clone)]69#[derive(Default, Clone)]
59pub struct PostDispatchInfo {70pub struct PostDispatchInfo {
71 /// Actual weight consumed by call
60 actual_weight: Option<Weight>,72 actual_weight: Option<Weight>,
61}73}
6274
63impl PostDispatchInfo {75impl PostDispatchInfo {
76 /// Calculate amount to be returned back to user
64 pub fn calc_unspent(&self, info: &DispatchInfo) -> Weight {77 pub fn calc_unspent(&self, info: &DispatchInfo) -> Weight {
65 info.weight - self.calc_actual_weight(info)78 info.weight - self.calc_actual_weight(info)
66 }79 }
6780
81 /// Calculate actual cansumed weight, saturating to weight reported
82 /// pre-dispatch
68 pub fn calc_actual_weight(&self, info: &DispatchInfo) -> Weight {83 pub fn calc_actual_weight(&self, info: &DispatchInfo) -> Weight {
69 if let Some(actual_weight) = self.actual_weight {84 if let Some(actual_weight) = self.actual_weight {
70 actual_weight.min(info.weight)85 actual_weight.min(info.weight)
74 }89 }
75}90}
7691
92/// Wrapper for PostDispatchInfo and any user-provided data
77#[derive(Clone)]93#[derive(Clone)]
78pub struct WithPostDispatchInfo<T> {94pub struct WithPostDispatchInfo<T> {
95 /// User provided data
79 pub data: T,96 pub data: T,
97 /// Info known after dispatch
80 pub post_info: PostDispatchInfo,98 pub post_info: PostDispatchInfo,
81}99}
82100
89 }107 }
90}108}
91109
110/// Return type of items in [`crate::solidity_interface`] definition
92pub type ResultWithPostInfo<T> =111pub type ResultWithPostInfo<T> =
93 core::result::Result<WithPostDispatchInfo<T>, WithPostDispatchInfo<Error>>;112 core::result::Result<WithPostDispatchInfo<T>, WithPostDispatchInfo<Error>>;
94113
modifiedcrates/evm-coder/src/lib.rsdiffbeforeafterboth
14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17#![doc = include_str!("../README.md")]
18#![deny(missing_docs)]
17#![cfg_attr(not(feature = "std"), no_std)]19#![cfg_attr(not(feature = "std"), no_std)]
18#[cfg(not(feature = "std"))]20#[cfg(not(feature = "std"))]
19extern crate alloc;21extern crate alloc;
2022
21use abi::{AbiRead, AbiReader, AbiWriter};23use abi::{AbiRead, AbiReader, AbiWriter};
22pub use evm_coder_macros::{event_topic, fn_selector, solidity_interface, solidity, weight, ToLog};24pub use evm_coder_procedural::{event_topic, fn_selector};
23pub mod abi;25pub mod abi;
24pub mod events;
25pub use events::ToLog;26pub use events::{ToLog, ToTopic};
26use execution::DispatchInfo;27use execution::DispatchInfo;
27pub mod execution;28pub mod execution;
29
30/// Derives call enum implementing [`crate::Callable`], [`crate::Weighted`]
31/// and [`crate::Call`] from impl block.
32///
33/// ## Macro syntax
34///
35/// `#[solidity_interface(name, is, inline_is, events)]`
36/// - *name* - used in generated code, and for Call enum name
37/// - *is* - used to provide call inheritance, not found methods will be delegated to all contracts
38/// specified in is/inline_is
39/// - *inline_is* - same as is, but selectors for passed contracts will be used by derived ERC165
40/// implementation
41///
42/// `#[weight(value)]`
43/// Can be added to every method of impl block, used for deriving [`crate::Weighted`], which
44/// is used by substrate bridge.
45/// - *value*: expression, which evaluates to weight required to call this method.
46/// This expression can use call arguments to calculate non-constant execution time.
47/// This expression should evaluate faster than actual execution does, and may provide worser case
48/// than one is called.
49///
50/// `#[solidity_interface(rename_selector)]`
51/// - *rename_selector* - by default, selector name will be generated by transforming method name
52/// from snake_case to camelCase. Use this option, if other naming convention is required.
53/// I.e: method `token_uri` will be automatically renamed to `tokenUri` in selector, but name
54/// required by ERC721 standard is `tokenURI`, thus we need to specify `rename_selector = "tokenURI"`
55/// explicitly.
56///
57/// Both contract and contract methods may have doccomments, which will end up in a generated
58/// solidity interface file, thus you should use [solidity syntax](https://docs.soliditylang.org/en/latest/natspec-format.html) for writing documentation in this macro
59///
60/// ## Example
61///
62/// ```ignore
63/// struct SuperContract;
64/// struct InlineContract;
65/// struct Contract;
66///
67/// #[derive(ToLog)]
68/// enum ContractEvents {
69/// Event(#[indexed] uint32),
70/// }
71///
72/// /// @dev This contract provides function to multiply two numbers
73/// #[solidity_interface(name = MyContract, is(SuperContract), inline_is(InlineContract))]
74/// impl Contract {
75/// /// Multiply two numbers
76/// /// @param a First number
77/// /// @param b Second number
78/// /// @return uint32 Product of two passed numbers
79/// /// @dev This function returns error in case of overflow
80/// #[weight(200 + a + b)]
81/// #[solidity_interface(rename_selector = "mul")]
82/// fn mul(&mut self, a: uint32, b: uint32) -> Result<uint32> {
83/// Ok(a.checked_mul(b).ok_or("overflow")?)
84/// }
85/// }
86/// ```
87pub use evm_coder_procedural::solidity_interface;
88/// See [`solidity_interface`]
89pub use evm_coder_procedural::solidity;
90/// See [`solidity_interface`]
91pub use evm_coder_procedural::weight;
92
93/// Derives [`ToLog`] for enum
94///
95/// Selectors will be derived from variant names, there is currently no way to have custom naming
96/// for them
97///
98/// `#[indexed]`
99/// Marks this field as indexed, so it will appear in [`ethereum::Log`] topics instead of data
100pub use evm_coder_procedural::ToLog;
101
102// Api of those modules shouldn't be consumed directly, it is only exported for usage in proc macros
103#[doc(hidden)]
104pub mod events;
105#[doc(hidden)]
28pub mod solidity;106pub mod solidity;
29107
30/// Solidity type definitions108/// Solidity type definitions (aliases from solidity name to rust type)
109/// To be used in [`solidity_interface`] definitions, to make sure there is no
110/// type conflict between Rust code and generated definitions
31pub mod types {111pub mod types {
32 #![allow(non_camel_case_types)]112 #![allow(non_camel_case_types, missing_docs)]
33113
34 #[cfg(not(feature = "std"))]114 #[cfg(not(feature = "std"))]
35 use alloc::{vec::Vec};115 use alloc::{vec::Vec};
54 pub type string = ::std::string::String;134 pub type string = ::std::string::String;
55 pub type bytes = Vec<u8>;135 pub type bytes = Vec<u8>;
56136
137 /// Solidity doesn't have `void` type, however we have special implementation
138 /// for empty tuple return type
57 pub type void = ();139 pub type void = ();
58140
59 //#region Special types141 //#region Special types
63 pub type caller = address;145 pub type caller = address;
64 //#endregion146 //#endregion
65147
148 /// Ethereum typed call message, similar to solidity
149 /// `msg` object.
66 pub struct Msg<C> {150 pub struct Msg<C> {
67 pub call: C,151 pub call: C,
152 /// Address of user, which called this contract.
68 pub caller: H160,153 pub caller: H160,
154 /// Payment amount to contract.
155 /// Contract should reject payment, if target call is not payable,
156 /// and there is no `receiver()` function defined.
69 pub value: U256,157 pub value: U256,
70 }158 }
71}159}
72160
161/// Parseable EVM call, this trait should be implemented with [`solidity_interface`] macro
73pub trait Call: Sized {162pub trait Call: Sized {
163 /// Parse call buffer into typed call enum
74 fn parse(selector: types::bytes4, input: &mut AbiReader) -> execution::Result<Option<Self>>;164 fn parse(selector: types::bytes4, input: &mut AbiReader) -> execution::Result<Option<Self>>;
75}165}
76166
167/// Intended to be used as `#[weight]` output type
168/// Should be same between evm-coder and substrate to avoid confusion
169///
170/// Isn't same thing as gas, some mapping is required between those types
77pub type Weight = u64;171pub type Weight = u64;
78172
173/// In substrate, we have benchmarking, which allows
174/// us to not rely on gas metering, but instead predict amount of gas to execute call
79pub trait Weighted: Call {175pub trait Weighted: Call {
176 /// Predict weight of this call
80 fn weight(&self) -> DispatchInfo;177 fn weight(&self) -> DispatchInfo;
81}178}
82179
180/// Type callable with ethereum message, may be implemented by [`solidity_interface`] macro
181/// on interface implementation, or for externally-owned real EVM contract
83pub trait Callable<C: Call> {182pub trait Callable<C: Call> {
183 /// Call contract using specified call data
84 fn call(&mut self, call: types::Msg<C>) -> execution::ResultWithPostInfo<AbiWriter>;184 fn call(&mut self, call: types::Msg<C>) -> execution::ResultWithPostInfo<AbiWriter>;
85}185}
86186
87/// Implementation is implicitly provided for all interfaces187/// Implementation of ERC165 is implicitly generated for all interfaces in [`solidity_interface`],
188/// this structure holds parsed data for ERC165Call subvariant
88///189///
89/// Note: no Callable implementation is provided190/// Note: no [`Callable`] implementation is provided, call implementation is inlined into every
191/// implementing contract
192///
193/// See <https://eips.ethereum.org/EIPS/eip-165>
90#[derive(Debug)]194#[derive(Debug)]
91pub enum ERC165Call {195pub enum ERC165Call {
196 /// ERC165 provides single method, which returns true, if contract
197 /// implements specified interface
92 SupportsInterface { interface_id: types::bytes4 },198 SupportsInterface {
199 /// Requested interface
200 interface_id: types::bytes4,
201 },
93}202}
94203
95impl ERC165Call {204impl ERC165Call {
205 /// ERC165 selector is provided by standard
96 pub const INTERFACE_ID: types::bytes4 = u32::to_be_bytes(0x01ffc9a7);206 pub const INTERFACE_ID: types::bytes4 = u32::to_be_bytes(0x01ffc9a7);
97}207}
98208
modifiedcrates/evm-coder/src/solidity.rsdiffbeforeafterboth
14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17//! Implementation detail of [`evm_coder::solidity_interface`] macro code-generation17//! Implementation detail of [`crate::solidity_interface`] macro code-generation.
18//! You should not rely on any public item from this module, as it is only intended to be used
19//! by procedural macro, API and output format may be changed at any time.
20//!
21//! Purpose of this module is to receive solidity contract definition in module-specified
22//! format, and then output string, representing interface of this contract in solidity language
1823
19#[cfg(not(feature = "std"))]24#[cfg(not(feature = "std"))]
20use alloc::{25use alloc::{string::String, vec::Vec, collections::BTreeMap, format};
21 string::String,
22 vec::Vec,
23 collections::BTreeMap,
24 format,
25};
26#[cfg(feature = "std")]26#[cfg(feature = "std")]
27use std::collections::BTreeMap;27use std::collections::BTreeMap;
8181
82pub trait SolidityTypeName: 'static {82pub trait SolidityTypeName: 'static {
83 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;83 fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
84 /// "simple" types are stored inline, no `memory` modifier should be used in solidity
84 fn is_simple() -> bool;85 fn is_simple() -> bool;
85 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;86 fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
87 /// Specialization
86 fn is_void() -> bool {88 fn is_void() -> bool {
87 false89 false
88 }90 }
133}135}
134136
135mod sealed {137mod sealed {
138 /// Not every type should be directly placed in vec.
139 /// Vec encoding is not memory efficient, as every item will be padded
140 /// to 32 bytes.
141 /// Instead you should use specialized types (`bytes` in case of `Vec<u8>`)
136 pub trait CanBePlacedInVec {}142 pub trait CanBePlacedInVec {}
137}143}
138144