From 45f4d88a58915d0c20cde44798ee8c0803157b96 Mon Sep 17 00:00:00 2001 From: Itshypen <075bct064.ranju@pcampus.edu.np> Date: Wed, 13 Nov 2024 23:13:45 +0545 Subject: [PATCH] feat: sicx-baln implementation --- contracts/balanced_dollar/Cargo.toml | 6 +- contracts/balanced_dollar/src/allowance.rs | 63 --- contracts/balanced_dollar/src/balance.rs | 35 -- .../balanced_dollar/src/balanced_dollar.rs | 164 -------- contracts/balanced_dollar/src/contract.rs | 150 ------- contracts/balanced_dollar/src/errors.rs | 19 - contracts/balanced_dollar/src/lib.rs | 103 ++++- contracts/balanced_dollar/src/metadata.rs | 22 - .../balanced_dollar/src/storage_types.rs | 87 ---- .../src/tests/balanced_dollar_test.rs | 3 +- contracts/balanced_dollar/src/tests/setup.rs | 4 +- .../src/xcall_manager_interface.rs | 10 - contracts/balanced_token/Cargo.toml | 2 +- contracts/balanced_token/src/lib.rs | 80 +--- contracts/sicx/Cargo.toml | 19 + contracts/sicx/src/lib.rs | 97 +++++ libs/spoke_token/Cargo.toml | 2 +- libs/spoke_token/src/contract.rs | 151 ------- libs/spoke_token/src/lib.rs | 6 +- .../src/tests/balanced_dollar_test.rs | 378 ------------------ libs/spoke_token/src/tests/mod.rs | 2 - libs/spoke_token/src/tests/setup.rs | 153 ------- libs/spoke_token/src/token_lib.rs | 148 ++++++- 23 files changed, 380 insertions(+), 1324 deletions(-) delete mode 100644 contracts/balanced_dollar/src/allowance.rs delete mode 100644 contracts/balanced_dollar/src/balance.rs delete mode 100644 contracts/balanced_dollar/src/balanced_dollar.rs delete mode 100644 contracts/balanced_dollar/src/contract.rs delete mode 100644 contracts/balanced_dollar/src/errors.rs delete mode 100644 contracts/balanced_dollar/src/metadata.rs delete mode 100644 contracts/balanced_dollar/src/storage_types.rs delete mode 100644 contracts/balanced_dollar/src/xcall_manager_interface.rs create mode 100644 contracts/sicx/Cargo.toml create mode 100644 contracts/sicx/src/lib.rs delete mode 100644 libs/spoke_token/src/contract.rs delete mode 100644 libs/spoke_token/src/tests/balanced_dollar_test.rs delete mode 100644 libs/spoke_token/src/tests/mod.rs delete mode 100644 libs/spoke_token/src/tests/setup.rs diff --git a/contracts/balanced_dollar/Cargo.toml b/contracts/balanced_dollar/Cargo.toml index efa6b9f..5e6ba3c 100644 --- a/contracts/balanced_dollar/Cargo.toml +++ b/contracts/balanced_dollar/Cargo.toml @@ -12,7 +12,9 @@ testutils = ["soroban-sdk/testutils"] [dependencies] soroban-sdk = { workspace = true } soroban-token-sdk = { version = "21.6.0" } -soroban-rlp = { path = "../../libs/soroban-rlp" } +spoke-token = { path = "../../libs/spoke_token" } + [dev-dependencies] -soroban-sdk = { workspace = true, features = ["testutils"] } \ No newline at end of file +soroban-sdk = { workspace = true, features = ["testutils"] } +soroban-rlp = { path = "../../libs/soroban-rlp" } \ No newline at end of file diff --git a/contracts/balanced_dollar/src/allowance.rs b/contracts/balanced_dollar/src/allowance.rs deleted file mode 100644 index ad74687..0000000 --- a/contracts/balanced_dollar/src/allowance.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::storage_types::{AllowanceDataKey, AllowanceValue, DataKey}; -use soroban_sdk::{Address, Env}; - -pub fn read_allowance(e: &Env, from: Address, spender: Address) -> AllowanceValue { - let key = DataKey::Allowance(AllowanceDataKey { from, spender }); - if let Some(allowance) = e.storage().temporary().get::<_, AllowanceValue>(&key) { - if allowance.expiration_ledger < e.ledger().sequence() { - AllowanceValue { - amount: 0, - expiration_ledger: allowance.expiration_ledger, - } - } else { - allowance - } - } else { - AllowanceValue { - amount: 0, - expiration_ledger: 0, - } - } -} - -pub fn write_allowance( - e: &Env, - from: Address, - spender: Address, - amount: i128, - expiration_ledger: u32, -) { - let allowance = AllowanceValue { - amount, - expiration_ledger, - }; - - if amount > 0 && expiration_ledger < e.ledger().sequence() { - panic!("expiration_ledger is less than ledger seq when amount > 0") - } - - let key = DataKey::Allowance(AllowanceDataKey { from, spender }); - e.storage().temporary().set(&key.clone(), &allowance); - - if amount > 0 { - let live_for = expiration_ledger - .checked_sub(e.ledger().sequence()) - .unwrap(); - - e.storage().temporary().extend_ttl(&key, live_for, live_for) - } -} - -pub fn spend_allowance(e: &Env, from: Address, spender: Address, amount: i128) { - let allowance = read_allowance(e, from.clone(), spender.clone()); - if allowance.amount < amount { - panic!("insufficient allowance"); - } - write_allowance( - e, - from, - spender, - allowance.amount - amount, - allowance.expiration_ledger, - ); -} diff --git a/contracts/balanced_dollar/src/balance.rs b/contracts/balanced_dollar/src/balance.rs deleted file mode 100644 index 66184d4..0000000 --- a/contracts/balanced_dollar/src/balance.rs +++ /dev/null @@ -1,35 +0,0 @@ -use crate::storage_types::{DataKey, BALANCE_BUMP_AMOUNT, BALANCE_LIFETIME_THRESHOLD}; -use soroban_sdk::{Address, Env}; - -pub fn read_balance(e: &Env, addr: Address) -> i128 { - let key = DataKey::Balance(addr); - if let Some(balance) = e.storage().persistent().get::(&key) { - e.storage() - .persistent() - .extend_ttl(&key, BALANCE_LIFETIME_THRESHOLD, BALANCE_BUMP_AMOUNT); - balance - } else { - 0 - } -} - -fn write_balance(e: &Env, addr: Address, amount: i128) { - let key = DataKey::Balance(addr); - e.storage().persistent().set(&key, &amount); - e.storage() - .persistent() - .extend_ttl(&key, BALANCE_LIFETIME_THRESHOLD, BALANCE_BUMP_AMOUNT); -} - -pub fn receive_balance(e: &Env, addr: Address, amount: i128) { - let balance = read_balance(e, addr.clone()); - write_balance(e, addr, balance + amount); -} - -pub fn spend_balance(e: &Env, addr: Address, amount: i128) { - let balance = read_balance(e, addr.clone()); - if balance < amount { - panic!("insufficient balance"); - } - write_balance(e, addr, balance - amount); -} \ No newline at end of file diff --git a/contracts/balanced_dollar/src/balanced_dollar.rs b/contracts/balanced_dollar/src/balanced_dollar.rs deleted file mode 100644 index 895dd21..0000000 --- a/contracts/balanced_dollar/src/balanced_dollar.rs +++ /dev/null @@ -1,164 +0,0 @@ -use crate::balance::{receive_balance, spend_balance}; -use crate::storage_types::{get_icon_bnusd, get_xcall, get_xcall_manager}; -use soroban_sdk::{xdr::ToXdr, Address, Bytes, Env, String, Vec}; -mod xcall { - soroban_sdk::contractimport!(file = "../../wasm/xcall.wasm"); -} - -use crate::contract; -use crate::errors::ContractError; -use crate::xcall_manager_interface::XcallManagerClient; -use soroban_rlp::balanced::address_utils::is_valid_bytes_address; -use soroban_rlp::balanced::messages::{ - cross_transfer::CrossTransfer, cross_transfer_revert::CrossTransferRevert, -}; -use soroban_token_sdk::TokenUtils; -use xcall::{AnyMessage, CallMessageWithRollback, Client, Envelope}; -const CROSS_TRANSFER: &str = "xCrossTransfer"; -const CROSS_TRANSFER_REVERT: &str = "xCrossTransferRevert"; - - -pub fn _cross_transfer( - e: Env, - from: Address, - amount: u128, - to: String, - data: Bytes, -) -> Result<(), ContractError> { - if amount <= i128::MAX as u128 { - _burn(&e, from.clone(), amount as i128); - }else{ - return Err(ContractError::InvalidAmount); - } - let xcall_message = CrossTransfer::new(from.clone().to_string(), to, amount, data); - let rollback = CrossTransferRevert::new(from.clone(), amount); - let icon_bn_usd = get_icon_bnusd(&e)?; - - let rollback_bytes = rollback.encode(&e, String::from_str(&e, CROSS_TRANSFER_REVERT)); - let message_bytes = xcall_message.encode(&e, String::from_str(&e, CROSS_TRANSFER)); - - let (sources, destinations) = xcall_manager_client(&e, &get_xcall_manager(&e)?).get_protocols(); - - let message = AnyMessage::CallMessageWithRollback(CallMessageWithRollback { - data: message_bytes, - rollback: rollback_bytes, - }); - let envelope: &Envelope = &Envelope { - message, - sources, - destinations, - }; - - let current_address = e.current_contract_address(); - xcall_client(&e, &get_xcall(&e)?).send_call(&from, ¤t_address, envelope, &icon_bn_usd); - Ok(()) -} - -fn verify_protocol( - e: &Env, - xcall_manager: &Address, - protocols: Vec, -) -> Result<(), ContractError> { - let verified: bool = xcall_manager_client(e, xcall_manager).verify_protocols(&protocols); - if !verified { - return Err(ContractError::ProtocolMismatch); - } - Ok(()) -} - -pub fn _handle_call_message( - e: Env, - from: String, - data: Bytes, - protocols: Vec, -) -> Result<(), ContractError> { - let xcall = get_xcall(&e)?; - xcall.require_auth(); - - let method = CrossTransfer::get_method(&e, data.clone()); - let icon_bn_usd: String = get_icon_bnusd(&e)?; - if method == String::from_str(&e, &CROSS_TRANSFER) { - if from != icon_bn_usd { - return Err(ContractError::OnlyIconBnUSD); - } - let message = CrossTransfer::decode(&e, data); - let to_network_address: Address = get_address(message.to, &e)?; - if message.amount <= i128::MAX as u128 { - _mint(&e, to_network_address, message.amount as i128); - }else{ - return Err(ContractError::InvalidAmount); - } - } else if method == String::from_str(&e, &CROSS_TRANSFER_REVERT) { - let xcall_network_address = xcall_client(&e, &xcall).get_network_address(); - if xcall_network_address != from { - return Err(ContractError::OnlyCallService); - } - let message = CrossTransferRevert::decode(&e, data); - if message.amount <= i128::MAX as u128 { - _mint(&e, message.to, message.amount as i128); - }else{ - return Err(ContractError::InvalidAmount); - } - } else { - return Err(ContractError::UnknownMessageType); - } - verify_protocol(&e, &get_xcall_manager(&e)?, protocols)?; - Ok(()) -} - -pub fn get_address(network_address: String, env: &Env) -> Result { - let bytes = network_address.to_xdr(&env); - - if bytes.get(6).unwrap() > 0 { - return Err(ContractError::InvalidNetworkAddressLength); - } - - let value_len = bytes.get(7).unwrap(); - let slice = bytes.slice(8..value_len as u32 + 8); - let mut nid = Bytes::new(&env); - let mut account = Bytes::new(&env); - - let mut has_seperator = false; - for (index, value) in slice.clone().iter().enumerate() { - if has_seperator { - account.append(&slice.slice(index as u32..slice.len())); - break; - } else if value == 47 { - has_seperator = true; - } else { - nid.push_back(value) - } - } - - if !has_seperator { - return Err(ContractError::InvalidNetworkAddress); - } - - if !is_valid_bytes_address(&account) { - return Err(ContractError::InvalidAddress); - } - Ok(Address::from_string_bytes(&account)) -} - -fn _mint(e: &Env, to: Address, amount: i128) { - contract::check_nonnegative_amount(amount); - let admin = e.current_contract_address(); - receive_balance(e, to.clone(), amount); - TokenUtils::new(e).events().mint(admin, to, amount); -} - -pub fn _burn(e: &Env, from: Address, amount: i128) { - contract::check_nonnegative_amount(amount); - - spend_balance(e, from.clone(), amount); - TokenUtils::new(e).events().burn(from, amount); -} - -fn xcall_client(e: &Env, xcall: &Address) -> Client<'static> { - return xcall::Client::new(e, xcall); -} - -fn xcall_manager_client(e: &Env, xcall_manager: &Address) -> XcallManagerClient<'static> { - let client = XcallManagerClient::new(e, xcall_manager); - return client; -} diff --git a/contracts/balanced_dollar/src/contract.rs b/contracts/balanced_dollar/src/contract.rs deleted file mode 100644 index 1ed053f..0000000 --- a/contracts/balanced_dollar/src/contract.rs +++ /dev/null @@ -1,150 +0,0 @@ -//! This contract demonstrates a sample implementation of the Soroban token -//! interface. -use crate::allowance::{read_allowance, spend_allowance, write_allowance}; -use crate::balance::{read_balance, receive_balance, spend_balance}; -use crate::{balanced_dollar, storage_types}; -use crate::errors::ContractError; -use crate::metadata::{read_decimal, read_name, read_symbol, write_metadata}; -use crate::storage_types::{get_upgrade_authority, set_icon_bnusd, set_upgrade_authority, set_xcall, set_xcall_manager, INSTANCE_BUMP_AMOUNT, INSTANCE_LIFETIME_THRESHOLD -}; -use soroban_sdk::{ - contract, contractimpl, panic_with_error, Address, Bytes, BytesN, Env, String, Vec, -}; -use soroban_token_sdk::metadata::TokenMetadata; -use soroban_token_sdk::TokenUtils; -pub fn check_nonnegative_amount(amount: i128) { - if amount < 0 { - panic!("negative amount is not allowed: {}", amount) - } -} - -#[contract] -pub struct BalancedDollar; - -#[contractimpl] -impl BalancedDollar { - pub fn initialize(e: Env, xcall: Address, xcall_manager: Address, icon_bnusd: String, upgrade_auth: Address) { - if storage_types::has_upgrade_auth(&e) { - panic_with_error!(e, ContractError::ContractAlreadyInitialized) - } - //initialize token properties - let decimal = 18; - let name = String::from_str(&e, "Balanced Dollar"); - let symbol = String::from_str(&e, "bnUSD"); - - write_metadata( - &e, - TokenMetadata { - decimal, - name, - symbol, - }, - ); - set_xcall(&e, xcall); - set_icon_bnusd(&e, icon_bnusd); - set_xcall_manager(&e, xcall_manager); - set_upgrade_authority(&e, upgrade_auth); - } - - pub fn cross_transfer( - e: Env, - from: Address, - amount: u128, - to: String, - data: Option, - ) -> Result<(), ContractError> { - from.require_auth(); - let transfer_data = data.unwrap_or(Bytes::from_array(&e, &[0u8; 32])); - return balanced_dollar::_cross_transfer(e.clone(), from, amount, to, transfer_data); - } - - pub fn handle_call_message( - e: Env, - from: String, - data: Bytes, - protocols: Vec, - ) -> Result<(), ContractError> { - return balanced_dollar::_handle_call_message(e, from, data, protocols); - } - - pub fn is_initialized(e: Env) -> bool { - storage_types::has_upgrade_auth(&e) - } - - pub fn set_upgrade_authority(e: Env, new_upgrade_authority: Address) { - let upgrade_authority = get_upgrade_authority(&e)?; - upgrade_authority.require_auth(); - set_upgrade_authority(&e, new_upgrade_authority); - } - - pub fn upgrade(e: Env, new_wasm_hash: BytesN<32>) { - let upgrade_authority = get_upgrade_authority(&e)?; - upgrade_authority.require_auth(); - e.deployer().update_current_contract_wasm(new_wasm_hash); - } - - pub fn extend_ttl(e: Env) { - e.storage() - .instance() - .extend_ttl(INSTANCE_LIFETIME_THRESHOLD, INSTANCE_BUMP_AMOUNT); - } - - pub fn allowance(e: Env, from: Address, spender: Address) -> i128 { - read_allowance(&e, from, spender).amount - } - - pub fn approve(e: Env, from: Address, spender: Address, amount: i128, expiration_ledger: u32) { - from.require_auth(); - - check_nonnegative_amount(amount); - - write_allowance(&e, from.clone(), spender.clone(), amount, expiration_ledger); - TokenUtils::new(&e) - .events() - .approve(from, spender, amount, expiration_ledger); - } - - pub fn balance(e: Env, id: Address) -> i128 { - read_balance(&e, id) - } - - pub fn transfer(e: Env, from: Address, to: Address, amount: i128) { - from.require_auth(); - - check_nonnegative_amount(amount); - spend_balance(&e, from.clone(), amount); - receive_balance(&e, to.clone(), amount); - TokenUtils::new(&e).events().transfer(from, to, amount); - } - - pub fn transfer_from(e: Env, spender: Address, from: Address, to: Address, amount: i128) { - spender.require_auth(); - - check_nonnegative_amount(amount); - - spend_allowance(&e, from.clone(), spender, amount); - spend_balance(&e, from.clone(), amount); - receive_balance(&e, to.clone(), amount); - TokenUtils::new(&e).events().transfer(from, to, amount) - } - - pub fn decimals(e: Env) -> u32 { - read_decimal(&e) - } - - pub fn name(e: Env) -> String { - read_name(&e) - } - - pub fn symbol(e: Env) -> String { - read_symbol(&e) - } - - pub fn xcall_manager(e: Env) -> Address { - storage_types::get_xcall_manager(&e)? - } - - pub fn xcall(e: Env) -> Address { - storage_types::get_xcall(&e)? - } -} diff --git a/contracts/balanced_dollar/src/errors.rs b/contracts/balanced_dollar/src/errors.rs deleted file mode 100644 index 83f5266..0000000 --- a/contracts/balanced_dollar/src/errors.rs +++ /dev/null @@ -1,19 +0,0 @@ -use soroban_sdk::contracterror; - -#[contracterror] -#[repr(u32)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum ContractError { - InvalidRlpLength = 1, - ContractAlreadyInitialized = 2, - DecimalMustFitInAu8 = 3, - ProtocolMismatch = 4, - OnlyIconBnUSD = 5, - OnlyCallService = 6, - UnknownMessageType = 7, - InvalidAddress = 8, - InvalidNetworkAddressLength = 9, - InvalidNetworkAddress = 10, - InvalidAmount = 11, - Uninitialized = 12 -} diff --git a/contracts/balanced_dollar/src/lib.rs b/contracts/balanced_dollar/src/lib.rs index 01ec4d2..a170365 100644 --- a/contracts/balanced_dollar/src/lib.rs +++ b/contracts/balanced_dollar/src/lib.rs @@ -1,11 +1,96 @@ #![no_std] - -mod allowance; -mod balance; -pub mod balanced_dollar; -pub mod contract; -mod errors; -mod metadata; -mod storage_types; mod tests; -mod xcall_manager_interface; +use soroban_sdk::{ + contract, contractimpl, Address, Bytes, BytesN, Env, String, Vec, +}; +use spoke_token::{token_lib, errors::ContractError}; + +#[contract] +pub struct BalancedDollar; + +#[contractimpl] +impl BalancedDollar { + pub fn initialize(e: Env, xcall: Address, xcall_manager: Address, icon_bnusd: String, upgrade_auth: Address) { + + let decimal = 18; + let name = String::from_str(&e, "Balanced Dollar"); + let symbol = String::from_str(&e, "bnUSD"); + + token_lib::_initialize(e.clone(), xcall, xcall_manager, icon_bnusd, upgrade_auth, name, symbol, decimal); + } + + pub fn cross_transfer( + e: Env, + from: Address, + amount: u128, + to: String, + data: Option, + ) -> Result<(), ContractError> { + token_lib::_cross_transfer(e, from, amount, to, data) + } + + pub fn handle_call_message( + e: Env, + from: String, + data: Bytes, + protocols: Vec, + ) -> Result<(), ContractError> { + token_lib::_handle_call_message(e, from, data, protocols) + } + + pub fn is_initialized(e: Env) -> bool { + token_lib::_is_initialized(e) + } + + pub fn set_upgrade_authority(e: Env, new_upgrade_authority: Address) { + token_lib::_set_upgrade_authority(e, new_upgrade_authority); + } + + pub fn upgrade(e: Env, new_wasm_hash: BytesN<32>) { + token_lib::_upgrade(e, new_wasm_hash); + } + + pub fn extend_ttl(e: Env) { + token_lib::_extend_ttl(e); + } + + pub fn allowance(e: Env, from: Address, spender: Address) -> i128 { + token_lib::_allowance(e, from, spender) + } + + pub fn approve(e: Env, from: Address, spender: Address, amount: i128, expiration_ledger: u32) { + token_lib::_approve(e, from, spender, amount, expiration_ledger); + } + + pub fn balance(e: Env, id: Address) -> i128 { + token_lib::_balance(e, id) + } + + pub fn transfer(e: Env, from: Address, to: Address, amount: i128) { + token_lib::_transfer(e, from, to, amount); + } + + pub fn transfer_from(e: Env, spender: Address, from: Address, to: Address, amount: i128) { + token_lib::_transfer_from(e, spender, from, to, amount); + } + + pub fn decimals(e: Env) -> u32 { + token_lib::_decimals(e) + } + + pub fn name(e: Env) -> String { + token_lib::_name(e) + } + + pub fn symbol(e: Env) -> String { + token_lib::_symbol(e) + } + + pub fn xcall_manager(e: Env) -> Address { + token_lib::_xcall_manager(e) + } + + pub fn xcall(e: Env) -> Address { + token_lib::_xcall(e) + } +} diff --git a/contracts/balanced_dollar/src/metadata.rs b/contracts/balanced_dollar/src/metadata.rs deleted file mode 100644 index 715feee..0000000 --- a/contracts/balanced_dollar/src/metadata.rs +++ /dev/null @@ -1,22 +0,0 @@ -use soroban_sdk::{Env, String}; -use soroban_token_sdk::{metadata::TokenMetadata, TokenUtils}; - -pub fn read_decimal(e: &Env) -> u32 { - let util = TokenUtils::new(e); - util.metadata().get_metadata().decimal -} - -pub fn read_name(e: &Env) -> String { - let util = TokenUtils::new(e); - util.metadata().get_metadata().name -} - -pub fn read_symbol(e: &Env) -> String { - let util = TokenUtils::new(e); - util.metadata().get_metadata().symbol -} - -pub fn write_metadata(e: &Env, metadata: TokenMetadata) { - let util = TokenUtils::new(e); - util.metadata().set_metadata(&metadata); -} diff --git a/contracts/balanced_dollar/src/storage_types.rs b/contracts/balanced_dollar/src/storage_types.rs deleted file mode 100644 index 7f33015..0000000 --- a/contracts/balanced_dollar/src/storage_types.rs +++ /dev/null @@ -1,87 +0,0 @@ -use soroban_sdk::{contracttype, Address, Env, String}; - -use crate::errors::ContractError; - -pub(crate) const DAY_IN_LEDGERS: u32 = 17280; -pub(crate) const INSTANCE_BUMP_AMOUNT: u32 = 7 * DAY_IN_LEDGERS; -pub(crate) const INSTANCE_LIFETIME_THRESHOLD: u32 = INSTANCE_BUMP_AMOUNT - DAY_IN_LEDGERS; - -pub(crate) const BALANCE_BUMP_AMOUNT: u32 = 30 * DAY_IN_LEDGERS; -pub(crate) const BALANCE_LIFETIME_THRESHOLD: u32 = BALANCE_BUMP_AMOUNT - DAY_IN_LEDGERS; - -#[derive(Clone)] -#[contracttype] -pub struct AllowanceDataKey { - pub from: Address, - pub spender: Address, -} - -#[contracttype] -pub struct AllowanceValue { - pub amount: i128, - pub expiration_ledger: u32, -} - -#[derive(Clone)] -#[contracttype] -pub enum DataKey { - Allowance(AllowanceDataKey), - Balance(Address), - XcallManager, - XCall, - IconBnusd, - UpgradeAuthority -} - -pub fn set_xcall_manager(e: &Env, value: Address) { - e.storage().instance().set(&DataKey::XcallManager, &value); -} - -pub fn set_xcall(e: &Env, value: Address) { - e.storage().instance().set(&DataKey::XCall, &value); -} - -pub fn set_icon_bnusd(e: &Env, value: String) { - e.storage().instance().set(&DataKey::IconBnusd, &value); -} - -pub fn set_upgrade_authority(e: &Env, value: Address) { - e.storage().instance().set(&DataKey::UpgradeAuthority, &value); -} - -pub fn has_upgrade_auth(e: &Env) -> bool { - let key = DataKey::UpgradeAuthority; - e.storage().instance().has(&key) -} - -pub fn get_xcall_manager(e: &Env) -> Result { - let key = DataKey::XcallManager; - e.storage() - .instance() - .get(&key) - .ok_or(ContractError::Uninitialized)} - -pub fn get_xcall(e: &Env) -> Result { - let key = DataKey::XCall; - e.storage() - .instance() - .get(&key) - .ok_or(ContractError::Uninitialized)} - -pub fn get_icon_bnusd(e: &Env) -> Result { - let key = DataKey::IconBnusd; - e.storage() - .instance() - .get(&key) - .ok_or(ContractError::Uninitialized)} - -pub fn get_upgrade_authority(e: &Env) -> Result { - let key = DataKey::UpgradeAuthority; - e.storage() - .instance() - .get(&key) - .ok_or(ContractError::Uninitialized)} - - - - diff --git a/contracts/balanced_dollar/src/tests/balanced_dollar_test.rs b/contracts/balanced_dollar/src/tests/balanced_dollar_test.rs index 3643020..7468ea1 100644 --- a/contracts/balanced_dollar/src/tests/balanced_dollar_test.rs +++ b/contracts/balanced_dollar/src/tests/balanced_dollar_test.rs @@ -1,7 +1,8 @@ #![cfg(test)] extern crate std; -use crate::{contract::BalancedDollarClient, storage_types::get_upgrade_authority}; +use crate::BalancedDollarClient; +use spoke_token::storage_types::get_upgrade_authority; use super::setup::*; use soroban_rlp::balanced::messages::{ diff --git a/contracts/balanced_dollar/src/tests/setup.rs b/contracts/balanced_dollar/src/tests/setup.rs index 5bcfb03..52962ee 100644 --- a/contracts/balanced_dollar/src/tests/setup.rs +++ b/contracts/balanced_dollar/src/tests/setup.rs @@ -1,7 +1,7 @@ #![cfg(test)] extern crate std; -use crate::contract::{BalancedDollar, BalancedDollarClient}; +use crate::{BalancedDollar, BalancedDollarClient}; use soroban_sdk::{testutils::Address as _, token, Address, Env, String, Vec}; @@ -88,6 +88,8 @@ impl TestContext { icon_bn_usd: self.icon_bn_usd.clone(), upgrade_authority: self.upgrade_authority.clone(), }; + + client.initialize(&config.xcall, &config.xcall_manager, &config.icon_bn_usd, &config.upgrade_authority); } diff --git a/contracts/balanced_dollar/src/xcall_manager_interface.rs b/contracts/balanced_dollar/src/xcall_manager_interface.rs deleted file mode 100644 index 224bb14..0000000 --- a/contracts/balanced_dollar/src/xcall_manager_interface.rs +++ /dev/null @@ -1,10 +0,0 @@ -use soroban_sdk::{contractclient, Env, String, Vec}; - -use crate::errors::ContractError; - -#[contractclient(name = "XcallManagerClient")] -pub trait IXcallManager { - fn verify_protocols(e: Env, protocols: Vec) -> Result; - - fn get_protocols(e: Env) -> Result<(Vec, Vec), ContractError>; -} diff --git a/contracts/balanced_token/Cargo.toml b/contracts/balanced_token/Cargo.toml index 577c569..a794444 100644 --- a/contracts/balanced_token/Cargo.toml +++ b/contracts/balanced_token/Cargo.toml @@ -12,7 +12,7 @@ testutils = ["soroban-sdk/testutils"] [dependencies] soroban-sdk = { workspace = true } soroban-token-sdk = { version = "21.6.0" } -spoke_token = { path = "../../libs/spoke_token" } +spoke-token = { path = "../../libs/spoke_token" } [dev-dependencies] diff --git a/contracts/balanced_token/src/lib.rs b/contracts/balanced_token/src/lib.rs index 5b091e5..fbda738 100644 --- a/contracts/balanced_token/src/lib.rs +++ b/contracts/balanced_token/src/lib.rs @@ -1,22 +1,9 @@ #![no_std] -use crate::allowance::{read_allowance, spend_allowance, write_allowance}; -use crate::balance::{read_balance, receive_balance, spend_balance}; -use crate::{balanced_dollar, storage_types}; -use crate::errors::ContractError; -use crate::metadata::{read_decimal, read_name, read_symbol, write_metadata}; -use crate::storage_types::{get_upgrade_authority, set_icon_bnusd, set_upgrade_authority, set_xcall, set_xcall_manager, INSTANCE_BUMP_AMOUNT, INSTANCE_LIFETIME_THRESHOLD -}; use soroban_sdk::{ - contract, contractimpl, panic_with_error, Address, Bytes, BytesN, Env, String, Vec, + contract, contractimpl, Address, Bytes, BytesN, Env, String, Vec, }; -use soroban_token_sdk::metadata::TokenMetadata; -use soroban_token_sdk::TokenUtils; -pub fn check_nonnegative_amount(amount: i128) { - if amount < 0 { - panic!("negative amount is not allowed: {}", amount) - } -} +use spoke_token::{token_lib, errors::ContractError}; #[contract] pub struct BalancedToken; @@ -26,9 +13,11 @@ impl BalancedToken { pub fn initialize(e: Env, xcall: Address, xcall_manager: Address, icon_bnusd: String, upgrade_auth: Address) { //initialize token properties - let decimal: u8 = 18; + let decimal = 18; let name = String::from_str(&e, "Balanced Token"); let symbol = String::from_str(&e, "BALN"); + + token_lib::_initialize(e.clone(), xcall, xcall_manager, icon_bnusd, upgrade_auth, name, symbol, decimal); } pub fn cross_transfer( @@ -38,9 +27,7 @@ impl BalancedToken { to: String, data: Option, ) -> Result<(), ContractError> { - from.require_auth(); - let transfer_data = data.unwrap_or(Bytes::from_array(&e, &[0u8; 32])); - return token_lib::_cross_transfer(e.clone(), from, amount, to, transfer_data); + token_lib::_cross_transfer(e, from, amount, to, data) } pub fn handle_call_message( @@ -49,87 +36,62 @@ impl BalancedToken { data: Bytes, protocols: Vec, ) -> Result<(), ContractError> { - return token_lib::_handle_call_message(e, from, data, protocols); + token_lib::_handle_call_message(e, from, data, protocols) } pub fn is_initialized(e: Env) -> bool { - storage_types::has_upgrade_auth(&e) + token_lib::_is_initialized(e) } pub fn set_upgrade_authority(e: Env, new_upgrade_authority: Address) { - let upgrade_authority = get_upgrade_authority(&e)?; - upgrade_authority.require_auth(); - set_upgrade_authority(&e, new_upgrade_authority); + token_lib::_set_upgrade_authority(e, new_upgrade_authority); } pub fn upgrade(e: Env, new_wasm_hash: BytesN<32>) { - let upgrade_authority = get_upgrade_authority(&e)?; - upgrade_authority.require_auth(); - e.deployer().update_current_contract_wasm(new_wasm_hash); + token_lib::_upgrade(e, new_wasm_hash); } pub fn extend_ttl(e: Env) { - e.storage() - .instance() - .extend_ttl(INSTANCE_LIFETIME_THRESHOLD, INSTANCE_BUMP_AMOUNT); + token_lib::_extend_ttl(e); } pub fn allowance(e: Env, from: Address, spender: Address) -> i128 { - read_allowance(&e, from, spender).amount + token_lib::_allowance(e, from, spender) } pub fn approve(e: Env, from: Address, spender: Address, amount: i128, expiration_ledger: u32) { - from.require_auth(); - - check_nonnegative_amount(amount); - - write_allowance(&e, from.clone(), spender.clone(), amount, expiration_ledger); - TokenUtils::new(&e) - .events() - .approve(from, spender, amount, expiration_ledger); + token_lib::_approve(e, from, spender, amount, expiration_ledger); } pub fn balance(e: Env, id: Address) -> i128 { - read_balance(&e, id) + token_lib::_balance(e, id) } pub fn transfer(e: Env, from: Address, to: Address, amount: i128) { - from.require_auth(); - - check_nonnegative_amount(amount); - spend_balance(&e, from.clone(), amount); - receive_balance(&e, to.clone(), amount); - TokenUtils::new(&e).events().transfer(from, to, amount); + token_lib::_transfer(e, from, to, amount); } pub fn transfer_from(e: Env, spender: Address, from: Address, to: Address, amount: i128) { - spender.require_auth(); - - check_nonnegative_amount(amount); - - spend_allowance(&e, from.clone(), spender, amount); - spend_balance(&e, from.clone(), amount); - receive_balance(&e, to.clone(), amount); - TokenUtils::new(&e).events().transfer(from, to, amount) + token_lib::_transfer_from(e, spender, from, to, amount); } pub fn decimals(e: Env) -> u32 { - read_decimal(&e) + token_lib::_decimals(e) } pub fn name(e: Env) -> String { - read_name(&e) + token_lib::_name(e) } pub fn symbol(e: Env) -> String { - read_symbol(&e) + token_lib::_symbol(e) } pub fn xcall_manager(e: Env) -> Address { - storage_types::get_xcall_manager(&e)? + token_lib::_xcall_manager(e) } pub fn xcall(e: Env) -> Address { - storage_types::get_xcall(&e)? + token_lib::_xcall(e) } } diff --git a/contracts/sicx/Cargo.toml b/contracts/sicx/Cargo.toml new file mode 100644 index 0000000..fb43fd8 --- /dev/null +++ b/contracts/sicx/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "sicx" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +testutils = ["soroban-sdk/testutils"] + +[dependencies] +soroban-sdk = { workspace = true } +soroban-token-sdk = { version = "21.6.0" } +spoke-token = { path = "../../libs/spoke_token" } + + +[dev-dependencies] +soroban-sdk = { workspace = true, features = ["testutils"] } \ No newline at end of file diff --git a/contracts/sicx/src/lib.rs b/contracts/sicx/src/lib.rs new file mode 100644 index 0000000..ae02a52 --- /dev/null +++ b/contracts/sicx/src/lib.rs @@ -0,0 +1,97 @@ +#![no_std] + +use soroban_sdk::{ + contract, contractimpl, Address, Bytes, BytesN, Env, String, Vec, +}; +use spoke_token::{token_lib, errors::ContractError}; + +#[contract] +pub struct StakedICX; + +#[contractimpl] +impl StakedICX { + pub fn initialize(e: Env, xcall: Address, xcall_manager: Address, icon_bnusd: String, upgrade_auth: Address) { + + //initialize token properties + let decimal = 18; + let name = String::from_str(&e, "Staked ICX"); + let symbol = String::from_str(&e, "SICX"); + + token_lib::_initialize(e.clone(), xcall, xcall_manager, icon_bnusd, upgrade_auth, name, symbol, decimal); + } + + pub fn cross_transfer( + e: Env, + from: Address, + amount: u128, + to: String, + data: Option, + ) -> Result<(), ContractError> { + token_lib::_cross_transfer(e, from, amount, to, data) + } + + pub fn handle_call_message( + e: Env, + from: String, + data: Bytes, + protocols: Vec, + ) -> Result<(), ContractError> { + token_lib::_handle_call_message(e, from, data, protocols) + } + + pub fn is_initialized(e: Env) -> bool { + token_lib::_is_initialized(e) + } + + pub fn set_upgrade_authority(e: Env, new_upgrade_authority: Address) { + token_lib::_set_upgrade_authority(e, new_upgrade_authority); + } + + pub fn upgrade(e: Env, new_wasm_hash: BytesN<32>) { + token_lib::_upgrade(e, new_wasm_hash); + } + + pub fn extend_ttl(e: Env) { + token_lib::_extend_ttl(e); + } + + pub fn allowance(e: Env, from: Address, spender: Address) -> i128 { + token_lib::_allowance(e, from, spender) + } + + pub fn approve(e: Env, from: Address, spender: Address, amount: i128, expiration_ledger: u32) { + token_lib::_approve(e, from, spender, amount, expiration_ledger); + } + + pub fn balance(e: Env, id: Address) -> i128 { + token_lib::_balance(e, id) + } + + pub fn transfer(e: Env, from: Address, to: Address, amount: i128) { + token_lib::_transfer(e, from, to, amount); + } + + pub fn transfer_from(e: Env, spender: Address, from: Address, to: Address, amount: i128) { + token_lib::_transfer_from(e, spender, from, to, amount); + } + + pub fn decimals(e: Env) -> u32 { + token_lib::_decimals(e) + } + + pub fn name(e: Env) -> String { + token_lib::_name(e) + } + + pub fn symbol(e: Env) -> String { + token_lib::_symbol(e) + } + + pub fn xcall_manager(e: Env) -> Address { + token_lib::_xcall_manager(e) + } + + pub fn xcall(e: Env) -> Address { + token_lib::_xcall(e) + } +} diff --git a/libs/spoke_token/Cargo.toml b/libs/spoke_token/Cargo.toml index 87be963..08cb309 100644 --- a/libs/spoke_token/Cargo.toml +++ b/libs/spoke_token/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "spoke_token" +name = "spoke-token" version = "0.1.0" edition = "2021" diff --git a/libs/spoke_token/src/contract.rs b/libs/spoke_token/src/contract.rs deleted file mode 100644 index 97d4aec..0000000 --- a/libs/spoke_token/src/contract.rs +++ /dev/null @@ -1,151 +0,0 @@ -//! This contract demonstrates a sample implementation of the Soroban token -//! interface. -use crate::allowance::{read_allowance, spend_allowance, write_allowance}; -use crate::balance::{read_balance, receive_balance, spend_balance}; -use crate::errors::ContractError; -use crate::metadata::{read_decimal, read_name, read_symbol, write_metadata}; -use crate::storage_types::{ - get_upgrade_authority, set_icon_bnusd, set_upgrade_authority, set_xcall, set_xcall_manager, - INSTANCE_BUMP_AMOUNT, INSTANCE_LIFETIME_THRESHOLD, -}; -use crate::{token_lib, storage_types}; -use soroban_sdk::{ - contract, contractimpl, panic_with_error, Address, Bytes, BytesN, Env, String, Vec, -}; -use soroban_token_sdk::metadata::TokenMetadata; -use soroban_token_sdk::TokenUtils; -pub fn check_nonnegative_amount(amount: i128) { - if amount < 0 { - panic!("negative amount is not allowed: {}", amount) - } -} - -pub fn initialize( - e: Env, - xcall: Address, - xcall_manager: Address, - icon_bnusd: String, - upgrade_auth: Address, - name: String, - symbol: String, - decimal: u8 -) { - if storage_types::has_upgrade_auth(&e) { - panic_with_error!(e, ContractError::ContractAlreadyInitialized) - } - - write_metadata( - &e, - TokenMetadata { - decimal, - name, - symbol, - }, - ); - set_xcall(&e, xcall); - set_icon_bnusd(&e, icon_bnusd); - set_xcall_manager(&e, xcall_manager); - set_upgrade_authority(&e, upgrade_auth); -} - -pub fn cross_transfer( - e: Env, - from: Address, - amount: u128, - to: String, - data: Option, -) -> Result<(), ContractError> { - from.require_auth(); - let transfer_data = data.unwrap_or(Bytes::from_array(&e, &[0u8; 32])); - return token_lib::_cross_transfer(e.clone(), from, amount, to, transfer_data); -} - -pub fn handle_call_message( - e: Env, - from: String, - data: Bytes, - protocols: Vec, -) -> Result<(), ContractError> { - return token_lib::_handle_call_message(e, from, data, protocols); -} - -pub fn is_initialized(e: Env) -> bool { - storage_types::has_upgrade_auth(&e) -} - -pub fn set_upgrade_authority(e: Env, new_upgrade_authority: Address) { - let upgrade_authority = get_upgrade_authority(&e)?; - upgrade_authority.require_auth(); - set_upgrade_authority(&e, new_upgrade_authority); -} - -pub fn upgrade(e: Env, new_wasm_hash: BytesN<32>) { - let upgrade_authority = get_upgrade_authority(&e)?; - upgrade_authority.require_auth(); - e.deployer().update_current_contract_wasm(new_wasm_hash); -} - -pub fn extend_ttl(e: Env) { - e.storage() - .instance() - .extend_ttl(INSTANCE_LIFETIME_THRESHOLD, INSTANCE_BUMP_AMOUNT); -} - -pub fn allowance(e: Env, from: Address, spender: Address) -> i128 { - read_allowance(&e, from, spender).amount -} - -pub fn approve(e: Env, from: Address, spender: Address, amount: i128, expiration_ledger: u32) { - from.require_auth(); - - check_nonnegative_amount(amount); - - write_allowance(&e, from.clone(), spender.clone(), amount, expiration_ledger); - TokenUtils::new(&e) - .events() - .approve(from, spender, amount, expiration_ledger); -} - -pub fn balance(e: Env, id: Address) -> i128 { - read_balance(&e, id) -} - -pub fn transfer(e: Env, from: Address, to: Address, amount: i128) { - from.require_auth(); - - check_nonnegative_amount(amount); - spend_balance(&e, from.clone(), amount); - receive_balance(&e, to.clone(), amount); - TokenUtils::new(&e).events().transfer(from, to, amount); -} - -pub fn transfer_from(e: Env, spender: Address, from: Address, to: Address, amount: i128) { - spender.require_auth(); - - check_nonnegative_amount(amount); - - spend_allowance(&e, from.clone(), spender, amount); - spend_balance(&e, from.clone(), amount); - receive_balance(&e, to.clone(), amount); - TokenUtils::new(&e).events().transfer(from, to, amount) -} - -pub fn decimals(e: Env) -> u32 { - read_decimal(&e) -} - -pub fn name(e: Env) -> String { - read_name(&e) -} - -pub fn symbol(e: Env) -> String { - read_symbol(&e) -} - -pub fn xcall_manager(e: Env) -> Address { - storage_types::get_xcall_manager(&e)? -} - -pub fn xcall(e: Env) -> Address { - storage_types::get_xcall(&e)? -} diff --git a/libs/spoke_token/src/lib.rs b/libs/spoke_token/src/lib.rs index be3207d..7acc166 100644 --- a/libs/spoke_token/src/lib.rs +++ b/libs/spoke_token/src/lib.rs @@ -3,9 +3,7 @@ mod allowance; mod balance; pub mod token_lib; -pub mod contract; -mod errors; +pub mod errors; mod metadata; -mod storage_types; -mod tests; +pub mod storage_types; mod xcall_manager_interface; diff --git a/libs/spoke_token/src/tests/balanced_dollar_test.rs b/libs/spoke_token/src/tests/balanced_dollar_test.rs deleted file mode 100644 index 3643020..0000000 --- a/libs/spoke_token/src/tests/balanced_dollar_test.rs +++ /dev/null @@ -1,378 +0,0 @@ -#![cfg(test)] -extern crate std; - -use crate::{contract::BalancedDollarClient, storage_types::get_upgrade_authority}; - -use super::setup::*; -use soroban_rlp::balanced::messages::{ - cross_transfer::CrossTransfer, cross_transfer_revert::CrossTransferRevert, -}; -use soroban_sdk::{ - testutils::{Address as _, AuthorizedFunction, AuthorizedInvocation}, - Address, Bytes, IntoVal, String, Symbol, Vec, -}; - -#[test] -fn test_initialize() { - let ctx = TestContext::default(); - let client = BalancedDollarClient::new(&ctx.env, &ctx.registry); - - ctx.init_context(&client); - - let initialized = client.is_initialized(); - assert_eq!(initialized, true) -} - -#[test] -fn test_cross_transfer_with_to_and_data() { - let ctx = TestContext::default(); - let client = BalancedDollarClient::new(&ctx.env, &ctx.registry); - ctx.init_context(&client); - - let amount_i128: i128 = 100000i128; - let amount = &(amount_i128 as u128); - - let bnusd_amount = 1000000u128; - - let items: [u8; 32] = [0; 32]; - let to = String::from_str( - &ctx.env, - "stellar/CA36FQITV33RO5SJFPTNLRQBD6ZNAEJG7F7J5KWCV4OP7SQHDMIZCT33", - ); - let from_address = &Address::from_string(&String::from_str( - &ctx.env, - "CA36FQITV33RO5SJFPTNLRQBD6ZNAEJG7F7J5KWCV4OP7SQHDMIZCT33", - )); - - std::println!("to address is: {:?}", to); - let data = CrossTransfer::new( - ctx.depositor.to_string(), - to.clone(), - bnusd_amount, - Bytes::from_array(&ctx.env, &items), - ) - .encode(&ctx.env, String::from_str(&ctx.env, "xCrossTransfer")); - - let sources = Vec::from_array(&ctx.env, [ctx.centralized_connection.to_string()]); - client.handle_call_message(&ctx.icon_bn_usd, &data, &sources); - - ctx.mint_native_token(&from_address, 500u128); - assert_eq!(ctx.get_native_token_balance(&from_address), 500u128); - - client.approve( - &from_address, - &ctx.registry, - &(amount_i128 + amount_i128), - &1312000, - ); - let data: [u8; 32] = [ - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, - 0x1F, 0x20, - ]; - client.cross_transfer( - &from_address, - &amount, - &String::from_str(&ctx.env, "icon01/hxjkdvhui"), - &Option::Some(Bytes::from_array(&ctx.env, &data)), - ); - assert_eq!(ctx.get_native_token_balance(&from_address), 400u128) // why 300? -} - -#[test] -fn test_handle_call_message_for_cross_transfer() { - let ctx = TestContext::default(); - let client = BalancedDollarClient::new(&ctx.env, &ctx.registry); - ctx.env.mock_all_auths(); - - ctx.init_context(&client); - - let bnusd_amount = 100000u128; - - let items: [u8; 32] = [ - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, - 0x1F, 0x20, - ]; - let withdrawer = String::from_str( - &ctx.env, - "stellar/CA36FQITV33RO5SJFPTNLRQBD6ZNAEJG7F7J5KWCV4OP7SQHDMIZCT33", - ); - let data = CrossTransfer::new( - ctx.depositor.to_string(), - withdrawer.clone(), - bnusd_amount, - Bytes::from_array(&ctx.env, &items), - ) - .encode(&ctx.env, String::from_str(&ctx.env, "xCrossTransfer")); - let decoded = CrossTransfer::decode(&ctx.env, data.clone()); - assert_eq!(decoded.to, withdrawer); - - let withdrawer_address = &Address::from_string(&String::from_str( - &ctx.env, - "CA36FQITV33RO5SJFPTNLRQBD6ZNAEJG7F7J5KWCV4OP7SQHDMIZCT33", - )); - assert_eq!(client.balance(withdrawer_address), 0); - - let sources = Vec::from_array(&ctx.env, [ctx.centralized_connection.to_string()]); - client.handle_call_message(&ctx.icon_bn_usd, &data, &sources); - assert_eq!(client.balance(withdrawer_address), bnusd_amount as i128) -} - -#[test] -#[should_panic(expected = "HostError: Error(Contract, #8)")] -fn test_handle_call_message_for_cross_transfer_invalid_addres_fail() { - let ctx = TestContext::default(); - let client = BalancedDollarClient::new(&ctx.env, &ctx.registry); - ctx.env.mock_all_auths(); - - ctx.init_context(&client); - - let bnusd_amount = 100000u128; - - let items: [u8; 32] = [ - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, - 0x1F, 0x20, - ]; - let withdrawer = String::from_str(&ctx.env, "stellar/InvalidAddress"); - let data = CrossTransfer::new( - ctx.depositor.to_string(), - withdrawer.clone(), - bnusd_amount, - Bytes::from_array(&ctx.env, &items), - ) - .encode(&ctx.env, String::from_str(&ctx.env, "xCrossTransfer")); - let decoded = CrossTransfer::decode(&ctx.env, data.clone()); - assert_eq!(decoded.to, withdrawer); - - // let withdrawer_address = &Address::from_string(&String::from_str(&ctx.env, "InvalidAddress")); - // assert_eq!(client.balance(withdrawer_address), 0); - - let sources = Vec::from_array(&ctx.env, [ctx.centralized_connection.to_string()]); - client.handle_call_message(&ctx.icon_bn_usd, &data, &sources); - // assert_eq!(client.balance(withdrawer_address), bnusd_amount as i128) -} - -#[test] -#[should_panic(expected = "HostError: Error(Contract, #4)")] -fn test_handle_call_message_for_cross_transfer_panic_for_protocol_mismatch() { - let ctx = TestContext::default(); - let client = BalancedDollarClient::new(&ctx.env, &ctx.registry); - ctx.env.mock_all_auths(); - - ctx.init_context(&client); - - let bnusd_amount = 100000u128; - - let items: [u8; 32] = [ - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, - 0x1F, 0x20, - ]; - let withdrawer = String::from_str( - &ctx.env, - "stellar/CA36FQITV33RO5SJFPTNLRQBD6ZNAEJG7F7J5KWCV4OP7SQHDMIZCT33", - ); - let data = CrossTransfer::new( - ctx.depositor.to_string(), - withdrawer.clone(), - bnusd_amount, - Bytes::from_array(&ctx.env, &items), - ) - .encode(&ctx.env, String::from_str(&ctx.env, "xCrossTransfer")); - let decoded = CrossTransfer::decode(&ctx.env, data.clone()); - assert_eq!(decoded.to, withdrawer); - - let withdrawer_address = &Address::from_string(&String::from_str( - &ctx.env, - "CA36FQITV33RO5SJFPTNLRQBD6ZNAEJG7F7J5KWCV4OP7SQHDMIZCT33", - )); - assert_eq!(client.balance(withdrawer_address), 0); - - let sources = Vec::from_array(&ctx.env, [ctx.xcall.to_string()]); - client.handle_call_message(&ctx.icon_bn_usd, &data, &sources); - - assert_eq!(client.balance(withdrawer_address), bnusd_amount as i128) -} - -#[test] -#[should_panic(expected = "HostError: Error(Contract, #5)")] -fn test_handle_call_message_for_cross_transfer_panic_for_icon_bnusd() { - let ctx = TestContext::default(); - let client = BalancedDollarClient::new(&ctx.env, &ctx.registry); - ctx.env.mock_all_auths(); - - ctx.init_context(&client); - - let bnusd_amount = 100000u128; - - let items: [u8; 32] = [ - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, - 0x1F, 0x20, - ]; - let withdrawer = String::from_str( - &ctx.env, - "stellar/CA36FQITV33RO5SJFPTNLRQBD6ZNAEJG7F7J5KWCV4OP7SQHDMIZCT33", - ); - let data = CrossTransfer::new( - ctx.depositor.to_string(), - withdrawer.clone(), - bnusd_amount, - Bytes::from_array(&ctx.env, &items), - ) - .encode(&ctx.env, String::from_str(&ctx.env, "xCrossTransfer")); - let decoded = CrossTransfer::decode(&ctx.env, data.clone()); - assert_eq!(decoded.to, withdrawer); - - let withdrawer_address = &Address::from_string(&String::from_str( - &ctx.env, - "CA36FQITV33RO5SJFPTNLRQBD6ZNAEJG7F7J5KWCV4OP7SQHDMIZCT33", - )); - assert_eq!(client.balance(withdrawer_address), 0); - - let sources = Vec::from_array(&ctx.env, [ctx.centralized_connection.to_string()]); - client.handle_call_message(&ctx.icon_governance, &data, &sources); - - assert_eq!(client.balance(withdrawer_address), bnusd_amount as i128) -} - -#[test] -#[should_panic(expected = "HostError: Error(Contract, #7)")] -fn test_handle_call_message_for_cross_transfer_panic_for_wront_message_type() { - let ctx = TestContext::default(); - let client = BalancedDollarClient::new(&ctx.env, &ctx.registry); - ctx.env.mock_all_auths(); - - ctx.init_context(&client); - - let bnusd_amount = 100000u128; - - let items: [u8; 32] = [ - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, - 0x1F, 0x20, - ]; - let withdrawer = String::from_str( - &ctx.env, - "stellar/CA36FQITV33RO5SJFPTNLRQBD6ZNAEJG7F7J5KWCV4OP7SQHDMIZCT33", - ); - let data = CrossTransfer::new( - ctx.depositor.to_string(), - withdrawer.clone(), - bnusd_amount, - Bytes::from_array(&ctx.env, &items), - ) - .encode(&ctx.env, String::from_str(&ctx.env, "xCrossTransferPanic")); - - let decoded: CrossTransfer = CrossTransfer::decode(&ctx.env, data.clone()); - let withdrawer_address = &Address::from_string(&String::from_str( - &ctx.env, - "CA36FQITV33RO5SJFPTNLRQBD6ZNAEJG7F7J5KWCV4OP7SQHDMIZCT33", - )); - assert_eq!(decoded.to, withdrawer); - - assert_eq!(client.balance(withdrawer_address), 0); - - let sources = Vec::from_array(&ctx.env, [ctx.centralized_connection.to_string()]); - client.handle_call_message(&ctx.icon_bn_usd, &data, &sources); - - assert_eq!(client.balance(withdrawer_address), bnusd_amount as i128) -} - -#[test] -fn test_handle_call_message_for_cross_transfer_revert() { - let ctx = TestContext::default(); - let client = BalancedDollarClient::new(&ctx.env, &ctx.registry); - ctx.env.mock_all_auths(); - - ctx.init_context(&client); - - let bnusd_amount = 100000u128; - - let data = CrossTransferRevert::new(ctx.withdrawer.clone(), bnusd_amount) - .encode(&ctx.env, String::from_str(&ctx.env, "xCrossTransferRevert")); - let decoded = CrossTransferRevert::decode(&ctx.env, data.clone()); - assert_eq!(decoded.to, ctx.withdrawer); - - assert_eq!(client.balance(&ctx.withdrawer), 0); - - let sources = Vec::from_array(&ctx.env, [ctx.centralized_connection.to_string()]); - client.handle_call_message(&ctx.xcall_client.get_network_address(), &data, &sources); - - assert_eq!(client.balance(&ctx.withdrawer), bnusd_amount as i128) -} - -#[test] -#[should_panic(expected = "HostError: Error(Contract, #6)")] -fn test_handle_call_message_for_cross_transfer_revert_panic_for_xcall() { - let ctx = TestContext::default(); - let client = BalancedDollarClient::new(&ctx.env, &ctx.registry); - ctx.env.mock_all_auths(); - - ctx.init_context(&client); - - let bnusd_amount = 100000u128; - - let data = CrossTransferRevert::new(ctx.withdrawer.clone(), bnusd_amount) - .encode(&ctx.env, String::from_str(&ctx.env, "xCrossTransferRevert")); - let decoded = CrossTransferRevert::decode(&ctx.env, data.clone()); - assert_eq!(decoded.to, ctx.withdrawer); - - assert_eq!(client.balance(&ctx.withdrawer), 0); - - let sources = Vec::from_array(&ctx.env, [ctx.centralized_connection.to_string()]); - let wrong_network_address: String = String::from_str( - &ctx.env, - &std::format!( - "{}/{}", - "soroban", - "CBEPDNVYXQGWB5YUBXKJWYJA7OXTZW5LFLNO5JRRGE6Z6C5OSUZPCCEL" - ), - ); - client.handle_call_message(&wrong_network_address, &data, &sources); - - assert_eq!(client.balance(&ctx.withdrawer), bnusd_amount as i128) -} - -#[test] -fn test_extend_ttl() { - let ctx = TestContext::default(); - let client = BalancedDollarClient::new(&ctx.env, &ctx.registry); - ctx.env.mock_all_auths(); - - ctx.init_context(&client); - - client.extend_ttl() -} - -#[test] -fn test_set_upgrade_authority() { - let ctx = TestContext::default(); - let client = BalancedDollarClient::new(&ctx.env, &ctx.registry); - ctx.init_context(&client); - - let new_upgrade_authority = Address::generate(&ctx.env); - client.set_upgrade_authority(&new_upgrade_authority); - - assert_eq!( - ctx.env.auths(), - std::vec![( - ctx.upgrade_authority.clone(), - AuthorizedInvocation { - function: AuthorizedFunction::Contract(( - ctx.registry.clone(), - Symbol::new(&ctx.env, "set_upgrade_authority"), - (&new_upgrade_authority,).into_val(&ctx.env) - )), - sub_invocations: std::vec![] - } - )] - ); - - ctx.env.as_contract(&client.address, || { - let upgrade_authority = get_upgrade_authority(&ctx.env).unwrap(); - assert_eq!(upgrade_authority, new_upgrade_authority) - }); -} diff --git a/libs/spoke_token/src/tests/mod.rs b/libs/spoke_token/src/tests/mod.rs deleted file mode 100644 index 7918a54..0000000 --- a/libs/spoke_token/src/tests/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod setup; -pub mod balanced_dollar_test; \ No newline at end of file diff --git a/libs/spoke_token/src/tests/setup.rs b/libs/spoke_token/src/tests/setup.rs deleted file mode 100644 index 5bcfb03..0000000 --- a/libs/spoke_token/src/tests/setup.rs +++ /dev/null @@ -1,153 +0,0 @@ -#![cfg(test)] -extern crate std; - -use crate::contract::{BalancedDollar, BalancedDollarClient}; - -use soroban_sdk::{testutils::Address as _, token, Address, Env, String, Vec}; - -mod xcall { - soroban_sdk::contractimport!(file = "../../wasm/xcall.wasm"); -} - -mod connection { - soroban_sdk::contractimport!(file = "../../wasm/centralized_connection.wasm"); -} - -mod xcall_manager { - soroban_sdk::contractimport!( - file = "../../target/wasm32-unknown-unknown/release/xcall_manager.wasm" - ); -} - -use xcall_manager::ConfigData as XcallManagerConfigData; - -pub struct TestContext { - pub env: Env, - pub registry: Address, - pub admin: Address, - pub depositor: Address, - pub withdrawer: Address, - pub upgrade_authority: Address, - pub xcall: Address, - pub xcall_manager: Address, - pub icon_bn_usd: String, - pub icon_governance: String, - pub centralized_connection: Address, - pub nid: String, - pub native_token: Address, - pub xcall_client: xcall::Client<'static>, -} - -pub struct ConfigData { - pub xcall: Address, - pub xcall_manager: Address, - pub icon_bn_usd: String, - pub upgrade_authority: Address, -} - -impl TestContext { - pub fn default() -> Self { - let env = Env::default(); - let token_admin = Address::generate(&env); - let balanced_dollar = env.register_contract(None, BalancedDollar); - let centralized_connection = env.register_contract_wasm(None, connection::WASM); - let xcall_manager = env.register_contract_wasm(None, xcall_manager::WASM); - let xcall = env.register_contract_wasm(None, xcall::WASM); - std::println!("asset manager {:?}", balanced_dollar); - std::println!("xcall manager{:?}", xcall_manager); - std::println!("xcall {:?}", xcall); - std::println!("centralized {:?}", centralized_connection); - - Self { - registry: balanced_dollar, - admin: Address::generate(&env), - depositor: Address::generate(&env), - withdrawer: Address::generate(&env), - upgrade_authority: Address::generate(&env), - xcall: xcall.clone(), - xcall_manager, - icon_bn_usd: String::from_str(&env, "icon01/hxjnfh4u"), - icon_governance: String::from_str(&env, "icon01/kjdnoi"), - centralized_connection, - nid: String::from_str(&env, "stellar"), - native_token: env - .register_stellar_asset_contract_v2(token_admin.clone()) - .address(), - xcall_client: xcall::Client::new(&env, &xcall), - env, - } - } - - pub fn init_context(&self, client: &BalancedDollarClient<'static>) { - self.env.mock_all_auths(); - self.init_xcall_manager_context(); - self.init_xcall_state(); - let config = ConfigData { - xcall: self.xcall.clone(), - xcall_manager: self.xcall_manager.clone(), - icon_bn_usd: self.icon_bn_usd.clone(), - upgrade_authority: self.upgrade_authority.clone(), - }; - client.initialize(&config.xcall, &config.xcall_manager, &config.icon_bn_usd, &config.upgrade_authority); - } - - pub fn init_xcall_manager_context(&self) { - let client = self::xcall_manager::Client::new(&self.env, &self.xcall_manager); - let config = XcallManagerConfigData { - xcall: self.xcall.clone(), - icon_governance: self.icon_governance.clone(), - upgrade_authority: self.upgrade_authority.clone(), - }; - let sources = Vec::from_array(&self.env, [self.centralized_connection.to_string()]); - let destinations = - Vec::from_array(&self.env, [String::from_str(&self.env, "icon/address")]); - client.initialize( - &self.xcall_manager, - &self.admin, - &config, - &sources, - &destinations, - ); - } - - pub fn init_xcall_state(&self) { - let xcall_client = xcall::Client::new(&self.env, &self.xcall); - - xcall_client.initialize(&xcall::InitializeMsg { - sender: self.admin.clone(), - network_id: self.nid.clone(), - native_token: self.native_token.clone(), - }); - - self.init_connection_state(); - xcall_client.set_protocol_fee(&100); - xcall_client.set_default_connection(&self.nid, &self.centralized_connection); - } - - pub fn init_connection_state(&self) { - let connection_client = connection::Client::new(&self.env, &self.centralized_connection); - - let initialize_msg = connection::InitializeMsg { - native_token: self.native_token.clone(), - relayer: self.admin.clone(), - xcall_address: self.xcall.clone(), - }; - connection_client.initialize(&initialize_msg); - - let message_fee = 100; - let response_fee = 100; - connection_client.set_fee(&self.nid, &message_fee, &response_fee); - } - - pub fn mint_native_token(&self, address: &Address, amount: u128) { - let native_token_client = token::StellarAssetClient::new(&self.env, &self.native_token); - native_token_client.mint(&address, &(*&amount as i128)); - } - - pub fn get_native_token_balance(&self, address: &Address) -> u128 { - let native_token_client = token::TokenClient::new(&self.env, &self.native_token); - let balance = native_token_client.balance(address); - - *&balance as u128 - } -} diff --git a/libs/spoke_token/src/token_lib.rs b/libs/spoke_token/src/token_lib.rs index 895dd21..f472ca1 100644 --- a/libs/spoke_token/src/token_lib.rs +++ b/libs/spoke_token/src/token_lib.rs @@ -1,33 +1,76 @@ -use crate::balance::{receive_balance, spend_balance}; -use crate::storage_types::{get_icon_bnusd, get_xcall, get_xcall_manager}; -use soroban_sdk::{xdr::ToXdr, Address, Bytes, Env, String, Vec}; +//! This contract demonstrates a sample implementation of the Soroban token +//! interface. +use crate::allowance::{read_allowance, spend_allowance, write_allowance}; +use crate::balance::{read_balance, receive_balance, spend_balance}; +use crate::errors::ContractError; +use crate::metadata::{read_decimal, read_name, read_symbol, write_metadata}; +use crate::storage_types::{ + get_upgrade_authority, set_icon_bnusd, set_upgrade_authority, set_xcall, set_xcall_manager, + INSTANCE_BUMP_AMOUNT, INSTANCE_LIFETIME_THRESHOLD, +}; +use soroban_sdk::xdr::ToXdr; +use soroban_sdk::{panic_with_error, Address, Bytes, BytesN, Env, String, Vec}; +use soroban_token_sdk::metadata::TokenMetadata; +use soroban_token_sdk::TokenUtils; + +use crate::storage_types::{self, get_icon_bnusd, get_xcall, get_xcall_manager}; mod xcall { soroban_sdk::contractimport!(file = "../../wasm/xcall.wasm"); } -use crate::contract; -use crate::errors::ContractError; use crate::xcall_manager_interface::XcallManagerClient; use soroban_rlp::balanced::address_utils::is_valid_bytes_address; use soroban_rlp::balanced::messages::{ cross_transfer::CrossTransfer, cross_transfer_revert::CrossTransferRevert, }; -use soroban_token_sdk::TokenUtils; use xcall::{AnyMessage, CallMessageWithRollback, Client, Envelope}; const CROSS_TRANSFER: &str = "xCrossTransfer"; const CROSS_TRANSFER_REVERT: &str = "xCrossTransferRevert"; +pub fn check_nonnegative_amount(amount: i128) { + if amount < 0 { + panic!("negative amount is not allowed: {}", amount) + } +} +pub fn _initialize( + e: Env, + xcall: Address, + xcall_manager: Address, + icon_bnusd: String, + upgrade_auth: Address, + name: String, + symbol: String, + decimal: u32, +) { + if storage_types::has_upgrade_auth(&e) { + panic_with_error!(e, ContractError::ContractAlreadyInitialized) + } + write_metadata( + &e, + TokenMetadata { + decimal, + name, + symbol, + }, + ); + set_xcall(&e, xcall); + set_icon_bnusd(&e, icon_bnusd); + set_xcall_manager(&e, xcall_manager); + set_upgrade_authority(&e, upgrade_auth); +} pub fn _cross_transfer( e: Env, from: Address, amount: u128, to: String, - data: Bytes, + data: Option, ) -> Result<(), ContractError> { + from.require_auth(); + let data = data.unwrap_or(Bytes::from_array(&e, &[0u8; 32])); if amount <= i128::MAX as u128 { _burn(&e, from.clone(), amount as i128); - }else{ + } else { return Err(ContractError::InvalidAmount); } let xcall_message = CrossTransfer::new(from.clone().to_string(), to, amount, data); @@ -85,7 +128,7 @@ pub fn _handle_call_message( let to_network_address: Address = get_address(message.to, &e)?; if message.amount <= i128::MAX as u128 { _mint(&e, to_network_address, message.amount as i128); - }else{ + } else { return Err(ContractError::InvalidAmount); } } else if method == String::from_str(&e, &CROSS_TRANSFER_REVERT) { @@ -96,7 +139,7 @@ pub fn _handle_call_message( let message = CrossTransferRevert::decode(&e, data); if message.amount <= i128::MAX as u128 { _mint(&e, message.to, message.amount as i128); - }else{ + } else { return Err(ContractError::InvalidAmount); } } else { @@ -106,6 +149,67 @@ pub fn _handle_call_message( Ok(()) } +pub fn _is_initialized(e: Env) -> bool { + storage_types::has_upgrade_auth(&e) +} + +pub fn _set_upgrade_authority(e: Env, new_upgrade_authority: Address) { + let upgrade_authority = get_upgrade_authority(&e).unwrap(); + upgrade_authority.require_auth(); + set_upgrade_authority(&e, new_upgrade_authority); +} + +pub fn _upgrade(e: Env, new_wasm_hash: BytesN<32>) { + let upgrade_authority = get_upgrade_authority(&e).unwrap(); + upgrade_authority.require_auth(); + e.deployer().update_current_contract_wasm(new_wasm_hash); +} + +pub fn _extend_ttl(e: Env) { + e.storage() + .instance() + .extend_ttl(INSTANCE_LIFETIME_THRESHOLD, INSTANCE_BUMP_AMOUNT); +} + +pub fn _allowance(e: Env, from: Address, spender: Address) -> i128 { + read_allowance(&e, from, spender).amount +} + +pub fn _approve(e: Env, from: Address, spender: Address, amount: i128, expiration_ledger: u32) { + from.require_auth(); + + check_nonnegative_amount(amount); + + write_allowance(&e, from.clone(), spender.clone(), amount, expiration_ledger); + TokenUtils::new(&e) + .events() + .approve(from, spender, amount, expiration_ledger); +} + +pub fn _balance(e: Env, id: Address) -> i128 { + read_balance(&e, id) +} + +pub fn _transfer(e: Env, from: Address, to: Address, amount: i128) { + from.require_auth(); + + check_nonnegative_amount(amount); + spend_balance(&e, from.clone(), amount); + receive_balance(&e, to.clone(), amount); + TokenUtils::new(&e).events().transfer(from, to, amount); +} + +pub fn _transfer_from(e: Env, spender: Address, from: Address, to: Address, amount: i128) { + spender.require_auth(); + + check_nonnegative_amount(amount); + + spend_allowance(&e, from.clone(), spender, amount); + spend_balance(&e, from.clone(), amount); + receive_balance(&e, to.clone(), amount); + TokenUtils::new(&e).events().transfer(from, to, amount) +} + pub fn get_address(network_address: String, env: &Env) -> Result { let bytes = network_address.to_xdr(&env); @@ -141,14 +245,14 @@ pub fn get_address(network_address: String, env: &Env) -> Result XcallManagerClient< let client = XcallManagerClient::new(e, xcall_manager); return client; } + +pub fn _decimals(e: Env) -> u32 { + read_decimal(&e) +} + +pub fn _name(e: Env) -> String { + read_name(&e) +} + +pub fn _symbol(e: Env) -> String { + read_symbol(&e) +} + +pub fn _xcall_manager(e: Env) -> Address { + storage_types::get_xcall_manager(&e).unwrap() +} + +pub fn _xcall(e: Env) -> Address { + storage_types::get_xcall(&e).unwrap() +}