#![cfg_attr(not(feature = "mock"), no_std)]
#![feature(proc_macro_hygiene)]
extern crate alloc;
extern crate common;
extern crate ontio_std as ostd;
use common::*;
use ostd::abi::{EventBuilder, Sink, Source};
use ostd::database;
use ostd::prelude::*;
use ostd::runtime;
use ostd::types::{Address, U128};
mod basic;
use crate::oep8::{AppMulParam, TrFromMulParam, TrMulParam};
use crate::utils::generate_agent_key;
use basic::*;
use common::CONTRACT_COMMON;
use ostd::runtime::check_witness;
pub mod oep8;
#[cfg(test)]
mod test;
#[cfg(test)]
mod oep8_test;
const KEY_ADMIN: &[u8] = b"03";
const KEY_TT_ID: &[u8] = b"05";
const PRE_TT: &[u8] = b"06";
const PRE_AUTHORIZED: &[u8] = b"07";
const PRE_TOKEN_ID: &[u8] = b"08";
const PRE_TEMPLATE_ID: &[u8] = b"09";
const PRE_AGENT: &[u8] = b"10";
#[cfg(feature = "layer1")]
const PRE_LAYER2: &[u8] = b"L";
pub fn update_admin(new_admin: &Address) -> bool {
let old_admin = get_admin();
assert!(check_witness(&old_admin));
database::put(KEY_ADMIN, new_admin);
true
}
pub fn get_admin() -> Address {
database::get(KEY_ADMIN).unwrap_or(*CONTRACT_COMMON.admin())
}
pub fn generate_dtoken(acc: &Address, token_template_id: &[u8], n: U128) -> Vec<u8> {
generate_dtoken_for_other(acc, acc, token_template_id, n)
}
fn generate_dtoken_inner(
acc: &Address,
to: &Address,
token_template_id: &[u8],
n: U128,
) -> Vec<u8> {
let tt = get_token_template(token_template_id).unwrap();
let token_id =
oep8::generate_token(tt.token_name.as_slice(), tt.token_symbol.as_slice(), n, to);
let key = get_key(PRE_TEMPLATE_ID, token_id.as_slice());
database::put(key.as_slice(), token_template_id);
EventBuilder::new()
.string("generateDToken")
.address(acc)
.address(to)
.bytearray(token_template_id)
.number(n)
.bytearray(token_id.as_slice())
.notify();
token_id
}
pub fn generate_dtoken_for_other(
acc: &Address,
to: &Address,
token_template_id: &[u8],
n: U128,
) -> Vec<u8> {
let caller = runtime::caller();
assert!(is_valid_addr(&[&caller, acc], token_template_id));
assert!(check_witness(acc));
generate_dtoken_inner(acc, to, token_template_id, n)
}
pub fn generate_dtoken_multi(
acc: &Address,
token_template_ids: &[Vec<u8>],
n: U128,
) -> Vec<Vec<u8>> {
token_template_ids
.iter()
.map(|x| generate_dtoken(acc, x, n))
.collect::<Vec<Vec<u8>>>()
}
pub fn get_token_template(token_template_id: &[u8]) -> Option<TokenTemplate> {
let info: Option<TokenTemplateInfo> = database::get(get_key(PRE_TT, token_template_id));
info.map(|data| data.token_template)
}
pub fn create_token_template(creator: &Address, tt: TokenTemplate) -> bool {
assert!(check_witness(creator));
let tt_id = get_next_tt_id();
let tt_id_str = tt_id.to_string();
database::put(
get_key(PRE_TT, tt_id_str.as_bytes()),
TokenTemplateInfo {
creator: creator.clone(),
token_template: tt,
},
);
update_next_tt_id(tt_id + 1);
EventBuilder::new()
.string("createTokenTemplate")
.address(creator)
.bytearray(tt_id_str.as_bytes())
.notify();
true
}
pub fn update_token_template(template_id: &[u8], tt: TokenTemplate) -> bool {
let creator = database::get(get_key(PRE_TT, template_id)).expect("not existed token template");
assert!(check_witness(&creator));
database::put(
get_key(PRE_TT, template_id),
TokenTemplateInfo {
creator,
token_template: tt,
},
);
EventBuilder::new()
.string("updateTokenTemplate")
.bytearray(template_id)
.notify();
true
}
pub fn remove_token_template(token_template_id: &[u8]) -> bool {
assert!(verify_creator_sig(token_template_id));
database::delete(get_key(PRE_TT, token_template_id));
EventBuilder::new()
.string("removeTokenTemplate")
.bytearray(token_template_id)
.notify();
true
}
pub fn verify_creator_sig(token_template_id: &[u8]) -> bool {
let creator =
database::get(get_key(PRE_TT, token_template_id)).expect("not existed token template");
assert!(check_witness(&creator));
true
}
pub fn verify_creator_sig_multi(token_template_ids: &[Vec<u8>]) -> bool {
for token_template_id in token_template_ids.iter() {
assert!(verify_creator_sig(token_template_id));
}
true
}
pub fn authorize_token_template(token_template_id: &[u8], authorized_addr: &[Address]) -> bool {
assert!(verify_creator_sig(token_template_id));
let mut addrs = get_authorized_addr(token_template_id);
for addr in authorized_addr.iter() {
if !addrs.iter().any(|add| add == addr) {
addrs.push(addr.clone());
}
}
let key = get_key(PRE_AUTHORIZED, token_template_id);
database::put(key.as_slice(), addrs);
EventBuilder::new()
.string("authorizeTokenTemplate")
.bytearray(token_template_id)
.address_list(authorized_addr)
.notify();
true
}
pub fn remove_authorize_addr(token_template_id: &[u8], authorized_addr: &[Address]) -> bool {
assert!(verify_creator_sig(token_template_id));
let mut addrs = get_authorized_addr(token_template_id);
for addr in authorized_addr.iter() {
let index = addrs.iter().position(|x| x == addr);
if let Some(ind) = index {
addrs.remove(ind);
}
}
let key = get_key(PRE_AUTHORIZED, token_template_id);
database::put(key.as_slice(), addrs);
EventBuilder::new()
.string("removeAuthorizeAddr")
.bytearray(token_template_id)
.address_list(authorized_addr)
.notify();
true
}
pub fn authorize_token_template_multi(
token_template_ids: &[Vec<u8>],
authorized_addr: &[Address],
) -> bool {
for token_template_id in token_template_ids.iter() {
assert!(authorize_token_template(token_template_id, authorized_addr));
}
true
}
pub fn get_authorized_addr(token_template_id: &[u8]) -> Vec<Address> {
let key = get_key(PRE_AUTHORIZED, token_template_id);
database::get(key.as_slice()).unwrap_or(vec![])
}
fn is_valid_addr(acc: &[&Address], token_template_id: &[u8]) -> bool {
let tt_info = database::get::<_, TokenTemplateInfo>(get_key(PRE_TT, token_template_id))
.expect("not existed token template");
if acc.contains(&&tt_info.creator) {
return true;
}
let authed = get_authorized_addr(token_template_id);
authed.iter().any(|auth| acc.contains(&auth))
}
pub fn get_template_id_by_token_id(token_id: &[u8]) -> Vec<u8> {
let key = get_key(PRE_TEMPLATE_ID, token_id);
database::get::<_, Vec<u8>>(key.as_slice()).unwrap_or(vec![])
}
fn get_key(pre: &[u8], post: &[u8]) -> Vec<u8> {
[pre, post].concat()
}
fn get_next_tt_id() -> U128 {
database::get::<_, U128>(KEY_TT_ID).unwrap_or(0)
}
fn update_next_tt_id(new_id: U128) {
database::put(KEY_TT_ID, new_id)
}
pub fn use_token(account: &Address, token_id: &[u8], n: U128) -> bool {
assert!(check_witness(account));
let ba = oep8::balance_of(account, token_id);
assert!(ba >= n);
oep8::destroy_token(account, token_id, n);
EventBuilder::new()
.string("useToken")
.address(account)
.bytearray(token_id)
.number(n)
.notify();
true
}
pub fn delete_token(account: &Address, token_id: &[u8]) -> bool {
assert!(check_witness(account) || check_witness(CONTRACT_COMMON.admin()));
let template_id = get_template_id_by_token_id(token_id);
oep8::delete_token(token_id);
let key = get_key(PRE_TT, template_id.as_slice());
database::delete(key.as_slice());
let key = get_key(PRE_TOKEN_ID, template_id.as_slice());
database::delete(key.as_slice());
let key = get_key(PRE_TEMPLATE_ID, token_id);
database::delete(key.as_slice());
let key = get_key(PRE_AUTHORIZED, template_id.as_slice());
database::delete(key.as_slice());
EventBuilder::new()
.string("deleteToken")
.address(account)
.bytearray(token_id)
.notify();
true
}
pub fn use_token_by_agent(account: &Address, agent: &Address, token_id: &[u8], n: U128) -> bool {
assert!(check_witness(agent));
let ba = oep8::balance_of(account, token_id);
assert!(ba >= n);
let mut sink = Sink::new(64);
let agent_count =
database::get(generate_agent_key(&mut sink, account, agent, token_id)).unwrap_or(0);
assert!(agent_count >= n);
if agent_count == n {
database::delete(sink.bytes());
} else {
let agent_count = agent_count.checked_sub(n).unwrap();
database::put(sink.bytes(), agent_count);
}
oep8::destroy_token(account, token_id, n);
EventBuilder::new()
.string("useTokenByAgent")
.address(account)
.bytearray(token_id)
.number(n)
.notify();
true
}
pub fn set_agents(
account: &Address,
agents: Vec<Address>,
n: Vec<U128>,
token_ids: Vec<Vec<u8>>,
) -> bool {
assert!(check_witness(account));
for id in token_ids.iter() {
assert!(set_token_agents_inner(
account,
id,
agents.as_slice(),
n.as_slice()
));
}
true
}
pub fn set_token_agents(
account: &Address,
token_id: &[u8],
agents: Vec<Address>,
n: Vec<U128>,
) -> bool {
assert!(check_witness(account));
set_token_agents_inner(account, token_id, agents.as_slice(), n.as_slice())
}
pub fn set_token_agents_inner(
account: &Address,
token_id: &[u8],
agents: &[Address],
n: &[U128],
) -> bool {
let ba = oep8::balance_of(account, token_id);
let sum = n.iter().sum();
assert!(ba >= sum);
let mut sink = Sink::new(64);
for (agent, num) in agents.into_iter().zip(n.iter()) {
sink.clear();
database::put(generate_agent_key(&mut sink, account, agent, token_id), num);
EventBuilder::new()
.string("setTokenAgents")
.address(account)
.bytearray(token_id)
.address(agent)
.number(*num)
.notify();
}
true
}
pub fn get_agent_balance(owner: &Address, agent: &Address, token_id: &[u8]) -> U128 {
let mut sink = Sink::new(64);
database::get(generate_agent_key(&mut sink, owner, agent, token_id)).unwrap_or(0)
}
pub fn add_agents(
account: &Address,
agents: Vec<Address>,
n: Vec<U128>,
token_ids: Vec<Vec<u8>>,
) -> bool {
assert!(check_witness(account));
for id in token_ids.iter() {
assert!(add_token_agents_inner(account, id, &agents, n.as_slice()));
}
true
}
pub fn add_token_agents(
account: &Address,
token_id: &[u8],
agents: &[Address],
n: Vec<U128>,
) -> bool {
assert!(check_witness(account));
add_token_agents_inner(account, token_id, agents, n.as_slice())
}
pub fn add_token_agents_inner(
account: &Address,
token_id: &[u8],
agents: &[Address],
n: &[U128],
) -> bool {
let mut sink = Sink::new(64);
for (agent, &n) in agents.into_iter().zip(n.into_iter()) {
sink.clear();
let ba: u128 =
database::get(generate_agent_key(&mut sink, account, agent, token_id)).unwrap_or(0);
let ba = ba.checked_add(n).unwrap();
database::put(sink.bytes(), ba);
EventBuilder::new()
.string("addTokenAgents")
.address(account)
.bytearray(token_id)
.address(agent)
.number(n)
.notify();
}
true
}
pub fn remove_agents(account: &Address, agents: Vec<Address>, token_ids: Vec<Vec<u8>>) -> bool {
assert!(check_witness(account));
for id in token_ids.iter() {
assert!(remove_token_agents_inner(account, id, agents.as_slice()));
}
true
}
pub fn remove_token_agents(account: &Address, token_id: &[u8], agents: &[Address]) -> bool {
assert!(check_witness(account));
remove_token_agents_inner(account, token_id, agents)
}
pub fn remove_token_agents_inner(account: &Address, token_id: &[u8], agents: &[Address]) -> bool {
let mut sink = Sink::new(64);
for agent in agents.iter() {
sink.clear();
generate_agent_key(&mut sink, account, agent, token_id);
database::delete(sink.bytes());
}
EventBuilder::new()
.string("removeTokenAgents")
.address(account)
.bytearray(token_id)
.notify();
true
}
#[cfg(feature = "layer1")]
pub fn transfer_to_layer2(from: &Address, to: &Address, id: &[u8], amt: u128) -> bool {
let l2id = database::get(PRE_LAYER2).expect("layer2 id is not set!");
oep8::transfer_to_layer2(from, to, id, amt, l2id)
}
#[cfg(feature = "layer1")]
pub fn set_layer2_id(l2id: u128) -> bool {
assert_ne!(l2id, 0);
assert!(check_witness(&get_admin()));
database::put(PRE_LAYER2, l2id);
true
}
#[cfg(feature = "layer1")]
pub fn get_layer2_id() -> u128 {
database::get(PRE_LAYER2).unwrap_or_default()
}
#[cfg(not(feature = "layer1"))]
fn layer_action(action: &[u8], sink: &mut Sink, source: &mut Source) -> bool {
match action {
b"transferFromLayer1" => {
let (from, to, id, amt) = source.read().unwrap();
sink.write(oep8::transfer_from_layer1(from, to, id, amt, &get_admin()));
}
_ => return false,
}
true
}
#[cfg(feature = "layer1")]
fn layer_action(action: &[u8], sink: &mut Sink, source: &mut Source) -> bool {
match action {
b"createTokenTemplate" => {
let (creator, token_template_bs) = source.read().unwrap();
sink.write(create_token_template(creator, token_template_bs));
}
b"getTokenTemplateById" => {
let token_template_id = source.read().unwrap();
sink.write(get_token_template(token_template_id));
}
b"updateTokenTemplate" => {
let (token_template_id, token_template_bs) = source.read().unwrap();
sink.write(update_token_template(token_template_id, token_template_bs));
}
b"removeTokenTemplate" => {
let token_template_id = source.read().unwrap();
sink.write(remove_token_template(token_template_id));
}
b"verifyCreatorSig" => {
let token_template_id = source.read().unwrap();
sink.write(verify_creator_sig(token_template_id));
}
b"verifyCreatorSigMulti" => {
let token_template_ids: Vec<Vec<u8>> = source.read().unwrap();
sink.write(verify_creator_sig_multi(token_template_ids.as_slice()));
}
b"authorizeTokenTemplate" => {
let (token_template_id, authorized_addr): (&[u8], Vec<Address>) =
source.read().unwrap();
sink.write(authorize_token_template(
token_template_id,
authorized_addr.as_slice(),
));
}
b"removeAuthorizeAddr" => {
let (token_template_id, authorized_addr): (&[u8], Vec<Address>) =
source.read().unwrap();
sink.write(remove_authorize_addr(
token_template_id,
authorized_addr.as_slice(),
));
}
b"authorizeTokenTemplateMulti" => {
let (token_template_ids, authorized_addr): (Vec<Vec<u8>>, Vec<Address>) =
source.read().unwrap();
sink.write(authorize_token_template_multi(
token_template_ids.as_slice(),
authorized_addr.as_slice(),
));
}
b"getAuthorizedAddr" => {
let token_template_id = source.read().unwrap();
sink.write(get_authorized_addr(token_template_id));
}
b"getTemplateIdByTokenId" => {
let token_id = source.read().unwrap();
sink.write(get_template_id_by_token_id(token_id));
}
b"generateDToken" => {
let (account, token_template_id, n) = source.read().unwrap();
sink.write(generate_dtoken(account, token_template_id, n));
}
b"generateDTokenForOther" => {
let (account, to, token_template_id, n) = source.read().unwrap();
sink.write(generate_dtoken_for_other(account, to, token_template_id, n));
}
b"generateDTokenMulti" => {
let (account, token_template_ids, n): (&Address, Vec<Vec<u8>>, U128) =
source.read().unwrap();
sink.write(generate_dtoken_multi(
account,
token_template_ids.as_slice(),
n,
));
}
b"deleteToken" => {
let (account, token_id) = source.read().unwrap();
sink.write(delete_token(account, token_id));
}
b"setLayer2Id" => sink.write(set_layer2_id(source.read().unwrap())),
b"transferToLayer2" => {
let (from, to, id, amt) = source.read().unwrap();
sink.write(transfer_to_layer2(from, to, id, amt));
}
_ => return false,
}
true
}
#[no_mangle]
pub fn invoke() {
let input = runtime::input();
let mut source = Source::new(&input);
let action: &[u8] = source.read().unwrap();
let mut sink = Sink::new(12);
let handled = layer_action(action, &mut sink, &mut source);
if !handled {
match action {
b"updateAdmin" => {
let new_admin = source.read().unwrap();
sink.write(update_admin(&new_admin));
}
b"getAdmin" => {
sink.write(get_admin());
}
b"migrate" => {
let (code, vm_type, name, version, author, email, desc) = source.read().unwrap();
sink.write(
CONTRACT_COMMON.migrate(code, vm_type, name, version, author, email, desc),
);
}
b"getAgentBalance" => {
let (owner, agent, token_id) = source.read().unwrap();
sink.write(get_agent_balance(owner, agent, token_id));
}
b"useToken" => {
let (account, token_id, n) = source.read().unwrap();
sink.write(use_token(account, token_id, n));
}
b"useTokenByAgent" => {
let (account, agent, token_id, n) = source.read().unwrap();
sink.write(use_token_by_agent(account, agent, token_id, n));
}
b"setAgents" => {
let (account, agents, n, token_ids) = source.read().unwrap();
sink.write(set_agents(account, agents, n, token_ids));
}
b"setTokenAgents" => {
let (account, token_id, agents, n) = source.read().unwrap();
sink.write(set_token_agents(account, token_id, agents, n));
}
b"addAgents" => {
let (account, agents, n, token_ids) = source.read().unwrap();
sink.write(add_agents(account, agents, n, token_ids));
}
b"addTokenAgents" => {
let (account, token_id, agents, n): (&Address, &[u8], Vec<Address>, Vec<U128>) =
source.read().unwrap();
sink.write(add_token_agents(account, token_id, agents.as_slice(), n));
}
b"removeAgents" => {
let (account, agents, token_ids) = source.read().unwrap();
sink.write(remove_agents(account, agents, token_ids));
}
b"removeTokenAgents" => {
let (account, token_id, agents): (&Address, &[u8], Vec<Address>) =
source.read().unwrap();
sink.write(remove_token_agents(account, token_id, agents.as_slice()));
}
b"transfer" => {
let (from, to, token_id, n) = source.read().unwrap();
sink.write(oep8::transfer(from, to, token_id, n));
}
b"transferMulti" => {
let param: Vec<TrMulParam> = source.read().unwrap();
sink.write(oep8::transfer_multi(param.as_slice()));
}
b"name" => {
let token_id = source.read().unwrap();
sink.write(oep8::name(token_id));
}
b"symbol" => {
let token_id = source.read().unwrap();
sink.write(oep8::symbol(token_id));
}
b"totalSupply" => {
let token_id = source.read().unwrap();
sink.write(oep8::total_supply(token_id));
}
b"balanceOf" => {
let (addr, token_id) = source.read().unwrap();
sink.write(oep8::balance_of(addr, token_id));
}
b"approve" => {
let (owner, spender, token_id, amt) = source.read().unwrap();
sink.write(oep8::approve(owner, spender, token_id, amt));
}
b"approveMulti" => {
let args: Vec<AppMulParam> = source.read().unwrap();
sink.write(oep8::approve_multi(args.as_slice()));
}
b"allowance" => {
let (owner, spender, token_id) = source.read().unwrap();
sink.write(oep8::allowance(owner, spender, token_id));
}
b"transferFrom" => {
let (spender, from, to, token_id, amt) = source.read().unwrap();
sink.write(oep8::transfer_from(spender, from, to, token_id, amt));
}
b"transferFromMulti" => {
let args: Vec<TrFromMulParam> = source.read().unwrap();
sink.write(oep8::transfer_from_multi(args.as_slice()));
}
_ => {
let method = str::from_utf8(action).ok().unwrap();
panic!("dtoken contract, not support method:{}", method)
}
}
}
runtime::ret(sink.bytes());
}
mod utils {
use super::*;
pub fn generate_agent_key<'a>(
sink: &'a mut Sink,
owner: &Address,
agent: &Address,
token_id: &[u8],
) -> &'a [u8] {
sink.write(PRE_AGENT);
sink.write(owner);
sink.write(agent);
sink.write(token_id);
sink.bytes()
}
}