use super::{ETH_GAS_LIMIT, WEIGHT_LIMIT, deposit_limit};
use crate::{
AccountIdOf, BalanceOf, Code, Config, ContractResult, ExecConfig, ExecReturnValue,
InstantiateReturnValue, OriginFor, Pallet, U256, Weight, address::AddressMapper,
evm::TransactionSigned, metering::TransactionLimits,
};
use alloc::{vec, vec::Vec};
use frame_support::pallet_prelude::DispatchResultWithPostInfo;
use paste::paste;
use sp_core::H160;
macro_rules! builder {
(
$method:ident($($field:ident: $type:ty,)*) -> $result:ty;
$($extra:item)*
) => {
paste!{
builder!(with_builder_fn, [< $method:camel Builder >], $method($($field: $type,)* ) -> $result; $($extra)*);
}
};
(
$full_expand:path,
$method:ident($($field:ident: $type:ty,)*) -> $result:ty;
$($extra:item)*
) => {
paste!{
builder!($full_expand, [< $method:camel Builder >], $method($($field: $type,)* ) -> $result; $($extra)*);
}
};
(
with_builder_fn,
$name:ident,
$method:ident($($field:ident: $type:ty,)*) -> $result:ty;
$($extra:item)*
) => {
builder!(without_builder_fn, $name, $method($($field: $type,)* ) -> $result; $($extra)*);
#[allow(dead_code)]
impl<T: Config> $name<T> {
#[doc = concat!("Build the ", stringify!($method), " call")]
pub fn build(self) -> $result {
Pallet::<T>::$method(
$(self.$field,)*
)
}
}
};
(
without_builder_fn,
$name:ident,
$method:ident($($field:ident: $type:ty,)*) -> $result:ty;
$($extra:item)*
) => {
#[doc = concat!("A builder to construct a ", stringify!($method), " call")]
pub struct $name<T: Config> {
$($field: $type,)*
}
#[allow(dead_code)]
impl<T: Config> $name<T> {
$(
#[doc = concat!("Set the ", stringify!($field))]
pub fn $field(mut self, value: $type) -> Self {
self.$field = value;
self
}
)*
$($extra)*
}
};
}
pub struct Contract<T: Config> {
pub account_id: AccountIdOf<T>,
pub addr: H160,
}
builder!(
instantiate_with_code(
origin: OriginFor<T>,
value: BalanceOf<T>,
weight_limit: Weight,
storage_deposit_limit: BalanceOf<T>,
code: Vec<u8>,
data: Vec<u8>,
salt: Option<[u8; 32]>,
) -> DispatchResultWithPostInfo;
pub fn instantiate_with_code(origin: OriginFor<T>, code: Vec<u8>) -> Self {
Self {
origin,
value: 0u32.into(),
weight_limit: WEIGHT_LIMIT,
storage_deposit_limit: deposit_limit::<T>(),
code,
data: vec![],
salt: Some([0; 32]),
}
}
);
builder!(
instantiate(
origin: OriginFor<T>,
value: BalanceOf<T>,
weight_limit: Weight,
storage_deposit_limit: BalanceOf<T>,
code_hash: sp_core::H256,
data: Vec<u8>,
salt: Option<[u8; 32]>,
) -> DispatchResultWithPostInfo;
pub fn instantiate(origin: OriginFor<T>, code_hash: sp_core::H256) -> Self {
Self {
origin,
value: 0u32.into(),
weight_limit: WEIGHT_LIMIT,
storage_deposit_limit: deposit_limit::<T>(),
code_hash,
data: vec![],
salt: Some([0; 32]),
}
}
);
builder!(
without_builder_fn,
bare_instantiate(
origin: OriginFor<T>,
evm_value: U256,
transaction_limits: TransactionLimits<T>,
code: Code,
data: Vec<u8>,
salt: Option<[u8; 32]>,
exec_config: ExecConfig<T>,
) -> ContractResult<InstantiateReturnValue, BalanceOf<T>>;
pub fn constructor_data(mut self, data: Vec<u8>) -> Self {
match self.code {
Code::Upload(ref mut code) if !code.starts_with(&polkavm_common::program::BLOB_MAGIC) => {
code.extend_from_slice(&data);
self
},
_ => {
self.data(data)
}
}
}
pub fn build(self) -> ContractResult<InstantiateReturnValue, BalanceOf<T>> {
Pallet::<T>::bare_instantiate(
self.origin,
self.evm_value,
self.transaction_limits,
self.code,
self.data,
self.salt,
&self.exec_config
)
}
pub fn native_value(mut self, value: BalanceOf<T>) -> Self {
self.evm_value = Pallet::<T>::convert_native_to_evm(value);
self
}
pub fn build_and_unwrap_result(self) -> InstantiateReturnValue {
self.build().result.unwrap()
}
pub fn build_and_unwrap_contract(self) -> Contract<T> {
let result = self.build().result.unwrap();
assert!(!result.result.did_revert(), "instantiation did revert");
let addr = result.addr;
let account_id = T::AddressMapper::to_account_id(&addr);
Contract{ account_id, addr }
}
pub fn bare_instantiate(origin: OriginFor<T>, code: Code) -> Self {
Self {
origin,
evm_value: Default::default(),
transaction_limits: TransactionLimits::WeightAndDeposit {
weight_limit: WEIGHT_LIMIT,
deposit_limit: deposit_limit::<T>()
},
code,
data: vec![],
salt: Some([0; 32]),
exec_config: ExecConfig::new_substrate_tx(),
}
}
);
builder!(
call(
origin: OriginFor<T>,
dest: H160,
value: BalanceOf<T>,
weight_limit: Weight,
storage_deposit_limit: BalanceOf<T>,
data: Vec<u8>,
) -> DispatchResultWithPostInfo;
pub fn call(origin: OriginFor<T>, dest: H160) -> Self {
CallBuilder {
origin,
dest,
value: 0u32.into(),
weight_limit: WEIGHT_LIMIT,
storage_deposit_limit: deposit_limit::<T>(),
data: vec![],
}
}
);
builder!(
without_builder_fn,
bare_call(
origin: OriginFor<T>,
dest: H160,
evm_value: U256,
transaction_limits: TransactionLimits<T>,
data: Vec<u8>,
exec_config: ExecConfig<T>,
) -> ContractResult<ExecReturnValue, BalanceOf<T>>;
pub fn build(self) -> ContractResult<ExecReturnValue, BalanceOf<T>> {
Pallet::<T>::bare_call(
self.origin,
self.dest,
self.evm_value,
self.transaction_limits,
self.data,
&self.exec_config
)
}
pub fn native_value(mut self, value: BalanceOf<T>) -> Self {
self.evm_value = Pallet::<T>::convert_native_to_evm(value);
self
}
pub fn build_and_unwrap_result(self) -> ExecReturnValue {
self.build().result.unwrap()
}
pub fn bare_call(origin: OriginFor<T>, dest: H160) -> Self {
Self {
origin,
dest,
evm_value: Default::default(),
transaction_limits: TransactionLimits::WeightAndDeposit {
weight_limit: WEIGHT_LIMIT,
deposit_limit: deposit_limit::<T>()
},
data: vec![],
exec_config: ExecConfig::new_substrate_tx(),
}
}
);
builder!(
eth_call(
origin: OriginFor<T>,
dest: H160,
value: U256,
weight_limit: Weight,
eth_gas_limit: U256,
data: Vec<u8>,
transaction_encoded: Vec<u8>,
effective_gas_price: U256,
encoded_len: u32,
) -> DispatchResultWithPostInfo;
pub fn eth_call(origin: OriginFor<T>, dest: H160) -> Self {
Self {
origin,
dest,
value: 0u32.into(),
weight_limit: WEIGHT_LIMIT,
eth_gas_limit: ETH_GAS_LIMIT.into(),
data: vec![],
transaction_encoded: TransactionSigned::TransactionLegacySigned(Default::default()).signed_payload(),
effective_gas_price: 0u32.into(),
encoded_len: 0,
}
}
);
builder!(
eth_instantiate_with_code(
origin: OriginFor<T>,
value: U256,
gas_limit: Weight,
eth_gas_limit: U256,
code: Vec<u8>,
data: Vec<u8>,
transaction_encoded: Vec<u8>,
effective_gas_price: U256,
encoded_len: u32,
) -> DispatchResultWithPostInfo;
pub fn eth_instantiate_with_code(origin: OriginFor<T>, code: Vec<u8>) -> Self {
Self {
origin,
value: 0u32.into(),
gas_limit: WEIGHT_LIMIT,
eth_gas_limit: ETH_GAS_LIMIT.into(),
code,
data: vec![],
transaction_encoded: TransactionSigned::Transaction4844Signed(Default::default()).signed_payload(),
effective_gas_price: 0u32.into(),
encoded_len: 0,
}
}
);