Skip to content

Commit

Permalink
wasm_planner support gas fees & add ICS20 withdrawal (#3198)
Browse files Browse the repository at this point in the history
* wasm_planner support gas fees & add ICS20 withdrawal

* ./deployments/scripts/protobuf-codegen

* wasm_planner support gas fees & add ICS20 withdrawal

* wasm_planner support gas fees & add ICS20 withdrawal
  • Loading branch information
Valentine1898 authored Oct 23, 2023
1 parent 601b511 commit 7aefd27
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 4 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ penumbra-chain = { path = "../core/component/chain", default-features =
penumbra-compact-block = { path = "../core/component/compact-block", default-features = false }
penumbra-dex = { path = "../core/component/dex", default-features = false, features = ["proving-keys"] }
penumbra-fee = { path = "../core/component/fee", default-features = false }
penumbra-ibc = { path = "../core/component/ibc", default-features = false }
penumbra-keys = { path = "../core/keys" }
penumbra-num = { path = "../core/num" }
penumbra-proto = { path = "../proto", default-features = false }
Expand Down
64 changes: 61 additions & 3 deletions crates/wasm/src/planner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::{
use anyhow::{anyhow, Result};
use rand_core::{CryptoRng, RngCore};

use penumbra_asset::{asset::DenomMetadata, Balance, Value};
use penumbra_asset::{asset::DenomMetadata, Balance, Value, STAKING_TOKEN_ASSET_ID};
use penumbra_chain::params::{ChainParameters, FmdParameters};
use penumbra_dex::{
lp::action::{PositionClose, PositionOpen},
Expand All @@ -19,18 +19,20 @@ use penumbra_dex::{
swap_claim::SwapClaimPlan,
TradingPair,
};
use penumbra_fee::Fee;
use penumbra_fee::{Fee, GasPrices};
use penumbra_governance::{
proposal_state::Outcome as ProposalOutcome, DelegatorVotePlan, Proposal, ProposalDepositClaim,
ProposalSubmit, ProposalWithdraw, ValidatorVote, Vote,
};
use penumbra_ibc::{IbcAction, Ics20Withdrawal};
use penumbra_keys::Address;
use penumbra_num::Amount;
use penumbra_proto::view::v1alpha1::{NotesForVotingRequest, NotesRequest};
use penumbra_shielded_pool::{Note, OutputPlan, SpendPlan};
use penumbra_stake::{rate::RateData, validator};
use penumbra_stake::{IdentityKey, UndelegateClaimPlan};
use penumbra_tct as tct;
use penumbra_transaction::gas::GasCost;
use penumbra_transaction::{
memo::MemoPlaintext,
plan::{ActionPlan, MemoPlan, TransactionPlan},
Expand All @@ -45,6 +47,8 @@ pub struct Planner<R: RngCore + CryptoRng> {
balance: Balance,
vote_intents: BTreeMap<u64, VoteIntent>,
plan: TransactionPlan,
ibc_actions: Vec<IbcAction>,
gas_prices: GasPrices,
// IMPORTANT: if you add more fields here, make sure to clear them when the planner is finished
}

Expand Down Expand Up @@ -73,9 +77,17 @@ impl<R: RngCore + CryptoRng> Planner<R> {
balance: Balance::default(),
vote_intents: BTreeMap::default(),
plan: TransactionPlan::default(),
ibc_actions: Vec::new(),
gas_prices: GasPrices::zero(),
}
}

/// Set the current gas prices for fee prediction.
pub fn set_gas_prices(&mut self, gas_prices: GasPrices) -> &mut Self {
self.gas_prices = gas_prices;
self
}

/// Get the current transaction balance of the planner.
pub fn balance(&self) -> &Balance {
&self.balance
Expand Down Expand Up @@ -135,6 +147,22 @@ impl<R: RngCore + CryptoRng> Planner<R> {
self
}

/// Calculate gas cost-based fees and add to the transaction plan.
///
/// This function should be called once.
pub fn add_gas_fees(&mut self) -> &mut Self {
let minimum_fee = self.gas_prices.price(&self.plan.gas_cost());

// Since paying the fee possibly requires adding an additional Spend to the
// transaction, which would then change the fee calculation, we multiply the
// fee here by a factor of 2 and then recalculate and capture the excess as
// change outputs.
let fee = Fee::from_staking_token_amount(minimum_fee * Amount::from(2u32));
self.balance += fee.0;
self.plan.fee = fee;
self
}

/// Spend a specific positioned note in the transaction.
///
/// If you don't use this method to specify spends, they will be filled in automatically from
Expand Down Expand Up @@ -305,6 +333,18 @@ impl<R: RngCore + CryptoRng> Planner<R> {
self
}

/// Perform an ICS-20 withdrawal
pub fn ics20_withdrawal(&mut self, withdrawal: Ics20Withdrawal) -> &mut Self {
self.action(ActionPlan::Withdrawal(withdrawal));
self
}

/// Perform an IBC action
pub fn ibc_action(&mut self, ibc_action: IbcAction) -> &mut Self {
self.action(ActionPlan::IbcAction(ibc_action));
self
}

/// Vote with all possible vote weight on a given proposal.
///
/// Voting twice on the same proposal in the same planner will overwrite the previous vote.
Expand Down Expand Up @@ -387,6 +427,11 @@ impl<R: RngCore + CryptoRng> Planner<R> {
self.spend(record.note, record.position);
}

// Add any IBC actions to the planner
for ibc_action in self.ibc_actions.clone() {
self.ibc_action(ibc_action);
}

// Add the required votes to the planner
for (
records,
Expand Down Expand Up @@ -450,8 +495,19 @@ impl<R: RngCore + CryptoRng> Planner<R> {
}
}

// For any remaining provided balance, make a single change note for each
// Since we over-estimate the fees to be paid upfront by a fixed multiple to account
// for the cost of any additional `Spend` actions necessary to pay the fee, we need
// to now calculate the transaction's fee again and capture the excess as change
// by subtracting the excess from the required value balance.
let tx_real_fee = self.gas_prices.price(&self.plan.gas_cost());
let excess_fee_spent = self.plan.fee.amount() - tx_real_fee;
self.balance -= Value {
amount: excess_fee_spent,
asset_id: *STAKING_TOKEN_ASSET_ID,
};
self.plan.fee = Fee::from_staking_token_amount(tx_real_fee);

// For any remaining provided balance, make a single change note for each
for value in self.balance.provided().collect::<Vec<_>>() {
self.output(value, self_address);
}
Expand Down Expand Up @@ -486,6 +542,8 @@ impl<R: RngCore + CryptoRng> Planner<R> {
// Clear the planner and pull out the plan to return
self.balance = Balance::zero();
self.vote_intents = BTreeMap::new();
self.ibc_actions = Vec::new();
self.gas_prices = GasPrices::zero();
let plan = mem::take(&mut self.plan);

Ok(plan)
Expand Down
27 changes: 26 additions & 1 deletion crates/wasm/src/wasm_planner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use wasm_bindgen::JsValue;
use penumbra_chain::params::{ChainParameters, FmdParameters};
use penumbra_dex::swap_claim::SwapClaimPlan;
use penumbra_proto::core::asset::v1alpha1::{DenomMetadata, Value};
use penumbra_proto::core::component::fee::v1alpha1::Fee;
use penumbra_proto::core::component::fee::v1alpha1::{Fee, GasPrices};
use penumbra_proto::core::component::ibc::v1alpha1::Ics20Withdrawal;
use penumbra_proto::core::keys::v1alpha1::Address;
use penumbra_proto::core::transaction::v1alpha1::MemoPlaintext;
use penumbra_proto::crypto::tct::v1alpha1::StateCommitment;
Expand Down Expand Up @@ -65,6 +66,15 @@ impl WasmPlanner {
Ok(())
}

/// Set gas prices
/// Arguments:
/// gas_prices: `GasPrices`
pub fn set_gas_prices(&mut self, gas_prices: JsValue) -> WasmResult<()> {
let gas_prices_proto: GasPrices = serde_wasm_bindgen::from_value(gas_prices)?;
self.planner.set_gas_prices(gas_prices_proto.try_into()?);
Ok(())
}

/// Add memo to plan
/// Arguments:
/// memo: `MemoPlaintext`
Expand Down Expand Up @@ -164,6 +174,15 @@ impl WasmPlanner {
Ok(())
}

/// Add ICS20 withdrawal to plan
/// Arguments:
/// withdrawal: `Ics20Withdrawal`
pub fn ics20_withdrawal(&mut self, withdrawal: JsValue) -> WasmResult<()> {
let withdrawal_proto: Ics20Withdrawal = serde_wasm_bindgen::from_value(withdrawal)?;
self.planner.ics20_withdrawal(withdrawal_proto.try_into()?);
Ok(())
}

/// Builds transaction plan.
/// Refund address provided in the case there is extra balances to be returned.
/// Arguments:
Expand All @@ -172,6 +191,12 @@ impl WasmPlanner {
pub async fn plan(&mut self, refund_address: JsValue) -> WasmResult<JsValue> {
utils::set_panic_hook();

// Calculate the gas that needs to be paid for the transaction based on the configured gas prices.
// Note that _paying the fee might incur an additional `Spend` action_, thus increasing the fee,
// so we slightly overpay here and then capture the excess as change later during `plan_with_spendable_and_votable_notes`.
// Add the fee to the planner's internal balance.
self.planner.add_gas_fees();

let mut spendable_notes = Vec::new();

let (spendable_requests, _) = self.planner.notes_requests();
Expand Down

0 comments on commit 7aefd27

Please sign in to comment.