1use crate::{
21 BalanceOf, Config, H160, U256, deposit_payment::Funds, exec::MomentOf, mock::MockHandler,
22 storage::WriteOutcome, transient_storage::TransientStorage,
23};
24use alloc::{boxed::Box, fmt::Debug, string::String, vec::Vec};
25use codec::{Decode, Encode, MaxEncodedLen};
26use core::cell::RefCell;
27use frame_support::{DefaultNoBound, traits::tokens::Balance, weights::Weight};
28use pallet_revive_types::runtime_api::{
29 CodeV1, ContractResultV1, EthTransactInfoV1, ExecReturnValueV1, InstantiateReturnValueV1,
30 StorageDepositV1,
31};
32use pallet_revive_uapi::ReturnFlags;
33use scale_info::TypeInfo;
34use sp_core::Get;
35use sp_runtime::{
36 DispatchError,
37 traits::{One, Saturating, Zero},
38};
39
40#[derive(Clone, Eq, PartialEq, Debug)]
51pub struct ContractResult<R, Balance> {
52 pub weight_consumed: Weight,
54 pub weight_required: Weight,
65 pub storage_deposit: StorageDeposit<Balance>,
72 pub max_storage_deposit: StorageDeposit<Balance>,
76 pub gas_consumed: Balance,
78 pub result: Result<R, DispatchError>,
80}
81
82impl<R: Default, B: Balance> Default for ContractResult<R, B> {
83 fn default() -> Self {
84 Self {
85 weight_consumed: Default::default(),
86 weight_required: Default::default(),
87 storage_deposit: Default::default(),
88 max_storage_deposit: Default::default(),
89 gas_consumed: Default::default(),
90 result: Ok(Default::default()),
91 }
92 }
93}
94
95impl<R, RV1, Balance> From<ContractResult<R, Balance>> for ContractResultV1<RV1, Balance>
96where
97 RV1: From<R>,
98{
99 fn from(value: ContractResult<R, Balance>) -> Self {
100 Self {
101 weight_consumed: value.weight_consumed,
102 weight_required: value.weight_required,
103 storage_deposit: value.storage_deposit.into(),
104 max_storage_deposit: value.max_storage_deposit.into(),
105 gas_consumed: value.gas_consumed,
106 result: value.result.map(Into::into),
107 }
108 }
109}
110
111#[derive(Clone, Eq, PartialEq, Default, Debug)]
113pub struct EthTransactInfo<Balance> {
114 pub weight_required: Weight,
116 pub storage_deposit: Balance,
118 pub max_storage_deposit: Balance,
120 pub eth_gas: U256,
122 pub data: Vec<u8>,
124}
125
126impl<Balance> From<EthTransactInfo<Balance>> for EthTransactInfoV1<Balance> {
127 fn from(value: EthTransactInfo<Balance>) -> Self {
128 Self {
129 weight_required: value.weight_required,
130 storage_deposit: value.storage_deposit,
131 max_storage_deposit: value.max_storage_deposit,
132 eth_gas: value.eth_gas,
133 data: value.data,
134 }
135 }
136}
137
138#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
140pub enum EthTransactError {
141 Data(Vec<u8>),
142 Message(String),
143}
144
145#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug, TypeInfo)]
146pub enum BalanceConversionError {
148 Value,
150 Dust,
152}
153
154#[derive(Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug)]
157pub struct BalanceWithDust<Balance> {
158 value: Balance,
160 dust: u32,
163}
164
165impl<Balance> From<Balance> for BalanceWithDust<Balance> {
166 fn from(value: Balance) -> Self {
167 Self { value, dust: 0 }
168 }
169}
170
171impl<Balance> BalanceWithDust<Balance> {
172 pub fn deconstruct(self) -> (Balance, u32) {
174 (self.value, self.dust)
175 }
176
177 pub fn new_unchecked<T: Config>(value: Balance, dust: u32) -> Self {
179 debug_assert!(dust < T::NativeToEthRatio::get());
180 Self { value, dust }
181 }
182
183 pub fn from_value<T: Config>(
185 value: U256,
186 ) -> Result<BalanceWithDust<BalanceOf<T>>, BalanceConversionError> {
187 if value.is_zero() {
188 return Ok(Default::default());
189 }
190
191 let (quotient, remainder) = value.div_mod(T::NativeToEthRatio::get().into());
192 let value = quotient.try_into().map_err(|_| BalanceConversionError::Value)?;
193 let dust = remainder.try_into().map_err(|_| BalanceConversionError::Dust)?;
194
195 Ok(BalanceWithDust { value, dust })
196 }
197}
198
199impl<Balance: Zero + One + Saturating> BalanceWithDust<Balance> {
200 pub fn is_zero(&self) -> bool {
202 self.value.is_zero() && self.dust == 0
203 }
204
205 pub fn into_rounded_balance(self) -> Balance {
207 if self.dust == 0 { self.value } else { self.value.saturating_add(Balance::one()) }
208 }
209}
210
211pub type CodeUploadResult<Balance> = Result<CodeUploadReturnValue<Balance>, DispatchError>;
213
214pub type GetStorageResult = Result<Option<Vec<u8>>, ContractAccessError>;
216
217pub type SetStorageResult = Result<WriteOutcome, ContractAccessError>;
219
220#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, MaxEncodedLen, Debug, TypeInfo)]
222pub enum ContractAccessError {
223 DoesntExist,
225 KeyDecodingFailed,
227 StorageWriteFailed(DispatchError),
229}
230
231#[derive(Clone, PartialEq, Eq, Debug, Default)]
233pub struct ExecReturnValue {
234 pub flags: ReturnFlags,
236 pub data: Vec<u8>,
238}
239
240impl ExecReturnValue {
241 pub fn did_revert(&self) -> bool {
243 self.flags.contains(ReturnFlags::REVERT)
244 }
245}
246
247impl From<ExecReturnValue> for ExecReturnValueV1 {
248 fn from(value: ExecReturnValue) -> Self {
249 Self { flags: value.flags, data: value.data }
250 }
251}
252
253#[derive(Clone, PartialEq, Eq, Debug, Default)]
255pub struct InstantiateReturnValue {
256 pub result: ExecReturnValue,
258 pub addr: H160,
260}
261
262impl From<InstantiateReturnValue> for InstantiateReturnValueV1 {
263 fn from(value: InstantiateReturnValue) -> Self {
264 Self { result: value.result.into(), addr: value.addr }
265 }
266}
267
268#[derive(Clone, PartialEq, Eq, Encode, Decode, MaxEncodedLen, Debug, TypeInfo)]
270pub struct CodeUploadReturnValue<Balance> {
271 pub code_hash: sp_core::H256,
273 pub deposit: Balance,
275}
276
277impl<Balance> From<CodeUploadReturnValue<Balance>>
278 for pallet_revive_types::runtime_api::CodeUploadReturnValueV1<Balance>
279{
280 fn from(value: CodeUploadReturnValue<Balance>) -> Self {
281 Self { code_hash: value.code_hash, deposit: value.deposit }
282 }
283}
284
285#[derive(Clone, Eq, PartialEq, Debug)]
287pub enum Code {
288 Upload(Vec<u8>),
290 Existing(sp_core::H256),
292}
293
294impl From<CodeV1> for Code {
295 fn from(value: CodeV1) -> Self {
296 match value {
297 CodeV1::Upload(code) => Self::Upload(code),
298 CodeV1::Existing(code_hash) => Self::Existing(code_hash),
299 }
300 }
301}
302
303#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
305pub enum StorageDeposit<Balance> {
306 Refund(Balance),
311 Charge(Balance),
316}
317
318impl<Balance> From<StorageDeposit<Balance>> for StorageDepositV1<Balance> {
319 fn from(value: StorageDeposit<Balance>) -> Self {
320 match value {
321 StorageDeposit::Refund(amount) => Self::Refund(amount),
322 StorageDeposit::Charge(amount) => Self::Charge(amount),
323 }
324 }
325}
326
327impl<T, Balance> ContractResult<T, Balance> {
328 pub fn map_result<V>(self, map_fn: impl FnOnce(T) -> V) -> ContractResult<V, Balance> {
329 ContractResult {
330 weight_consumed: self.weight_consumed,
331 weight_required: self.weight_required,
332 storage_deposit: self.storage_deposit,
333 max_storage_deposit: self.max_storage_deposit,
334 gas_consumed: self.gas_consumed,
335 result: self.result.map(map_fn),
336 }
337 }
338}
339
340impl<Balance: Zero> Default for StorageDeposit<Balance> {
341 fn default() -> Self {
342 Self::Charge(Zero::zero())
343 }
344}
345
346impl<Balance: Zero + Copy> StorageDeposit<Balance> {
347 pub fn charge_or_zero(&self) -> Balance {
349 match self {
350 Self::Charge(amount) => *amount,
351 Self::Refund(_) => Zero::zero(),
352 }
353 }
354
355 pub fn is_zero(&self) -> bool {
356 match self {
357 Self::Charge(amount) => amount.is_zero(),
358 Self::Refund(amount) => amount.is_zero(),
359 }
360 }
361}
362
363impl<Balance> StorageDeposit<Balance>
364where
365 Balance: frame_support::traits::tokens::Balance + Saturating + Ord + Copy,
366{
367 pub fn saturating_add(&self, rhs: &Self) -> Self {
369 use StorageDeposit::*;
370 match (self, rhs) {
371 (Charge(lhs), Charge(rhs)) => Charge(lhs.saturating_add(*rhs)),
372 (Refund(lhs), Refund(rhs)) => Refund(lhs.saturating_add(*rhs)),
373 (Charge(lhs), Refund(rhs)) => {
374 if lhs >= rhs {
375 Charge(lhs.saturating_sub(*rhs))
376 } else {
377 Refund(rhs.saturating_sub(*lhs))
378 }
379 },
380 (Refund(lhs), Charge(rhs)) => {
381 if lhs > rhs {
382 Refund(lhs.saturating_sub(*rhs))
383 } else {
384 Charge(rhs.saturating_sub(*lhs))
385 }
386 },
387 }
388 }
389
390 pub fn saturating_sub(&self, rhs: &Self) -> Self {
392 use StorageDeposit::*;
393 match (self, rhs) {
394 (Charge(lhs), Refund(rhs)) => Charge(lhs.saturating_add(*rhs)),
395 (Refund(lhs), Charge(rhs)) => Refund(lhs.saturating_add(*rhs)),
396 (Charge(lhs), Charge(rhs)) => {
397 if lhs >= rhs {
398 Charge(lhs.saturating_sub(*rhs))
399 } else {
400 Refund(rhs.saturating_sub(*lhs))
401 }
402 },
403 (Refund(lhs), Refund(rhs)) => {
404 if lhs > rhs {
405 Refund(lhs.saturating_sub(*rhs))
406 } else {
407 Charge(rhs.saturating_sub(*lhs))
408 }
409 },
410 }
411 }
412
413 pub fn available(&self, limit: &Balance) -> Option<Balance> {
420 use StorageDeposit::*;
421 match self {
422 Charge(amount) => limit.checked_sub(amount),
423 Refund(amount) => Some(limit.saturating_add(*amount)),
424 }
425 }
426}
427
428#[derive(DefaultNoBound)]
430pub struct ExecConfig<T: Config> {
431 pub bump_nonce: bool,
448 pub collect_deposit_from_hold: Option<(u32, Weight)>,
453 pub effective_gas_price: Option<U256>,
457 pub is_dry_run: Option<DryRunConfigurations<MomentOf<T>>>,
460 pub mock_handler: Option<Box<dyn MockHandler<T>>>,
464 pub test_env_transient_storage: Option<RefCell<TransientStorage<T>>>,
469}
470
471impl<T: Config> ExecConfig<T> {
472 pub fn new_substrate_tx() -> Self {
474 Self {
475 bump_nonce: true,
476 collect_deposit_from_hold: None,
477 effective_gas_price: None,
478 is_dry_run: None,
479 mock_handler: None,
480 test_env_transient_storage: None,
481 }
482 }
483
484 pub fn new_substrate_tx_without_bump() -> Self {
485 Self {
486 bump_nonce: false,
487 collect_deposit_from_hold: None,
488 effective_gas_price: None,
489 mock_handler: None,
490 is_dry_run: None,
491 test_env_transient_storage: None,
492 }
493 }
494
495 pub fn new_eth_tx(effective_gas_price: U256, encoded_len: u32, base_weight: Weight) -> Self {
497 Self {
498 bump_nonce: false,
499 collect_deposit_from_hold: Some((encoded_len, base_weight)),
500 effective_gas_price: Some(effective_gas_price),
501 mock_handler: None,
502 is_dry_run: None,
503 test_env_transient_storage: None,
504 }
505 }
506
507 pub fn with_dry_run(mut self, timestamp_override: impl Into<Option<MomentOf<T>>>) -> Self {
509 self.is_dry_run =
510 Some(DryRunConfigurations { timestamp_override: timestamp_override.into() });
511 self
512 }
513
514 pub fn funds<'a>(&self, account: &'a T::AccountId) -> Funds<'a, T::AccountId> {
518 if self.collect_deposit_from_hold.is_some() {
519 Funds::TxFee(account)
520 } else {
521 Funds::Balance(account)
522 }
523 }
524
525 #[cfg(test)]
527 pub fn clone(&self) -> Self {
528 Self {
529 bump_nonce: self.bump_nonce,
530 collect_deposit_from_hold: self.collect_deposit_from_hold,
531 effective_gas_price: self.effective_gas_price,
532 is_dry_run: self.is_dry_run.clone(),
533 mock_handler: None,
534 test_env_transient_storage: None,
535 }
536 }
537}
538
539#[derive(Clone)]
540pub struct DryRunConfigurations<Moment> {
541 pub timestamp_override: Option<Moment>,
542}
543
544#[must_use = "You must handle whether the code was removed or not."]
546pub enum CodeRemoved {
547 No,
549 Yes,
551}