#![no_std]
elrond_wasm::imports!();
elrond_wasm::derive_imports!();
type SwapOperationType<M> =
MultiValue4<ManagedAddress<M>, ManagedBuffer<M>, TokenIdentifier<M>, BigUint<M>>;
pub const SWAP_TOKENS_FIXED_INPUT_FUNC_NAME: &[u8] = b"swapTokensFixedInput";
pub const SWAP_TOKENS_FIXED_OUTPUT_FUNC_NAME: &[u8] = b"swapTokensFixedOutput";
mod pair {
elrond_wasm::imports!();
#[elrond_wasm::proxy]
pub trait PairProxy {
#[payable("*")]
#[endpoint(swapTokensFixedInput)]
fn swap_tokens_fixed_input(
&self,
token_out: TokenIdentifier,
amount_out_min: BigUint,
);
#[payable("*")]
#[endpoint(swapTokensFixedOutput)]
fn swap_tokens_fixed_output(
&self,
token_out: TokenIdentifier,
amount_out: BigUint,
);
}
}
#[elrond_wasm::module]
pub trait MultiPairSwap {
#[init]
fn init(&self) {
}
#[payable("*")]
#[endpoint(multiPairSwap)]
fn multi_pair_swap(&self, swap_operations: MultiValueEncoded<SwapOperationType<Self::Api>>) {
let (token_id, nonce, amount) = self.call_value().single_esdt().into_tuple();
let swap_fixed_input_endpoint = ManagedBuffer::from(SWAP_TOKENS_FIXED_INPUT_FUNC_NAME);
let swap_fixed_output_endpoint = ManagedBuffer::from(SWAP_TOKENS_FIXED_OUTPUT_FUNC_NAME);
let caller = self.blockchain().get_caller();
let mut payments = ManagedVec::new();
let mut last_payment = OldEsdtTokenPayment::new(token_id, nonce, amount);
for entry in swap_operations.into_iter() {
let (pair_address, function, token_wanted, amount_wanted) = entry.into_tuple();
if function == swap_fixed_input_endpoint {
last_payment = self.actual_swap_fixed_input(
pair_address,
last_payment.token_identifier,
last_payment.amount,
token_wanted,
amount_wanted,
);
} else if function == swap_fixed_output_endpoint {
let (payment, residuum) = self.actual_swap_fixed_output(
pair_address,
last_payment.token_identifier,
last_payment.amount,
token_wanted,
amount_wanted,
);
last_payment = payment;
payments.push(residuum);
} else {
sc_panic!("Invalid function to call");
}
}
payments.push(last_payment);
self.send().direct_multi(&caller, &payments);
}
fn actual_swap_fixed_input(
&self,
pair_address: ManagedAddress,
token_in: TokenIdentifier,
amount_in: BigUint,
token_out: TokenIdentifier,
amount_out_min: BigUint,
) -> OldEsdtTokenPayment<Self::Api> {
self.pair_contract_proxy(pair_address)
.swap_tokens_fixed_input(token_out, amount_out_min)
.add_esdt_token_transfer(token_in, 0, amount_in)
.execute_on_dest_context()
}
fn actual_swap_fixed_output(
&self,
pair_address: ManagedAddress,
token_in: TokenIdentifier,
amount_in_max: BigUint,
token_out: TokenIdentifier,
amount_out: BigUint,
) -> (OldEsdtTokenPayment<Self::Api>, OldEsdtTokenPayment<Self::Api>) {
let call_result: MultiValue2<OldEsdtTokenPayment<Self::Api>, OldEsdtTokenPayment<Self::Api>> =
self.pair_contract_proxy(pair_address)
.swap_tokens_fixed_output(token_out, amount_out)
.add_esdt_token_transfer(token_in, 0, amount_in_max)
.execute_on_dest_context();
call_result.into_tuple()
}
#[proxy]
fn pair_contract_proxy(&self, to: ManagedAddress) -> pair::Proxy<Self::Api>;
}
#[derive(TopDecode, TopEncode, NestedDecode, NestedEncode, TypeAbi, Clone, PartialEq, Debug)]
pub struct OldEsdtTokenPayment<M: ManagedTypeApi> {
pub token_type: EsdtTokenType,
pub token_identifier: TokenIdentifier<M>,
pub token_nonce: u64,
pub amount: BigUint<M>,
}
impl<M: ManagedTypeApi> OldEsdtTokenPayment<M> {
pub fn no_payment(api: M) -> Self {
OldEsdtTokenPayment {
token_type: EsdtTokenType::Invalid,
token_identifier: TokenIdentifier::egld(),
token_nonce: 0,
amount: BigUint::zero(),
}
}
pub fn new(token_identifier: TokenIdentifier<M>, token_nonce: u64, amount: BigUint<M>) -> Self {
let token_type = if amount != 0 && token_identifier.is_valid_esdt_identifier() {
if token_nonce == 0 {
EsdtTokenType::Fungible
} else if amount == 1u64 {
EsdtTokenType::NonFungible
} else {
EsdtTokenType::SemiFungible
}
} else {
EsdtTokenType::Invalid
};
OldEsdtTokenPayment {
token_type,
token_identifier,
token_nonce,
amount,
}
}
}