Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/progress bar #31

Merged
merged 12 commits into from
Jan 4, 2024
98 changes: 80 additions & 18 deletions components/chainCards.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo, useCallback } from 'react'
import { useMemo, useCallback, useState, useEffect } from 'react'
import { toast } from 'react-hot-toast';
import { ChainSigningClient } from '../contexts/userClients';
import NextImage from "next/image";
Expand All @@ -8,13 +8,17 @@ import JunoLogo from "../public/JUNO400x400.png";
import AuraLogo from "../public/AURA400x400.jpg";
import OsmosisLogo from "../public/OSMOSIS400x400.png";
import DiceLoader from '../components/diceLoader';
import Progress from './progressBar';
import { ChainType, CheckResponse } from '../pages/api/check';
import { parseTimestamp } from '../services/parsing'
import { randdropClaimMsg } from '../services/contractTx'
import { ethLedgerTxHelper } from '../services/ledgerHelpers';
import { routeNewTab } from '../services/misc';
import { AirdropLiveStatus } from '../pages';
import { signSendAndBroadcastOnInjective } from '../services/injective';
import { calculatePercentage } from '../hooks/cosmwasm';
import { getContractAddress } from '../pages/api/check';
import { Popover, Spin } from 'antd';

const BridgeLinks = {
"injective": "https://tfm.com/bridge?chainTo=nois-1&chainFrom=injective-1&token0=ibc%2FDD9182E8E2B13C89D6B4707C7B43E8DB6193F9FF486AFA0E6CF86B427B0D231A&token1=unois",
Expand Down Expand Up @@ -62,8 +66,8 @@ export const PausedChainCard = ({
return InjectiveLogo;
case "aura":
return AuraLogo;
case "osmosis":
return OsmosisLogo;
case "osmosis":
return OsmosisLogo;
case "stargaze":
return StargazeLogo;
default:
Expand Down Expand Up @@ -133,8 +137,8 @@ export const LiveChainCard = ({
return InjectiveLogo;
case "aura":
return AuraLogo;
case "osmosis":
return OsmosisLogo;
case "osmosis":
return OsmosisLogo;
case "stargaze":
return StargazeLogo;
default:
Expand Down Expand Up @@ -235,7 +239,7 @@ export const LiveChainCard = ({
)}
</div>
{/* Claim Info*/}
<div className="h-[44%] flex justify-center items-center ">
<div className="flex justify-center items-center ">
{!checkResponse || !client ? (
<div className="w-full h-full flex flex-col justify-center items-center gap-y-4 pb-10">
<div className="circle-spinner" />
Expand All @@ -251,6 +255,23 @@ export const LiveChainCard = ({
)
}

const ProgressBar = ({ tokenLeft, claimPercentageLeft }) => (
<div style={{ width: '75%' }}>
<Popover placement="top" content={`${(tokenLeft / Math.pow(10, 6)).toFixed(2)} Nois left`}>
<div>
<Progress percentageLeft={claimPercentageLeft} />
</div>
</Popover>
{
(tokenLeft < 20_000_000 || claimPercentageLeft === 0) && (
<div style={{ color: 'white', display: 'flex', justifyContent: 'center' }}>
All tokens claimed
</div>
)
}
</div>
);

export const ClaimInfo = ({
client,
checkResponse,
Expand All @@ -260,6 +281,34 @@ export const ClaimInfo = ({
checkResponse: CheckResponse;
refetch: () => {}
}) => {
const [claimPercentageLeft, setClaimPercentageLeft] = useState(0)
const [tokenLeft, setTokenLeft] = useState(0)

useEffect(() => {
(async () => {
// If no client, or client is not metamask or ledger, return
if (!client || !client.chain) {
toast.error(`Wallet or Ledger not connected for ${checkResponse.chain}`);
return;
}

const contractAddress = getContractAddress(client.chain)

if (contractAddress === "") {
toast.error(`No randdrop contract available for ${checkResponse.chain}`);
return;
}

const resp = await calculatePercentage(client?.chain, contractAddress)
if (!resp) {
toast.error(`Unable to fetch contract balance for ${checkResponse.chain}`);
return;
}
console.log(resp)
setClaimPercentageLeft(parseFloat(resp.percentageLeft.toFixed(2)))
setTokenLeft(resp.amountLeft)
})()
}, [])

const {
submitted,
Expand Down Expand Up @@ -379,24 +428,37 @@ export const ClaimInfo = ({
}, [client?.walletAddress])

if (!client || checkResponse.userStatus === "not_eligible") {
return <></>
return <ProgressBar tokenLeft={tokenLeft} claimPercentageLeft={claimPercentageLeft} />;
} else {
switch (checkResponse.userStatus) {
case "ready": {
return (
<div className={`w-full h-full p-6 flex justify-center items-start`}>
<button
onClick={() => handleClaimRanddrop()}
className={`py-2 px-6 animate-pulse hover:animate-none hover:shaxdow-neon-md hover:bg-green-500/10 text-green-500 border border-green-500 rounded-xl bg-gradient-to-b from-green-500/10`}
>
{"Roll the dice!"}
</button>
<div style={{ width: '100%'}}>
<div style={{display: 'flex', justifyContent: 'center' }}>
{/* Progress bar */}
<ProgressBar tokenLeft={tokenLeft} claimPercentageLeft={claimPercentageLeft} />
</div>

<div style={{display: 'flex', justifyContent: 'center' }}>
{/* Claim button */}
{
(
<button
onClick={() => handleClaimRanddrop()}
className={`py-2 px-6 animate-pulse hover:animate-none hover:shaxdow-neon-md hover:bg-green-500/10 text-green-500 border border-green-500 rounded-xl bg-gradient-to-b from-green-500/10`}
>
{"Roll the dice!"}
</button>
)
}
</div>
</div>
)
);
}
case "already_won": {
return (
<div className={`w-full h-full p-2 flex flex-col justify-start gap-y-2 items-center`}>
<ProgressBar tokenLeft={tokenLeft} claimPercentageLeft={claimPercentageLeft} />
<div className="text-nois-white/80 text-xs">
{`Submitted at: ${submitted}`}
</div>
Expand Down Expand Up @@ -430,6 +492,7 @@ export const ClaimInfo = ({
case "already_lost": {
return (
<div className={`w-full h-full p-4 flex flex-col justify-start gap-y-2 items-center`}>
<ProgressBar tokenLeft={tokenLeft} claimPercentageLeft={claimPercentageLeft} />
<div className="text-nois-white/80 text-sm">
{`Submitted at: ${submitted}`}
</div>
Expand All @@ -450,9 +513,8 @@ export const ClaimInfo = ({
)
}
default: {
return (
<></>
)
return <ProgressBar tokenLeft={tokenLeft} claimPercentageLeft={claimPercentageLeft} />

}
}
}
Expand Down
11 changes: 11 additions & 0 deletions components/progressBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import ProgressBar from 'react-bootstrap/ProgressBar';

const Progress = ({ percentageLeft }) => {
return (
<div style={{marginBottom: '20px'}}>
<ProgressBar striped animated now={percentageLeft} label={`${percentageLeft}%`} style={{color: 'black'}}/>
</div>
);
};

export default Progress;
92 changes: 80 additions & 12 deletions hooks/cosmwasm.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { useCallback, useState } from "react";
import {
SigningCosmWasmClient,
setupWasmExtension,
} from "@cosmjs/cosmwasm-stargate";
import { setupWasmExtension } from "@cosmjs/cosmwasm-stargate";
import { setupBankExtension } from "@cosmjs/stargate";
import { toBase64, fromBase64, toUtf8, fromUtf8 } from "@cosmjs/encoding";
import { HttpBatchClient, Tendermint34Client, Tendermint37Client } from "@cosmjs/tendermint-rpc";
import { QueryClient } from "@cosmjs/stargate";
import {
getChainConfig,
} from "../services/chainConfig";
import {
ChainType
} from "../pages/api/check";
HttpBatchClient,
Tendermint34Client,
Tendermint37Client,
} from "@cosmjs/tendermint-rpc";
import { QueryClient } from "@cosmjs/stargate";
import { getChainConfig } from "../services/chainConfig";
import { ChainType } from "../pages/api/check";

export const getBatchClient = async (chain: ChainType) => {
const thisChain = getChainConfig(chain);
Expand All @@ -21,7 +19,17 @@ export const getBatchClient = async (chain: ChainType) => {
const tmint = await Tendermint37Client.create(httpBatch);
const queryClient = QueryClient.withExtensions(tmint, setupWasmExtension);
return queryClient;
}
};

const getBankClient = async (chain: ChainType) => {
const thisChain = getChainConfig(chain);
const endpoints = [thisChain.rpc];
const endpoint = endpoints[Math.floor(Math.random() * endpoints.length)];
const httpBatch = new HttpBatchClient(endpoint);
const tmint = await Tendermint37Client.create(httpBatch);
const queryClient = QueryClient.withExtensions(tmint, setupBankExtension);
return queryClient;
};

export function toBinary(obj: any): string {
return toBase64(toUtf8(JSON.stringify(obj)));
Expand All @@ -30,3 +38,63 @@ export function toBinary(obj: any): string {
export function fromBinary(base64: string): any {
return JSON.parse(fromUtf8(fromBase64(base64)));
}

export async function queryContractBalance(
chain: ChainType,
contractAddress: string
): Promise<number> {
// get client with bank extension
const batchClient = await getBankClient(chain);

// Query contract's total NOIS token balances
const balances = await batchClient.bank.allBalances(contractAddress);

// Mapping of known IBC denoms to their chains
const ibcDenoms = {
'stargaze-1': 'ibc/0F181D9F5BB18A8496153C1666E934169515592C135E8E9FCCC355889858EAF9',
'juno-1': 'ibc/1D9E14A1F00613ED39E4B8A8763A20C9BE5B5EA0198F2FE47EAE43CD91A0137B',
'injective-1': 'ibc/DD9182E8E2B13C89D6B4707C7B43E8DB6193F9FF486AFA0E6CF86B427B0D231A',
'aura': 'ibc/1FD48481DAA1B05575FE6D3E35929264437B8424A73243B207BCB67401C7F1FD',
'osmosis': 'ibc/6928AFA9EA721938FED13B051F9DBF1272B16393D20C49EA5E4901BB76D94A90'
};

// Find the NOIS token balance, handling both native and IBC denoms
const noisTokenIdentifier = "unois";
const noisBalance = balances.find((balance) => {
return (
balance.denom === noisTokenIdentifier ||
Object.values(ibcDenoms).includes(balance.denom)
)});

return noisBalance ? parseInt(noisBalance.amount) : 0;
}

export interface ContractQueryResp {
percentageLeft: number,
amountLeft: number
}

export async function calculatePercentage(
chain: ChainType,
contractAddress: string
): Promise<ContractQueryResp> {
// Total amount to be distributed per chain (in unois)
const totalDistribution = {
osmosis: 4_000_000_000_000,
injective: 4_100_000_000_000,
juno: 2_600_000_000_000,
stargaze: 1_200_000_000_000,
aura: 100_000_000_000,
};

const balance = await queryContractBalance(chain, contractAddress);

// Calculate the percentage of NOIS tokens left
const totalForChain = totalDistribution[chain];
const percentageLeft = (balance / totalForChain) * 100;

return {
percentageLeft,
amountLeft: balance
};
}
Loading