use std::marker::PhantomData;
use frc46_token::token::types::{
BurnFromParams, BurnFromReturn, BurnParams, BurnReturn, DecreaseAllowanceParams,
GetAllowanceParams, IncreaseAllowanceParams, MintReturn, RevokeAllowanceParams,
TransferFromParams, TransferFromReturn, TransferParams, TransferReturn,
};
use frc46_token::token::{Token, TokenError, TOKEN_PRECISION};
use fvm_actor_utils::messaging::{Messaging, MessagingError};
use fvm_ipld_blockstore::Blockstore;
use fvm_ipld_encoding::RawBytes;
use fvm_shared::address::Address;
use fvm_shared::bigint::BigInt;
use fvm_shared::econ::TokenAmount;
use fvm_shared::error::{ErrorNumber, ExitCode};
use fvm_shared::receipt::Receipt;
use fvm_shared::{ActorID, MethodNum, METHOD_CONSTRUCTOR, METHOD_SEND};
use lazy_static::lazy_static;
use log::info;
use num_derive::FromPrimitive;
use num_traits::{FromPrimitive, Zero};
use fil_actors_runtime::cbor::serialize;
use fil_actors_runtime::runtime::{ActorCode, Runtime};
use fil_actors_runtime::{
actor_error, cbor, ActorContext, ActorError, AsActorError, SYSTEM_ACTOR_ADDR,
};
pub use self::state::State;
pub use self::types::*;
#[cfg(feature = "fil-actor")]
fil_actors_runtime::wasm_trampoline!(Actor);
mod state;
pub mod testing;
mod types;
pub const DATACAP_GRANULARITY: u64 = TOKEN_PRECISION as u64;
lazy_static! {
static ref INFINITE_ALLOWANCE: TokenAmount = TokenAmount::from_atto(
BigInt::from(TOKEN_PRECISION)
* BigInt::from(1_000_000_000_000_000_000_000_i128)
);
}
#[derive(FromPrimitive)]
#[repr(u64)]
pub enum Method {
Constructor = METHOD_CONSTRUCTOR,
Mint = 2,
Destroy = 3,
Name = 10,
Symbol = 11,
TotalSupply = 12,
BalanceOf = 13,
Transfer = 14,
TransferFrom = 15,
IncreaseAllowance = 16,
DecreaseAllowance = 17,
RevokeAllowance = 18,
Burn = 19,
BurnFrom = 20,
}
pub struct Actor;
impl Actor {
pub fn constructor<BS, RT>(rt: &mut RT, governor: Address) -> Result<(), ActorError>
where
BS: Blockstore,
RT: Runtime<BS>,
{
rt.validate_immediate_caller_is(std::iter::once(&SYSTEM_ACTOR_ADDR))?;
rt.resolve_address(&governor)
.ok_or_else(|| actor_error!(illegal_argument, "failed to resolve governor address"))?;
let st = State::new(rt.store(), governor).context("failed to create datacap state")?;
rt.create(&st)?;
Ok(())
}
pub fn name<BS, RT>(_: &RT, _: ()) -> Result<String, ActorError>
where
BS: Blockstore,
RT: Runtime<BS>,
{
Ok("DataCap".to_string())
}
pub fn symbol<BS, RT>(_: &RT, _: ()) -> Result<String, ActorError>
where
BS: Blockstore,
RT: Runtime<BS>,
{
Ok("DCAP".to_string())
}
pub fn total_supply<BS, RT>(rt: &mut RT, _: ()) -> Result<TokenAmount, ActorError>
where
BS: Blockstore,
RT: Runtime<BS>,
{
rt.validate_immediate_caller_accept_any()?;
let mut st: State = rt.state()?;
let msg = Messenger { rt, dummy: Default::default() };
let token = as_token(&mut st, &msg);
Ok(token.total_supply())
}
pub fn balance_of<BS, RT>(rt: &mut RT, address: Address) -> Result<TokenAmount, ActorError>
where
BS: Blockstore,
RT: Runtime<BS>,
{
rt.validate_immediate_caller_accept_any()?;
let mut st: State = rt.state()?;
let msg = Messenger { rt, dummy: Default::default() };
let token = as_token(&mut st, &msg);
token.balance_of(&address).actor_result()
}
pub fn allowance<BS, RT>(
rt: &mut RT,
params: GetAllowanceParams,
) -> Result<TokenAmount, ActorError>
where
BS: Blockstore,
RT: Runtime<BS>,
{
rt.validate_immediate_caller_accept_any()?;
let mut st: State = rt.state()?;
let msg = Messenger { rt, dummy: Default::default() };
let token = as_token(&mut st, &msg);
token.allowance(¶ms.owner, ¶ms.operator).actor_result()
}
pub fn mint<BS, RT>(rt: &mut RT, params: MintParams) -> Result<MintReturn, ActorError>
where
BS: Blockstore,
RT: Runtime<BS>,
{
let mut hook = rt
.transaction(|st: &mut State, rt| {
rt.validate_immediate_caller_is(std::iter::once(&st.governor))?;
let operator = st.governor;
let msg = Messenger { rt, dummy: Default::default() };
let mut token = as_token(st, &msg);
let ret = token
.mint(
&operator,
¶ms.to,
¶ms.amount,
RawBytes::default(),
RawBytes::default(),
)
.actor_result();
for delegate in ¶ms.operators {
token
.set_allowance(¶ms.to, delegate, &INFINITE_ALLOWANCE)
.actor_result()?;
}
ret
})
.context("state transaction failed")?;
let mut st: State = rt.state()?;
let msg = Messenger { rt, dummy: Default::default() };
let intermediate = hook.call(&&msg).actor_result()?;
as_token(&mut st, &msg).mint_return(intermediate).actor_result()
}
pub fn destroy<BS, RT>(rt: &mut RT, params: DestroyParams) -> Result<BurnReturn, ActorError>
where
BS: Blockstore,
RT: Runtime<BS>,
{
rt.transaction(|st: &mut State, rt| {
rt.validate_immediate_caller_is(std::iter::once(&st.governor))?;
let msg = Messenger { rt, dummy: Default::default() };
let mut token = as_token(st, &msg);
token.burn(¶ms.owner, ¶ms.amount).actor_result()
})
.context("state transaction failed")
}
pub fn transfer<BS, RT>(
rt: &mut RT,
params: TransferParams,
) -> Result<TransferReturn, ActorError>
where
BS: Blockstore,
RT: Runtime<BS>,
{
rt.validate_immediate_caller_accept_any()?;
let operator = &rt.message().caller();
let from = operator;
let to = rt
.resolve_address(¶ms.to)
.context_code(ExitCode::USR_ILLEGAL_ARGUMENT, "to must be ID address")?;
let to_address = Address::new_id(to);
let mut hook = rt
.transaction(|st: &mut State, rt| {
let allowed = to_address == st.governor || *from == st.governor;
if !allowed {
return Err(actor_error!(
forbidden,
"transfer not allowed from {} to {} (governor is {})",
from,
to_address,
st.governor
));
}
let msg = Messenger { rt, dummy: Default::default() };
let mut token = as_token(st, &msg);
token
.transfer(
from,
&to_address,
¶ms.amount,
params.operator_data.clone(),
RawBytes::default(),
)
.actor_result()
})
.context("state transaction failed")?;
let mut st: State = rt.state()?;
let msg = Messenger { rt, dummy: Default::default() };
let intermediate = hook.call(&&msg).actor_result()?;
as_token(&mut st, &msg).transfer_return(intermediate).actor_result()
}
pub fn transfer_from<BS, RT>(
rt: &mut RT,
params: TransferFromParams,
) -> Result<TransferFromReturn, ActorError>
where
BS: Blockstore,
RT: Runtime<BS>,
{
rt.validate_immediate_caller_accept_any()?;
let operator = rt.message().caller();
let from = params.from;
let to = rt
.resolve_address(¶ms.to)
.context_code(ExitCode::USR_ILLEGAL_ARGUMENT, "to must be an ID address")?;
let to_address = Address::new_id(to);
let mut hook = rt
.transaction(|st: &mut State, rt| {
let allowed = to_address == st.governor;
if !allowed {
return Err(actor_error!(
forbidden,
"transfer not allowed from {} to {} (governor is {})",
from,
to_address,
st.governor
));
}
let msg = Messenger { rt, dummy: Default::default() };
let mut token = as_token(st, &msg);
token
.transfer_from(
&operator,
&from,
&to_address,
¶ms.amount,
params.operator_data.clone(),
RawBytes::default(),
)
.actor_result()
})
.context("state transaction failed")?;
let mut st: State = rt.state()?;
let msg = Messenger { rt, dummy: Default::default() };
let intermediate = hook.call(&&msg).actor_result()?;
as_token(&mut st, &msg).transfer_from_return(intermediate).actor_result()
}
pub fn increase_allowance<BS, RT>(
rt: &mut RT,
params: IncreaseAllowanceParams,
) -> Result<TokenAmount, ActorError>
where
BS: Blockstore,
RT: Runtime<BS>,
{
rt.validate_immediate_caller_accept_any()?;
let owner = rt.message().caller();
let operator = params.operator;
rt.transaction(|st: &mut State, rt| {
let msg = Messenger { rt, dummy: Default::default() };
let mut token = as_token(st, &msg);
token.increase_allowance(&owner, &operator, ¶ms.increase).actor_result()
})
.context("state transaction failed")
}
pub fn decrease_allowance<BS, RT>(
rt: &mut RT,
params: DecreaseAllowanceParams,
) -> Result<TokenAmount, ActorError>
where
BS: Blockstore,
RT: Runtime<BS>,
{
rt.validate_immediate_caller_accept_any()?;
let owner = &rt.message().caller();
let operator = ¶ms.operator;
rt.transaction(|st: &mut State, rt| {
let msg = Messenger { rt, dummy: Default::default() };
let mut token = as_token(st, &msg);
token.decrease_allowance(owner, operator, ¶ms.decrease).actor_result()
})
.context("state transaction failed")
}
pub fn revoke_allowance<BS, RT>(
rt: &mut RT,
params: RevokeAllowanceParams,
) -> Result<TokenAmount, ActorError>
where
BS: Blockstore,
RT: Runtime<BS>,
{
rt.validate_immediate_caller_accept_any()?;
let owner = &rt.message().caller();
let operator = ¶ms.operator;
rt.transaction(|st: &mut State, rt| {
let msg = Messenger { rt, dummy: Default::default() };
let mut token = as_token(st, &msg);
token.revoke_allowance(owner, operator).actor_result()
})
.context("state transaction failed")
}
pub fn burn<BS, RT>(rt: &mut RT, params: BurnParams) -> Result<BurnReturn, ActorError>
where
BS: Blockstore,
RT: Runtime<BS>,
{
rt.validate_immediate_caller_accept_any()?;
let owner = &rt.message().caller();
rt.transaction(|st: &mut State, rt| {
let msg = Messenger { rt, dummy: Default::default() };
let mut token = as_token(st, &msg);
token.burn(owner, ¶ms.amount).actor_result()
})
.context("state transaction failed")
}
pub fn burn_from<BS, RT>(
rt: &mut RT,
params: BurnFromParams,
) -> Result<BurnFromReturn, ActorError>
where
BS: Blockstore,
RT: Runtime<BS>,
{
rt.validate_immediate_caller_accept_any()?;
let operator = &rt.message().caller();
let owner = ¶ms.owner;
rt.transaction(|st: &mut State, rt| {
let msg = Messenger { rt, dummy: Default::default() };
let mut token = as_token(st, &msg);
token.burn_from(operator, owner, ¶ms.amount).actor_result()
})
.context("state transaction failed")
}
}
struct Messenger<'a, BS, RT>
where
BS: Blockstore,
RT: Runtime<BS>,
{
rt: &'a mut RT,
dummy: PhantomData<BS>,
}
impl<'a, BS, RT> Messaging for &Messenger<'a, BS, RT>
where
BS: Blockstore,
RT: Runtime<BS>,
{
fn actor_id(&self) -> ActorID {
self.rt.message().receiver().id().unwrap()
}
fn send(
&self,
to: &Address,
method: MethodNum,
params: &RawBytes,
value: &TokenAmount,
) -> fvm_actor_utils::messaging::Result<Receipt> {
let fake_gas_used = 0;
let res = self.rt.send(to, method, params.clone(), value.clone());
let rec = match res {
Ok(bytes) => {
Receipt { exit_code: ExitCode::OK, return_data: bytes, gas_used: fake_gas_used }
}
Err(ae) => {
info!("datacap messenger failed: {}", ae.msg());
Receipt {
exit_code: ae.exit_code(),
return_data: RawBytes::default(),
gas_used: fake_gas_used,
}
}
};
Ok(rec)
}
fn resolve_id(&self, address: &Address) -> fvm_actor_utils::messaging::Result<ActorID> {
self.rt.resolve_address(address).ok_or(MessagingError::AddressNotInitialized(*address))
}
fn initialize_account(&self, address: &Address) -> fvm_actor_utils::messaging::Result<ActorID> {
let fake_syscall_error_number = ErrorNumber::NotFound;
if self.rt.send(address, METHOD_SEND, Default::default(), TokenAmount::zero()).is_err() {
return Err(MessagingError::Syscall(fake_syscall_error_number));
}
self.resolve_id(address)
}
}
fn as_token<'st, BS, RT>(
st: &'st mut State,
msg: &'st Messenger<'st, BS, RT>,
) -> Token<'st, &'st BS, &'st Messenger<'st, BS, RT>>
where
BS: Blockstore,
RT: Runtime<BS>,
{
Token::wrap(msg.rt.store(), msg, DATACAP_GRANULARITY, &mut st.token)
}
trait AsActorResult<T> {
fn actor_result(self) -> Result<T, ActorError>;
}
impl<T> AsActorResult<T> for Result<T, TokenError> {
fn actor_result(self) -> Result<T, ActorError> {
self.map_err(|e| ActorError::unchecked(ExitCode::from(&e), e.to_string()))
}
}
impl ActorCode for Actor {
fn invoke_method<BS, RT>(
rt: &mut RT,
method: MethodNum,
params: &RawBytes,
) -> Result<RawBytes, ActorError>
where
BS: Blockstore,
RT: Runtime<BS>,
{
match FromPrimitive::from_u64(method) {
Some(Method::Constructor) => {
Self::constructor(rt, cbor::deserialize_params(params)?)?;
Ok(RawBytes::default())
}
Some(Method::Mint) => {
let ret = Self::mint(rt, cbor::deserialize_params(params)?)?;
serialize(&ret, "mint result")
}
Some(Method::Destroy) => {
let ret = Self::destroy(rt, cbor::deserialize_params(params)?)?;
serialize(&ret, "destroy result")
}
Some(Method::Name) => {
let ret = Self::name(rt, cbor::deserialize_params(params)?)?;
serialize(&ret, "name result")
}
Some(Method::Symbol) => {
let ret = Self::symbol(rt, cbor::deserialize_params(params)?)?;
serialize(&ret, "symbol result")
}
Some(Method::TotalSupply) => {
let ret = Self::total_supply(rt, cbor::deserialize_params(params)?)?;
serialize(&ret, "total_supply result")
}
Some(Method::BalanceOf) => {
let ret = Self::balance_of(rt, cbor::deserialize_params(params)?)?;
serialize(&ret, "balance_of result")
}
Some(Method::Transfer) => {
let ret = Self::transfer(rt, cbor::deserialize_params(params)?)?;
serialize(&ret, "transfer result")
}
Some(Method::TransferFrom) => {
let ret = Self::transfer_from(rt, cbor::deserialize_params(params)?)?;
serialize(&ret, "transfer_from result")
}
Some(Method::IncreaseAllowance) => {
let ret = Self::increase_allowance(rt, cbor::deserialize_params(params)?)?;
serialize(&ret, "increase_allowance result")
}
Some(Method::DecreaseAllowance) => {
let ret = Self::decrease_allowance(rt, cbor::deserialize_params(params)?)?;
serialize(&ret, "decrease_allowance result")
}
Some(Method::RevokeAllowance) => {
Self::revoke_allowance(rt, cbor::deserialize_params(params)?)?;
Ok(RawBytes::default())
}
Some(Method::Burn) => {
let ret = Self::burn(rt, cbor::deserialize_params(params)?)?;
serialize(&ret, "burn result")
}
Some(Method::BurnFrom) => {
let ret = Self::burn_from(rt, cbor::deserialize_params(params)?)?;
serialize(&ret, "burn_from result")
}
None => Err(actor_error!(unhandled_message; "Invalid method")),
}
}
}