Skip to content

Commit

Permalink
feat: vault top up
Browse files Browse the repository at this point in the history
  • Loading branch information
bucurdavid committed Jul 17, 2024
1 parent c51654e commit 20dcb4c
Show file tree
Hide file tree
Showing 8 changed files with 237 additions and 6 deletions.
6 changes: 3 additions & 3 deletions interaction/devnet.snippets.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PROXY=https://devnet-gateway.multiversx.com
CHAIN_ID="D"

WALLET="./wallet.pem"
WALLET="../wallet_dev.pem"
USER="./wallet2.pem"

ADDRESS=$(mxpy data load --key=address-devnet)
Expand All @@ -16,7 +16,7 @@ TOKEN_HEX="0x$(echo -n ${TOKEN} | xxd -p -u | tr -d '\n')"
# --bytecode output-docker/core-mx-life-bonding-sc/core-mx-life-bonding-sc.wasm \
deploy(){
mxpy --verbose contract deploy \
--bytecode output-docker/core-mx-life-bonding-sc/core-mx-life-bonding-sc.wasm \
--bytecode output/core-mx-life-bonding-sc.wasm \
--outfile deployOutput \
--metadata-not-readable \
--metadata-payable-by-sc \
Expand All @@ -42,7 +42,7 @@ deploy(){
# in below code example we added --metadata-payable to add PAYABLE to the prop of the SC and removed --metadata-not-readable to make it READABLE
upgrade(){
mxpy --verbose contract upgrade ${ADDRESS} \
--bytecode output-docker/core-mx-life-bonding-sc/core-mx-life-bonding-sc.wasm \
--bytecode output/core-mx-life-bonding-sc.wasm \
--metadata-not-readable \
--metadata-payable-by-sc \
--pem ${WALLET} \
Expand Down
1 change: 1 addition & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ pub const ERR_ALREADY_IN_STORAGE: &str = "Already in storage";
pub const ERR_NOT_IN_STORAGE: &str = "Not in storage";
pub const ERR_ALREADY_ACTIVE: &str = "Already active";
pub const ERR_ALREADY_INACTIVE: &str = "Already inactive";
pub const ERR_VAULT_NONCE_NOT_SET: &str = "Vault nonce not set";
51 changes: 50 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
ERR_BOND_NOT_FOUND, ERR_CONTRACT_NOT_READY, ERR_ENDPOINT_CALLABLE_ONLY_BY_ACCEPTED_CALLERS,
ERR_INVALID_AMOUNT, ERR_INVALID_LOCK_PERIOD, ERR_INVALID_TIMELINE_TO_PROOF,
ERR_INVALID_TIMELINE_TO_REFUND, ERR_INVALID_TOKEN_IDENTIFIER,
ERR_PENALTIES_EXCEED_WITHDRAWAL_AMOUNT, ERR_REFUND_NOT_FOUND,
ERR_PENALTIES_EXCEED_WITHDRAWAL_AMOUNT, ERR_REFUND_NOT_FOUND, ERR_VAULT_NONCE_NOT_SET,
},
storage::Refund,
};
Expand Down Expand Up @@ -362,4 +362,53 @@ pub trait LifeBondingContract:
self.compensations().swap_remove(&compensation_id);
}
}

#[endpoint(setVaultNonce)]
fn set_vault_nonce(&self, token_identifier: TokenIdentifier, nonce: u64) {
let caller = self.blockchain().get_caller();

let bond_id = self
.bonds_ids()
.get_id_non_zero((token_identifier.clone(), nonce));

let bond_cache = BondCache::new(self, bond_id);

require!(bond_cache.address == caller, ERR_BOND_NOT_FOUND);

self.address_vault_nonce(&caller, &token_identifier)
.set(nonce);
}

#[payable("*")]
#[endpoint(topUpVault)]
fn top_up_vault(&self, token_identifier: TokenIdentifier, nonce: u64) {
let caller = self.blockchain().get_caller();

require!(
self.address_vault_nonce(&caller, &token_identifier).get() == nonce,
ERR_VAULT_NONCE_NOT_SET
);

let bond_id = self
.bonds_ids()
.get_id_non_zero((token_identifier.clone(), nonce));

let mut bond_cache = BondCache::new(self, bond_id);

require!(bond_cache.address == caller, ERR_BOND_NOT_FOUND);

let payment = self.call_value().single_esdt();

require!(
payment.token_identifier == self.bond_payment_token().get(),
ERR_INVALID_TOKEN_IDENTIFIER
);

let current_timestamp = self.blockchain().get_block_timestamp();

bond_cache.unbond_timestamp = current_timestamp + bond_cache.lock_period;
bond_cache.bond_timestamp = current_timestamp;
bond_cache.bond_amount += &payment.amount;
bond_cache.remaining_amount += &payment.amount;
}
}
10 changes: 10 additions & 0 deletions src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,14 @@ pub trait StorageModule {
#[view(getTotalBondAmount)]
#[storage_mapper("total_bond_amount")]
fn total_bond_amount(&self) -> SingleValueMapper<BigUint>;

// NFMEid storage

#[view(getAddressVaultNone)]
#[storage_mapper("address_vault_nonce")]
fn address_vault_nonce(
&self,
address: &ManagedAddress,
token_identifier: &TokenIdentifier,
) -> SingleValueMapper<u64>;
}
40 changes: 40 additions & 0 deletions tests/bonding_state/bonding_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -602,4 +602,44 @@ impl ContractState {
);
self
}

pub fn set_vault_nonce(
&mut self,
caller: &str,
token_identifier: &[u8],
nonce: u64,
expect: Option<TxExpect>,
) -> &mut Self {
self.world.sc_call(
ScCallStep::new()
.from(caller)
.call(
self.contract
.set_vault_nonce(managed_token_id!(token_identifier), nonce),
)
.expect(expect.unwrap_or(TxExpect::ok())),
);
self
}

pub fn top_up_vault(
&mut self,
caller: &str,
payment: (&str, u64, u64),
token_identifier: &[u8],
nonce: u64,
expect: Option<TxExpect>,
) -> &mut Self {
self.world.sc_call(
ScCallStep::new()
.from(caller)
.esdt_transfer(payment.0, payment.1, payment.2)
.call(
self.contract
.top_up_vault(managed_token_id!(token_identifier), nonce),
)
.expect(expect.unwrap_or(TxExpect::ok())),
);
self
}
}
1 change: 1 addition & 0 deletions tests/endpoints/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ mod claim_refund;
mod deploy_upgrade;
mod proof;
mod renew;
mod vault;
mod views;
mod withdraw;
127 changes: 127 additions & 0 deletions tests/endpoints/vault.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use multiversx_sc::{imports::SingleValue, types::BigUint};
use multiversx_sc_scenario::{
imports::{
Account, AddressValue, BytesValue, CheckAccount, CheckStateStep, ScQueryMandos,
ScQueryStep, SetStateStep, TransferStep, TxExpect,
},
managed_address, managed_token_id,
};

use core_mx_life_bonding_sc::storage::{Bond, ProxyTrait};
use core_mx_life_bonding_sc::views::ProxyTrait as _;

use crate::bonding_state::bonding_state::{
ContractState, BONDING_CONTRACT_ADDRESS_EXPR, DATA_NFT_IDENTIFIER, DATA_NFT_IDENTIFIER_EXPR,
FIRST_USER_ADDRESS_EXPR, ITHEUM_TOKEN_IDENTIFIER, ITHEUM_TOKEN_IDENTIFIER_EXPR,
MINTER_CONTRACT_ADDRESS_EXPR, OWNER_BONDING_CONTRACT_ADDRESS_EXPR,
};

#[test]
fn vault_tests() {
let mut state = ContractState::new();
let first_user_address = state.first_user_address.clone();
let admin = state.admin.clone();

state
.default_deploy_and_set(10u64, 100u64)
.remove_accepted_caller(OWNER_BONDING_CONTRACT_ADDRESS_EXPR, admin.clone(), None)
.set_accepted_caller(
OWNER_BONDING_CONTRACT_ADDRESS_EXPR,
AddressValue::from(MINTER_CONTRACT_ADDRESS_EXPR).to_address(),
None,
);

state.world.set_state_step(
SetStateStep::new().put_account(
FIRST_USER_ADDRESS_EXPR,
Account::new()
.nonce(1)
.balance("1_000")
.esdt_balance(ITHEUM_TOKEN_IDENTIFIER_EXPR, "300")
.esdt_nft_balance(DATA_NFT_IDENTIFIER_EXPR, 1u64, 2u64, None::<BytesValue>),
),
);

state.unpause_contract(OWNER_BONDING_CONTRACT_ADDRESS_EXPR, None);

state.world.transfer_step(
// mocks the mint call in minter and transfers the bond amount
TransferStep::new()
.from(FIRST_USER_ADDRESS_EXPR)
.to(MINTER_CONTRACT_ADDRESS_EXPR)
.esdt_transfer(ITHEUM_TOKEN_IDENTIFIER, 0u64, 100u64),
);

state.set_vault_nonce(
FIRST_USER_ADDRESS_EXPR,
DATA_NFT_IDENTIFIER,
1u64,
Some(TxExpect::user_error("str:Unknown object")),
);

state.bond(
MINTER_CONTRACT_ADDRESS_EXPR, // another bond contract acts as minter mock
first_user_address.clone(),
DATA_NFT_IDENTIFIER,
1u64,
10u64,
(ITHEUM_TOKEN_IDENTIFIER_EXPR, 0u64, 100u64), // bond amount
None,
);

state.set_vault_nonce(FIRST_USER_ADDRESS_EXPR, DATA_NFT_IDENTIFIER, 1u64, None);

state.world.sc_query(
ScQueryStep::new()
.call(state.contract.address_vault_nonce(
AddressValue::from(FIRST_USER_ADDRESS_EXPR).to_address(),
DATA_NFT_IDENTIFIER,
))
.expect_value(SingleValue::from(1u64)), // vault nonce 1
);

state.top_up_vault(
FIRST_USER_ADDRESS_EXPR,
(ITHEUM_TOKEN_IDENTIFIER_EXPR, 0u64, 100u64),
DATA_NFT_IDENTIFIER,
1u64,
None,
);

state.top_up_vault(
FIRST_USER_ADDRESS_EXPR,
(ITHEUM_TOKEN_IDENTIFIER_EXPR, 0u64, 100u64),
DATA_NFT_IDENTIFIER,
1u64,
None,
);

state.world.sc_query(
ScQueryStep::new()
.call(state.contract.get_bond(1u64))
.expect_value(Bond {
bond_id: 1u64,
address: managed_address!(&first_user_address.clone()),
token_identifier: managed_token_id!(DATA_NFT_IDENTIFIER),
nonce: 1u64,
lock_period: 10u64,
bond_timestamp: 0u64,
bond_amount: BigUint::from(300u64),
unbond_timestamp: 10u64,
remaining_amount: BigUint::from(300u64), // 100 (bond) + 200 top up
}),
);

state
.world
.set_state_step(SetStateStep::new().block_timestamp(11u64));

state.withdraw(FIRST_USER_ADDRESS_EXPR, DATA_NFT_IDENTIFIER, 1u64, None);

state
.world
.check_state_step(CheckStateStep::new().put_account(
FIRST_USER_ADDRESS_EXPR,
CheckAccount::new().esdt_balance(ITHEUM_TOKEN_IDENTIFIER_EXPR, "300"), //withdraw full amount after lock
));
}
7 changes: 5 additions & 2 deletions wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

// Init: 1
// Upgrade: 1
// Endpoints: 50
// Endpoints: 53
// Async Callback (empty): 1
// Total number of exported functions: 53
// Total number of exported functions: 56

#![no_std]

Expand All @@ -25,8 +25,11 @@ multiversx_sc_wasm_adapter::endpoints! {
renew => renew
proof => add_proof
claimRefund => claim_refund
setVaultNonce => set_vault_nonce
topUpVault => top_up_vault
getCompensationBlacklist => compensation_blacklist
getTotalBondAmount => total_bond_amount
getAddressVaultNone => address_vault_nonce
getBond => get_bond
getCompensation => get_compensation
getCompensations => get_compensations
Expand Down

0 comments on commit 20dcb4c

Please sign in to comment.