Skip to content

Commit

Permalink
Add delegate! inner macro (#303)
Browse files Browse the repository at this point in the history
* Add delegate! inner macro
* Module members validation (#304)
  • Loading branch information
kpob authored Jan 8, 2024
1 parent 2d49ebd commit 5d5f497
Show file tree
Hide file tree
Showing 34 changed files with 1,154 additions and 369 deletions.
8 changes: 4 additions & 4 deletions core/src/entry_point_callback.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
use crate::call_def::CallDef;
use crate::Bytes;
use crate::{Bytes, OdraError};
use crate::{ContractEnv, HostEnv};

#[derive(Clone)]
pub struct EntryPointsCaller {
pub f: fn(contract_env: ContractEnv, call_def: CallDef) -> Bytes,
pub f: fn(contract_env: ContractEnv, call_def: CallDef) -> Result<Bytes, OdraError>,
host_env: HostEnv
}

impl EntryPointsCaller {
pub fn new(
host_env: HostEnv,
f: fn(contract_env: ContractEnv, call_def: CallDef) -> Bytes
f: fn(contract_env: ContractEnv, call_def: CallDef) -> Result<Bytes, OdraError>
) -> Self {
EntryPointsCaller { f, host_env }
}

pub fn call(&self, call_def: CallDef) -> Bytes {
pub fn call(&self, call_def: CallDef) -> Result<Bytes, OdraError> {
(self.f)(self.host_env.contract_env(), call_def)
}
}
20 changes: 7 additions & 13 deletions core/src/mapping.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::arithmetic::{OverflowingAdd, OverflowingSub};
use crate::contract_def::HasEvents;
use crate::module::ModuleComponent;
use crate::prelude::*;
use crate::{
module::{Module, ModuleWrapper},
Expand All @@ -15,8 +15,8 @@ pub struct Mapping<K, V> {
index: u8
}

impl<K: ToBytes, V> Mapping<K, V> {
pub const fn new(env: Rc<ContractEnv>, index: u8) -> Self {
impl<K: ToBytes, V> ModuleComponent for Mapping<K, V> {
fn instance(env: Rc<ContractEnv>, index: u8) -> Self {
Self {
parent_env: env,
phantom: core::marker::PhantomData,
Expand All @@ -37,28 +37,28 @@ impl<K: ToBytes, V> Mapping<K, V> {
impl<K: ToBytes, V: FromBytes + CLTyped + Default> Mapping<K, V> {
pub fn get_or_default(&self, key: &K) -> V {
let env = self.env_for_key(key);
Variable::<V>::new(Rc::new(env), self.index).get_or_default()
Variable::<V>::instance(Rc::new(env), self.index).get_or_default()
}
}

impl<K: ToBytes, V: FromBytes + CLTyped> Mapping<K, V> {
pub fn get(&self, key: &K) -> Option<V> {
let env = self.env_for_key(key);
Variable::<V>::new(Rc::new(env), self.index).get()
Variable::<V>::instance(Rc::new(env), self.index).get()
}
}

impl<K: ToBytes, V: ToBytes + CLTyped> Mapping<K, V> {
pub fn set(&mut self, key: &K, value: V) {
let env = self.env_for_key(key);
Variable::<V>::new(Rc::new(env), self.index).set(value)
Variable::<V>::instance(Rc::new(env), self.index).set(value)
}
}

impl<K: ToBytes, V: Module> Mapping<K, V> {
pub fn module(&self, key: &K) -> ModuleWrapper<V> {
let env = self.env_for_key(key);
ModuleWrapper::new(Rc::new(env), self.index)
ModuleWrapper::instance(Rc::new(env), self.index)
}
}

Expand Down Expand Up @@ -93,9 +93,3 @@ impl<
self.set(key, new_value);
}
}

impl<K: ToBytes, V: HasEvents> HasEvents for Mapping<K, V> {
fn events() -> Vec<crate::contract_def::Event> {
V::events()
}
}
22 changes: 14 additions & 8 deletions core/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,33 @@ pub trait Module {
fn env(&self) -> Rc<ContractEnv>;
}

pub trait ModuleComponent {
fn instance(env: Rc<ContractEnv>, index: u8) -> Self;
}

pub struct ModuleWrapper<T> {
env: Rc<ContractEnv>,
module: OnceCell<T>,
index: u8
}

impl<T: Module> ModuleWrapper<T> {
pub fn new(env: Rc<ContractEnv>, index: u8) -> Self {
impl<T: Module> ModuleComponent for ModuleWrapper<T> {
fn instance(env: Rc<ContractEnv>, index: u8) -> Self {
Self {
env,
module: OnceCell::new(),
index
}
}
}

impl<M: ModuleComponent> HasEvents for M {
fn events() -> Vec<crate::contract_def::Event> {
Vec::new()
}
}

impl<T: Module> ModuleWrapper<T> {
pub fn module(&self) -> &T {
self.module
.get_or_init(|| T::new(Rc::new(self.env.child(self.index))))
Expand All @@ -68,9 +80,3 @@ impl<T: Module> DerefMut for ModuleWrapper<T> {
self.module_mut()
}
}

impl<T: HasEvents> HasEvents for ModuleWrapper<T> {
fn events() -> Vec<crate::contract_def::Event> {
T::events()
}
}
11 changes: 3 additions & 8 deletions core/src/variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::prelude::*;
use crate::{CLTyped, FromBytes, OdraError, ToBytes, UnwrapOrRevert};

use crate::contract_env::ContractEnv;
use crate::module::ModuleComponent;

pub struct Variable<T> {
env: Rc<ContractEnv>,
Expand All @@ -15,8 +16,8 @@ impl<T> Variable<T> {
}
}

impl<T> Variable<T> {
pub const fn new(env: Rc<ContractEnv>, index: u8) -> Self {
impl<T> ModuleComponent for Variable<T> {
fn instance(env: Rc<ContractEnv>, index: u8) -> Self {
Self {
env,
phantom: core::marker::PhantomData,
Expand Down Expand Up @@ -79,9 +80,3 @@ impl<V: ToBytes + FromBytes + CLTyped + OverflowingSub + Default> Variable<V> {
self.set(new_value);
}
}

impl<T> crate::contract_def::HasEvents for Variable<T> {
fn events() -> Vec<crate::contract_def::Event> {
vec![]
}
}
72 changes: 42 additions & 30 deletions examples2/src/erc20.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use crate::counter::Counter;
use casper_event_standard::Event;
use odra::{casper_event_standard, Bytes, Module, OdraError, PublicKey};
use odra::{
casper_event_standard, Bytes, ContractEnv, Module, ModuleComponent, OdraError, PublicKey
};
use odra::{prelude::*, CallDef, ModuleWrapper};
use odra::{Address, Mapping, Variable, U256, U512};

Expand Down Expand Up @@ -36,14 +39,23 @@ trait TotalSupply {
fn total_supply(&self) -> U256;
}

type VarU256 = Variable<U256>;

#[odra::module(events = [OnTransfer, OnCrossTransfer, OnApprove])]
pub struct Erc20 {
total_supply: Variable<U256>,
balances: Mapping<Address, U256>
total_supply: VarU256,
balances: Mapping<Address, U256>,
counter: ModuleWrapper<Counter>
}

#[odra::module]
impl Erc20 {
delegate! {
to self.counter {
fn get_count(&self, index: u8) -> u32;
}
}

pub fn init(&mut self, total_supply: Option<U256>) {
if let Some(total_supply) = total_supply {
self.total_supply.set(total_supply);
Expand Down Expand Up @@ -184,22 +196,22 @@ mod tests {

// Test cross calls
let mut pobcoin = Erc20Deployer::init(&env, Some(100.into()));
assert_eq!(erc20.cross_total(pobcoin.address), 200.into());
assert_eq!(erc20.cross_total(*pobcoin.address()), 200.into());

// Test attaching value and balances
let initial_balance = U512::from(100000000000000000u64);
assert_eq!(env.balance_of(&erc20.address), 0.into());
assert_eq!(env.balance_of(erc20.address()), 0.into());
assert_eq!(env.balance_of(&alice), initial_balance);

env.set_caller(alice);
pobcoin.with_tokens(100.into()).pay_to_mint();
assert_eq!(env.balance_of(&pobcoin.address), 100.into());
assert_eq!(env.balance_of(pobcoin.address()), 100.into());
assert_eq!(pobcoin.total_supply(), 200.into());
assert_eq!(pobcoin.balance_of(alice), 100.into());
assert_eq!(pobcoin.balance_of(bob), 100.into());

assert_eq!(env.balance_of(&alice), initial_balance - U512::from(100));
assert_eq!(env.balance_of(&pobcoin.address), 100.into());
assert_eq!(env.balance_of(pobcoin.address()), 100.into());

// Test block time
let block_time = pobcoin.get_current_block_time();
Expand Down Expand Up @@ -231,15 +243,15 @@ mod tests {
erc20.transfer(bob, 30.into());

// Test call result
assert_eq!(env.events_count(&erc20.address), 2);
assert_eq!(env.events_count(erc20.address()), 2);

let call_result = env.last_call();
assert!(call_result.result().is_ok());
assert_eq!(call_result.contract_address(), erc20.address);
assert_eq!(call_result.contract_address(), *erc20.address());
assert_eq!(call_result.caller(), alice);
assert_eq!(call_result.bytes(), vec![].into());
assert_eq!(
call_result.contract_events(&erc20.address),
call_result.contract_events(erc20.address()),
vec![Bytes::from(
OnTransfer {
from: Some(alice),
Expand All @@ -255,20 +267,20 @@ mod tests {
erc20.try_transfer(bob, 100_000_000.into()).unwrap_err();
let call_result = env.last_call();
assert!(call_result.result().is_err());
assert_eq!(call_result.contract_address(), erc20.address);
assert_eq!(call_result.contract_address(), *erc20.address());
assert_eq!(call_result.caller(), alice);
assert_eq!(call_result.contract_events(&erc20.address), vec![]);
assert_eq!(call_result.contract_events(erc20.address()), vec![]);

// cross call
pobcoin.transfer(erc20.address, 100.into());
erc20.cross_transfer(pobcoin.address, alice, 50.into());
pobcoin.transfer(*erc20.address(), 100.into());
erc20.cross_transfer(*pobcoin.address(), alice, 50.into());
let call_result = env.last_call();

assert_eq!(
call_result.contract_events(&pobcoin.address),
call_result.contract_events(&pobcoin.address()),
vec![Bytes::from(
OnTransfer {
from: Some(erc20.address),
from: Some(*erc20.address()),
to: Some(alice),
amount: 50.into()
}
Expand All @@ -277,12 +289,12 @@ mod tests {
)]
);
assert_eq!(
call_result.contract_events(&erc20.address),
call_result.contract_events(erc20.address()),
vec![Bytes::from(
OnCrossTransfer {
from: Some(erc20.address),
from: Some(*erc20.address()),
to: Some(alice),
other_contract: pobcoin.address,
other_contract: *pobcoin.address(),
amount: 50.into()
}
.to_bytes()
Expand All @@ -307,7 +319,7 @@ mod tests {
erc20.transfer(charlie, 20.into());

// Test events
let event: OnTransfer = env.get_event(&erc20.address, 0).unwrap();
let event: OnTransfer = env.get_event(erc20.address(), 0).unwrap();
assert_eq!(
event,
OnTransfer {
Expand All @@ -317,7 +329,7 @@ mod tests {
}
);

let event: OnApprove = env.get_event(&erc20.address, 1).unwrap();
let event: OnApprove = env.get_event(erc20.address(), 1).unwrap();
assert_eq!(
event,
OnApprove {
Expand All @@ -327,7 +339,7 @@ mod tests {
}
);

let event: OnTransfer = env.get_event(&erc20.address, 2).unwrap();
let event: OnTransfer = env.get_event(erc20.address(), 2).unwrap();
assert_eq!(
event,
OnTransfer {
Expand All @@ -338,7 +350,7 @@ mod tests {
);

// Test negative indices
let event: OnTransfer = env.get_event(&erc20.address, -1).unwrap();
let event: OnTransfer = env.get_event(erc20.address(), -1).unwrap();
assert_eq!(
event,
OnTransfer {
Expand Down Expand Up @@ -381,10 +393,10 @@ mod tests {

// Then we can check it
// If contract emitted a specific event during whole lifetime
assert!(env.emitted(&erc20.address, "OnTransfer"));
assert!(env.emitted(erc20.address(), "OnTransfer"));
// or all of them
assert_eq!(
env.event_names(&erc20.address),
env.event_names(erc20.address()),
vec!["OnApprove".to_string(), "OnTransfer".to_string()]
);

Expand All @@ -405,11 +417,11 @@ mod tests {
// or
assert!(erc20.last_call().emitted_event(&emitted_in_second_call));
// or for whole lifetime
assert!(env.emitted_event(&erc20.address, &emitted_in_second_call));
assert!(env.emitted_event(erc20.address(), &emitted_in_second_call));

// To check the order of events, use the power of vec:
assert_eq!(
env.events(&erc20.address)[0..2],
env.events(erc20.address())[0..2],
[
Bytes::from(first_emitted.to_bytes().unwrap()),
Bytes::from(emitted_in_second_call.to_bytes().unwrap())
Expand All @@ -418,13 +430,13 @@ mod tests {
);

assert!(env
.event_names(&erc20.address)
.event_names(erc20.address())
.ends_with(vec!["OnApprove".to_string(), "OnTransfer".to_string()].as_slice()));

// Counter examples
assert!(!erc20.last_call().emitted("OnApprove"));
assert!(!env.emitted(&erc20.address, "OnCrossTransfer"));
assert!(!env.emitted_event(&erc20.address, &not_emitted));
assert!(!env.emitted(erc20.address(), "OnCrossTransfer"));
assert!(!env.emitted_event(erc20.address(), &not_emitted));
}

#[test]
Expand Down
Loading

0 comments on commit 5d5f497

Please sign in to comment.