diff --git a/modules/src/access/ownable.rs b/modules/src/access/ownable.rs index e089a328..96da9bc3 100644 --- a/modules/src/access/ownable.rs +++ b/modules/src/access/ownable.rs @@ -1,7 +1,7 @@ -use crate::access::errors::Error::{CallerNotTheOwner, OwnerNotSet}; -use crate::access::events::OwnershipTransferred; +use crate::access::errors::Error::{CallerNotTheNewOwner, CallerNotTheOwner, OwnerNotSet}; +use crate::access::events::{OwnershipTransferStarted, OwnershipTransferred}; use odra::prelude::*; -use odra::{Address, Module, UnwrapOrRevert, Variable}; +use odra::{Address, Module, ModuleWrapper, UnwrapOrRevert, Variable}; /// This module provides a straightforward access control feature that enables /// exclusive access to particular functions by an account, known as the owner. @@ -75,3 +75,296 @@ impl Ownable { }); } } + +/// This module provides a straightforward access control feature that enables +/// exclusive access to particular functions by an account, known as the owner. +/// The account that initiates contract deployment is automatically assigned as +/// the owner. However, ownership can be transferred later by using the +/// `transfer_ownership()` and `accept_ownership()` functions. +/// +/// You can use this module as a standalone contract or integrate it into +/// a custom module by adding it as a field. +/// +/// When used in a custom module, the `only_owner()` function is available, +/// allowing you to restrict function usage to the owner. +#[odra::module(events = [OwnershipTransferStarted])] +pub struct Ownable2Step { + ownable: ModuleWrapper, + pending_owner: Variable> +} + +#[odra::module] +impl Ownable2Step { + /// Initializes the module setting the caller as the initial owner. + pub fn init(&mut self) { + self.ownable.init(); + } + + /// Returns the address of the current owner. + pub fn get_owner(&self) -> Address { + self.ownable.get_owner() + } + + /// Returns the address of the pending owner. + pub fn get_pending_owner(&self) -> Option
{ + self.pending_owner.get().flatten() + } + + /// Starts the ownership transfer of the module to a `new_owner`. + /// Replaces the `pending_owner`if there is one. + /// + /// This function can only be accessed by the current owner of the module. + pub fn transfer_ownership(&mut self, new_owner: &Address) { + self.ownable.assert_owner(&self.env().caller()); + + let previous_owner = self.ownable.get_optional_owner(); + let new_owner = Some(*new_owner); + self.pending_owner.set(new_owner); + self.env().emit_event(OwnershipTransferred { + previous_owner, + new_owner + }); + } + + /// If the contract's owner chooses to renounce their ownership, the contract + /// will no longer have an owner. This means that any functions that can only + /// be accessed by the owner will no longer be available. + /// + /// The function can only be called by the current owner, and it will permanently + /// remove the owner's privileges. + pub fn renounce_ownership(&mut self) { + self.ownable.renounce_ownership() + } + + /// The new owner accepts the ownership transfer. Replaces the current owner and clears + /// the pending owner. + pub fn accept_ownership(&mut self) { + let caller = self.env().caller(); + let caller = Some(caller); + let pending_owner = self.pending_owner.get().flatten(); + if pending_owner != caller { + self.env().revert(CallerNotTheNewOwner) + } + self.pending_owner.set(None); + self.ownable.unchecked_transfer_ownership(caller); + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::access::errors::Error; + use odra::{external_contract, HostEnv}; + + #[test] + fn init() { + // given new contacts + let (env, ownable, ownable_2step, deployer) = setup_owned(); + + // then the deployer is the owner + assert_eq!(deployer, ownable.get_owner()); + assert_eq!(deployer, ownable_2step.get_owner()); + // then a OwnershipTransferred event was emitted + + let event = OwnershipTransferred { + previous_owner: None, + new_owner: Some(deployer) + }; + + env.emitted_event(&ownable.address, &event); + env.emitted_event(&ownable_2step.address, &event); + } + + #[test] + fn plain_ownership_transfer() { + // given a new contract + let (mut contract, initial_owner) = setup_ownable(); + + // when the current owner transfers ownership + let new_owner = contract.env.get_account(1); + contract.transfer_ownership(new_owner); + + // then the new owner is set + assert_eq!(new_owner, contract.get_owner()); + // then a OwnershipTransferred event was emitted + contract.env.emitted_event( + &contract.address, + &OwnershipTransferred { + previous_owner: Some(initial_owner), + new_owner: Some(new_owner) + } + ); + } + + #[test] + fn two_step_ownership_transfer() { + // given a new contract + let (mut contract, initial_owner) = setup_ownable_2_step(); + + // when the current owner transfers ownership + let new_owner = contract.env.get_account(1); + contract.transfer_ownership(new_owner); + + // when the pending owner accepts the transfer + contract.env.set_caller(new_owner); + contract.accept_ownership(); + + // then the new owner is set + assert_eq!(new_owner, contract.get_owner()); + // then the pending owner is unset + assert_eq!(None, contract.get_pending_owner()); + // then OwnershipTransferStarted and OwnershipTransferred events were emitted + contract.env.emitted_event( + &contract.address, + &OwnershipTransferStarted { + previous_owner: Some(initial_owner), + new_owner: Some(new_owner) + } + ); + contract.env.emitted_event( + &contract.address, + &OwnershipTransferred { + previous_owner: Some(initial_owner), + new_owner: Some(new_owner) + } + ); + } + + #[test] + fn failing_plain_ownership_transfer() { + // given a new contract + let (mut contract, _) = setup_ownable(); + + // when a non-owner account is the caller + let (caller, new_owner) = (contract.env.get_account(1), contract.env.get_account(2)); + contract.env.set_caller(caller); + + // then ownership transfer fails + let err = contract.try_transfer_ownership(new_owner).unwrap_err(); + assert_eq!(err, CallerNotTheOwner.into()); + } + + #[test] + fn failing_two_step_transfer() { + // given a new contract + let (mut contract, initial_owner) = setup_ownable_2_step(); + + // when a non-owner account is the caller + let (caller, new_owner) = (contract.env.get_account(1), contract.env.get_account(2)); + contract.env.set_caller(caller); + + // then ownership transfer fails + let err = contract.try_transfer_ownership(new_owner).unwrap_err(); + assert_eq!(err, CallerNotTheOwner.into()); + + // when the owner is the caller + contract.env.set_caller(initial_owner); + contract.transfer_ownership(new_owner); + + // then the pending owner is set + assert_eq!(contract.get_pending_owner(), Some(new_owner)); + + // when someone else than the pending owner accepts the ownership + // transfer, it should fail + let err = contract.try_accept_ownership().unwrap_err(); + assert_eq!(err, Error::CallerNotTheNewOwner.into()); + + // then the owner remain the same + assert_eq!(contract.get_owner(), initial_owner); + // then the pending owner remain the same + assert_eq!(contract.get_pending_owner(), Some(new_owner)); + } + + #[test] + fn renounce_ownership() { + // given new contracts + let (mut contracts, initial_owner) = setup_renounceable(); + + contracts + .iter_mut() + .for_each(|contract: &mut RenounceableHostRef| { + // when the current owner renounce ownership + contract.renounce_ownership(); + + // then an event is emitted + contract.env.emitted_event( + &contract.address, + &OwnershipTransferred { + previous_owner: Some(initial_owner), + new_owner: None + } + ); + // then the owner is not set + let err = contract.try_get_owner().unwrap_err(); + assert_eq!(err, Error::OwnerNotSet.into()); + // then cannot renounce ownership again + let err = contract.try_renounce_ownership().unwrap_err(); + assert_eq!(err, Error::CallerNotTheOwner.into()); + }); + } + + #[test] + fn renounce_ownership_fail() { + // given new contracts + let (mut contracts, _) = setup_renounceable(); + + contracts.iter_mut().for_each(|contract| { + // when a non-owner account is the caller + let caller = contract.env.get_account(1); + contract.env.set_caller(caller); + + // then renounce ownership fails + let err = contract.try_renounce_ownership().unwrap_err(); + assert_eq!(err, Error::CallerNotTheOwner.into()); + }); + } + + #[external_contract] + trait Owned { + fn get_owner(&self) -> Address; + } + + #[external_contract] + trait Renounceable { + fn renounce_ownership(&mut self); + fn get_owner(&self) -> Address; + } + + fn setup_ownable() -> (OwnableHostRef, Address) { + let env = odra::test_env(); + (OwnableDeployer::init(&env), env.get_account(0)) + } + + fn setup_ownable_2_step() -> (Ownable2StepHostRef, Address) { + let env = odra::test_env(); + (Ownable2StepDeployer::init(&env), env.get_account(0)) + } + + fn setup_renounceable() -> (Vec, Address) { + let env = odra::test_env(); + let ownable = OwnableDeployer::init(&env); + let ownable_2_step = Ownable2StepDeployer::init(&env); + ( + vec![ + RenounceableHostRef { + address: ownable.address, + env: env.clone(), + attached_value: Default::default() + }, + RenounceableHostRef { + address: ownable_2_step.address, + env: env.clone(), + attached_value: Default::default() + }, + ], + env.get_account(0) + ) + } + + fn setup_owned() -> (HostEnv, OwnableHostRef, Ownable2StepHostRef, Address) { + let env = odra::test_env(); + let ownable = OwnableDeployer::init(&env); + let ownable_2_step = Ownable2StepDeployer::init(&env); + (env.clone(), ownable, ownable_2_step, env.get_account(0)) + } +} diff --git a/modules/src/erc1155/erc1155_base.rs b/modules/src/erc1155/erc1155_base.rs index c28c9531..e91350f5 100644 --- a/modules/src/erc1155/erc1155_base.rs +++ b/modules/src/erc1155/erc1155_base.rs @@ -18,7 +18,7 @@ impl Erc1155 for Erc1155Base { self.balances.get_or_default(&(*owner, *id)) } - fn balance_of_batch(&self, owners: Vec
, ids: Vec) -> Vec { + fn balance_of_batch(&self, owners: &[Address], ids: &[U256]) -> Vec { if owners.len() != ids.len() { self.env().revert(Error::AccountsAndIdsLengthMismatch); } diff --git a/modules/src/erc1155/mod.rs b/modules/src/erc1155/mod.rs index fd68c540..4a51b929 100644 --- a/modules/src/erc1155/mod.rs +++ b/modules/src/erc1155/mod.rs @@ -13,7 +13,7 @@ pub trait Erc1155 { /// Batched version of [Erc1155::balance_of](Self::balance_of). /// /// The length of `owners` and `ids` must be the same. - fn balance_of_batch(&self, owners: Vec
, ids: Vec) -> Vec; + fn balance_of_batch(&self, owners: &[Address], ids: &[U256]) -> Vec; /// Allows or denials the `operator` to transfer the caller’s tokens. /// /// Emits [crate::erc1155::events::ApprovalForAll]. diff --git a/modules/src/erc1155/owned_erc1155.rs b/modules/src/erc1155/owned_erc1155.rs index a255f2dd..3730e5e8 100644 --- a/modules/src/erc1155/owned_erc1155.rs +++ b/modules/src/erc1155/owned_erc1155.rs @@ -28,7 +28,7 @@ pub trait OwnedErc1155 { /// Batched version of [Erc1155::balance_of](Self::balance_of). /// /// The length of `owners` and `ids` must be the same. - fn balance_of_batch(&self, owners: Vec
, ids: Vec) -> Vec; + fn balance_of_batch(&self, owners: &[Address], ids: &[U256]) -> Vec; /// Allows or denials the `operator` to transfer the caller’s tokens. /// /// Emits [crate::erc1155::events::ApprovalForAll]. diff --git a/modules/src/erc1155_token.rs b/modules/src/erc1155_token.rs index a47f118b..50d80abd 100644 --- a/modules/src/erc1155_token.rs +++ b/modules/src/erc1155_token.rs @@ -25,7 +25,7 @@ impl OwnedErc1155 for Erc1155Token { fn balance_of(&self, owner: &Address, id: &U256) -> U256 { self.core.balance_of(owner, id) } - fn balance_of_batch(&self, owners: Vec
, ids: Vec) -> Vec { + fn balance_of_batch(&self, owners: &[Address], ids: &[U256]) -> Vec { self.core.balance_of_batch(owners, ids) } fn set_approval_for_all(&mut self, operator: &Address, approved: bool) { diff --git a/modules/src/erc721_token.rs b/modules/src/erc721_token.rs index 01f02f36..9806a534 100644 --- a/modules/src/erc721_token.rs +++ b/modules/src/erc721_token.rs @@ -501,7 +501,6 @@ mod tests { // Then safe transfer the token to the contract which does not support nfts throws an error. erc721_env.env.set_caller(erc721_env.alice); - // TODO: Enable this after fixing mockvm assert_eq!( Err(OdraError::VmError(VmError::NoSuchMethod( "on_erc721_received".to_string() @@ -528,14 +527,14 @@ mod tests { erc721_env.env.set_caller(erc721_env.alice); erc721_env.token.safe_transfer_from( erc721_env.alice, - receiver.address().clone(), + *receiver.address(), U256::from(1) ); // Then the owner of the token is the contract assert_eq!( erc721_env.token.owner_of(U256::from(1)), - receiver.address().clone() + *receiver.address() ); // And the receiver contract is aware of the transfer erc721_env.env.emitted_event( @@ -563,7 +562,7 @@ mod tests { erc721_env.env.set_caller(erc721_env.alice); erc721_env.token.safe_transfer_from_with_data( erc721_env.alice, - receiver.address().clone(), + *receiver.address(), U256::from(1), b"data".to_vec().into() ); diff --git a/modules/src/lib.rs b/modules/src/lib.rs index 678e1b37..9a1c1083 100644 --- a/modules/src/lib.rs +++ b/modules/src/lib.rs @@ -11,6 +11,5 @@ pub mod erc20; pub mod erc721; pub mod erc721_receiver; pub mod erc721_token; -pub mod ownable_2step; pub mod security; pub mod wrapped_native; diff --git a/modules/src/ownable_2step.rs b/modules/src/ownable_2step.rs deleted file mode 100644 index e2951eac..00000000 --- a/modules/src/ownable_2step.rs +++ /dev/null @@ -1,293 +0,0 @@ -use crate::access::errors::Error::CallerNotTheNewOwner; -use crate::access::events::{OwnershipTransferStarted, OwnershipTransferred}; -use crate::access::Ownable; -use odra::prelude::*; -use odra::{Address, Module, ModuleWrapper, Variable}; - -/// This module provides a straightforward access control feature that enables -/// exclusive access to particular functions by an account, known as the owner. -/// The account that initiates contract deployment is automatically assigned as -/// the owner. However, ownership can be transferred later by using the -/// `transfer_ownership()` and `accept_ownership()` functions. -/// -/// You can use this module as a standalone contract or integrate it into -/// a custom module by adding it as a field. -/// -/// When used in a custom module, the `only_owner()` function is available, -/// allowing you to restrict function usage to the owner. -#[odra::module(events = [OwnershipTransferStarted])] -pub struct Ownable2Step { - ownable: ModuleWrapper, - pending_owner: Variable> -} - -#[odra::module] -impl Ownable2Step { - /// Initializes the module setting the caller as the initial owner. - pub fn init(&mut self) { - self.ownable.init(); - } - - /// Returns the address of the current owner. - pub fn get_owner(&self) -> Address { - self.ownable.get_owner() - } - - /// Returns the address of the pending owner. - pub fn get_pending_owner(&self) -> Option
{ - self.pending_owner.get().flatten() - } - - /// Starts the ownership transfer of the module to a `new_owner`. - /// Replaces the `pending_owner`if there is one. - /// - /// This function can only be accessed by the current owner of the module. - pub fn transfer_ownership(&mut self, new_owner: &Address) { - self.ownable.assert_owner(&self.env().caller()); - - let previous_owner = self.ownable.get_optional_owner(); - let new_owner = &Some(*new_owner); - self.pending_owner.set(*new_owner); - self.env().emit_event(OwnershipTransferred { - previous_owner, - new_owner: *new_owner - }); - } - - /// If the contract's owner chooses to renounce their ownership, the contract - /// will no longer have an owner. This means that any functions that can only - /// be accessed by the owner will no longer be available. - /// - /// The function can only be called by the current owner, and it will permanently - /// remove the owner's privileges. - pub fn renounce_ownership(&mut self) { - self.ownable.renounce_ownership() - } - - /// The new owner accepts the ownership transfer. Replaces the current owner and clears - /// the pending owner. - pub fn accept_ownership(&mut self) { - let caller = self.env().caller(); - let caller = Some(caller); - let pending_owner = self.pending_owner.get().flatten(); - if pending_owner != caller { - self.env().revert(CallerNotTheNewOwner) - } - self.pending_owner.set(None); - self.ownable.unchecked_transfer_ownership(caller); - } -} - -#[cfg(test)] -mod test { - use crate::access::errors::Error; - use crate::access::{OwnableDeployer, OwnableHostRef}; - use odra::{external_contract, HostEnv}; - // use odra::prelude::*; - - use super::*; - - #[test] - fn init() { - // given new contacts - let (env, ownable, ownable_2step, deployer) = setup_owned(); - - // then the deployer is the owner - assert_eq!(deployer, ownable.get_owner()); - assert_eq!(deployer, ownable_2step.get_owner()); - // then a OwnershipTransferred event was emitted - - let event = OwnershipTransferred { - previous_owner: None, - new_owner: Some(deployer) - }; - - env.emitted_event(ownable.address(), &event); - env.emitted_event(ownable_2step.address(), &event); - } - - #[test] - fn plain_ownership_transfer() { - // given a new contract - let (mut contract, initial_owner) = setup_ownable(); - - // when the current owner transfers ownership - let new_owner = contract.env().get_account(1); - contract.transfer_ownership(new_owner); - - // then the new owner is set - assert_eq!(new_owner, contract.get_owner()); - // then a OwnershipTransferred event was emitted - contract.env().emitted_event( - contract.address(), - &OwnershipTransferred { - previous_owner: Some(initial_owner), - new_owner: Some(new_owner) - } - ); - } - - #[test] - fn two_step_ownership_transfer() { - // given a new contract - let (mut contract, initial_owner) = setup_ownable_2_step(); - - // when the current owner transfers ownership - let new_owner = contract.env().get_account(1); - contract.transfer_ownership(new_owner); - - // when the pending owner accepts the transfer - contract.env().set_caller(new_owner); - contract.accept_ownership(); - - // then the new owner is set - assert_eq!(new_owner, contract.get_owner()); - // then the pending owner is unset - assert_eq!(None, contract.get_pending_owner()); - // then OwnershipTransferStarted and OwnershipTransferred events were emitted - contract.env().emitted_event( - contract.address(), - &OwnershipTransferStarted { - previous_owner: Some(initial_owner), - new_owner: Some(new_owner) - } - ); - contract.env().emitted_event( - contract.address(), - &OwnershipTransferred { - previous_owner: Some(initial_owner), - new_owner: Some(new_owner) - } - ); - } - - #[test] - fn failing_plain_ownership_transfer() { - // given a new contract - let (mut contract, _) = setup_ownable(); - - // when a non-owner account is the caller - let (caller, new_owner) = (contract.env().get_account(1), contract.env().get_account(2)); - contract.env().set_caller(caller); - - // then ownership transfer fails - let err = contract.try_transfer_ownership(new_owner).unwrap_err(); - assert_eq!(err, Error::CallerNotTheOwner.into()); - } - - #[test] - fn failing_two_step_transfer() { - // given a new contract - let (mut contract, initial_owner) = setup_ownable_2_step(); - - // when a non-owner account is the caller - let (caller, new_owner) = (contract.env().get_account(1), contract.env().get_account(2)); - contract.env().set_caller(caller); - - // then ownership transfer fails - let err = contract.try_transfer_ownership(new_owner).unwrap_err(); - assert_eq!(err, Error::CallerNotTheOwner.into()); - - // when the owner is the caller - contract.env().set_caller(initial_owner); - contract.transfer_ownership(new_owner); - - // then the pending owner is set - assert_eq!(contract.get_pending_owner(), Some(new_owner)); - - // when someone else than the pending owner accepts the ownership - // transfer, it should fail - let err = contract.try_accept_ownership().unwrap_err(); - assert_eq!(err, Error::CallerNotTheNewOwner.into()); - - // then the owner remain the same - assert_eq!(contract.get_owner(), initial_owner); - // then the pending owner remain the same - assert_eq!(contract.get_pending_owner(), Some(new_owner)); - } - - #[test] - fn renounce_ownership() { - // given new contracts - let (mut contracts, initial_owner) = setup_renounceable(); - - contracts - .iter_mut() - .for_each(|contract: &mut RenounceableHostRef| { - // when the current owner renounce ownership - contract.renounce_ownership(); - - // then an event is emitted - contract.env().emitted_event( - contract.address(), - &OwnershipTransferred { - previous_owner: Some(initial_owner), - new_owner: None - } - ); - // then the owner is not set - let err = contract.try_get_owner().unwrap_err(); - assert_eq!(err, Error::OwnerNotSet.into()); - // then cannot renounce ownership again - let err = contract.try_renounce_ownership().unwrap_err(); - assert_eq!(err, Error::CallerNotTheOwner.into()); - }); - } - - #[test] - fn renounce_ownership_fail() { - // given new contracts - let (mut contracts, _) = setup_renounceable(); - - contracts.iter_mut().for_each(|contract| { - // when a non-owner account is the caller - let caller = contract.env().get_account(1); - contract.env().set_caller(caller); - - // then renounce ownership fails - let err = contract.try_renounce_ownership().unwrap_err(); - assert_eq!(err, Error::CallerNotTheOwner.into()); - }); - } - - #[external_contract] - trait Owned { - fn get_owner(&self) -> Address; - } - - #[external_contract] - trait Renounceable { - fn renounce_ownership(&mut self); - fn get_owner(&self) -> Address; - } - - fn setup_ownable() -> (OwnableHostRef, Address) { - let env = odra::test_env(); - (OwnableDeployer::init(&env), env.get_account(0)) - } - - fn setup_ownable_2_step() -> (Ownable2StepHostRef, Address) { - let env = odra::test_env(); - (Ownable2StepDeployer::init(&env), env.get_account(0)) - } - - fn setup_renounceable() -> (Vec, Address) { - let env = odra::test_env(); - let ownable = OwnableDeployer::init(&env); - let ownable_2_step = Ownable2StepDeployer::init(&env); - ( - vec![ - RenounceableHostRef::new(ownable.address().clone(), env.clone()), - RenounceableHostRef::new(ownable_2_step.address().clone(), env.clone()), - ], - env.get_account(0) - ) - } - - fn setup_owned() -> (HostEnv, OwnableHostRef, Ownable2StepHostRef, Address) { - let env = odra::test_env(); - let ownable = OwnableDeployer::init(&env); - let ownable_2_step = Ownable2StepDeployer::init(&env); - (env.clone(), ownable, ownable_2_step, env.get_account(0)) - } -} diff --git a/odra-macros/src/ast/deployer_item.rs b/odra-macros/src/ast/deployer_item.rs index 5e347e9b..20f3108f 100644 --- a/odra-macros/src/ast/deployer_item.rs +++ b/odra-macros/src/ast/deployer_item.rs @@ -91,19 +91,23 @@ mod deployer_impl { let caller = odra::EntryPointsCaller::new(env.clone(), |contract_env, call_def| { match call_def.method() { "init" => { - let result = execute_init(contract_env); + let result = __erc20_exec_parts::execute_init(contract_env); odra::ToBytes::to_bytes(&result).map(Into::into).map_err(|err| odra::OdraError::ExecutionError(err.into())) } "total_supply" => { - let result = execute_total_supply(contract_env); + let result = __erc20_exec_parts::execute_total_supply(contract_env); odra::ToBytes::to_bytes(&result).map(Into::into).map_err(|err| odra::OdraError::ExecutionError(err.into())) } "pay_to_mint" => { - let result = execute_pay_to_mint(contract_env); + let result = __erc20_exec_parts::execute_pay_to_mint(contract_env); odra::ToBytes::to_bytes(&result).map(Into::into).map_err(|err| odra::OdraError::ExecutionError(err.into())) } "approve" => { - let result = execute_approve(contract_env); + let result = __erc20_exec_parts::execute_approve(contract_env); + odra::ToBytes::to_bytes(&result).map(Into::into).map_err(|err| odra::OdraError::ExecutionError(err.into())) + } + "airdrop" => { + let result = __erc20_exec_parts::execute_airdrop(contract_env); odra::ToBytes::to_bytes(&result).map(Into::into).map_err(|err| odra::OdraError::ExecutionError(err.into())) } name => Err(odra::OdraError::VmError( @@ -144,11 +148,11 @@ mod deployer_impl { let caller = odra::EntryPointsCaller::new(env.clone(), |contract_env, call_def| { match call_def.method() { "total_supply" => { - let result = execute_total_supply(contract_env); + let result = __erc20_exec_parts::execute_total_supply(contract_env); odra::ToBytes::to_bytes(&result).map(Into::into).map_err(|err| odra::OdraError::ExecutionError(err.into())) } "pay_to_mint" => { - let result = execute_pay_to_mint(contract_env); + let result = __erc20_exec_parts::execute_pay_to_mint(contract_env); odra::ToBytes::to_bytes(&result).map(Into::into).map_err(|err| odra::OdraError::ExecutionError(err.into())) } name => Err(odra::OdraError::VmError( @@ -185,23 +189,23 @@ mod deployer_impl { let caller = odra::EntryPointsCaller::new(env.clone(), |contract_env, call_def| { match call_def.method() { "total_supply" => { - let result = execute_total_supply(contract_env); + let result = __erc20_exec_parts::execute_total_supply(contract_env); odra::ToBytes::to_bytes(&result).map(Into::into).map_err(|err| odra::OdraError::ExecutionError(err.into())) } "get_owner" => { - let result = execute_get_owner(contract_env); + let result = __erc20_exec_parts::execute_get_owner(contract_env); odra::ToBytes::to_bytes(&result).map(Into::into).map_err(|err| odra::OdraError::ExecutionError(err.into())) } "set_owner" => { - let result = execute_set_owner(contract_env); + let result = __erc20_exec_parts::execute_set_owner(contract_env); odra::ToBytes::to_bytes(&result).map(Into::into).map_err(|err| odra::OdraError::ExecutionError(err.into())) } "name" => { - let result = execute_name(contract_env); + let result = __erc20_exec_parts::execute_name(contract_env); odra::ToBytes::to_bytes(&result).map(Into::into).map_err(|err| odra::OdraError::ExecutionError(err.into())) } "symbol" => { - let result = execute_symbol(contract_env); + let result = __erc20_exec_parts::execute_symbol(contract_env); odra::ToBytes::to_bytes(&result).map(Into::into).map_err(|err| odra::OdraError::ExecutionError(err.into())) } name => Err(odra::OdraError::VmError( diff --git a/odra-macros/src/ast/deployer_utils.rs b/odra-macros/src/ast/deployer_utils.rs index 250e6cba..2d732a13 100644 --- a/odra-macros/src/ast/deployer_utils.rs +++ b/odra-macros/src/ast/deployer_utils.rs @@ -64,7 +64,7 @@ impl TryFrom<&'_ ModuleImplIR> for EntrypointCallerExpr { } impl EntrypointCallerExpr { - fn entrypoint_caller(module: &ModuleImplIR) -> Result { + fn entrypoint_caller(module: &ModuleImplIR) -> syn::Result { let env_ident = utils::ident::env(); let contract_env_ident = utils::ident::contract_env(); let call_def_ident = utils::ident::call_def(); @@ -73,8 +73,9 @@ impl EntrypointCallerExpr { let mut branches: Vec = module .functions()? .iter() - .map(|f| CallerBranch::Function(FunctionCallBranch::from(f))) - .collect(); + .map(|f| FunctionCallBranch::try_from((module, f))) + .map(|r| r.map(CallerBranch::Function)) + .collect::>()?; branches.push(CallerBranch::Default(DefaultBranch)); Ok(parse_quote!( @@ -178,24 +179,30 @@ struct FunctionCallBranch { result_expr: syn::Expr } -impl From<&'_ FnIR> for FunctionCallBranch { - fn from(func: &'_ FnIR) -> Self { - Self { +impl TryFrom<(&'_ ModuleImplIR, &'_ FnIR)> for FunctionCallBranch { + type Error = syn::Error; + + fn try_from(value: (&'_ ModuleImplIR, &'_ FnIR)) -> Result { + let (module, func) = value; + Ok(Self { function_name: func.name_str(), arrow_token: Default::default(), brace_token: Default::default(), - call_stmt: Self::call_stmt(func), + call_stmt: Self::call_stmt(module, func)?, result_expr: utils::expr::parse_bytes(&utils::ident::result()) - } + }) } } impl<'a> FunctionCallBranch { - fn call_stmt(func: &'a FnIR) -> syn::Stmt { + fn call_stmt(module: &'a ModuleImplIR, func: &'a FnIR) -> syn::Result { let result_ident = utils::ident::result(); let function_ident = func.execute_name(); let contract_env_ident = utils::ident::contract_env(); - parse_quote!(let #result_ident = #function_ident(#contract_env_ident);) + let exec_parts_ident = module.exec_parts_mod_ident()?; + Ok( + parse_quote!(let #result_ident = #exec_parts_ident::#function_ident(#contract_env_ident);) + ) } } diff --git a/odra-macros/src/ast/entrypoints_item.rs b/odra-macros/src/ast/entrypoints_item.rs index e08a0354..7c07602e 100644 --- a/odra-macros/src/ast/entrypoints_item.rs +++ b/odra-macros/src/ast/entrypoints_item.rs @@ -182,6 +182,27 @@ mod test { ret: <() as odra::casper_types::CLTyped>::cl_type(), ty: odra::contract_def::EntrypointType::Public, attributes: vec![odra::contract_def::EntrypointAttribute::NonReentrant] + }, + odra::contract_def::Entrypoint { + ident: String::from("airdrop"), + args: vec![ + odra::contract_def::Argument { + ident: String::from("to"), + ty: as odra::casper_types::CLTyped>::cl_type(), + is_ref: false, + is_slice: false + }, + odra::contract_def::Argument { + ident: String::from("amount"), + ty: ::cl_type(), + is_ref: false, + is_slice: false + } + ], + is_mut: false, + ret: <() as odra::casper_types::CLTyped>::cl_type(), + ty: odra::contract_def::EntrypointType::Public, + attributes: vec![] } ] } diff --git a/odra-macros/src/ast/exec_parts.rs b/odra-macros/src/ast/exec_parts.rs index 64966cd8..78aaad6c 100644 --- a/odra-macros/src/ast/exec_parts.rs +++ b/odra-macros/src/ast/exec_parts.rs @@ -7,22 +7,6 @@ use derive_try_from::TryFromRef; use quote::TokenStreamExt; use syn::parse_quote; -#[derive(syn_derive::ToTokens)] -pub struct ExecPartsReexportItem { - reexport_stmt: syn::Stmt -} - -impl TryFrom<&'_ ModuleImplIR> for ExecPartsReexportItem { - type Error = syn::Error; - - fn try_from(module: &'_ ModuleImplIR) -> Result { - let test_parts_ident = module.exec_parts_mod_ident()?; - Ok(Self { - reexport_stmt: parse_quote!(pub use #test_parts_ident::*;) - }) - } -} - #[derive(syn_derive::ToTokens)] pub struct ExecPartsItem { parts_module: ExecPartsModuleItem, @@ -107,10 +91,17 @@ impl TryFrom<(&'_ ModuleImplIR, &'_ FnIR)> for ExecFunctionItem { }).collect::, syn::Error>>()?; let args = func - .arg_names() + .named_args() .iter() - .map(|ident| utils::stmt::get_named_arg(ident, &exec_env_ident)) - .collect(); + .map(|arg| { + let ty = utils::ty::unreferenced_ty(&arg.ty()?); + Ok(utils::stmt::get_named_arg( + &arg.name()?, + &exec_env_ident, + &ty + )) + }) + .collect::, syn::Error>>()?; let init_contract_stmt = match func.is_mut() { true => utils::stmt::new_mut_module(&contract_ident, &module_ident, &env_rc_ident), @@ -235,7 +226,7 @@ mod test { pub fn execute_init(env: odra::ContractEnv) { let env_rc = Rc::new(env); let exec_env = odra::ExecutionEnv::new(env_rc.clone()); - let total_supply = exec_env.get_named_arg("total_supply"); + let total_supply = exec_env.get_named_arg::>("total_supply"); let mut contract = ::new(env_rc); let result = contract.init(total_supply); return result; @@ -265,13 +256,24 @@ mod test { let env_rc = Rc::new(env); let exec_env = odra::ExecutionEnv::new(env_rc.clone()); exec_env.non_reentrant_before(); - let to = exec_env.get_named_arg("to"); - let amount = exec_env.get_named_arg("amount"); + let to = exec_env.get_named_arg::
("to"); + let amount = exec_env.get_named_arg::("amount"); let mut contract = ::new(env_rc); let result = contract.approve(&to, &amount); exec_env.non_reentrant_after(); return result; } + + #[inline] + pub fn execute_airdrop(env: odra::ContractEnv) { + let env_rc = Rc::new(env); + let exec_env = odra::ExecutionEnv::new(env_rc.clone()); + let to = exec_env.get_named_arg::>("to"); + let amount = exec_env.get_named_arg::("amount"); + let contract = ::new(env_rc); + let result = contract.airdrop(&to, &amount); + return result; + } } }; @@ -342,7 +344,7 @@ mod test { pub fn execute_set_owner(env: odra::ContractEnv) { let env_rc = Rc::new(env); let exec_env = odra::ExecutionEnv::new(env_rc.clone()); - let new_owner = exec_env.get_named_arg("new_owner"); + let new_owner = exec_env.get_named_arg::
("new_owner"); let mut contract = ::new(env_rc); let result = contract.set_owner(new_owner); return result; diff --git a/odra-macros/src/ast/host_ref_item.rs b/odra-macros/src/ast/host_ref_item.rs index a4bcfdb5..0b768edb 100644 --- a/odra-macros/src/ast/host_ref_item.rs +++ b/odra-macros/src/ast/host_ref_item.rs @@ -341,6 +341,28 @@ mod ref_item_tests { pub fn approve(&mut self, to: Address, amount: U256) { self.try_approve(to, amount).unwrap() } + + pub fn try_airdrop(&self, to: odra::prelude::vec::Vec
, amount: U256) -> Result<(), odra::OdraError> { + self.env.call_contract( + self.address, + odra::CallDef::new( + String::from("airdrop"), + { + let mut named_args = odra::RuntimeArgs::new(); + if self.attached_value > odra::U512::zero() { + let _ = named_args.insert("amount", self.attached_value); + } + let _ = named_args.insert("to", to); + let _ = named_args.insert("amount", amount); + named_args + } + ).with_amount(self.attached_value), + ) + } + + pub fn airdrop(&self, to: odra::prelude::vec::Vec
, amount: U256) { + self.try_airdrop(to, amount).unwrap() + } } }; let actual = HostRefItem::try_from(&module).unwrap(); diff --git a/odra-macros/src/ast/module_impl_item.rs b/odra-macros/src/ast/module_impl_item.rs index 44942b40..785d0a49 100644 --- a/odra-macros/src/ast/module_impl_item.rs +++ b/odra-macros/src/ast/module_impl_item.rs @@ -1,6 +1,6 @@ use crate::ast::blueprint::BlueprintItem; use crate::ast::entrypoints_item::HasEntrypointsImplItem; -use crate::ast::exec_parts::{ExecPartsItem, ExecPartsReexportItem}; +use crate::ast::exec_parts::ExecPartsItem; use crate::ast::ref_item::RefItem; use crate::ast::test_parts::{TestPartsItem, TestPartsReexportItem}; use crate::ast::wasm_parts::WasmPartsModuleItem; @@ -17,7 +17,6 @@ pub struct ModuleImplItem { test_parts: TestPartsItem, test_parts_reexport: TestPartsReexportItem, exec_parts: ExecPartsItem, - exec_parts_reexport: ExecPartsReexportItem, wasm_parts: WasmPartsModuleItem, blueprint: BlueprintItem } diff --git a/odra-macros/src/ast/ref_item.rs b/odra-macros/src/ast/ref_item.rs index de4393bd..eb581cb2 100644 --- a/odra-macros/src/ast/ref_item.rs +++ b/odra-macros/src/ast/ref_item.rs @@ -202,6 +202,22 @@ mod ref_item_tests { ), ) } + + pub fn airdrop(&self, to: odra::prelude::vec::Vec
, amount: U256) { + self.env + .call_contract( + self.address, + odra::CallDef::new( + String::from("airdrop"), + { + let mut named_args = odra::RuntimeArgs::new(); + let _ = named_args.insert("to", to); + let _ = named_args.insert("amount", amount); + named_args + }, + ), + ) + } } }; let actual = RefItem::try_from(&module).unwrap(); diff --git a/odra-macros/src/ast/test_parts.rs b/odra-macros/src/ast/test_parts.rs index 4e96c04d..07974ab8 100644 --- a/odra-macros/src/ast/test_parts.rs +++ b/odra-macros/src/ast/test_parts.rs @@ -11,7 +11,7 @@ use super::{ #[derive(syn_derive::ToTokens)] pub struct TestPartsReexportItem { - attr: syn::Attribute, + not_wasm_attr: syn::Attribute, reexport_stmt: syn::Stmt } @@ -21,7 +21,7 @@ impl TryFrom<&'_ ModuleImplIR> for TestPartsReexportItem { fn try_from(module: &'_ ModuleImplIR) -> Result { let test_parts_ident = module.test_parts_mod_ident()?; Ok(Self { - attr: utils::attr::not_wasm32(), + not_wasm_attr: utils::attr::not_wasm32(), reexport_stmt: parse_quote!(pub use #test_parts_ident::*;) }) } @@ -184,6 +184,28 @@ mod test { pub fn approve(&mut self, to: Address, amount: U256) { self.try_approve(to, amount).unwrap() } + + pub fn try_airdrop(&self, to: odra::prelude::vec::Vec
, amount: U256) -> Result<(), odra::OdraError> { + self.env.call_contract( + self.address, + odra::CallDef::new( + String::from("airdrop"), + { + let mut named_args = odra::RuntimeArgs::new(); + if self.attached_value > odra::U512::zero() { + let _ = named_args.insert("amount", self.attached_value); + } + let _ = named_args.insert("to", to); + let _ = named_args.insert("amount", amount); + named_args + } + ).with_amount(self.attached_value), + ) + } + + pub fn airdrop(&self, to: odra::prelude::vec::Vec
, amount: U256) { + self.try_airdrop(to, amount).unwrap() + } } pub struct Erc20Deployer; @@ -193,19 +215,23 @@ mod test { let caller = odra::EntryPointsCaller::new(env.clone(), |contract_env, call_def| { match call_def.method() { "init" => { - let result = execute_init(contract_env); + let result = __erc20_exec_parts::execute_init(contract_env); odra::ToBytes::to_bytes(&result).map(Into::into).map_err(|err| odra::OdraError::ExecutionError(err.into())) } "total_supply" => { - let result = execute_total_supply(contract_env); + let result = __erc20_exec_parts::execute_total_supply(contract_env); odra::ToBytes::to_bytes(&result).map(Into::into).map_err(|err| odra::OdraError::ExecutionError(err.into())) } "pay_to_mint" => { - let result = execute_pay_to_mint(contract_env); + let result = __erc20_exec_parts::execute_pay_to_mint(contract_env); odra::ToBytes::to_bytes(&result).map(Into::into).map_err(|err| odra::OdraError::ExecutionError(err.into())) } "approve" => { - let result = execute_approve(contract_env); + let result = __erc20_exec_parts::execute_approve(contract_env); + odra::ToBytes::to_bytes(&result).map(Into::into).map_err(|err| odra::OdraError::ExecutionError(err.into())) + } + "airdrop" => { + let result = __erc20_exec_parts::execute_airdrop(contract_env); odra::ToBytes::to_bytes(&result).map(Into::into).map_err(|err| odra::OdraError::ExecutionError(err.into())) } name => Err(odra::OdraError::VmError( @@ -337,11 +363,11 @@ mod test { let caller = odra::EntryPointsCaller::new(env.clone(), |contract_env, call_def| { match call_def.method() { "total_supply" => { - let result = execute_total_supply(contract_env); + let result = __erc20_exec_parts::execute_total_supply(contract_env); odra::ToBytes::to_bytes(&result).map(Into::into).map_err(|err| odra::OdraError::ExecutionError(err.into())) } "pay_to_mint" => { - let result = execute_pay_to_mint(contract_env); + let result = __erc20_exec_parts::execute_pay_to_mint(contract_env); odra::ToBytes::to_bytes(&result).map(Into::into).map_err(|err| odra::OdraError::ExecutionError(err.into())) } name => Err(odra::OdraError::VmError( diff --git a/odra-macros/src/ast/wasm_parts.rs b/odra-macros/src/ast/wasm_parts.rs index b6145577..619b202a 100644 --- a/odra-macros/src/ast/wasm_parts.rs +++ b/odra-macros/src/ast/wasm_parts.rs @@ -51,6 +51,7 @@ impl TryFrom<&'_ ModuleImplIR> for WasmPartsModuleItem { entry_points: module .functions()? .iter() + .map(|f| (module, f)) .map(TryInto::try_into) .collect::, _>>()? }) @@ -156,18 +157,22 @@ struct NoMangleFnItem { ret_stmt: Option } -impl TryFrom<&'_ FnIR> for NoMangleFnItem { +impl TryFrom<(&'_ ModuleImplIR, &'_ FnIR)> for NoMangleFnItem { type Error = syn::Error; - fn try_from(func: &'_ FnIR) -> Result { + fn try_from(value: (&'_ ModuleImplIR, &'_ FnIR)) -> Result { + let (module, func) = value; let fn_ident = func.name(); let result_ident = utils::ident::result(); + let exec_parts_ident = module.exec_parts_mod_ident()?; let exec_fn = func.execute_name(); let new_env = utils::expr::new_wasm_contract_env(); let execute_stmt = match func.return_type() { - syn::ReturnType::Default => parse_quote!(#exec_fn(#new_env);), - syn::ReturnType::Type(_, _) => parse_quote!(let #result_ident = #exec_fn(#new_env);) + syn::ReturnType::Default => parse_quote!(#exec_parts_ident::#exec_fn(#new_env);), + syn::ReturnType::Type(_, _) => { + parse_quote!(let #result_ident = #exec_parts_ident::#exec_fn(#new_env);) + } }; let ret_stmt = match func.return_type() { @@ -315,6 +320,21 @@ mod test { ), ); entry_points + .add_entry_point( + odra::casper_types::EntryPoint::new( + "airdrop", + vec![ + odra::casper_types::Parameter::new("to", as + odra::casper_types::CLTyped > ::cl_type()), + odra::casper_types::Parameter::new("amount", ::cl_type()) + ], + <() as odra::casper_types::CLTyped>::cl_type(), + odra::casper_types::EntryPointAccess::Public, + odra::casper_types::EntryPointType::Contract, + ), + ); + entry_points } #[no_mangle] @@ -339,12 +359,12 @@ mod test { #[no_mangle] fn init() { - execute_init(odra::odra_casper_wasm_env::WasmContractEnv::new_env()); + __erc20_exec_parts::execute_init(odra::odra_casper_wasm_env::WasmContractEnv::new_env()); } #[no_mangle] fn total_supply() { - let result = execute_total_supply(odra::odra_casper_wasm_env::WasmContractEnv::new_env()); + let result = __erc20_exec_parts::execute_total_supply(odra::odra_casper_wasm_env::WasmContractEnv::new_env()); odra::odra_casper_wasm_env::casper_contract::contract_api::runtime::ret( odra::odra_casper_wasm_env::casper_contract::unwrap_or_revert::UnwrapOrRevert::unwrap_or_revert( odra::casper_types::CLValue::from_t(result) @@ -354,12 +374,17 @@ mod test { #[no_mangle] fn pay_to_mint() { - execute_pay_to_mint(odra::odra_casper_wasm_env::WasmContractEnv::new_env()); + __erc20_exec_parts::execute_pay_to_mint(odra::odra_casper_wasm_env::WasmContractEnv::new_env()); } #[no_mangle] fn approve() { - execute_approve(odra::odra_casper_wasm_env::WasmContractEnv::new_env()); + __erc20_exec_parts::execute_approve(odra::odra_casper_wasm_env::WasmContractEnv::new_env()); + } + + #[no_mangle] + fn airdrop() { + __erc20_exec_parts::execute_airdrop(odra::odra_casper_wasm_env::WasmContractEnv::new_env()); } } }; @@ -417,7 +442,7 @@ mod test { #[no_mangle] fn total_supply() { - let result = execute_total_supply(odra::odra_casper_wasm_env::WasmContractEnv::new_env()); + let result = __erc20_exec_parts::execute_total_supply(odra::odra_casper_wasm_env::WasmContractEnv::new_env()); odra::odra_casper_wasm_env::casper_contract::contract_api::runtime::ret( odra::odra_casper_wasm_env::casper_contract::unwrap_or_revert::UnwrapOrRevert::unwrap_or_revert( odra::casper_types::CLValue::from_t(result) @@ -427,7 +452,7 @@ mod test { #[no_mangle] fn pay_to_mint() { - execute_pay_to_mint(odra::odra_casper_wasm_env::WasmContractEnv::new_env()); + __erc20_exec_parts::execute_pay_to_mint(odra::odra_casper_wasm_env::WasmContractEnv::new_env()); } } }; @@ -518,7 +543,7 @@ mod test { #[no_mangle] fn total_supply() { - let result = execute_total_supply(odra::odra_casper_wasm_env::WasmContractEnv::new_env()); + let result = __erc20_exec_parts::execute_total_supply(odra::odra_casper_wasm_env::WasmContractEnv::new_env()); odra::odra_casper_wasm_env::casper_contract::contract_api::runtime::ret( odra::odra_casper_wasm_env::casper_contract::unwrap_or_revert::UnwrapOrRevert::unwrap_or_revert( odra::casper_types::CLValue::from_t(result) @@ -528,7 +553,7 @@ mod test { #[no_mangle] fn get_owner() { - let result = execute_get_owner( + let result = __erc20_exec_parts::execute_get_owner( odra::odra_casper_wasm_env::WasmContractEnv::new_env(), ); odra::odra_casper_wasm_env::casper_contract::contract_api::runtime::ret( @@ -540,12 +565,12 @@ mod test { #[no_mangle] fn set_owner() { - execute_set_owner(odra::odra_casper_wasm_env::WasmContractEnv::new_env()); + __erc20_exec_parts::execute_set_owner(odra::odra_casper_wasm_env::WasmContractEnv::new_env()); } #[no_mangle] fn name() { - let result = execute_name( + let result = __erc20_exec_parts::execute_name( odra::odra_casper_wasm_env::WasmContractEnv::new_env(), ); odra::odra_casper_wasm_env::casper_contract::contract_api::runtime::ret( @@ -557,7 +582,7 @@ mod test { #[no_mangle] fn symbol() { - let result = execute_symbol( + let result = __erc20_exec_parts::execute_symbol( odra::odra_casper_wasm_env::WasmContractEnv::new_env(), ); odra::odra_casper_wasm_env::casper_contract::contract_api::runtime::ret( diff --git a/odra-macros/src/ir/mod.rs b/odra-macros/src/ir/mod.rs index f3a6c03b..f7d674d9 100644 --- a/odra-macros/src/ir/mod.rs +++ b/odra-macros/src/ir/mod.rs @@ -459,7 +459,7 @@ impl FnIR { self.typed_args() .into_iter() .map(|pat_ty| syn::PatType { - ty: Box::new(utils::syn::unreferenced_ty(&pat_ty.ty)), + ty: Box::new(utils::ty::unreferenced_ty(&pat_ty.ty)), ..pat_ty }) .collect() diff --git a/odra-macros/src/test_utils.rs b/odra-macros/src/test_utils.rs index 9c194be0..d90201be 100644 --- a/odra-macros/src/test_utils.rs +++ b/odra-macros/src/test_utils.rs @@ -34,6 +34,9 @@ pub mod mock { }); } + pub fn airdrop(to: &[Address], amount: &U256) { + } + fn private_function() { } diff --git a/odra-macros/src/utils/expr.rs b/odra-macros/src/utils/expr.rs index c01cb8ad..6d23d6fd 100644 --- a/odra-macros/src/utils/expr.rs +++ b/odra-macros/src/utils/expr.rs @@ -55,7 +55,7 @@ pub fn new_parameter(name: String, ty: syn::Type) -> syn::Expr { } pub fn as_cl_type(ty: &syn::Type) -> syn::Expr { - let ty = super::syn::unreferenced_ty(ty); + let ty = super::ty::unreferenced_ty(ty); let ty_cl_typed = super::ty::cl_typed(); let ty = super::syn::as_casted_ty_stream(&ty, ty_cl_typed); parse_quote!(#ty::cl_type()) diff --git a/odra-macros/src/utils/stmt.rs b/odra-macros/src/utils/stmt.rs index 8ef59b27..3dcec07c 100644 --- a/odra-macros/src/utils/stmt.rs +++ b/odra-macros/src/utils/stmt.rs @@ -39,9 +39,9 @@ pub fn install_contract(entry_points: syn::Expr, schemas: syn::Expr, args: syn:: );) } -pub fn get_named_arg(arg_ident: &syn::Ident, env_ident: &syn::Ident) -> syn::Stmt { +pub fn get_named_arg(arg_ident: &syn::Ident, env_ident: &syn::Ident, ty: &syn::Type) -> syn::Stmt { let arg_name = arg_ident.to_string(); - parse_quote!(let #arg_ident = #env_ident.get_named_arg(#arg_name);) + parse_quote!(let #arg_ident = #env_ident.get_named_arg::<#ty>(#arg_name);) } pub fn new_execution_env(ident: &syn::Ident, env_rc_ident: &syn::Ident) -> syn::Stmt { diff --git a/odra-macros/src/utils/syn.rs b/odra-macros/src/utils/syn.rs index 2074f727..97a780a9 100644 --- a/odra-macros/src/utils/syn.rs +++ b/odra-macros/src/utils/syn.rs @@ -185,9 +185,3 @@ pub fn is_ref(ty: &syn::Type) -> bool { matches!(ty, syn::Type::Reference(_)) } -pub fn unreferenced_ty(ty: &syn::Type) -> syn::Type { - match ty { - syn::Type::Reference(syn::TypeReference { elem, .. }) => *elem.clone(), - _ => ty.clone() - } -} diff --git a/odra-macros/src/utils/ty.rs b/odra-macros/src/utils/ty.rs index 2b24dfd4..f4e4a3b4 100644 --- a/odra-macros/src/utils/ty.rs +++ b/odra-macros/src/utils/ty.rs @@ -228,3 +228,23 @@ pub fn btree_map() -> syn::Type { pub fn module_component() -> syn::Type { parse_quote!(odra::module::ModuleComponent) } + +fn slice_to_vec(ty: &syn::Type) -> syn::Type { + match ty { + syn::Type::Slice(ty) => vec_of(ty.elem.as_ref()), + _ => ty.clone() + } +} + +pub fn unreferenced_ty(ty: &syn::Type) -> syn::Type { + match ty { + syn::Type::Reference(syn::TypeReference { elem, .. }) => { + if matches!(**elem, syn::Type::Reference(_)) { + unreferenced_ty(elem) + } else { + slice_to_vec(elem) + } + } + _ => slice_to_vec(ty) + } +}