use borsh::BorshSchema;
use near_vm_logic::types::{AccountId, Balance, Gas, PromiseIndex, PublicKey};
use std::cell::RefCell;
use std::collections::HashMap;
use std::io::{Error, Write};
use std::rc::Rc;
pub enum PromiseAction {
CreateAccount,
DeployContract {
code: Vec<u8>,
},
FunctionCall {
method_name: Vec<u8>,
arguments: Vec<u8>,
amount: Balance,
gas: Gas,
},
Transfer {
amount: Balance,
},
Stake {
amount: Balance,
public_key: PublicKey,
},
AddFullAccessKey {
public_key: PublicKey,
},
AddAccessKey {
public_key: PublicKey,
allowance: Balance,
receiver_id: AccountId,
method_names: Vec<u8>,
},
DeleteKey {
public_key: PublicKey,
},
DeleteAccount {
beneficiary_id: AccountId,
},
}
impl PromiseAction {
pub fn add(&self, promise_index: PromiseIndex) {
use PromiseAction::*;
match self {
CreateAccount => crate::env::promise_batch_action_create_account(promise_index),
DeployContract { code } => {
crate::env::promise_batch_action_deploy_contract(promise_index, &code)
}
FunctionCall { method_name, arguments, amount, gas } => {
crate::env::promise_batch_action_function_call(
promise_index,
&method_name,
&arguments,
*amount,
*gas,
)
}
Transfer { amount } => {
crate::env::promise_batch_action_transfer(promise_index, *amount)
}
Stake { amount, public_key } => {
crate::env::promise_batch_action_stake(promise_index, *amount, public_key)
}
AddFullAccessKey { public_key } => {
crate::env::promise_batch_action_add_key_with_full_access(
promise_index,
public_key,
0,
)
}
AddAccessKey { public_key, allowance, receiver_id, method_names } => {
crate::env::promise_batch_action_add_key_with_function_call(
promise_index,
public_key,
0,
*allowance,
receiver_id,
&method_names,
)
}
DeleteKey { public_key } => {
crate::env::promise_batch_action_delete_key(promise_index, public_key)
}
DeleteAccount { beneficiary_id } => {
crate::env::promise_batch_action_delete_account(promise_index, beneficiary_id)
}
}
}
}
pub struct PromiseSingle {
pub account_id: AccountId,
pub actions: RefCell<Vec<PromiseAction>>,
pub after: RefCell<Option<Promise>>,
pub promise_index: RefCell<Option<PromiseIndex>>,
}
impl PromiseSingle {
pub fn construct_recursively(&self) -> PromiseIndex {
let mut promise_lock = self.promise_index.borrow_mut();
if let Some(res) = promise_lock.as_ref() {
return *res;
}
let promise_index = if let Some(after) = self.after.borrow().as_ref() {
crate::env::promise_batch_then(after.construct_recursively(), &self.account_id)
} else {
crate::env::promise_batch_create(&self.account_id)
};
let actions_lock = self.actions.borrow();
for action in actions_lock.iter() {
action.add(promise_index);
}
*promise_lock = Some(promise_index);
promise_index
}
}
pub struct PromiseJoint {
pub promise_a: Promise,
pub promise_b: Promise,
pub promise_index: RefCell<Option<PromiseIndex>>,
}
impl PromiseJoint {
pub fn construct_recursively(&self) -> PromiseIndex {
let mut promise_lock = self.promise_index.borrow_mut();
if let Some(res) = promise_lock.as_ref() {
return *res;
}
let res = crate::env::promise_and(&[
self.promise_a.construct_recursively(),
self.promise_b.construct_recursively(),
]);
*promise_lock = Some(res);
res
}
}
#[derive(Clone)]
pub struct Promise {
subtype: PromiseSubtype,
should_return: RefCell<bool>,
}
impl BorshSchema for Promise {
fn add_definitions_recursively(
definitions: &mut HashMap<borsh::schema::Declaration, borsh::schema::Definition>,
) {
<()>::add_definitions_recursively(definitions);
}
fn declaration() -> borsh::schema::Declaration {
<()>::declaration()
}
}
#[derive(Clone)]
pub enum PromiseSubtype {
Single(Rc<PromiseSingle>),
Joint(Rc<PromiseJoint>),
}
impl Promise {
pub fn new(account_id: AccountId) -> Self {
Self {
subtype: PromiseSubtype::Single(Rc::new(PromiseSingle {
account_id,
actions: RefCell::new(vec![]),
after: RefCell::new(None),
promise_index: RefCell::new(None),
})),
should_return: RefCell::new(false),
}
}
fn add_action(self, action: PromiseAction) -> Self {
match &self.subtype {
PromiseSubtype::Single(x) => x.actions.borrow_mut().push(action),
PromiseSubtype::Joint(_) => panic!("Cannot add action to a joint promise."),
}
self
}
pub fn create_account(self) -> Self {
self.add_action(PromiseAction::CreateAccount)
}
pub fn deploy_contract(self, code: Vec<u8>) -> Self {
self.add_action(PromiseAction::DeployContract { code })
}
pub fn function_call(
self,
method_name: Vec<u8>,
arguments: Vec<u8>,
amount: Balance,
gas: Gas,
) -> Self {
self.add_action(PromiseAction::FunctionCall { method_name, arguments, amount, gas })
}
pub fn transfer(self, amount: Balance) -> Self {
self.add_action(PromiseAction::Transfer { amount })
}
pub fn stake(self, amount: Balance, public_key: PublicKey) -> Self {
self.add_action(PromiseAction::Stake { amount, public_key })
}
pub fn add_full_access_key(self, public_key: PublicKey) -> Self {
self.add_action(PromiseAction::AddFullAccessKey { public_key })
}
pub fn add_access_key(
self,
public_key: PublicKey,
allowance: Balance,
receiver_id: AccountId,
method_names: Vec<u8>,
) -> Self {
self.add_action(PromiseAction::AddAccessKey {
public_key,
allowance,
receiver_id,
method_names,
})
}
pub fn delete_key(self, public_key: PublicKey) -> Self {
self.add_action(PromiseAction::DeleteKey { public_key })
}
pub fn delete_account(self, beneficiary_id: AccountId) -> Self {
self.add_action(PromiseAction::DeleteAccount { beneficiary_id })
}
pub fn and(self, other: Promise) -> Promise {
Promise {
subtype: PromiseSubtype::Joint(Rc::new(PromiseJoint {
promise_a: self,
promise_b: other,
promise_index: RefCell::new(None),
})),
should_return: RefCell::new(false),
}
}
pub fn then(self, mut other: Promise) -> Promise {
match &mut other.subtype {
PromiseSubtype::Single(x) => *x.after.borrow_mut() = Some(self),
PromiseSubtype::Joint(_) => panic!("Cannot callback joint promise."),
}
other
}
pub fn as_return(self) -> Self {
*self.should_return.borrow_mut() = true;
self
}
fn construct_recursively(&self) -> PromiseIndex {
let res = match &self.subtype {
PromiseSubtype::Single(x) => x.construct_recursively(),
PromiseSubtype::Joint(x) => x.construct_recursively(),
};
if *self.should_return.borrow() {
crate::env::promise_return(res);
}
res
}
}
impl Drop for Promise {
fn drop(&mut self) {
self.construct_recursively();
}
}
impl serde::Serialize for Promise {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
*self.should_return.borrow_mut() = true;
serializer.serialize_unit()
}
}
pub enum PromiseOrValue<T> {
Promise(Promise),
Value(T),
}
impl<T> BorshSchema for PromiseOrValue<T>
where
T: BorshSchema,
{
fn add_definitions_recursively(
definitions: &mut HashMap<borsh::schema::Declaration, borsh::schema::Definition>,
) {
T::add_definitions_recursively(definitions);
}
fn declaration() -> borsh::schema::Declaration {
T::declaration()
}
}
impl<T> From<Promise> for PromiseOrValue<T> {
fn from(promise: Promise) -> Self {
PromiseOrValue::Promise(promise.as_return())
}
}
impl<T: serde::Serialize> serde::Serialize for PromiseOrValue<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
PromiseOrValue::Value(x) => x.serialize(serializer),
PromiseOrValue::Promise(_) => serializer.serialize_unit(),
}
}
}
impl<T: borsh::BorshSerialize> borsh::BorshSerialize for PromiseOrValue<T> {
fn serialize<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
match self {
PromiseOrValue::Value(x) => x.serialize(writer),
PromiseOrValue::Promise(_) => Ok(()),
}
}
}