Skip to content

Commit

Permalink
feat: continue with core
Browse files Browse the repository at this point in the history
  • Loading branch information
icfor committed Feb 28, 2024
1 parent 5972ced commit 052d4a1
Show file tree
Hide file tree
Showing 11 changed files with 287 additions and 63 deletions.
4 changes: 3 additions & 1 deletion package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@burnt-labs/constants": "^0.1.0-alpha.6",
"@burnt-labs/ui": "^0.1.0-alpha.6",
"@cosmjs/cosmwasm-stargate": "^0.31.0",
"@cosmjs/proto-signing": "^0.32.2",
"@cosmjs/stargate": "^0.32.2",
"bignumber.js": "^9.1.2",
"cosmjs-types": "^0.9.0",
Expand Down
5 changes: 4 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import "@burnt-labs/ui/dist/index.css";
import { Inter } from "next/font/google";

import { StakingProvider } from "@/features/staking/context/provider";
import { dashboardUrl, rpcEndpoint } from "@/features/staking/lib/constants";
import {
dashboardUrl,
rpcEndpoint,
} from "@/features/staking/lib/core/constants";

import "./globals.css";

Expand Down
2 changes: 1 addition & 1 deletion src/features/staking/components/debug-account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
import Link from "next/link";
import { useEffect, useState } from "react";

import { chainId } from "../lib/constants";
import { chainId } from "../lib/core/constants";

const DebugAccount = () => {
const [address, setAddress] = useState<null | string>(null);
Expand Down
58 changes: 45 additions & 13 deletions src/features/staking/components/logged-in.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import { memo, useState } from "react";
import type { StakingState } from "../context";
import { stakeValidator, unstakeValidator } from "../context/actions";
import { useStaking } from "../context/hooks";
import { formatCoin } from "../lib/coins";
import { chainId } from "../lib/constants";
import type { StakeAddresses } from "../lib/core";
import type { StakeAddresses } from "../lib/core/base";
import { formatCoin } from "../lib/core/coins";
import { chainId } from "../lib/core/constants";
import DebugAccount from "./debug-account";

type ValidatorItemProps = {
Expand Down Expand Up @@ -52,15 +52,18 @@ const ValidatorItem = ({
function StakingPage() {
const { account, staking } = useStaking();
const [isLoading, setIsLoading] = useState(false);
const { delegations, tokens, validators } = staking.state;
const { delegations, tokens, unbondings, validators } = staking.state;
const { client } = useAbstraxionSigningClient();

const validatorsMap: Record<string, Validator> =
validators?.items.reduce<Record<string, Validator>>((acc, validator) => {
acc[validator.operatorAddress] = validator;
const validatorsMap: Record<string, undefined | Validator> =
validators?.items.reduce<Record<string, undefined | Validator>>(
(acc, validator) => {
acc[validator.operatorAddress] = validator;

return acc;
}, {}) || {};
return acc;
},
{},
) || {};

return (
<>
Expand All @@ -75,26 +78,30 @@ function StakingPage() {
</div>
)}
</div>
{delegations && (
{!!delegations?.items.length && (
<div>
<div>Delegations:</div>
{delegations.items.map((delegation) => {
const validator = validatorsMap[delegation.validatorAddress];
const moniker = validator?.description.moniker;

return (
<div key={delegation.validatorAddress}>
<div
key={delegation.validatorAddress}
style={{ border: "1px solid #fff", marginBottom: 20 }}
>
<div>Delegated: {formatCoin(delegation.balance)}</div>
{delegation.rewards && (
<div>Rewards: {formatCoin(delegation.rewards)}</div>
)}
<div>{moniker || delegation.validatorAddress}</div>
<div>Validator: {moniker || delegation.validatorAddress}</div>
<div className="flex flex-row gap-4">
<Button
disabled={isLoading}
onClick={() => {
setIsLoading(true);

if (!client) return;
if (!client || !validator) return;

setIsLoading(true);

Expand All @@ -112,6 +119,7 @@ function StakingPage() {
</Button>
{delegation.rewards && (
<Button
disabled={isLoading || delegation.rewards.amount === "0"}
onClick={() => {
// @TODO
}}
Expand All @@ -120,6 +128,7 @@ function StakingPage() {
</Button>
)}
<Button
disabled={isLoading}
onClick={() => {
// @TODO
}}
Expand All @@ -132,6 +141,29 @@ function StakingPage() {
})}
</div>
)}
{!!unbondings?.items.length && (
<div>
<div>Unbondings:</div>
<div>
{unbondings?.items.map((unbondingItem) => {
const validator = validatorsMap[unbondingItem.validator];

return (
<div
key={`${unbondingItem.completionTime}-${unbondingItem.completionTimeNanos}`}
style={{ border: "1px solid #fff" }}
>
Unbonding tokens: {formatCoin(unbondingItem.balance)}{" "}
(completed by:{" "}
{new Date(unbondingItem.completionTime).toString()})
Validator:{" "}
{validator?.description.moniker || unbondingItem.validator}
</div>
);
})}
</div>
</div>
)}
<div className="flex items-center justify-center gap-4">
<Link
href={`https://explorer.burnt.com/${chainId}/account/${account.bech32Address}`}
Expand Down
100 changes: 73 additions & 27 deletions src/features/staking/context/actions.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,103 @@
import type { StakingContextType } from ".";
import { normaliseCoin } from "../lib/coins";
import type { SigningClient, StakeAddresses } from "../lib/core";
import type { StakingContextType, Unbonding } from ".";
import type { SigningClient, StakeAddresses } from "../lib/core/base";
import {
getBalance,
getDelegations,
getRewards,
getUnbondingDelegations,
getValidatorsList,
stakeAmount,
unstakeAmount,
} from "../lib/core";
import { addDelegations, setTokens, setValidators } from "./reducer";
} from "../lib/core/base";
import { sumAllCoins } from "../lib/core/coins";
import {
addDelegations,
addUnbondings,
setTokens,
setValidators,
} from "./reducer";

export const fetchStakingData = async (
address: string,
staking: StakingContextType,
) => {
try {
const [balance, validators, delegations] = await Promise.all([
const [balance, validators, delegations, unbondings] = await Promise.all([
getBalance(address),
getValidatorsList(),
getDelegations(address).then((newDelegations) =>
Promise.all(
getDelegations(address).then(async (newDelegations) => ({
items: await Promise.all(
newDelegations.delegationResponses.map(async (del) => ({
balance: del.balance,
rewards: await getRewards(
address,
del.delegation.validatorAddress,
).then((rewards) =>
rewards.reduce(
(acc, reward) => ({
amount: (
parseFloat(acc.amount) +
parseFloat(normaliseCoin(reward).amount)
).toString(),
denom: reward.denom,
}),
{ amount: "0", denom: "xion" },
),
),
).then((rewards) => sumAllCoins(rewards)),
validatorAddress: del.delegation.validatorAddress,
})),
),
),
pagination: newDelegations.pagination,
})),
getUnbondingDelegations(address)
.then((unbondingResponse) => ({
items: unbondingResponse.unbondingResponses.reduce((acc, res) => {
acc.push(
...res.entries.map((entry) => ({
balance: { amount: entry.balance, denom: "uxion" },
completionTime: Number(entry.completionTime.seconds),
completionTimeNanos: entry.completionTime.nanos,
id: entry.unbondingId.toString(),
validator: res.validatorAddress,
})),
);

return acc;
}, [] as Unbonding[]),
pagination: unbondingResponse.pagination,
}))
.catch(() =>
// It is expected that this will throw when there are no unbondings
({ items: [], pagination: null }),
)
.then((result) => ({
items: result.items,
pagination: result.pagination,
})),
]);

staking.dispatch(setTokens(balance));

staking.dispatch(
setValidators({ currentPage: 0, items: validators.validators }),
setValidators(
{
items: validators.validators,
nextKey: validators.pagination?.nextKey || null,
total: validators.pagination?.total || null,
},
true,
),
);

staking.dispatch(
addDelegations(
{
items: delegations.items,
nextKey: delegations.pagination?.nextKey || null,
total: delegations.pagination?.total || null,
},
true,
),
);

staking.dispatch(
addDelegations({
currentPage: 0,
items: delegations,
}),
addUnbondings(
{
items: unbondings.items,
nextKey: unbondings.pagination?.nextKey || null,
total: unbondings.pagination?.total || null,
},
true,
),
);
} catch (error) {
console.error("error fetching staking data:", error);
Expand All @@ -79,10 +122,13 @@ export const unstakeValidator = async (
client: SigningClient,
staking: StakingContextType,
) => {
await unstakeAmount(addressess, client, {
const result = await unstakeAmount(addressess, client, {
amount: "1000",
denom: "uxion",
});

// eslint-disable-next-line no-console
console.log("debug: actions.ts: result", result);

await fetchStakingData(addressess.delegator, staking);
};
13 changes: 12 additions & 1 deletion src/features/staking/context/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,19 @@ import { createContext } from "react";
import type { StakingAction } from "./reducer";

type Paginated<T> = {
currentPage: number;
items: T[];
nextKey: null | Uint8Array;
total: bigint | null;
} | null;

export type Unbonding = {
balance: Coin;
completionTime: number;
completionTimeNanos: number;
id: string;
validator: string;
};

type Delegation = {
balance: Coin;
rewards: Coin;
Expand All @@ -19,6 +28,7 @@ type Delegation = {
export type StakingState = {
delegations: Paginated<Delegation>;
tokens: Coin | null;
unbondings: Paginated<Unbonding>;
validators: Paginated<Validator>;
};

Expand All @@ -30,6 +40,7 @@ export type StakingContextType = {
export const defaultState: StakingState = {
delegations: null,
tokens: null,
unbondings: null,
validators: null,
};

Expand Down
Loading

0 comments on commit 052d4a1

Please sign in to comment.