use crate::access::Ownable;
use crate::erc1155::erc1155_base::Erc1155Base;
use crate::erc1155::errors::Error;
use crate::erc1155::events::{TransferBatch, TransferSingle};
use crate::erc1155::owned_erc1155::OwnedErc1155;
use crate::erc1155::Erc1155;
use odra::prelude::*;
use odra::{
casper_types::{bytesrepr::Bytes, U256},
Address, SubModule
};
#[odra::module(events = [TransferBatch, TransferSingle])]
pub struct Erc1155Token {
core: SubModule<Erc1155Base>,
ownable: SubModule<Ownable>
}
#[odra::module]
impl OwnedErc1155 for Erc1155Token {
fn init(&mut self) {
self.ownable.init();
}
fn balance_of(&self, owner: &Address, id: &U256) -> U256 {
self.core.balance_of(owner, id)
}
fn balance_of_batch(&self, owners: &[Address], ids: &[U256]) -> Vec<U256> {
self.core.balance_of_batch(owners, ids)
}
fn set_approval_for_all(&mut self, operator: &Address, approved: bool) {
self.core.set_approval_for_all(operator, approved);
}
fn is_approved_for_all(&self, owner: &Address, operator: &Address) -> bool {
self.core.is_approved_for_all(owner, operator)
}
fn safe_transfer_from(
&mut self,
from: &Address,
to: &Address,
id: &U256,
amount: &U256,
data: &Option<Bytes>
) {
self.core.safe_transfer_from(from, to, id, amount, data);
}
fn safe_batch_transfer_from(
&mut self,
from: &Address,
to: &Address,
ids: Vec<U256>,
amounts: Vec<U256>,
data: &Option<Bytes>
) {
self.core
.safe_batch_transfer_from(from, to, ids, amounts, data);
}
fn renounce_ownership(&mut self) {
self.ownable.renounce_ownership();
}
fn transfer_ownership(&mut self, new_owner: &Address) {
self.ownable.transfer_ownership(new_owner);
}
fn owner(&self) -> Address {
self.ownable.get_owner()
}
fn mint(&mut self, to: &Address, id: &U256, amount: &U256, data: &Option<Bytes>) {
let caller = self.env().caller();
self.ownable.assert_owner(&caller);
let current_balance = self.core.balances.get_or_default(&(*to, *id));
self.core
.balances
.set(&(*to, *id), *amount + current_balance);
self.env().emit_event(TransferSingle {
operator: Some(caller),
from: None,
to: Some(*to),
id: *id,
value: *amount
});
self.core
.safe_transfer_acceptance_check(&caller, &caller, to, id, amount, data);
}
fn mint_batch(
&mut self,
to: &Address,
ids: Vec<U256>,
amounts: Vec<U256>,
data: &Option<Bytes>
) {
if ids.len() != amounts.len() {
self.env().revert(Error::IdsAndAmountsLengthMismatch)
}
let caller = self.env().caller();
self.ownable.assert_owner(&caller);
for (id, amount) in ids.iter().zip(amounts.iter()) {
let current_balance = self.core.balances.get_or_default(&(*to, *id));
self.core
.balances
.set(&(*to, *id), *amount + current_balance);
}
self.env().emit_event(TransferBatch {
operator: Some(caller),
from: None,
to: Some(*to),
ids: ids.to_vec(),
values: amounts.to_vec()
});
self.core
.safe_batch_transfer_acceptance_check(&caller, &caller, to, ids, amounts, data);
}
fn burn(&mut self, from: &Address, id: &U256, amount: &U256) {
let caller = self.env().caller();
self.ownable.assert_owner(&caller);
let current_balance = self.core.balances.get_or_default(&(*from, *id));
if current_balance < *amount {
self.env().revert(Error::InsufficientBalance)
}
self.core
.balances
.set(&(*from, *id), current_balance - *amount);
self.env().emit_event(TransferSingle {
operator: Some(caller),
from: Some(*from),
to: None,
id: *id,
value: *amount
});
}
fn burn_batch(&mut self, from: &Address, ids: Vec<U256>, amounts: Vec<U256>) {
if ids.len() != amounts.len() {
self.env().revert(Error::IdsAndAmountsLengthMismatch)
}
let caller = self.env().caller();
self.ownable.assert_owner(&caller);
for (id, amount) in ids.iter().zip(amounts.iter()) {
let current_balance = self.core.balances.get_or_default(&(*from, *id));
if current_balance < *amount {
self.env().revert(Error::InsufficientBalance)
}
self.core
.balances
.set(&(*from, *id), current_balance - *amount);
}
self.env().emit_event(TransferBatch {
operator: Some(caller),
from: Some(*from),
to: None,
ids: ids.to_vec(),
values: amounts.to_vec()
});
}
}
#[cfg(test)]
mod tests {
use crate::erc1155::errors::Error;
use crate::erc1155::errors::Error::InsufficientBalance;
use crate::erc1155::events::{ApprovalForAll, TransferBatch, TransferSingle};
use crate::erc1155_receiver::events::{BatchReceived, SingleReceived};
use crate::erc1155_receiver::Erc1155ReceiverHostRef;
use crate::erc1155_token::Erc1155TokenHostRef;
use crate::wrapped_native::WrappedNativeTokenHostRef;
use odra::host::{Deployer, HostEnv, HostRef, NoArgs};
use odra::prelude::*;
use odra::{
casper_types::{bytesrepr::Bytes, U256},
Address, OdraError, VmError
};
struct TokenEnv {
env: HostEnv,
token: Erc1155TokenHostRef,
alice: Address,
bob: Address,
owner: Address
}
fn setup() -> TokenEnv {
let env = odra_test::env();
TokenEnv {
env: env.clone(),
token: Erc1155TokenHostRef::deploy(&env, NoArgs),
alice: env.get_account(1),
bob: env.get_account(2),
owner: env.get_account(0)
}
}
#[test]
fn mint() {
let mut env = setup();
env.token.mint(env.alice, U256::one(), 100.into(), None);
assert_eq!(env.token.balance_of(env.alice, U256::one()), 100.into());
let contract = env.token;
env.env.emitted_event(
contract.address(),
&TransferSingle {
operator: Some(env.owner),
from: None,
to: Some(env.alice),
id: U256::one(),
value: 100.into()
}
);
}
#[test]
fn mint_batch() {
let mut env = setup();
env.token.mint_batch(
env.alice,
[U256::one(), U256::from(2)].to_vec(),
[100.into(), 200.into()].to_vec(),
None
);
env.env.emitted_event(
env.token.address(),
&TransferBatch {
operator: Some(env.owner),
from: None,
to: Some(env.alice),
ids: vec![U256::one(), U256::from(2)],
values: vec![100.into(), 200.into()]
}
);
assert_eq!(env.token.balance_of(env.alice, U256::one()), 100.into());
assert_eq!(env.token.balance_of(env.alice, U256::from(2)), 200.into());
}
#[test]
fn mint_batch_errors() {
let mut env = setup();
let err = env
.token
.try_mint_batch(
env.alice,
[U256::one(), U256::from(2)].to_vec(),
[100.into()].to_vec(),
None
)
.unwrap_err();
assert_eq!(err, Error::IdsAndAmountsLengthMismatch.into());
}
#[test]
fn burn() {
let mut env = setup();
env.token.mint(env.alice, U256::one(), 100.into(), None);
env.token.burn(env.alice, U256::one(), 50.into());
assert_eq!(env.token.balance_of(env.alice, U256::one()), 50.into());
let contract = env.token;
env.env.emitted_event(
contract.address(),
&TransferSingle {
operator: Some(env.owner),
from: Some(env.alice),
to: None,
id: U256::one(),
value: 50.into()
}
);
}
#[test]
fn burn_errors() {
let mut env = setup();
env.token.mint(env.alice, U256::one(), 100.into(), None);
let err = env
.token
.try_burn(env.alice, U256::one(), 150.into())
.unwrap_err();
assert_eq!(err, InsufficientBalance.into());
let mut env = setup();
let err = env
.token
.try_burn(env.alice, U256::one(), 150.into())
.unwrap_err();
assert_eq!(err, InsufficientBalance.into());
}
#[test]
fn burn_batch() {
let mut env = setup();
env.token.mint_batch(
env.alice,
[U256::one(), U256::from(2)].to_vec(),
[100.into(), 200.into()].to_vec(),
None
);
env.token.burn_batch(
env.alice,
[U256::one(), U256::from(2)].to_vec(),
[50.into(), 100.into()].to_vec()
);
assert_eq!(env.token.balance_of(env.alice, U256::one()), 50.into());
assert_eq!(env.token.balance_of(env.alice, U256::from(2)), 100.into());
let contract = env.token;
env.env.emitted_event(
contract.address(),
&TransferBatch {
operator: Some(env.owner),
from: Some(env.alice),
to: None,
ids: vec![U256::one(), U256::from(2)],
values: vec![50.into(), 100.into()]
}
);
}
#[test]
fn burn_batch_errors() {
let mut env = setup();
env.token.mint_batch(
env.alice,
[U256::one(), U256::from(2)].to_vec(),
[100.into(), 200.into()].to_vec(),
None
);
let err = env
.token
.try_burn_batch(
env.alice,
[U256::one(), U256::from(2)].to_vec(),
[50.into()].to_vec()
)
.unwrap_err();
assert_eq!(err, Error::IdsAndAmountsLengthMismatch.into());
let mut env = setup();
env.token.mint_batch(
env.alice,
[U256::one(), U256::from(2)].to_vec(),
[100.into(), 200.into()].to_vec(),
None
);
let err = env
.token
.try_burn_batch(
env.alice,
[U256::one(), U256::from(2)].to_vec(),
[150.into(), 300.into()].to_vec()
)
.unwrap_err();
assert_eq!(err, InsufficientBalance.into());
let mut env = setup();
let err = env
.token
.try_burn_batch(
env.alice,
[U256::one(), U256::from(2)].to_vec(),
[150.into(), 300.into()].to_vec()
)
.unwrap_err();
assert_eq!(err, InsufficientBalance.into());
}
#[test]
fn balance_of() {
let mut env = setup();
env.token.mint(env.alice, U256::one(), 100.into(), None);
env.token.mint(env.alice, U256::from(2), 200.into(), None);
env.token.mint(env.bob, U256::one(), 300.into(), None);
assert_eq!(env.token.balance_of(env.alice, U256::one()), 100.into());
assert_eq!(env.token.balance_of(env.alice, U256::from(2)), 200.into());
assert_eq!(env.token.balance_of(env.bob, U256::one()), 300.into());
assert_eq!(env.token.balance_of(env.bob, U256::from(2)), 0.into());
}
#[test]
fn balance_of_batch() {
let mut env = setup();
env.token.mint_batch(
env.alice,
[U256::one(), U256::from(2)].to_vec(),
[100.into(), 200.into()].to_vec(),
None
);
env.token.mint_batch(
env.bob,
[U256::one(), U256::from(2)].to_vec(),
[300.into(), 400.into()].to_vec(),
None
);
assert_eq!(
env.token.balance_of_batch(
[env.alice, env.alice, env.alice, env.bob, env.bob, env.bob].to_vec(),
[
U256::one(),
U256::from(2),
U256::from(3),
U256::one(),
U256::from(2),
U256::from(3)
]
.to_vec()
),
[
U256::from(100),
U256::from(200),
U256::zero(),
U256::from(300),
U256::from(400),
U256::zero()
]
.to_vec()
);
}
#[test]
fn balance_of_batch_errors() {
let mut env = setup();
env.token.mint_batch(
env.alice,
[U256::one(), U256::from(2)].to_vec(),
[100.into(), 200.into()].to_vec(),
None
);
let err = env
.token
.try_balance_of_batch(
[env.alice, env.alice, env.alice].to_vec(),
[U256::one(), U256::from(2)].to_vec()
)
.unwrap_err();
assert_eq!(err, Error::AccountsAndIdsLengthMismatch.into());
}
#[test]
fn set_approval_for_all() {
let mut env = setup();
env.env.set_caller(env.alice);
env.token.set_approval_for_all(env.bob, true);
assert!(env.token.is_approved_for_all(env.alice, env.bob));
env.env.emitted_event(
env.token.address(),
&ApprovalForAll {
owner: env.alice,
operator: env.bob,
approved: true
}
);
}
#[test]
fn unset_approval_for_all() {
let mut env = setup();
env.env.set_caller(env.alice);
env.token.set_approval_for_all(env.bob, true);
env.env.set_caller(env.alice);
env.token.set_approval_for_all(env.bob, false);
assert!(!env.token.is_approved_for_all(env.alice, env.bob));
let contract = env.token;
env.env.emitted_event(
contract.address(),
&ApprovalForAll {
owner: env.alice,
operator: env.bob,
approved: false
}
);
}
#[test]
fn set_approval_to_self() {
let mut env = setup();
env.env.set_caller(env.alice);
let err = env
.token
.try_set_approval_for_all(env.alice, true)
.unwrap_err();
assert_eq!(err, Error::ApprovalForSelf.into());
}
#[test]
fn safe_transfer_from() {
let mut env = setup();
env.token.mint(env.alice, U256::one(), 100.into(), None);
env.env.set_caller(env.alice);
env.token
.safe_transfer_from(env.alice, env.bob, U256::one(), 50.into(), None);
assert_eq!(env.token.balance_of(env.alice, U256::one()), 50.into());
assert_eq!(env.token.balance_of(env.bob, U256::one()), 50.into());
let contract = env.token;
env.env.emitted_event(
contract.address(),
&TransferSingle {
operator: Some(env.alice),
from: Some(env.alice),
to: Some(env.bob),
id: U256::one(),
value: 50.into()
}
);
}
#[test]
fn safe_transfer_from_approved() {
let mut env = setup();
env.token.mint(env.alice, U256::one(), 100.into(), None);
env.env.set_caller(env.alice);
env.token.set_approval_for_all(env.bob, true);
env.env.set_caller(env.bob);
env.token
.safe_transfer_from(env.alice, env.bob, U256::one(), 50.into(), None);
assert_eq!(env.token.balance_of(env.alice, U256::one()), 50.into());
assert_eq!(env.token.balance_of(env.bob, U256::one()), 50.into());
let contract = env.token;
env.env.emitted_event(
contract.address(),
&TransferSingle {
operator: Some(env.bob),
from: Some(env.alice),
to: Some(env.bob),
id: U256::one(),
value: 50.into()
}
);
}
#[test]
fn safe_transfer_from_errors() {
let mut env = setup();
env.token.mint(env.alice, U256::one(), 100.into(), None);
env.env.set_caller(env.alice);
let err = env
.token
.try_safe_transfer_from(env.alice, env.bob, U256::one(), 200.into(), None)
.unwrap_err();
assert_eq!(err, Error::InsufficientBalance.into());
let mut env = setup();
env.token.mint(env.alice, U256::one(), 100.into(), None);
env.env.set_caller(env.bob);
let err = env
.token
.try_safe_transfer_from(env.alice, env.bob, U256::one(), 100.into(), None)
.unwrap_err();
assert_eq!(err, Error::NotAnOwnerOrApproved.into());
}
#[test]
fn safe_batch_transfer_from() {
let mut env = setup();
env.token.mint(env.alice, U256::one(), 100.into(), None);
env.token.mint(env.alice, U256::from(2), 200.into(), None);
env.env.set_caller(env.alice);
env.token.safe_batch_transfer_from(
env.alice,
env.bob,
[U256::one(), U256::from(2)].to_vec(),
[50.into(), 100.into()].to_vec(),
None
);
assert_eq!(env.token.balance_of(env.alice, U256::one()), 50.into());
assert_eq!(env.token.balance_of(env.alice, U256::from(2)), 100.into());
assert_eq!(env.token.balance_of(env.bob, U256::one()), 50.into());
assert_eq!(env.token.balance_of(env.bob, U256::from(2)), 100.into());
let contract = env.token;
env.env.emitted_event(
contract.address(),
&TransferBatch {
operator: Some(env.alice),
from: Some(env.alice),
to: Some(env.bob),
ids: vec![U256::one(), U256::from(2)],
values: vec![50.into(), 100.into()]
}
);
}
#[test]
fn safe_batch_transfer_from_approved() {
let mut env = setup();
env.token.mint(env.alice, U256::one(), 100.into(), None);
env.token.mint(env.alice, U256::from(2), 200.into(), None);
env.env.set_caller(env.alice);
env.token.set_approval_for_all(env.bob, true);
env.env.set_caller(env.bob);
env.token.safe_batch_transfer_from(
env.alice,
env.bob,
[U256::one(), U256::from(2)].to_vec(),
[50.into(), 100.into()].to_vec(),
None
);
assert_eq!(env.token.balance_of(env.alice, U256::one()), 50.into());
assert_eq!(env.token.balance_of(env.alice, U256::from(2)), 100.into());
assert_eq!(env.token.balance_of(env.bob, U256::one()), 50.into());
assert_eq!(env.token.balance_of(env.bob, U256::from(2)), 100.into());
let contract = env.token;
env.env.emitted_event(
contract.address(),
&TransferBatch {
operator: Some(env.bob),
from: Some(env.alice),
to: Some(env.bob),
ids: vec![U256::one(), U256::from(2)],
values: vec![50.into(), 100.into()]
}
);
}
#[test]
fn safe_batch_transfer_errors() {
let mut env = setup();
env.token.mint(env.alice, U256::one(), 100.into(), None);
env.token.mint(env.alice, U256::from(2), 200.into(), None);
env.env.set_caller(env.alice);
let err = env
.token
.try_safe_batch_transfer_from(
env.alice,
env.bob,
[U256::one(), U256::from(2)].to_vec(),
[50.into(), 300.into()].to_vec(),
None
)
.unwrap_err();
assert_eq!(err, Error::InsufficientBalance.into());
let mut env = setup();
env.token.mint(env.alice, U256::one(), 100.into(), None);
env.token.mint(env.alice, U256::from(2), 200.into(), None);
env.env.set_caller(env.bob);
let err = env
.token
.try_safe_batch_transfer_from(
env.alice,
env.bob,
[U256::one(), U256::from(2)].to_vec(),
[50.into(), 100.into()].to_vec(),
None
)
.unwrap_err();
assert_eq!(err, Error::NotAnOwnerOrApproved.into());
}
#[test]
fn safe_transfer_to_valid_receiver() {
let mut env = setup();
let receiver = Erc1155ReceiverHostRef::deploy(&env.env, NoArgs);
env.token.mint(env.alice, U256::one(), 100.into(), None);
env.env.set_caller(env.alice);
env.token.safe_transfer_from(
env.alice,
*receiver.address(),
U256::one(),
100.into(),
None
);
assert_eq!(env.token.balance_of(env.alice, U256::one()), 0.into());
assert_eq!(
env.token.balance_of(*receiver.address(), U256::one()),
100.into()
);
env.env.emitted_event(
receiver.address(),
&SingleReceived {
operator: Some(env.alice),
from: Some(env.alice),
token_id: U256::one(),
amount: 100.into(),
data: None
}
);
}
#[test]
fn safe_transfer_to_valid_receiver_with_data() {
let mut env = setup();
let receiver = Erc1155ReceiverHostRef::deploy(&env.env, NoArgs);
env.token.mint(env.alice, U256::one(), 100.into(), None);
env.env.set_caller(env.alice);
env.token.safe_transfer_from(
env.alice,
*receiver.address(),
U256::one(),
100.into(),
Some(Bytes::from(b"data".to_vec()))
);
assert_eq!(env.token.balance_of(env.alice, U256::one()), 0.into());
assert_eq!(
env.token.balance_of(*receiver.address(), U256::one()),
100.into()
);
env.env.emitted_event(
receiver.address(),
&SingleReceived {
operator: Some(env.alice),
from: Some(env.alice),
token_id: U256::one(),
amount: 100.into(),
data: Some(Bytes::from(b"data".to_vec()))
}
);
}
#[test]
fn safe_transfer_to_invalid_receiver() {
let mut env = setup();
let receiver = WrappedNativeTokenHostRef::deploy(&env.env, NoArgs);
env.token.mint(env.alice, U256::one(), 100.into(), None);
env.env.set_caller(env.alice);
let err = env.token.try_safe_transfer_from(
env.alice,
*receiver.address(),
U256::one(),
100.into(),
None
);
assert_eq!(
err,
Err(OdraError::VmError(VmError::NoSuchMethod(
"on_erc1155_received".to_string()
)))
);
}
#[test]
fn safe_batch_transfer_to_valid_receiver() {
let mut env = setup();
let receiver = Erc1155ReceiverHostRef::deploy(&env.env, NoArgs);
env.token.mint(env.alice, U256::one(), 100.into(), None);
env.token.mint(env.alice, U256::from(2), 100.into(), None);
env.env.set_caller(env.alice);
env.token.safe_batch_transfer_from(
env.alice,
*receiver.address(),
[U256::one(), U256::from(2)].to_vec(),
[100.into(), 100.into()].to_vec(),
None
);
assert_eq!(env.token.balance_of(env.alice, U256::one()), 0.into());
assert_eq!(
env.token.balance_of(*receiver.address(), U256::one()),
100.into()
);
assert_eq!(env.token.balance_of(env.alice, U256::from(2)), 0.into());
assert_eq!(
env.token.balance_of(*receiver.address(), U256::from(2)),
100.into()
);
env.env.emitted_event(
receiver.address(),
&BatchReceived {
operator: Some(env.alice),
from: Some(env.alice),
token_ids: [U256::one(), U256::from(2)].to_vec(),
amounts: [100.into(), 100.into()].to_vec(),
data: None
}
);
}
#[test]
fn safe_batch_transfer_to_valid_receiver_with_data() {
let mut env = setup();
let receiver = Erc1155ReceiverHostRef::deploy(&env.env, NoArgs);
env.token.mint(env.alice, U256::one(), 100.into(), None);
env.token.mint(env.alice, U256::from(2), 100.into(), None);
env.env.set_caller(env.alice);
env.token.safe_batch_transfer_from(
env.alice,
*receiver.address(),
[U256::one(), U256::from(2)].to_vec(),
[100.into(), 100.into()].to_vec(),
Some(Bytes::from(b"data".to_vec()))
);
assert_eq!(env.token.balance_of(env.alice, U256::one()), 0.into());
assert_eq!(
env.token.balance_of(*receiver.address(), U256::one()),
100.into()
);
assert_eq!(env.token.balance_of(env.alice, U256::from(2)), 0.into());
assert_eq!(
env.token.balance_of(*receiver.address(), U256::from(2)),
100.into()
);
env.env.emitted_event(
receiver.address(),
&BatchReceived {
operator: Some(env.alice),
from: Some(env.alice),
token_ids: [U256::one(), U256::from(2)].to_vec(),
amounts: [100.into(), 100.into()].to_vec(),
data: Some(Bytes::from(b"data".to_vec()))
}
);
}
#[test]
fn safe_batch_transfer_to_invalid_receiver() {
let mut env = setup();
let receiver = WrappedNativeTokenHostRef::deploy(&env.env, NoArgs);
env.token.mint(env.alice, U256::one(), 100.into(), None);
env.token.mint(env.alice, U256::from(2), 100.into(), None);
env.env.set_caller(env.alice);
let err = env
.token
.try_safe_batch_transfer_from(
env.alice,
*receiver.address(),
[U256::one(), U256::from(2)].to_vec(),
[100.into(), 100.into()].to_vec(),
None
)
.unwrap_err();
assert_eq!(
err,
OdraError::VmError(VmError::NoSuchMethod(
"on_erc1155_batch_received".to_string()
))
);
}
}