Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { MythTokenAssetsProvider } from "./context/mythTokenAssets";
import FellowshipTreasuryOnAssetHub from "./fellowshipTreasuryOnAssetHub";
import Loans from "./loans";
import Bounties from "./bounties";
import MultiAssetBounties from "./multiAssetBounties";
import CollapsePanel from "next-common/components/summary/polkadotTreasurySummary/common/collapsePanel";
import TreasuryStatus from "./treasuryStatus";

Expand All @@ -15,6 +16,7 @@ function PolkadotTreasurySummaryInContext() {
<Treasury />
<TreasuryOnHydration />
<Bounties />
<MultiAssetBounties />
<FellowshipTreasuryOnAssetHub />
<Loans />
<TreasuryStatus />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import SummaryItem from "next-common/components/summary/layout/item";
import Link from "next-common/components/link";
import LoadableContent from "next-common/components/common/loadableContent";
import NativeTokenSymbolAsset from "../common/nativeTokenSymbolAsset";
import TokenSymbolAsset from "../common/tokenSymbolAsset";
import FiatPriceLabel from "../common/fiatPriceLabel";
import { usePolkadotTreasury } from "next-common/context/treasury/polkadotTreasury";
import Tooltip from "next-common/components/tooltip";
import { toPrecision } from "next-common/utils";
import { useChainSettings } from "next-common/context/chain";

export default function MultiAssetBounties() {
const {
multiAssetBountiesCount,
multiAssetBountiesTotalByAsset: totalByAsset,
isMultiAssetBountiesLoading: isLoading,
} = usePolkadotTreasury();

const { symbol: chainSymbol } = useChainSettings();

const entries = Object.values(totalByAsset || {});

const Title = (
<>
<Link
href="/treasury/multi-asset-bounties"
className="text12Medium text-textTertiary hover:underline"
target="_blank"
rel="noreferrer"
>
Multi-Asset Bounties
</Link>
{isLoading ? null : (
<>
<span>{" · "}</span>
<Tooltip content="Active multi-asset bounties">
<span>{multiAssetBountiesCount}</span>
</Tooltip>
</>
)}
</>
);

return (
<SummaryItem title={Title}>
<LoadableContent isLoading={isLoading}>
<div className="flex flex-col gap-1">
<FiatPriceLabel
free={totalByAsset?.[chainSymbol]?.total?.toFixed() ?? 0}
usdtBalance={totalByAsset?.USDT?.total?.toFixed() ?? 0}
usdcBalance={totalByAsset?.USDC?.total?.toFixed() ?? 0}
/>
<div className="flex flex-col gap-y-1 ml-0!">
{entries.map(({ symbol, decimals, total }) =>
symbol === chainSymbol ? (
<NativeTokenSymbolAsset key={symbol} free={total.toFixed()} />
) : (
<TokenSymbolAsset
key={symbol}
amount={toPrecision(total.toFixed(), decimals)}
symbol={symbol}
/>
),
)}
</div>
</div>
</LoadableContent>
</SummaryItem>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { useEffect, useMemo, useState } from "react";
import BigNumber from "bignumber.js";
import { getAssetInfoFromPapiAssetKind } from "next-common/utils/treasury/multiAssetBounty/papiAssetKind";

const MULTI_ASSET_BOUNTY_ACTIVE_STATUS_TYPES = [
"FundingAttempted",
"Funded",
"Active",
];

function filterActiveMultiAssetBounties(items) {
return items.filter((item) =>
MULTI_ASSET_BOUNTY_ACTIVE_STATUS_TYPES.includes(item?.status?.type),
);
}

export function useQueryMultiAssetsBounties(papi, checkPallet) {
const [bounties, setBounties] = useState(null);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
if (!papi) {
return;
}

if (!checkPallet("MultiAssetBounties", "Bounties")) {
setIsLoading(false);
setBounties([]);
return;
}

setIsLoading(true);
papi.query.MultiAssetBounties.Bounties.getEntries()
.then((entries) => {
const all = entries.map(({ keyArgs, value }) => ({
index: keyArgs?.[0],
...value,
}));
setBounties(filterActiveMultiAssetBounties(all));
})
.finally(() => {
setIsLoading(false);
});
}, [papi, checkPallet]);

return {
bounties,
bountiesCount: bounties?.length ?? 0,
isLoading,
};
}

export function useMultiAssetsBountiesTotalBalance(
bounties,
chainDecimals,
chainSymbol,
) {
const totalByAsset = useMemo(() => {
if (!bounties?.length) {
return {};
}

const result = {};
bounties.forEach((bounty) => {
const { symbol, decimals } = getAssetInfoFromPapiAssetKind(
bounty?.asset_kind,
chainDecimals,
chainSymbol,
);
const amount = bounty?.value != null ? String(bounty.value) : "0";
if (!result[symbol]) {
result[symbol] = { symbol, decimals, total: new BigNumber(0) };
}
result[symbol].total = result[symbol].total.plus(amount);
});
return result;
}, [bounties, chainDecimals, chainSymbol]);

return { totalByAsset };
}
31 changes: 29 additions & 2 deletions packages/next-common/context/treasury/polkadotTreasury/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@ import {
useBountiesTotalBalance,
useQueryBounties,
} from "./hooks/useQueryBountiesData";
import {
useQueryMultiAssetsBounties,
useMultiAssetsBountiesTotalBalance,
} from "./hooks/useQueryMultiAssetBountiesData";
import useQueryFellowshipSalaryBalance from "./hooks/useQueryFellowshipSalaryBalance";
import usePolkadotTreasuryTotal from "next-common/utils/hooks/usePolkadotTreasuryTotal";
import { PapiProvider, useContextPapiApi } from "next-common/context/papi";
import { PapiProvider, useContextPapi } from "next-common/context/papi";
import { useChainSettings } from "next-common/context/chain";

const PolkadotTreasuryContext = createContext();

Expand All @@ -23,7 +28,8 @@ export default function PolkadotTreasuryProvider({ children }) {
}

function PolkadotTreasuryProviderInner({ children }) {
const papi = useContextPapiApi();
const { api: papi, checkPallet } = useContextPapi();
const { decimals: chainDecimals, symbol: chainSymbol } = useChainSettings();
const {
treasuryAccount,
relayChainTreasuryBalance: nativeTreasuryBalanceOnRelayChain,
Expand Down Expand Up @@ -56,6 +62,7 @@ function PolkadotTreasuryProviderInner({ children }) {
bountiesCount,
isLoading: isQueryBountiesLoading,
} = useQueryBounties(papi);

const {
balance: dotTreasuryBalanceOnBounties,
isLoading: isBountiesTotalBalanceLoading,
Expand All @@ -64,6 +71,19 @@ function PolkadotTreasuryProviderInner({ children }) {
const isDotTreasuryBalanceOnBountiesLoading =
isQueryBountiesLoading || isBountiesTotalBalanceLoading;

const {
bounties: multiAssetBounties,
bountiesCount: multiAssetBountiesCount,
isLoading: isMultiAssetBountiesLoading,
} = useQueryMultiAssetsBounties(papi, checkPallet);

const { totalByAsset: multiAssetBountiesTotalByAsset } =
useMultiAssetsBountiesTotalBalance(
multiAssetBounties,
chainDecimals,
chainSymbol,
);

return (
<PolkadotTreasuryContext.Provider
value={{
Expand Down Expand Up @@ -93,9 +113,16 @@ function PolkadotTreasuryProviderInner({ children }) {
loanBifrostDotBalance: 10000000000000000,
loadPendulumDotBalance: 500000000000000,
loanHydrationDotBalance: 10000000000000000,

// bounties
bountiesCount,
dotTreasuryBalanceOnBounties,
isDotTreasuryBalanceOnBountiesLoading,

// multi-asset bounties
multiAssetBountiesCount,
multiAssetBountiesTotalByAsset,
isMultiAssetBountiesLoading,
}}
>
{children}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { ASSET_HUB_GENERAL_INDEX_SYMBOL } from "next-common/asset";
import { SYMBOL_DECIMALS } from "next-common/utils/consts/asset";
import { isNil } from "lodash-es";

const ASSET_HUB_PARACHAIN_ID = 1000;
const ASSET_HUB_PALLET_INSTANCE = 50;

function papiGetJunctions(interior) {
if (!interior) {
return null;
}
if (interior === "Here" || interior?.type === "Here") {
return [];
}
const { type, value } = interior;
if (!type || value == null) {
return null;
}
return Array.isArray(value) ? value : [value];
}

function papiIsAssetHubLocation(location) {
if (!location) {
return false;
}
const junctions = papiGetJunctions(location.interior);
if (junctions === null) {
return false;
}
if (junctions.length === 0) {
return true;
}
return junctions.some(
(j) =>
j?.type === "Parachain" && Number(j?.value) === ASSET_HUB_PARACHAIN_ID,
);
}

function papiGetSymbolFromAssetId(assetId) {
const junctions = papiGetJunctions(assetId?.interior);
if (!junctions?.length) {
return null;
}
const palletInstance = junctions.find(
(j) => j?.type === "PalletInstance",
)?.value;
const generalIndex = junctions.find((j) => j?.type === "GeneralIndex")?.value;
if (
Number(palletInstance) === ASSET_HUB_PALLET_INSTANCE &&
!isNil(generalIndex)
) {
return ASSET_HUB_GENERAL_INDEX_SYMBOL[String(generalIndex)] ?? null;
}
return null;
}

export function getAssetInfoFromPapiAssetKind(
papiAssetKind,
chainDecimals,
chainSymbol,
) {
if (!papiAssetKind) {
return { symbol: chainSymbol, decimals: chainDecimals };
}
// papi format: { type: "V5", value: { location, asset_id } }
const inner = papiAssetKind?.value ?? papiAssetKind;
const { location, asset_id: assetId } = inner ?? {};
if (!papiIsAssetHubLocation(location)) {
return { symbol: chainSymbol, decimals: chainDecimals };
}
const symbol = papiGetSymbolFromAssetId(assetId);
if (!symbol) {
return { symbol: chainSymbol, decimals: chainDecimals };
}
const decimals = SYMBOL_DECIMALS[symbol] ?? chainDecimals;
return { symbol, decimals };
}
Loading