From 663f522778b11bdc6e914e48a2dcdc035dcf658a Mon Sep 17 00:00:00 2001 From: Jon Ator Date: Tue, 19 Sep 2023 18:24:03 -0400 Subject: [PATCH 1/3] add new packages to workspace (#2162) --- .vscode/osmosis-frontend.code-workspace | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.vscode/osmosis-frontend.code-workspace b/.vscode/osmosis-frontend.code-workspace index 3b86b7ac74d..36e38a842a5 100644 --- a/.vscode/osmosis-frontend.code-workspace +++ b/.vscode/osmosis-frontend.code-workspace @@ -12,6 +12,12 @@ { "path": "../packages/stores/" }, + { + "path": "../packages/types/" + }, + { + "path": "../packages/utils/" + }, { "path": "../packages/web/" } From fe36573096b049c07ea90a6f4512b7cce2821041 Mon Sep 17 00:00:00 2001 From: Matt Upham <30577966+mattupham@users.noreply.github.com> Date: Tue, 19 Sep 2023 16:18:46 -0700 Subject: [PATCH 2/3] mattupham/collect and reinvest (#2136) * base func * Add tsdoc * Add coin and types to call * Remove space * Test collect and reinvest * Convert to CoinPretty * Update buttons * Update add button disabled states * Remove LD redirect * get pools in price store (#2166) * Enable redirect * Rename --------- Co-authored-by: Jon Ator --- packages/stores/src/account/base.ts | 22 ++++ packages/stores/src/account/osmosis/index.ts | 55 +++++++++- packages/stores/src/price/pool.ts | 5 +- .../web/components/cards/rewards-card.tsx | 40 ++++--- .../web/components/cards/stake-dashboard.tsx | 102 ++++++++++++------ packages/web/localizations/en.json | 1 + 6 files changed, 174 insertions(+), 51 deletions(-) diff --git a/packages/stores/src/account/base.ts b/packages/stores/src/account/base.ts index de438c6daac..8745e2fc016 100644 --- a/packages/stores/src/account/base.ts +++ b/packages/stores/src/account/base.ts @@ -392,6 +392,28 @@ export class AccountStore[] = []> { return new Error(errorMessage); } + /** + * Signs a transaction message and broadcasts it to the specified blockchain. + * + * @param chainNameOrId - Chain name or ID where the transaction will be broadcasted. + * @param type - Type of the transaction - this string is used to identify the transaction going through the pipeline. + * @param msgs - Array of messages to be included in the transaction or a function that returns such array. + * @param memo - Optional memo for the transaction. Default is an empty string. + * @param fee - Optional transaction fee details, if not provided the fee will be estimated. + * @param _signOptions - Optional Keplr sign options for customizing the sign process. + * @param onTxEvents - Optional callback or set of callbacks to be called based on transaction lifecycle events: + * - `onBroadcastFailed`: Invoked when the broadcast fails. + * - `onBroadcasted`: Invoked when the transaction is successfully broadcasted. + * - `onFulfill`: Invoked when the transaction is successfully fulfilled. + * + * @throws {Error} Throws an error if: + * - Wallet for the given chain is not provided or not connected. + * - There are no messages to send. + * - Wallet address is missing. + * - Broadcasting the transaction fails. + * + * @returns {Promise} Resolves when the transaction is broadcasted and all events are processed, otherwise it rejects. + */ async signAndBroadcast( chainNameOrId: string, type: string | "unknown", diff --git a/packages/stores/src/account/osmosis/index.ts b/packages/stores/src/account/osmosis/index.ts index 23be9c2d4db..eed33d22d36 100644 --- a/packages/stores/src/account/osmosis/index.ts +++ b/packages/stores/src/account/osmosis/index.ts @@ -2437,7 +2437,6 @@ export class OsmosisAccountImpl { if (!tx.code) { // Refresh the balances const queries = this.queriesStore.get(this.chainId); - queries.queryBalances .getQueryBech32Address(this.address) .balances.forEach((balance) => balance.waitFreshResponse()); @@ -2460,6 +2459,60 @@ export class OsmosisAccountImpl { ); } + /** + * Method to withdraw delegation rewards and delegate to validator set - staking collect and reinvest + * @param coin The coin object with denom and amount to delegate. + * @param memo Transaction memo. + * @param onFulfill Callback to handle tx fulfillment given raw response. + */ + async sendWithdrawDelegationRewardsAndSendDelegateToValidatorSetMsgs( + coin: { amount: string; denom: Currency }, + memo: string = "", + onFulfill?: (tx: DeliverTxResponse) => void + ) { + const withdrawDelegationRewardsMsg = + this.msgOpts.withdrawDelegationRewards.messageComposer({ + delegator: this.address, + }); + + const delegateToValidatorSetMsg = + this.msgOpts.delegateToValidatorSet.messageComposer({ + delegator: this.address, + coin: { + denom: coin.denom.coinMinimalDenom, + amount: coin.amount, + }, + }); + + await this.base.signAndBroadcast( + this.chainId, + "withdrawDelegationRewardsAndSendDelegateToValidatorSet", + [withdrawDelegationRewardsMsg, delegateToValidatorSetMsg], + memo, + undefined, + undefined, + (tx) => { + if (!tx.code) { + // Refresh the balances + const queries = this.queriesStore.get(this.chainId); + + queries.queryBalances + .getQueryBech32Address(this.address) + .balances.forEach((balance) => balance.waitFreshResponse()); + + queries.cosmos.queryDelegations + .getQueryBech32Address(this.address) + .waitFreshResponse(); + + queries.cosmos.queryRewards + .getQueryBech32Address(this.address) + .waitFreshResponse(); + } + onFulfill?.(tx); + } + ); + } + protected get queries() { // eslint-disable-next-line return this.queriesStore.get(this.chainId).osmosis!; diff --git a/packages/stores/src/price/pool.ts b/packages/stores/src/price/pool.ts index 465d21de1df..27e880b8351 100644 --- a/packages/stores/src/price/pool.ts +++ b/packages/stores/src/price/pool.ts @@ -48,7 +48,10 @@ export class PoolFallbackPriceStore vsCurrency = this.defaultVsCurrency; } - if (!this.queryPools.response) return; + if (!this.queryPools.response) { + this.queryPools.getAllPools(); + return; + } try { const route = this._intermediateRoutesMap.get(coinId); diff --git a/packages/web/components/cards/rewards-card.tsx b/packages/web/components/cards/rewards-card.tsx index c5e9b387a6b..8995d61fc5d 100644 --- a/packages/web/components/cards/rewards-card.tsx +++ b/packages/web/components/cards/rewards-card.tsx @@ -1,39 +1,49 @@ -import classNames from "classnames"; import React from "react"; import { Icon } from "~/components/assets"; +import { Button } from "~/components/buttons"; import { Tooltip } from "~/components/tooltip"; export const RewardsCard: React.FC<{ title: string; tooltipContent: string; + disabledTooltipContent?: string; onClick: () => void; image?: JSX.Element; - containerClasses?: string; + disabled: boolean; }> = ({ title, tooltipContent, + disabledTooltipContent, onClick, image = null, - containerClasses = "", + disabled, }) => { return ( -
{image} -
+
{title} -
- - - -
+ {disabled && ( +
+ + + +
+ )} + {!disabled && ( +
+ + + +
+ )}
-
+ ); }; diff --git a/packages/web/components/cards/stake-dashboard.tsx b/packages/web/components/cards/stake-dashboard.tsx index 83bd1ec1e09..f0ee7f5e3e8 100644 --- a/packages/web/components/cards/stake-dashboard.tsx +++ b/packages/web/components/cards/stake-dashboard.tsx @@ -1,5 +1,6 @@ import { Staking } from "@keplr-wallet/stores"; -import { CoinPretty, Dec } from "@keplr-wallet/unit"; +import { Currency } from "@keplr-wallet/types"; +import { CoinPretty, Dec, PricePretty } from "@keplr-wallet/unit"; import { DeliverTxResponse } from "@osmosis-labs/stores"; import { observer } from "mobx-react-lite"; import React, { useCallback } from "react"; @@ -10,7 +11,7 @@ import { GenericMainCard } from "~/components/cards/generic-main-card"; import { RewardsCard } from "~/components/cards/rewards-card"; import { ValidatorSquadCard } from "~/components/cards/validator-squad-card"; import { EventName } from "~/config"; -import { useAmplitudeAnalytics } from "~/hooks"; +import { useAmplitudeAnalytics, useFakeFeeConfig } from "~/hooks"; import { useStore } from "~/stores"; export const StakeDashboard: React.FC<{ @@ -29,22 +30,23 @@ export const StakeDashboard: React.FC<{ const account = accountStore.getWallet(osmosisChainId); const address = account?.address ?? ""; const osmo = chainStore.osmosis.stakeCurrency; + const fiat = priceStore.getFiatCurrency(priceStore.defaultVsCurrency)!; const { rewards } = cosmosQueries.queryRewards.getQueryBech32Address(address); const summedStakeRewards = rewards?.reduce((acc, reward) => { - return reward.toDec().add(acc); - }, new Dec(0)); - - const coinPrettyStakeRewards = summedStakeRewards - ? new CoinPretty(osmo, summedStakeRewards) - : new CoinPretty(osmo, 0); + return reward.add(acc); + }, new CoinPretty(osmo, 0)); const fiatRewards = - priceStore.calculatePrice(coinPrettyStakeRewards) || "0"; + priceStore.calculatePrice(summedStakeRewards) || new PricePretty(fiat, 0); + + const fiatBalance = balance + ? priceStore.calculatePrice(balance) + : undefined; - const fiatBalance = balance ? priceStore.calculatePrice(balance) : 0; + const osmoRewardsAmount = summedStakeRewards.toCoin().amount; const icon = (
@@ -70,35 +72,66 @@ export const StakeDashboard: React.FC<{ } }, [account, logEvent]); + const gasForecastedCollectRewards = 2901105; // estimate based on gas simulation to run collect succesfully + const gasForecastedCollectAndReinvestRewards = 6329136; // estimate based on gas simulation to run collect and reinvest succesfully + + const { fee: collectRewardsFee } = useFakeFeeConfig( + chainStore, + chainStore.osmosis.chainId, + gasForecastedCollectRewards + ); + + const { fee: collectAndReinvestRewardsFee } = useFakeFeeConfig( + chainStore, + chainStore.osmosis.chainId, + gasForecastedCollectAndReinvestRewards + ); + + const collectRewardsDisabled = summedStakeRewards + .toDec() + .lte(collectRewardsFee ? collectRewardsFee.toDec() : new Dec(0)); + + const collectAndReinvestRewardsDisabled = summedStakeRewards + .toDec() + .lte( + collectAndReinvestRewardsFee + ? collectAndReinvestRewardsFee.toDec() + : new Dec(0) + ); + const collectAndReinvestRewards = useCallback(() => { logEvent([EventName.Stake.collectAndReinvestStarted]); - // if (account?.osmosis) { - // account.osmosis.collectAndReinvest_mock( - // "", - // (tx: DeliverTxResponse) => { - // if (tx.code === 0) { - // logEvent([EventName.Stake.collectAndReinvestStarted]); - // } - // } - // ); - // } - }, [account, logEvent]); + const collectAndReinvestCoin: { amount: string; denom: Currency } = { + amount: osmoRewardsAmount, + denom: osmo, + }; + + if (account?.osmosis) { + account.osmosis.sendWithdrawDelegationRewardsAndSendDelegateToValidatorSetMsgs( + collectAndReinvestCoin, + "", + (tx: DeliverTxResponse) => { + if (tx.code === 0) { + logEvent([EventName.Stake.collectAndReinvestCompleted]); + } + } + ); + } + }, [account, logEvent, osmo, osmoRewardsAmount]); return (
} /> } @@ -133,18 +168,17 @@ export const StakeDashboard: React.FC<{ const StakeBalances: React.FC<{ title: string; - dollarAmount: string; - osmoAmount?: string; + dollarAmount?: PricePretty; + osmoAmount?: CoinPretty; }> = ({ title, dollarAmount, osmoAmount }) => { return (
- {/*
*/} {title} -

{dollarAmount}

+

{dollarAmount?.toString() ?? ""}

- {osmoAmount} + {osmoAmount?.toString() ?? ""}
); diff --git a/packages/web/localizations/en.json b/packages/web/localizations/en.json index 3f1646e8331..b1c3ad3854c 100644 --- a/packages/web/localizations/en.json +++ b/packages/web/localizations/en.json @@ -545,6 +545,7 @@ "unbondingPeriodTooltip": "Locked tokens increase stability for the blockchain. When you unstake your tokens, they will be available after the specified unbonding period.", "estimatedEarningsTooltip": "These earning are calculated based on the current staking APR, which is subject to change.", "collectRewardsTooltip": "Collect all your unclaimed rewards. These tokens will be immediately available to you on Osmosis.", + "collectRewardsTooltipDisabled": "Your rewards are too low to collect, please try again once you have more rewards to cover gas costs.", "collectAndReinvestTooltip": "Collect and automatically re-stake your unclaimed earnings in one click. Compounding is a great way to earn more with your assets.", "isAPRTooHighTooltip": "This validator takes an unusually high commission. Consider selecting a different validator.", "isVotingPowerTooHighTooltip": "This is a top 10 validator with high voting power. Consider selecting a different validator to help promote decentralization.", From 3c29418de721189f0ec647c712adc4f18aa81eb1 Mon Sep 17 00:00:00 2001 From: JeremyParish69 <95667791+JeremyParish69@users.noreply.github.com> Date: Wed, 20 Sep 2023 10:08:46 -0600 Subject: [PATCH 3/3] add quasar.- prefix to quasar ibc assets (#2165) --- .../web/config/generate-chain-infos/source-chain-infos.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/web/config/generate-chain-infos/source-chain-infos.ts b/packages/web/config/generate-chain-infos/source-chain-infos.ts index 57786a3bcfb..596eb8bc76b 100644 --- a/packages/web/config/generate-chain-infos/source-chain-infos.ts +++ b/packages/web/config/generate-chain-infos/source-chain-infos.ts @@ -3840,7 +3840,7 @@ export const mainnetChainInfos: SimplifiedChainInfo[] = [ isStakeCurrency: true, }, { - coinDenom: "OSMO", + coinDenom: "quasar.OSMO", coinMinimalDenom: "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B", coinDecimals: 6, @@ -3854,7 +3854,7 @@ export const mainnetChainInfos: SimplifiedChainInfo[] = [ }, }, { - coinDenom: "ATOM", + coinDenom: "quasar.ATOM", coinMinimalDenom: "ibc/FA0006F056DB6719B8C16C551FC392B62F5729978FC0B125AC9A432DBB2AA1A5", coinDecimals: 6, @@ -3868,7 +3868,7 @@ export const mainnetChainInfos: SimplifiedChainInfo[] = [ }, }, { - coinDenom: "USDC.axl", + coinDenom: "quasar.USDC.axl", coinMinimalDenom: "ibc/FA7775734CC73176B7425910DE001A1D2AD9B6D9E93129A5D0750EAD13E4E63A", coinDecimals: 6,