Skip to content

Commit

Permalink
execute custom user call
Browse files Browse the repository at this point in the history
  • Loading branch information
dorin-iancu committed Oct 12, 2023
1 parent 58978f3 commit 4df7b0e
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 61 deletions.
5 changes: 4 additions & 1 deletion common/transaction/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ pub mod transaction_status;
pub const MIN_BLOCKS_FOR_FINALITY: u64 = 10;
pub const TX_MULTIRESULT_NR_FIELDS: usize = 6;

pub type BatchId = u64;
pub type TxId = u64;
pub type GasLimit = u64;
pub type TxNonce = u64;

pub type BlockNonce = u64;
pub type SenderAddress<M> = ManagedAddress<M>;
pub type ReceiverAddress<M> = ManagedAddress<M>;
Expand All @@ -24,7 +27,7 @@ pub type TxAsMultiValue<M> = MultiValue7<
Option<TransferData<M>>,
>;
pub type PaymentsVec<M> = ManagedVec<M, EsdtTokenPayment<M>>;
pub type TxBatchSplitInFields<M> = MultiValue2<u64, MultiValueEncoded<M, TxAsMultiValue<M>>>;
pub type TxBatchSplitInFields<M> = MultiValue2<BatchId, MultiValueEncoded<M, TxAsMultiValue<M>>>;

#[derive(TopEncode, TopDecode, NestedEncode, NestedDecode, TypeAbi, ManagedVecItem, Clone)]
pub struct TransferData<M: ManagedTypeApi> {
Expand Down
53 changes: 39 additions & 14 deletions common/tx-batch-module/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ multiversx_sc::imports!();
multiversx_sc::derive_imports!();

pub use batch_status::BatchStatus;
use transaction::{Transaction, TxBatchSplitInFields, MIN_BLOCKS_FOR_FINALITY};
use transaction::{
BatchId, GasLimit, Transaction, TxBatchSplitInFields, TxNonce, MIN_BLOCKS_FOR_FINALITY,
};
use tx_batch_mapper::TxBatchMapper;

pub const FIRST_BATCH_ID: BatchId = 1;
pub const MAX_GAS_LIMIT_PER_BATCH: GasLimit = 500_000_000;

pub mod batch_status;
pub mod tx_batch_mapper;

Expand Down Expand Up @@ -65,7 +70,7 @@ pub trait TxBatchModule {
}

#[view(getBatch)]
fn get_batch(&self, batch_id: u64) -> OptionalValue<TxBatchSplitInFields<Self::Api>> {
fn get_batch(&self, batch_id: BatchId) -> OptionalValue<TxBatchSplitInFields<Self::Api>> {
let tx_batch = self.pending_batches(batch_id);
if tx_batch.is_empty() {
return OptionalValue::None;
Expand All @@ -80,7 +85,7 @@ pub trait TxBatchModule {
}

#[view(getBatchStatus)]
fn get_batch_status(&self, batch_id: u64) -> BatchStatus<Self::Api> {
fn get_batch_status(&self, batch_id: BatchId) -> BatchStatus<Self::Api> {
let first_batch_id = self.first_batch_id().get();
if batch_id < first_batch_id {
return BatchStatus::AlreadyProcessed;
Expand Down Expand Up @@ -115,17 +120,34 @@ pub trait TxBatchModule {

// private

fn add_to_batch(&self, transaction: Transaction<Self::Api>) -> u64 {
fn add_to_batch(
&self,
transaction: Transaction<Self::Api>,
default_gas_limit: GasLimit,
) -> BatchId {
let first_batch_id = self.first_batch_id().get();
let last_batch_id = self.last_batch_id().get();
let mut last_batch = self.pending_batches(last_batch_id);

if self.is_batch_full(&last_batch, last_batch_id, first_batch_id) {
let gas_cost = match &transaction.opt_transfer_data {
Some(transfer_data) => transfer_data.gas_limit,
None => transaction.tokens.len() as u64 * default_gas_limit,
};

let gas_cost_mapper = self.total_gas_cost(last_batch_id);
let last_batch_total_gas_cost = gas_cost_mapper.get();
let new_total_gas_cost = last_batch_total_gas_cost + gas_cost;

if self.is_batch_full(&last_batch, last_batch_id, first_batch_id)
|| new_total_gas_cost > MAX_GAS_LIMIT_PER_BATCH
{
let (new_batch_id, _) = self.create_new_batch(transaction);
self.total_gas_cost(new_batch_id).set(gas_cost);

new_batch_id
} else {
last_batch.push(transaction);
gas_cost_mapper.set(new_total_gas_cost);

last_batch_id
}
Expand All @@ -135,7 +157,7 @@ pub trait TxBatchModule {
fn add_multiple_tx_to_batch(
&self,
transactions: &ManagedVec<Transaction<Self::Api>>,
) -> ManagedVec<u64> {
) -> ManagedVec<BatchId> {
if transactions.is_empty() {
return ManagedVec::new();
}
Expand All @@ -161,7 +183,7 @@ pub trait TxBatchModule {
fn create_new_batch(
&self,
transaction: Transaction<Self::Api>,
) -> (u64, TxBatchMapper<Self::Api>) {
) -> (BatchId, TxBatchMapper<Self::Api>) {
let last_batch_id = self.last_batch_id().get();
let new_batch_id = last_batch_id + 1;

Expand All @@ -176,8 +198,8 @@ pub trait TxBatchModule {
fn is_batch_full(
&self,
tx_batch: &TxBatchMapper<Self::Api>,
batch_id: u64,
first_batch_id: u64,
batch_id: BatchId,
first_batch_id: BatchId,
) -> bool {
if tx_batch.is_empty() {
return false;
Expand Down Expand Up @@ -242,7 +264,7 @@ pub trait TxBatchModule {
mapper.clear();
}

fn get_and_save_next_tx_id(&self) -> u64 {
fn get_and_save_next_tx_id(&self) -> TxNonce {
self.last_tx_nonce().update(|last_tx_nonce| {
*last_tx_nonce += 1;
*last_tx_nonce
Expand All @@ -253,17 +275,20 @@ pub trait TxBatchModule {

#[view(getFirstBatchId)]
#[storage_mapper("firstBatchId")]
fn first_batch_id(&self) -> SingleValueMapper<u64>;
fn first_batch_id(&self) -> SingleValueMapper<BatchId>;

#[view(getLastBatchId)]
#[storage_mapper("lastBatchId")]
fn last_batch_id(&self) -> SingleValueMapper<u64>;
fn last_batch_id(&self) -> SingleValueMapper<BatchId>;

#[storage_mapper("pendingBatches")]
fn pending_batches(&self, batch_id: u64) -> TxBatchMapper<Self::Api>;
fn pending_batches(&self, batch_id: BatchId) -> TxBatchMapper<Self::Api>;

#[storage_mapper("totalGasCost")]
fn total_gas_cost(&self, batch_id: BatchId) -> SingleValueMapper<GasLimit>;

#[storage_mapper("lastTxNonce")]
fn last_tx_nonce(&self) -> SingleValueMapper<u64>;
fn last_tx_nonce(&self) -> SingleValueMapper<TxNonce>;

// configurable

Expand Down
52 changes: 32 additions & 20 deletions esdt-safe/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
multiversx_sc::imports!();
multiversx_sc::derive_imports!();

use transaction::{transaction_status::TransactionStatus, Transaction, TransferData};
use transaction::{
transaction_status::TransactionStatus, BatchId, GasLimit, Transaction, TransferData, TxId,
};
use tx_batch_module::FIRST_BATCH_ID;

const DEFAULT_MAX_TX_BATCH_SIZE: usize = 10;
const DEFAULT_MAX_TX_BATCH_BLOCK_DURATION: u64 = 100; // ~10 minutes
const MAX_TRANSFERS_PER_TX: usize = 10;
const MAX_GAS_LIMIT: u64 = 300_000_000;

const MAX_USER_TX_GAS_LIMIT: GasLimit = 300_000_000;

#[derive(TopEncode, TopDecode, NestedEncode, NestedDecode, ManagedVecItem)]
pub struct NonceAmountPair<M: ManagedTypeApi> {
Expand All @@ -28,17 +32,16 @@ pub trait EsdtSafe:
/// In case of SC gas limits, this value is provided by the user
/// Will be used to compute the fees for the transfer
#[init]
fn init(&self, sovereign_tx_gas_limit: BigUint) {
self.sovereign_tx_gas_limit().set(&sovereign_tx_gas_limit);
fn init(&self, sovereign_tx_gas_limit: GasLimit) {
self.sovereign_tx_gas_limit().set(sovereign_tx_gas_limit);

self.max_tx_batch_size()
.set_if_empty(DEFAULT_MAX_TX_BATCH_SIZE);
self.max_tx_batch_size().set(DEFAULT_MAX_TX_BATCH_SIZE);
self.max_tx_batch_block_duration()
.set_if_empty(DEFAULT_MAX_TX_BATCH_BLOCK_DURATION);
.set(DEFAULT_MAX_TX_BATCH_BLOCK_DURATION);

// batch ID 0 is considered invalid
self.first_batch_id().set_if_empty(1);
self.last_batch_id().set_if_empty(1);
self.first_batch_id().set(FIRST_BATCH_ID);
self.last_batch_id().set(FIRST_BATCH_ID);

self.set_paused(true);
}
Expand All @@ -54,7 +57,7 @@ pub trait EsdtSafe:
#[endpoint(setTransactionBatchStatus)]
fn set_transaction_batch_status(
&self,
batch_id: u64,
batch_id: BatchId,
tx_statuses: MultiValueEncoded<TransactionStatus>,
) {
let first_batch_id = self.first_batch_id().get();
Expand Down Expand Up @@ -82,7 +85,7 @@ pub trait EsdtSafe:
// tokens will remain locked forever in that case
// otherwise, the whole batch would fail
for token in &tx.tokens {
if self.is_local_role_set(&token.token_identifier, &EsdtLocalRole::Burn) {
if self.is_burn_role_set(&token) {
self.send().esdt_local_burn(
&token.token_identifier,
token.token_nonce,
Expand Down Expand Up @@ -164,7 +167,7 @@ pub trait EsdtSafe:

if let OptionalValue::Some(transfer_data) = &opt_transfer_data {
require!(
transfer_data.gas_limit <= MAX_GAS_LIMIT,
transfer_data.gas_limit <= MAX_USER_TX_GAS_LIMIT,
"Gas limit too high"
);
}
Expand Down Expand Up @@ -201,7 +204,8 @@ pub trait EsdtSafe:
is_refund_tx: false,
};

let batch_id = self.add_to_batch(tx);
let default_gas_cost = self.sovereign_tx_gas_limit().get();
let batch_id = self.add_to_batch(tx, default_gas_cost);
self.create_transaction_event(batch_id, tx_nonce);
}

Expand Down Expand Up @@ -265,24 +269,32 @@ pub trait EsdtSafe:
});
}

fn is_burn_role_set(&self, payment: &EsdtTokenPayment) -> bool {
if payment.token_nonce == 0 {
self.is_local_role_set(&payment.token_identifier, &EsdtLocalRole::Burn)
} else {
self.is_local_role_set(&payment.token_identifier, &EsdtLocalRole::NftBurn)
}
}

// events

#[event("createTransactionEvent")]
fn create_transaction_event(&self, #[indexed] batch_id: u64, #[indexed] tx_id: u64);
fn create_transaction_event(&self, #[indexed] batch_id: BatchId, #[indexed] tx_id: TxId);

#[event("addRefundTransactionEvent")]
fn add_refund_transaction_event(
&self,
#[indexed] batch_id: u64,
#[indexed] tx_id: u64,
#[indexed] original_tx_id: u64,
#[indexed] batch_id: BatchId,
#[indexed] tx_id: TxId,
#[indexed] original_tx_id: TxId,
);

#[event("setStatusEvent")]
fn set_status_event(
&self,
#[indexed] batch_id: u64,
#[indexed] tx_id: u64,
#[indexed] batch_id: BatchId,
#[indexed] tx_id: TxId,
#[indexed] tx_status: TransactionStatus,
);

Expand All @@ -297,5 +309,5 @@ pub trait EsdtSafe:

#[view(getSovereignTxGasLimit)]
#[storage_mapper("sovereignTxGasLimit")]
fn sovereign_tx_gas_limit(&self) -> SingleValueMapper<BigUint>;
fn sovereign_tx_gas_limit(&self) -> SingleValueMapper<GasLimit>;
}
1 change: 1 addition & 0 deletions multi-transfer-esdt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ path = "../common/max-bridged-amount-module"

[dependencies.multiversx-sc]
version = "0.43.4"
features = ["promises"]

[dev-dependencies.multiversx-sc-scenario]
version = "0.43.4"
Loading

0 comments on commit 4df7b0e

Please sign in to comment.