// This file is part of Bizinikiwi. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use super::{deposit_limit, GAS_LIMIT}; use crate::{ address::AddressMapper, evm::TransactionSigned, AccountIdOf, BalanceOf, Code, Config, ContractResult, ExecConfig, ExecReturnValue, InstantiateReturnValue, OriginFor, Pezpallet, Weight, U256, }; use alloc::{vec, vec::Vec}; use paste::paste; use pezframe_support::pezpallet_prelude::DispatchResultWithPostInfo; use pezsp_core::H160; /// Helper macro to generate a builder for contract API calls. macro_rules! builder { // Entry point to generate a builder for the given method. ( $method:ident($($field:ident: $type:ty,)*) -> $result:ty; $($extra:item)* ) => { paste!{ builder!([< $method:camel Builder >], $method($($field: $type,)* ) -> $result; $($extra)*); } }; // Generate the builder struct and its methods. ( $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 { $($field: $type,)* } #[allow(dead_code)] impl $name { $( #[doc = concat!("Set the ", stringify!($field))] pub fn $field(mut self, value: $type) -> Self { self.$field = value; self } )* #[doc = concat!("Build the ", stringify!($method), " call")] pub fn build(self) -> $result { Pezpallet::::$method( $(self.$field,)* ) } $($extra)* } } } pub struct Contract { pub account_id: AccountIdOf, pub addr: H160, } builder!( instantiate_with_code( origin: OriginFor, value: BalanceOf, gas_limit: Weight, storage_deposit_limit: BalanceOf, code: Vec, data: Vec, salt: Option<[u8; 32]>, ) -> DispatchResultWithPostInfo; /// Create an [`InstantiateWithCodeBuilder`] with default values. pub fn instantiate_with_code(origin: OriginFor, code: Vec) -> Self { Self { origin, value: 0u32.into(), gas_limit: GAS_LIMIT, storage_deposit_limit: deposit_limit::(), code, data: vec![], salt: Some([0; 32]), } } ); builder!( instantiate( origin: OriginFor, value: BalanceOf, gas_limit: Weight, storage_deposit_limit: BalanceOf, code_hash: pezsp_core::H256, data: Vec, salt: Option<[u8; 32]>, ) -> DispatchResultWithPostInfo; /// Create an [`InstantiateBuilder`] with default values. pub fn instantiate(origin: OriginFor, code_hash: pezsp_core::H256) -> Self { Self { origin, value: 0u32.into(), gas_limit: GAS_LIMIT, storage_deposit_limit: deposit_limit::(), code_hash, data: vec![], salt: Some([0; 32]), } } ); builder!( bare_instantiate( origin: OriginFor, evm_value: U256, gas_limit: Weight, storage_deposit_limit: BalanceOf, code: Code, data: Vec, salt: Option<[u8; 32]>, exec_config: ExecConfig, ) -> ContractResult>; pub fn constructor_data(mut self, data: Vec) -> 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) } } } /// Set the call's evm_value using a native_value amount. pub fn native_value(mut self, value: BalanceOf) -> Self { self.evm_value = Pezpallet::::convert_native_to_evm(value); self } /// Build the instantiate call and unwrap the result. pub fn build_and_unwrap_result(self) -> InstantiateReturnValue { self.build().result.unwrap() } /// Build the instantiate call and unwrap the account id. pub fn build_and_unwrap_contract(self) -> Contract { 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 } } /// Create a [`BareInstantiateBuilder`] with default values. pub fn bare_instantiate(origin: OriginFor, code: Code) -> Self { Self { origin, evm_value: Default::default(), gas_limit: GAS_LIMIT, storage_deposit_limit: deposit_limit::(), code, data: vec![], salt: Some([0; 32]), exec_config: ExecConfig::new_bizinikiwi_tx(), } } ); builder!( call( origin: OriginFor, dest: H160, value: BalanceOf, gas_limit: Weight, storage_deposit_limit: BalanceOf, data: Vec, ) -> DispatchResultWithPostInfo; /// Create a [`CallBuilder`] with default values. pub fn call(origin: OriginFor, dest: H160) -> Self { CallBuilder { origin, dest, value: 0u32.into(), gas_limit: GAS_LIMIT, storage_deposit_limit: deposit_limit::(), data: vec![], } } ); builder!( bare_call( origin: OriginFor, dest: H160, evm_value: U256, gas_limit: Weight, storage_deposit_limit: BalanceOf, data: Vec, exec_config: ExecConfig, ) -> ContractResult>; /// Set the call's evm_value using a native_value amount. pub fn native_value(mut self, value: BalanceOf) -> Self { self.evm_value = Pezpallet::::convert_native_to_evm(value); self } /// Build the call and unwrap the result. pub fn build_and_unwrap_result(self) -> ExecReturnValue { self.build().result.unwrap() } /// Create a [`BareCallBuilder`] with default values. pub fn bare_call(origin: OriginFor, dest: H160) -> Self { Self { origin, dest, evm_value: Default::default(), gas_limit: GAS_LIMIT, storage_deposit_limit: deposit_limit::(), data: vec![], exec_config: ExecConfig::new_bizinikiwi_tx(), } } ); builder!( eth_call( origin: OriginFor, dest: H160, value: U256, gas_limit: Weight, data: Vec, transaction_encoded: Vec, effective_gas_price: U256, encoded_len: u32, ) -> DispatchResultWithPostInfo; /// Create a [`EthCallBuilder`] with default values. pub fn eth_call(origin: OriginFor, dest: H160) -> Self { Self { origin, dest, value: 0u32.into(), gas_limit: GAS_LIMIT, 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, value: U256, gas_limit: Weight, code: Vec, data: Vec, transaction_encoded: Vec, effective_gas_price: U256, encoded_len: u32, ) -> DispatchResultWithPostInfo; /// Create a [`EthInstantiateWithCodeBuilder`] with default values. pub fn eth_instantiate_with_code(origin: OriginFor, code: Vec) -> Self { Self { origin, value: 0u32.into(), gas_limit: GAS_LIMIT, code, data: vec![], transaction_encoded: TransactionSigned::Transaction4844Signed(Default::default()).signed_payload(), effective_gas_price: 0u32.into(), encoded_len: 0, } } );