diff --git a/src/ui/baby/index.tsx b/src/ui/baby/index.tsx
index 4900d4b71..8c03df268 100644
--- a/src/ui/baby/index.tsx
+++ b/src/ui/baby/index.tsx
@@ -1,9 +1,366 @@
-import Layout from "@/ui/legacy/layout";
+import { useState } from "react";
+
+import { useCosmosWallet } from "@/ui/common/context/wallet/CosmosWalletProvider";
+import { useBbnQuery } from "@/ui/legacy/hooks/client/rpc/queries/useBbnQuery";
+import { useEpochingService } from "@/ui/legacy/hooks/services/useEpochingService";
+import { babyToUbbn, ubbnToBaby } from "@/ui/legacy/utils/bbn";
export default function BabyStaking() {
+ const cosmosWallet = useCosmosWallet();
+ const { bech32Address, connected } = cosmosWallet;
+ console.log({ cosmosWallet });
+ const { stake, unstake, claimRewards } = useEpochingService();
+ const { delegationsQuery, delegationRewardsQuery, validatorsQuery } =
+ useBbnQuery();
+
+ const [validatorAddress, setValidatorAddress] = useState("");
+ const [stakeAmount, setStakeAmount] = useState("");
+ const [unstakeAmount, setUnstakeAmount] = useState("");
+ const [loading, setLoading] = useState(false);
+
+ const delegations = delegationsQuery.data || [];
+ const rewards = delegationRewardsQuery.data || [];
+ const validators = validatorsQuery.data || [];
+
+ // Helper function to get rewards for a specific validator
+ const getRewardsForValidator = (validatorAddress: string): number => {
+ const validatorReward = rewards.find(
+ (reward) => reward.validatorAddress === validatorAddress,
+ );
+ if (!validatorReward?.reward) return 0;
+
+ return validatorReward.reward.reduce((total, coin) => {
+ if (coin.denom === "ubbn") {
+ return total + ubbnToBaby(parseFloat(coin.amount));
+ }
+ return total;
+ }, 0);
+ };
+
+ const totalStaked = delegations.reduce((total, delegation) => {
+ if (delegation.balance?.denom === "ubbn") {
+ return total + ubbnToBaby(parseFloat(delegation.balance.amount));
+ }
+ return total;
+ }, 0);
+
+ const totalRewards = rewards.reduce((total, reward) => {
+ return (
+ total +
+ (reward.reward || []).reduce((acc, coin) => {
+ if (coin.denom === "ubbn") {
+ return acc + ubbnToBaby(parseFloat(coin.amount));
+ }
+ return acc;
+ }, 0)
+ );
+ }, 0);
+
+ const handleStake = async () => {
+ if (!validatorAddress || !stakeAmount) return;
+
+ const tbabyAmount = parseFloat(stakeAmount);
+ if (isNaN(tbabyAmount) || tbabyAmount <= 0) {
+ alert("Please enter a valid amount");
+ return;
+ }
+
+ setLoading(true);
+ try {
+ const ubbnAmount = babyToUbbn(tbabyAmount);
+ await stake(bech32Address, validatorAddress, {
+ denom: "ubbn",
+ amount: ubbnAmount.toString(),
+ });
+ alert(`Successfully staked ${tbabyAmount} tBABY!`);
+ await delegationsQuery.refetch();
+ await delegationRewardsQuery.refetch();
+ setStakeAmount("");
+ } catch (error: any) {
+ alert(`Staking failed: ${error.message || "Unknown error"}`);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleUnstake = async () => {
+ if (!validatorAddress || !unstakeAmount) return;
+
+ const tbabyAmount = parseFloat(unstakeAmount);
+ if (isNaN(tbabyAmount) || tbabyAmount <= 0) {
+ alert("Please enter a valid amount");
+ return;
+ }
+
+ setLoading(true);
+ try {
+ const ubbnAmount = babyToUbbn(tbabyAmount);
+ await unstake(bech32Address, validatorAddress, {
+ denom: "ubbn",
+ amount: ubbnAmount.toString(),
+ });
+ alert(`Successfully unstaked ${tbabyAmount} tBABY!`);
+ await delegationsQuery.refetch();
+ await delegationRewardsQuery.refetch();
+ setUnstakeAmount("");
+ } catch (error: any) {
+ alert(`Unstaking failed: ${error.message || "Unknown error"}`);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleClaimRewards = async () => {
+ if (!validatorAddress) return;
+
+ setLoading(true);
+ try {
+ await claimRewards(bech32Address, validatorAddress);
+ await delegationRewardsQuery.refetch();
+ alert("Rewards claimed successfully!");
+ } catch (error: any) {
+ alert(`Failed to claim rewards: ${error.message || "Unknown error"}`);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ if (!connected) {
+ return (
+
+
+
Connect Your Wallet
+
+ Please connect your Babylon wallet to use staking features.
+
+
+
+ );
+ }
+
return (
-
- Baby staking
-
+
+
Baby Staking
+
+
+
Wallet Info
+
Address: {bech32Address}
+
+
+
Active Delegations
+
{delegations.length}
+
+
+
Total Staked
+
+ {totalStaked.toLocaleString()} tBABY
+
+
+
+
Total Rewards
+
+ {totalRewards.toLocaleString()} tBABY
+
+
+
+
Available Validators
+
{validators.length}
+
+
+
+
+ {delegations.length > 0 && (
+
+
My Delegations
+
+ {delegations.map((delegation, index) => {
+ const validatorAddress =
+ delegation.delegation?.validatorAddress || "";
+ const stakedAmount = ubbnToBaby(
+ parseFloat(delegation.balance?.amount || "0"),
+ );
+ const rewardsAmount = getRewardsForValidator(validatorAddress);
+
+ return (
+
+
+
+ {validators.find(
+ (v) => v.operatorAddress === validatorAddress,
+ )?.description?.moniker || validatorAddress}
+
+
+ Staked: {stakedAmount.toLocaleString()} tBABY
+
+ {rewardsAmount > 0 && (
+
+ Rewards: {rewardsAmount.toLocaleString()} tBABY
+
+ )}
+
+
+
+ {rewardsAmount > 0 && (
+
+ )}
+
+
+ );
+ })}
+
+
+ )}
+
+
+ {/* Stake Section */}
+
+
Stake
+
+
+
+
+
+
+
+ setStakeAmount(e.target.value)}
+ className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
+ placeholder="Enter tBABY amount to stake (e.g., 10)"
+ />
+
+
+
+
+
+ {/* Unstake Section */}
+
+
Unstake
+
+
+
+
+
+
+
+ setUnstakeAmount(e.target.value)}
+ className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-red-500"
+ placeholder="Enter tBABY amount to unstake (e.g., 5)"
+ />
+
+
+
+
+
+ {/* Rewards Section */}
+
+
Rewards
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/src/ui/common/components/Header/Header.tsx b/src/ui/common/components/Header/Header.tsx
index 8e51bc3f9..bb6b92b95 100644
--- a/src/ui/common/components/Header/Header.tsx
+++ b/src/ui/common/components/Header/Header.tsx
@@ -1,4 +1,6 @@
import { useWalletConnect } from "@babylonlabs-io/wallet-connector";
+import { Link, useLocation } from "react-router";
+import { twJoin } from "tailwind-merge";
import { Container } from "@/ui/common/components/Container/Container";
import { useAppState } from "@/ui/common/state";
@@ -10,12 +12,41 @@ export const Header = () => {
const { open } = useWalletConnect();
const { isLoading: loading } = useAppState();
+ const { pathname } = useLocation();
+
return (
-
-
+
+
+
+
+
+
-
+
diff --git a/src/ui/legacy/hooks/client/rpc/queries/useBbnQuery.ts b/src/ui/legacy/hooks/client/rpc/queries/useBbnQuery.ts
index e087f0b35..ac9c403eb 100644
--- a/src/ui/legacy/hooks/client/rpc/queries/useBbnQuery.ts
+++ b/src/ui/legacy/hooks/client/rpc/queries/useBbnQuery.ts
@@ -6,7 +6,14 @@ import {
QueryClient,
createProtobufRpcClient,
setupBankExtension,
+ setupDistributionExtension,
+ setupStakingExtension,
} from "@cosmjs/stargate";
+import { QueryDelegationTotalRewardsResponse } from "cosmjs-types/cosmos/distribution/v1beta1/query";
+import {
+ QueryDelegatorDelegationsResponse,
+ QueryValidatorsResponse,
+} from "cosmjs-types/cosmos/staking/v1beta1/query";
import { ONE_MINUTE } from "@/ui/legacy/constants";
import { useBbnRpc } from "@/ui/legacy/context/rpc/BbnRpcProvider";
@@ -14,6 +21,7 @@ import { useCosmosWallet } from "@/ui/legacy/context/wallet/CosmosWalletProvider
import { ClientError } from "@/ui/legacy/errors";
import { ERROR_CODES } from "@/ui/legacy/errors/codes";
import { useHealthCheck } from "@/ui/legacy/hooks/useHealthCheck";
+import { normalizeRewardResponse } from "@/ui/legacy/utils/bbn";
import { useClientQuery } from "../../useClient";
import { useRpcErrorHandler } from "../useRpcErrorHandler";
@@ -21,6 +29,9 @@ import { useRpcErrorHandler } from "../useRpcErrorHandler";
const BBN_BTCLIGHTCLIENT_TIP_KEY = "BBN_BTCLIGHTCLIENT_TIP";
const BBN_BALANCE_KEY = "BBN_BALANCE";
const BBN_REWARDS_KEY = "BBN_REWARDS";
+const BBN_DELEGATIONS_KEY = "BBN_DELEGATIONS";
+const BBN_DELEGATION_REWARDS_KEY = "BBN_DELEGATION_REWARDS";
+const BBN_VALIDATORS_KEY = "BBN_VALIDATORS";
const REWARD_GAUGE_KEY_BTC_DELEGATION = "BTC_STAKER";
/**
@@ -34,7 +45,7 @@ export const useBbnQuery = () => {
const { hasRpcError, reconnect } = useRpcErrorHandler();
/**
- * Gets the rewards from the user's account.
+ * [BTC Staking] Gets the rewards of the user's account.
* @returns {Promise
} - The rewards from the user's account.
*/
const rewardsQuery = useClientQuery({
@@ -143,10 +154,119 @@ export const useBbnQuery = () => {
refetchInterval: false, // Disable automatic periodic refetching
});
+ /**
+ * [BABY Staking] Gets all delegations of the user's account.
+ */
+ const delegationsQuery = useClientQuery({
+ queryKey: [BBN_DELEGATIONS_KEY, bech32Address, connected],
+ queryFn: async () => {
+ if (!connected || !queryClient || !bech32Address) {
+ return undefined;
+ }
+
+ const { staking } = setupStakingExtension(queryClient);
+
+ try {
+ const response: QueryDelegatorDelegationsResponse =
+ await staking.delegatorDelegations(bech32Address);
+ return response.delegationResponses || [];
+ } catch (error) {
+ throw new ClientError(
+ ERROR_CODES.EXTERNAL_SERVICE_UNAVAILABLE,
+ "Error getting delegations",
+ { cause: error as Error },
+ );
+ }
+ },
+ enabled: Boolean(
+ queryClient &&
+ connected &&
+ bech32Address &&
+ !isGeoBlocked &&
+ !isHealthcheckLoading,
+ ),
+ staleTime: ONE_MINUTE,
+ refetchInterval: ONE_MINUTE,
+ });
+
+ /**
+ * [BABY Staking] Gets all delegation rewards of the user's account.
+ */
+ const delegationRewardsQuery = useClientQuery({
+ queryKey: [BBN_DELEGATION_REWARDS_KEY, bech32Address, connected],
+ queryFn: async () => {
+ if (!connected || !queryClient || !bech32Address) {
+ return undefined;
+ }
+
+ const { distribution } = setupDistributionExtension(queryClient);
+
+ try {
+ const response: QueryDelegationTotalRewardsResponse =
+ await distribution.delegationTotalRewards(bech32Address);
+
+ // Need to normalize the response to 6 decimals
+ const rewards = normalizeRewardResponse(response);
+
+ return rewards || [];
+ } catch (error) {
+ // If no rewards found, return empty array
+ if (error instanceof Error && error.message.includes("no delegation")) {
+ return [];
+ }
+ throw new ClientError(
+ ERROR_CODES.EXTERNAL_SERVICE_UNAVAILABLE,
+ "Error getting delegation rewards",
+ { cause: error as Error },
+ );
+ }
+ },
+ enabled: Boolean(
+ queryClient &&
+ connected &&
+ bech32Address &&
+ !isGeoBlocked &&
+ !isHealthcheckLoading,
+ ),
+ staleTime: ONE_MINUTE,
+ refetchInterval: ONE_MINUTE,
+ });
+
+ /**
+ * [BABY Staking] Gets all the validators.
+ */
+ const validatorsQuery = useClientQuery({
+ queryKey: [BBN_VALIDATORS_KEY],
+ queryFn: async () => {
+ if (!queryClient) {
+ return undefined;
+ }
+
+ const { staking } = setupStakingExtension(queryClient);
+
+ try {
+ const response: QueryValidatorsResponse = await staking.validators("");
+ return response.validators || [];
+ } catch (error) {
+ throw new ClientError(
+ ERROR_CODES.EXTERNAL_SERVICE_UNAVAILABLE,
+ "Error getting validators",
+ { cause: error as Error },
+ );
+ }
+ },
+ enabled: Boolean(queryClient && !isGeoBlocked && !isHealthcheckLoading),
+ staleTime: ONE_MINUTE,
+ refetchInterval: ONE_MINUTE,
+ });
+
return {
rewardsQuery,
balanceQuery,
btcTipQuery,
+ delegationsQuery,
+ delegationRewardsQuery,
+ validatorsQuery,
hasRpcError,
reconnectRpc: reconnect,
queryClient,
diff --git a/src/ui/legacy/hooks/services/useEpochingService.ts b/src/ui/legacy/hooks/services/useEpochingService.ts
new file mode 100644
index 000000000..8416976c2
--- /dev/null
+++ b/src/ui/legacy/hooks/services/useEpochingService.ts
@@ -0,0 +1,262 @@
+import { epochingtx } from "@babylonlabs-io/babylon-proto-ts";
+import { Coin } from "cosmjs-types/cosmos/base/v1beta1/coin";
+import { MsgWithdrawDelegatorReward } from "cosmjs-types/cosmos/distribution/v1beta1/tx";
+import { useCallback } from "react";
+
+import { useError } from "@/ui/legacy/context/Error/ErrorProvider";
+import { useLogger } from "@/ui/legacy/hooks/useLogger";
+import { BBN_REGISTRY_TYPE_URLS } from "@/ui/legacy/utils/wallet/bbnRegistry";
+
+import { useBbnTransaction } from "../client/rpc/mutation/useBbnTransaction";
+
+export const useEpochingService = () => {
+ const { handleError } = useError();
+ const logger = useLogger();
+ const { estimateBbnGasFee, sendBbnTx, signBbnTx } = useBbnTransaction();
+
+ /**
+ * Estimates the gas fee for BABY staking.
+ * @param delegatorAddress - The delegator address
+ * @param validatorAddress - The validator address
+ * @param amount - The amount to stake
+ * @returns The gas fee for staking
+ */
+ const estimateStakeGas = useCallback(
+ async (
+ delegatorAddress: string,
+ validatorAddress: string,
+ amount: Coin,
+ ): Promise => {
+ const stakeMsg = createStakeMsg(
+ delegatorAddress,
+ validatorAddress,
+ amount,
+ );
+ const gasFee = await estimateBbnGasFee(stakeMsg);
+ return gasFee.amount.reduce((acc, coin) => acc + Number(coin.amount), 0);
+ },
+ [estimateBbnGasFee],
+ );
+
+ /**
+ * Estimates the gas fee for unstaking.
+ * @param delegatorAddress - The delegator address
+ * @param validatorAddress - The validator address
+ * @param amount - The amount to unstake
+ * @returns The gas fee for unstaking
+ */
+ const estimateUnstakeGas = useCallback(
+ async (
+ delegatorAddress: string,
+ validatorAddress: string,
+ amount: Coin,
+ ): Promise => {
+ const unstakeMsg = createUnstakeMsg(
+ delegatorAddress,
+ validatorAddress,
+ amount,
+ );
+ const gasFee = await estimateBbnGasFee(unstakeMsg);
+ return gasFee.amount.reduce((acc, coin) => acc + Number(coin.amount), 0);
+ },
+ [estimateBbnGasFee],
+ );
+
+ /**
+ * Estimates the gas fee for claiming rewards.
+ * @param delegatorAddress - The delegator address
+ * @param validatorAddress - The validator address
+ * @returns The gas fee for claiming rewards
+ */
+ const estimateClaimRewardsGas = useCallback(
+ async (
+ delegatorAddress: string,
+ validatorAddress: string,
+ ): Promise => {
+ const claimMsg = createClaimRewardsMsg(
+ delegatorAddress,
+ validatorAddress,
+ );
+ const gasFee = await estimateBbnGasFee(claimMsg);
+ return gasFee.amount.reduce((acc, coin) => acc + Number(coin.amount), 0);
+ },
+ [estimateBbnGasFee],
+ );
+
+ /**
+ * Stakes tokens with a validator.
+ * @param delegatorAddress - The delegator address
+ * @param validatorAddress - The validator address
+ * @param amount - The amount to stake
+ * @returns The transaction result
+ */
+ const stake = useCallback(
+ async (
+ delegatorAddress: string,
+ validatorAddress: string,
+ amount: Coin,
+ ) => {
+ try {
+ const msg = createStakeMsg(delegatorAddress, validatorAddress, amount);
+ const signedTx = await signBbnTx(msg);
+ const result = await sendBbnTx(signedTx);
+
+ logger.info("Stake transaction completed", {
+ txHash: result?.txHash,
+ });
+
+ return result;
+ } catch (error: any) {
+ logger.error(error);
+ handleError({ error });
+ throw error;
+ }
+ },
+ [signBbnTx, sendBbnTx, logger, handleError],
+ );
+
+ /**
+ * Unstakes tokens from a validator.
+ * @param delegatorAddress - The delegator address
+ * @param validatorAddress - The validator address
+ * @param amount - The amount to unstake
+ * @returns The transaction result
+ */
+ const unstake = useCallback(
+ async (
+ delegatorAddress: string,
+ validatorAddress: string,
+ amount: Coin,
+ ) => {
+ try {
+ const msg = createUnstakeMsg(
+ delegatorAddress,
+ validatorAddress,
+ amount,
+ );
+ const signedTx = await signBbnTx(msg);
+ const result = await sendBbnTx(signedTx);
+
+ logger.info("Unstake transaction completed", {
+ txHash: result?.txHash,
+ });
+
+ return result;
+ } catch (error: any) {
+ logger.error(error);
+ handleError({ error });
+ throw error;
+ }
+ },
+ [signBbnTx, sendBbnTx, logger, handleError],
+ );
+
+ /**
+ * Claims rewards from a validator.
+ * @param delegatorAddress - The delegator address
+ * @param validatorAddress - The validator address
+ * @returns The transaction result
+ */
+ const claimRewards = useCallback(
+ async (delegatorAddress: string, validatorAddress: string) => {
+ try {
+ const msg = createClaimRewardsMsg(delegatorAddress, validatorAddress);
+ const signedTx = await signBbnTx(msg);
+ const result = await sendBbnTx(signedTx);
+
+ logger.info("Claim rewards transaction completed", {
+ txHash: result?.txHash,
+ });
+
+ return result;
+ } catch (error: any) {
+ logger.error(error);
+ handleError({ error });
+ throw error;
+ }
+ },
+ [signBbnTx, sendBbnTx, logger, handleError],
+ );
+
+ return {
+ stake,
+ unstake,
+ claimRewards,
+ estimateStakeGas,
+ estimateUnstakeGas,
+ estimateClaimRewardsGas,
+ };
+};
+
+/**
+ * Creates a wrapped delegate message for staking.
+ * @param delegatorAddress - The delegator address
+ * @param validatorAddress - The validator address
+ * @param amount - The amount to stake
+ * @returns The wrapped delegate message
+ */
+const createStakeMsg = (
+ delegatorAddress: string,
+ validatorAddress: string,
+ amount: Coin,
+) => {
+ const wrappedDelegateMsg = epochingtx.MsgWrappedDelegate.fromPartial({
+ msg: {
+ delegatorAddress,
+ validatorAddress,
+ amount,
+ },
+ });
+
+ return {
+ typeUrl: BBN_REGISTRY_TYPE_URLS.MsgWrappedDelegate,
+ value: wrappedDelegateMsg,
+ };
+};
+
+/**
+ * Creates a wrapped undelegate message for unstaking.
+ * @param delegatorAddress - The delegator address
+ * @param validatorAddress - The validator address
+ * @param amount - The amount to unstake
+ * @returns The wrapped undelegate message
+ */
+const createUnstakeMsg = (
+ delegatorAddress: string,
+ validatorAddress: string,
+ amount: Coin,
+) => {
+ const wrappedUndelegateMsg = epochingtx.MsgWrappedUndelegate.fromPartial({
+ msg: {
+ delegatorAddress,
+ validatorAddress,
+ amount,
+ },
+ });
+
+ return {
+ typeUrl: BBN_REGISTRY_TYPE_URLS.MsgWrappedUndelegate,
+ value: wrappedUndelegateMsg,
+ };
+};
+
+/**
+ * Creates a withdraw delegator reward message for claiming rewards.
+ * @param delegatorAddress - The delegator address
+ * @param validatorAddress - The validator address
+ * @returns The withdraw delegator reward message
+ */
+const createClaimRewardsMsg = (
+ delegatorAddress: string,
+ validatorAddress: string,
+) => {
+ const withdrawRewardMsg = MsgWithdrawDelegatorReward.fromPartial({
+ delegatorAddress,
+ validatorAddress,
+ });
+
+ return {
+ typeUrl: BBN_REGISTRY_TYPE_URLS.MsgWithdrawDelegatorReward,
+ value: withdrawRewardMsg,
+ };
+};
diff --git a/src/ui/legacy/utils/bbn.ts b/src/ui/legacy/utils/bbn.ts
index 735de9c8f..6bfcc8f3b 100644
--- a/src/ui/legacy/utils/bbn.ts
+++ b/src/ui/legacy/utils/bbn.ts
@@ -1,3 +1,5 @@
+import { QueryDelegationTotalRewardsResponse } from "cosmjs-types/cosmos/distribution/v1beta1/query";
+
/**
* Converts BABY to uBBN (micro BABY).
* should be used internally in the app
@@ -17,3 +19,32 @@ export function babyToUbbn(bbn: number): number {
export function ubbnToBaby(ubbn: number): number {
return ubbn / 1e6;
}
+
+/**
+ * Normalizes CosmJS amount from 18-decimal precision to standard ubbn format.
+ * CosmJS returns amounts with 18 decimal places, but standard ubbn format uses fewer decimals.
+ * @param amount The amount string from CosmJS (with 18 decimal precision).
+ * @returns The normalized amount as a string in standard ubbn format.
+ */
+export function normalizeCosmjsAmount(amount: string): string {
+ const numAmount = parseFloat(amount);
+ return (numAmount / 1e18).toString();
+}
+
+/**
+ * Normalizes reward response from CosmJS to standard ubbn format.
+ * Applies amount normalization to all coin amounts in the reward structure.
+ * @param response The QueryDelegationTotalRewardsResponse from CosmJS.
+ * @returns The normalized reward array with amounts in standard ubbn format.
+ */
+export function normalizeRewardResponse(
+ response: QueryDelegationTotalRewardsResponse,
+) {
+ return response.rewards?.map((reward) => ({
+ ...reward,
+ reward: reward.reward?.map((coin) => ({
+ ...coin,
+ amount: normalizeCosmjsAmount(coin.amount),
+ })),
+ }));
+}
diff --git a/src/ui/legacy/utils/wallet/amino.ts b/src/ui/legacy/utils/wallet/amino.ts
index d1ef97fc5..9dae4bf8d 100644
--- a/src/ui/legacy/utils/wallet/amino.ts
+++ b/src/ui/legacy/utils/wallet/amino.ts
@@ -1,10 +1,16 @@
-import { btcstakingtx, incentivetx } from "@babylonlabs-io/babylon-proto-ts";
+import {
+ btcstakingtx,
+ epochingtx,
+ incentivetx,
+} from "@babylonlabs-io/babylon-proto-ts";
import { AminoTypes } from "@cosmjs/stargate";
+import { MsgWithdrawDelegatorReward } from "cosmjs-types/cosmos/distribution/v1beta1/tx";
import { ClientError, ERROR_CODES } from "@/ui/legacy/errors";
import { BBN_REGISTRY_TYPE_URLS } from "./bbnRegistry";
+// BTC Staking
const msgCreateBTCDelegationConverter = {
[BBN_REGISTRY_TYPE_URLS.MsgCreateBTCDelegation]: {
aminoType: BBN_REGISTRY_TYPE_URLS.MsgCreateBTCDelegation,
@@ -107,6 +113,7 @@ const msgCreateBTCDelegationConverter = {
},
};
+// Incentives - Claiming BABY rewards from BTC Staking
const msgWithdrawRewardConverter = {
[BBN_REGISTRY_TYPE_URLS.MsgWithdrawReward]: {
aminoType: BBN_REGISTRY_TYPE_URLS.MsgWithdrawReward,
@@ -125,9 +132,81 @@ const msgWithdrawRewardConverter = {
},
};
+// Epoching - Staking BABY
+const msgWrappedDelegateConverter = {
+ [BBN_REGISTRY_TYPE_URLS.MsgWrappedDelegate]: {
+ aminoType: BBN_REGISTRY_TYPE_URLS.MsgWrappedDelegate,
+ toAmino: (msg: epochingtx.MsgWrappedDelegate) => {
+ return {
+ msg: {
+ delegator_address: msg.msg?.delegatorAddress,
+ validator_address: msg.msg?.validatorAddress,
+ amount: msg.msg?.amount,
+ },
+ };
+ },
+ fromAmino: (json: any): epochingtx.MsgWrappedDelegate => {
+ return {
+ msg: {
+ delegatorAddress: json.msg.delegator_address,
+ validatorAddress: json.msg.validator_address,
+ amount: json.msg.amount,
+ },
+ };
+ },
+ },
+};
+
+// Epoching - Unstaking BABY
+const msgWrappedUndelegateConverter = {
+ [BBN_REGISTRY_TYPE_URLS.MsgWrappedUndelegate]: {
+ aminoType: BBN_REGISTRY_TYPE_URLS.MsgWrappedUndelegate,
+ toAmino: (msg: epochingtx.MsgWrappedUndelegate) => {
+ return {
+ msg: {
+ delegator_address: msg.msg?.delegatorAddress,
+ validator_address: msg.msg?.validatorAddress,
+ amount: msg.msg?.amount,
+ },
+ };
+ },
+ fromAmino: (json: any): epochingtx.MsgWrappedUndelegate => {
+ return {
+ msg: {
+ delegatorAddress: json.msg.delegator_address,
+ validatorAddress: json.msg.validator_address,
+ amount: json.msg.amount,
+ },
+ };
+ },
+ },
+};
+
+// Cosmos Distribution - Claiming BABY rewards from BABY Staking
+const msgWithdrawDelegatorRewardConverter = {
+ [BBN_REGISTRY_TYPE_URLS.MsgWithdrawDelegatorReward]: {
+ aminoType: BBN_REGISTRY_TYPE_URLS.MsgWithdrawDelegatorReward,
+ toAmino: (msg: MsgWithdrawDelegatorReward) => {
+ return {
+ delegator_address: msg.delegatorAddress,
+ validator_address: msg.validatorAddress,
+ };
+ },
+ fromAmino: (json: any): MsgWithdrawDelegatorReward => {
+ return {
+ delegatorAddress: json.delegator_address,
+ validatorAddress: json.validator_address,
+ };
+ },
+ },
+};
+
export const bbnAminoConverters = {
...msgCreateBTCDelegationConverter,
...msgWithdrawRewardConverter,
+ ...msgWrappedDelegateConverter,
+ ...msgWrappedUndelegateConverter,
+ ...msgWithdrawDelegatorRewardConverter,
};
export function createBbnAminoTypes(): AminoTypes {
diff --git a/src/ui/legacy/utils/wallet/bbnRegistry.ts b/src/ui/legacy/utils/wallet/bbnRegistry.ts
index 208475bff..fe2afe5ce 100644
--- a/src/ui/legacy/utils/wallet/bbnRegistry.ts
+++ b/src/ui/legacy/utils/wallet/bbnRegistry.ts
@@ -1,6 +1,11 @@
-import { btcstakingtx, incentivetx } from "@babylonlabs-io/babylon-proto-ts";
+import {
+ btcstakingtx,
+ epochingtx,
+ incentivetx,
+} from "@babylonlabs-io/babylon-proto-ts";
import { MessageFns } from "@babylonlabs-io/babylon-proto-ts/dist/generated/google/protobuf/any";
import { GeneratedType, Registry } from "@cosmjs/proto-signing";
+import { MsgWithdrawDelegatorReward } from "cosmjs-types/cosmos/distribution/v1beta1/tx";
// Define the structure of each proto to register
type ProtoToRegister = {
@@ -11,6 +16,10 @@ type ProtoToRegister = {
export const BBN_REGISTRY_TYPE_URLS = {
MsgCreateBTCDelegation: "/babylon.btcstaking.v1.MsgCreateBTCDelegation",
MsgWithdrawReward: "/babylon.incentive.MsgWithdrawReward",
+ MsgWrappedDelegate: "/babylon.epoching.v1.MsgWrappedDelegate",
+ MsgWrappedUndelegate: "/babylon.epoching.v1.MsgWrappedUndelegate",
+ MsgWithdrawDelegatorReward:
+ "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward",
};
// List of protos to register in the registry
@@ -20,11 +29,25 @@ const protosToRegister: ProtoToRegister[] = [
typeUrl: BBN_REGISTRY_TYPE_URLS.MsgCreateBTCDelegation,
messageType: btcstakingtx.MsgCreateBTCDelegation,
},
- // Incentives
+ // Incentives - Claiming BABY rewards from BTC Staking
{
typeUrl: BBN_REGISTRY_TYPE_URLS.MsgWithdrawReward,
messageType: incentivetx.MsgWithdrawReward,
},
+ // Epoching - Staking / Unstaking BABY
+ {
+ typeUrl: BBN_REGISTRY_TYPE_URLS.MsgWrappedDelegate,
+ messageType: epochingtx.MsgWrappedDelegate,
+ },
+ {
+ typeUrl: BBN_REGISTRY_TYPE_URLS.MsgWrappedUndelegate,
+ messageType: epochingtx.MsgWrappedUndelegate,
+ },
+ // Cosmos Distribution - Claiming rewards from BABY Staking
+ {
+ typeUrl: BBN_REGISTRY_TYPE_URLS.MsgWithdrawDelegatorReward,
+ messageType: MsgWithdrawDelegatorReward as any,
+ },
];
// Utility function to create a `GeneratedType` from `MessageFns`