Skip to content

Commit

Permalink
Merge pull request #57 from spectrum-finance/fix_deploy_pool_box_sele…
Browse files Browse the repository at this point in the history
…ction

Fix box-selection bug
  • Loading branch information
kettlebell authored May 4, 2023
2 parents 7ce9509 + 75b62a9 commit 13edc0b
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 26 deletions.
162 changes: 138 additions & 24 deletions spectrum-deploy-lm-pool/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ async fn main() {
let tx_id = tx.id();
if let Err(e) = node.submit_tx(tx).await {
println!("ERROR SUBMITTING TO NODE: {:?}", e);
break;
} else {
println!(
"TX {:?} successfully submitted! (Description: {})",
Expand Down Expand Up @@ -154,6 +155,10 @@ pub async fn deploy_pool(
) -> Result<Vec<(Transaction, String)>, Error> {
let input = DeployPoolInputs::from(&config);
let (prover, addr) = Wallet::try_from_seed(config.operator_funding_secret).expect("Invalid seed");
println!(
"Wallet address: {:?}",
AddressEncoder::encode_address_as_string(NetworkPrefix::Mainnet, &addr)
);
let current_height = node.get_height().await;
validate_pool(&input, current_height)?;
let utxos = explorer.get_utxos(&addr).await;
Expand Down Expand Up @@ -192,6 +197,16 @@ fn deploy_pool_chain_transaction(
check_utxos(&utxos, &input)?;
println!("UTXOs fine: only reward and LQ tokens found.");

println!("UTXOS---------");
for ergo_box in &utxos {
println!(
" {}: value: {:?}, #tokens: {}",
ergo_box.box_id(),
ergo_box.value,
ergo_box.tokens.as_ref().map(|t| t.len()).unwrap_or(0)
);
}

let DeployPoolInputs {
conf,
tx_fee,
Expand Down Expand Up @@ -247,9 +262,20 @@ fn deploy_pool_chain_transaction(
token_desc,
token_amount|
-> Result<(Token, Transaction), Error> {
println!("Mint token");
let target_balance = calc_target_balance(*num_transactions_left)?;
println!(" Target balance: {:?}", target_balance);
let box_selector = SimpleBoxSelector::new();
let box_selection = box_selector.select(input_boxes, target_balance, &[])?;
println!(" Box selection:");
for ergo_box in &box_selection.boxes {
println!(
" {}: value: {:?}, #tokens: {}",
ergo_box.box_id(),
ergo_box.value,
ergo_box.tokens.as_ref().map(|t| t.len()).unwrap_or(0)
);
}
let token = Token {
token_id: box_selection.boxes.first().box_id().into(),
amount: token_amount,
Expand All @@ -259,18 +285,27 @@ fn deploy_pool_chain_transaction(
builder.mint_token(token.clone(), token_name, token_desc, 0);
let mut output_candidates = vec![builder.build()?];

let remaining_funds = ErgoBoxCandidateBuilder::new(
calc_target_balance(*num_transactions_left - 1)?,
ergo_tree,
height,
)
.build()?;
output_candidates.push(remaining_funds);
let next_target_balance = calc_target_balance(*num_transactions_left - 1)?;
let mut accumulated_cost = *next_target_balance.as_u64() + *erg_value_per_box.as_u64();

let funds_for_remaining_txs =
ErgoBoxCandidateBuilder::new(next_target_balance, ergo_tree.clone(), height).build()?;
output_candidates.push(funds_for_remaining_txs);

let miner_output = MinerOutput {
erg_value: NanoErg::from(tx_fee),
};
accumulated_cost += tx_fee.as_u64();
output_candidates.push(miner_output.into_candidate(height));

let input_funds_total = box_selection.boxes.iter().fold(NanoErg::from(0), |acc, ergobox| {
acc + NanoErg::from(ergobox.value)
});

let funds_remaining = input_funds_total.safe_sub(NanoErg::from(accumulated_cost));
let funds_remaining_out_box =
ErgoBoxCandidateBuilder::new(BoxValue::from(funds_remaining), ergo_tree, height).build()?;
output_candidates.push(funds_remaining_out_box);
let inputs = TxIoVec::from_vec(
box_selection
.boxes
Expand All @@ -288,6 +323,20 @@ fn deploy_pool_chain_transaction(
};
let signed_tx = prover.sign(tx_candidate)?;

println!(" outputs:");
let mut output_total = 0;
for ergo_box in &signed_tx.outputs {
println!(
" {}: value: {:?}, #tokens: {}",
ergo_box.box_id(),
ergo_box.value,
ergo_box.tokens.as_ref().map(|t| t.len()).unwrap_or(0)
);
output_total += ergo_box.value.as_u64();
}

assert_eq!(input_funds_total, NanoErg::from(output_total));

*num_transactions_left -= 1;
Ok((token, signed_tx))
};
Expand Down Expand Up @@ -394,6 +443,19 @@ fn deploy_pool_chain_transaction(
};
num_transactions_left -= 1;
let tx_0 = prover.sign(tx_candidate)?;
let mut output_total = 0;
println!("TX_0 outputs:");
for ergo_box in &tx_0.outputs {
println!(
" {}: value: {:?}, #tokens: {}",
ergo_box.box_id(),
ergo_box.value,
ergo_box.tokens.as_ref().map(|t| t.len()).unwrap_or(0)
);
output_total += ergo_box.value.as_u64();
}

assert_eq!(funds_total, NanoErg::from(output_total));

// TX 1: Mint pool NFT -------------------------------------------------------------------------
let inputs = if !remaining_tokens.is_empty() {
Expand All @@ -413,6 +475,7 @@ fn deploy_pool_chain_transaction(
} else {
filter_tx_outputs(tx_0.outputs.clone().to_vec())
};
println!("TX_1: pool NFT");
let (pool_nft, signed_mint_pool_nft_tx) = mint_token(
inputs,
&mut num_transactions_left,
Expand All @@ -423,6 +486,7 @@ fn deploy_pool_chain_transaction(

// TX 2: Mint vLQ tokens -----------------------------------------------------------------------
let inputs = filter_tx_outputs(signed_mint_pool_nft_tx.outputs.clone().to_vec());
println!("TX_2: vLQ tokens");
let (vlq_tokens, signed_mint_vlq_tokens_tx) = mint_token(
inputs,
&mut num_transactions_left,
Expand All @@ -433,6 +497,7 @@ fn deploy_pool_chain_transaction(

// TX 3: Mint TMP tokens -----------------------------------------------------------------------
let inputs = filter_tx_outputs(signed_mint_vlq_tokens_tx.outputs.clone().to_vec());
println!("TX_3: TMP tokens");
let (tmp_tokens, signed_mint_tmp_tokens_tx) = mint_token(
inputs,
&mut num_transactions_left,
Expand All @@ -454,6 +519,8 @@ fn deploy_pool_chain_transaction(
let box_with_remaining_funds = signed_mint_tmp_tokens_tx.outputs.get(1).unwrap().clone();

let target_balance = calc_target_balance(num_transactions_left)?;
println!("TX_4:");
println!(" Target balance: {:?}", target_balance);
let box_selector = SimpleBoxSelector::new();
let box_selection = box_selector.select(
vec![
Expand All @@ -473,6 +540,20 @@ fn deploy_pool_chain_transaction(
],
)?;

println!(" Box selection:");
for ergo_box in &box_selection.boxes {
println!(
" {}: value: {:?}, #tokens: {}",
ergo_box.box_id(),
ergo_box.value,
ergo_box.tokens.as_ref().map(|t| t.len()).unwrap_or(0)
);
}

let input_funds_total = box_selection.boxes.iter().fold(NanoErg::from(0), |acc, ergobox| {
acc + NanoErg::from(ergobox.value)
});

let inputs = TxIoVec::from_vec(
box_selection
.boxes
Expand All @@ -499,27 +580,22 @@ fn deploy_pool_chain_transaction(
);

let pool_init_box = pool_init_box_builder.build()?;
let remaining_funds = ErgoBoxCandidateBuilder::new(
calc_target_balance(num_transactions_left - 1)?,
addr.script()?,
height,
)
.build()?;

let box_of_consumed_tokens = ErgoBoxCandidateBuilder::new(
BoxValue::try_from(4 * erg_value_per_box.as_u64()).unwrap(),
addr.script()?,
height,
);
let next_target_balance = calc_target_balance(num_transactions_left - 1)?;
let funds_for_remaining_tx =
ErgoBoxCandidateBuilder::new(next_target_balance, addr.script()?, height).build()?;

let miner_output = MinerOutput {
erg_value: NanoErg::from(tx_fee),
};
let accumulated_cost = *next_target_balance.as_u64() + *erg_value_per_box.as_u64() + tx_fee.as_u64();
let funds_remaining = input_funds_total.safe_sub(NanoErg::from(accumulated_cost));
let funds_remaining_out_box =
ErgoBoxCandidateBuilder::new(BoxValue::from(funds_remaining), addr.script()?, height).build()?;
let output_candidates = vec![
pool_init_box,
remaining_funds,
funds_for_remaining_tx,
miner_output.into_candidate(height),
box_of_consumed_tokens.build()?,
funds_remaining_out_box,
];

let output_candidates = TxIoVec::from_vec(output_candidates).unwrap();
Expand All @@ -530,6 +606,19 @@ fn deploy_pool_chain_transaction(
};
let pool_input_tx = prover.sign(tx_candidate)?;

println!(" outputs:");
let mut output_total = 0;
for ergo_box in &pool_input_tx.outputs {
println!(
" {}: value: {:?}, #tokens: {}",
ergo_box.box_id(),
ergo_box.value,
ergo_box.tokens.as_ref().map(|t| t.len()).unwrap_or(0)
);
output_total += ergo_box.value.as_u64();
}

assert_eq!(input_funds_total, NanoErg::from(output_total));
num_transactions_left -= 1;

// TX 5: Create first LM pool, stake bundle and redeemer out boxes -----------------------------
Expand All @@ -549,6 +638,17 @@ fn deploy_pool_chain_transaction(
],
)?;

println!("TX_5:");
println!(" Target balance: {:?}", target_balance);
println!(" Box selection:");
for ergo_box in &box_selection.boxes {
println!(
" {}: value: {:?}, #tokens: {}",
ergo_box.box_id(),
ergo_box.value,
ergo_box.tokens.as_ref().map(|t| t.len()).unwrap_or(0)
);
}
let inputs = TxIoVec::from_vec(
box_selection
.boxes
Expand Down Expand Up @@ -618,12 +718,12 @@ fn deploy_pool_chain_transaction(
staking_bundle_candidate,
];

let funds_total = box_selection.boxes.iter().fold(NanoErg::from(0), |acc, ergobox| {
let input_funds_total = box_selection.boxes.iter().fold(NanoErg::from(0), |acc, ergobox| {
acc + NanoErg::from(ergobox.value)
});
let accumulated_cost =
NanoErg::from(3 * BoxValue::from(MIN_SAFE_FAT_BOX_VALUE).as_u64() + tx_fee.as_u64());
let remaining_funds = funds_total - accumulated_cost;
let remaining_funds = input_funds_total - accumulated_cost;
if remaining_funds >= MIN_SAFE_BOX_VALUE {
let box_of_consumed_tokens =
ErgoBoxCandidateBuilder::new(BoxValue::from(remaining_funds), addr.script()?, height);
Expand All @@ -642,6 +742,20 @@ fn deploy_pool_chain_transaction(
};
let init_pool_tx = prover.sign(tx_candidate)?;

println!(" outputs:");
let mut output_total = 0;
for ergo_box in &init_pool_tx.outputs {
println!(
" {}: value: {:?}, #tokens: {}",
ergo_box.box_id(),
ergo_box.value,
ergo_box.tokens.as_ref().map(|t| t.len()).unwrap_or(0)
);
output_total += ergo_box.value.as_u64();
}

assert_eq!(input_funds_total, NanoErg::from(output_total));

Ok(vec![
(tx_0, String::from("Move LQ and reward tokens to single box")),
(signed_mint_pool_nft_tx, String::from("Mint Pool NFT")),
Expand Down Expand Up @@ -742,7 +856,7 @@ fn validate_pool(input: &DeployPoolInputs, current_height: u32) -> Result<(), Po
let max_rounding_error = input.conf.max_rounding_error;
let budget_amt = input.conf.program_budget.amount;

if (epoch_num as u64) > max_rounding_error
if (epoch_num as u64) >= max_rounding_error
|| max_rounding_error * (epoch_num as u64) >= budget_amt / (epoch_num as u64)
{
return Err(PoolValidationError::FailedMaxRoundingErrorBounds);
Expand Down
18 changes: 16 additions & 2 deletions spectrum-offchain-lm/src/data/pool.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use std::fmt::{Display, Formatter};

use derive_more::Display;
use ergo_lib::ergo_chain_types::Digest32;
use ergo_lib::ergotree_ir::chain::ergo_box::{
BoxTokens, ErgoBox, ErgoBoxCandidate, NonMandatoryRegisterId, NonMandatoryRegisters,
};
use ergo_lib::ergotree_ir::chain::token::Token;
use ergo_lib::ergotree_ir::chain::token::{Token, TokenId};
use ergo_lib::ergotree_ir::mir::constant::{Constant, TryExtractInto};
use log::trace;
use nonempty::NonEmpty;
Expand Down Expand Up @@ -228,7 +229,17 @@ impl Pool {
if redeem.expected_lq.amount != bundle.vlq.amount {
return Err(PoolOperationError::Permanent(PermanentError::LiqudityMismatch));
}
if !self.epoch_completed() {

// The following pool is forever stuck, so we skip the `epoch_completed` check for it and
// automatically allow all redeems for this pool to be processed.
let stuck_pool_id = PoolId::from(TokenId::from(
Digest32::try_from(String::from(
"69eff57ea62b13c58e5668e3fbc9927fdb2dffb1c692261f98728a665b2f8abb",
))
.unwrap(),
));

if bundle.pool_id != stuck_pool_id && !self.epoch_completed() {
return Err(PoolOperationError::Temporal(TemporalError::LiquidityMoveBlocked));
}
let release_lq = redeem.expected_lq;
Expand Down Expand Up @@ -409,6 +420,9 @@ impl Pool {

fn current_epoch_ix(&self, height: u32) -> u32 {
let cur_block_ix = height as i64 - self.conf.program_start as i64 + 1;
if cur_block_ix < 0 {
return 0;
}
let cur_epoch_ix_rem = cur_block_ix % self.conf.epoch_len as i64;
let cur_epoch_ix_r = cur_block_ix / self.conf.epoch_len as i64;
if cur_epoch_ix_rem == 0 && cur_epoch_ix_r == 0 {
Expand Down

0 comments on commit 13edc0b

Please sign in to comment.