From 0f624ffd60fd68688f86243a5840547983e1cdab Mon Sep 17 00:00:00 2001 From: neocybereth Date: Mon, 14 Apr 2025 14:23:56 +1200 Subject: [PATCH 01/18] fix: update referrals code --- apps/namadillo/.gitignore | 1 + apps/namadillo/package.json | 1 + apps/namadillo/src/App/AppRoutes.tsx | 4 + apps/namadillo/src/App/Layout/AppLayout.tsx | 30 +- .../src/App/Layout/TopNavigation.tsx | 10 +- .../src/App/Referrals/ReferralSheetModal.tsx | 204 +++++++++++++ .../namadillo/src/App/Referrals/Referrals.tsx | 63 ++++ .../src/App/Referrals/ReferralsTable.tsx | 272 ++++++++++++++++++ apps/namadillo/src/App/routes.ts | 3 + .../src/hooks/useTransactionCallbacks.tsx | 21 +- apps/namadillo/src/utils/supabase.ts | 83 ++++++ apps/namadillo/vite.config.mjs | 10 +- 12 files changed, 696 insertions(+), 6 deletions(-) create mode 100644 apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx create mode 100644 apps/namadillo/src/App/Referrals/Referrals.tsx create mode 100644 apps/namadillo/src/App/Referrals/ReferralsTable.tsx create mode 100644 apps/namadillo/src/utils/supabase.ts diff --git a/apps/namadillo/.gitignore b/apps/namadillo/.gitignore index f263da0cc5..3bca05b5d3 100644 --- a/apps/namadillo/.gitignore +++ b/apps/namadillo/.gitignore @@ -10,3 +10,4 @@ /playwright/.cache/ /public/localnet-config.toml .vercel +.env*.local diff --git a/apps/namadillo/package.json b/apps/namadillo/package.json index 34940fa9f8..d3c01dc64d 100644 --- a/apps/namadillo/package.json +++ b/apps/namadillo/package.json @@ -12,6 +12,7 @@ "@keplr-wallet/types": "^0.12.136", "@namada/chain-registry": "^1.0.0", "@namada/indexer-client": "2.4.4", + "@supabase/supabase-js": "^2.49.4", "@tailwindcss/container-queries": "^0.1.1", "@tanstack/query-core": "^5.40.0", "@tanstack/react-query": "^5.40.0", diff --git a/apps/namadillo/src/App/AppRoutes.tsx b/apps/namadillo/src/App/AppRoutes.tsx index bec97e126a..136bb70590 100644 --- a/apps/namadillo/src/App/AppRoutes.tsx +++ b/apps/namadillo/src/App/AppRoutes.tsx @@ -21,6 +21,7 @@ import { SubmitVote } from "./Governance/SubmitVote"; import { ViewJson } from "./Governance/ViewJson"; import { IbcShieldAll } from "./Ibc/IbcShieldAll"; import { MoveAssets } from "./Layout/MoveAssets"; +import { Referrals } from "./Referrals/Referrals"; import { routes } from "./routes"; import { Advanced } from "./Settings/Advanced"; import { EnableFeatures } from "./Settings/EnableFeatures"; @@ -59,6 +60,9 @@ export const MainRoutes = (): JSX.Element => { element={} errorElement={} > + {/* Referrals */} + } /> + {/* Home */} } /> diff --git a/apps/namadillo/src/App/Layout/AppLayout.tsx b/apps/namadillo/src/App/Layout/AppLayout.tsx index 785b8aadbf..eeeb931de1 100644 --- a/apps/namadillo/src/App/Layout/AppLayout.tsx +++ b/apps/namadillo/src/App/Layout/AppLayout.tsx @@ -1,6 +1,10 @@ import { AlphaVersionTopHeader } from "App/Common/AlphaVersionTopHeader"; -import { ReactNode, useState } from "react"; +import { defaultAccountAtom } from "atoms/accounts"; +import { connectedWalletsAtom } from "atoms/integrations"; +import { useAtomValue } from "jotai"; +import { ReactNode, useEffect, useState } from "react"; import { IoMdClose } from "react-icons/io"; +import { useLocation, useNavigate } from "react-router-dom"; import { twMerge } from "tailwind-merge"; import { AppHeader } from "./AppHeader"; import { BurgerButton } from "./BurgerButton"; @@ -12,6 +16,30 @@ export const AppLayout = ({ children: ReactNode; }): JSX.Element => { const [displayNavigation, setDisplayNavigation] = useState(false); + const location = useLocation(); + const navigate = useNavigate(); + const connectedWallets = useAtomValue(connectedWalletsAtom); + const defaultAccount = useAtomValue(defaultAccountAtom); + + useEffect(() => { + const queryParams = new URLSearchParams(location.search); + const referrerAddress = queryParams.get("referral"); + + if (referrerAddress && referrerAddress.startsWith("tnam")) { + const userAddress = defaultAccount.data?.address; + if (userAddress) { + // Store referral address in local storage + localStorage.setItem("refereeAddress", userAddress); + localStorage.setItem("referrerAddress", referrerAddress); + + // Clear the query parameter from URL + queryParams.delete("referral"); + const newSearch = queryParams.toString(); + const newPath = location.pathname + (newSearch ? `?${newSearch}` : ""); + navigate(newPath, { replace: true }); + } + } + }, [location, navigate, connectedWallets, defaultAccount]); return ( <> diff --git a/apps/namadillo/src/App/Layout/TopNavigation.tsx b/apps/namadillo/src/App/Layout/TopNavigation.tsx index 4e5b810d89..27196014ca 100644 --- a/apps/namadillo/src/App/Layout/TopNavigation.tsx +++ b/apps/namadillo/src/App/Layout/TopNavigation.tsx @@ -30,6 +30,9 @@ export const TopNavigation = (): JSX.Element => { const defaultAccount = useAtomValue(defaultAccountAtom); const location = useLocation(); const navigate = useNavigate(); + const isValidityOps = true; + // defaultAccount.data?.address === + // "tnam1q8lhvxys53dlc8wzlg7dyqf9avd0vff6wvav4amt"; if (!userHasAccount) { return ( @@ -53,7 +56,7 @@ export const TopNavigation = (): JSX.Element => { return (
-
+
{maspEnabled && ( { Transfer )} + {isValidityOps && ( + + Referrals + + )}
diff --git a/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx b/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx new file mode 100644 index 0000000000..cfbc5e7a18 --- /dev/null +++ b/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx @@ -0,0 +1,204 @@ +import { Modal } from "@namada/components"; + +import { Tooltip } from "@namada/components"; + +import { TableRow } from "@namada/components"; +import { shortenAddress } from "@namada/utils"; +import { ModalTransition } from "App/Common/ModalTransition"; +import { TableWithPaginator } from "App/Common/TableWithPaginator"; +import BigNumber from "bignumber.js"; +import { useCallback, useState } from "react"; +import { IoClose } from "react-icons/io5"; +import { twMerge } from "tailwind-merge"; +import { ReferralReward } from "./ReferralsTable"; + +export const ReferralSheetModal = ({ + rewardsData, + onClose, +}: { + rewardsData: ReferralReward[]; + onClose: () => void; +}): JSX.Element => { + const [page, setPage] = useState(0); + const resultsPerPage = 10; + + // Define table headers + const headers = [ + "Referrer Address", + "Referee Address", + "Epoch", + "Reward (NAM)", + ]; + + const renderRow = useCallback((reward: ReferralReward): TableRow => { + return { + key: `reward-${reward.referrerAddress}-${reward.refereeAddress}-${reward.epoch}`, + cells: [ + // Referrer address (truncated for display) +
+ {shortenAddress(reward.referrerAddress, 10, 6)} + + {reward.referrerAddress} + +
, + // Referee address (truncated for display) +
+ {shortenAddress(reward.refereeAddress, 10, 6)} + + {reward.refereeAddress} + +
, + // Epoch +
+ {reward.epoch} +
, + // Reward amount in NAM +
+ {reward.amount.toFormat(6)} +
, + ], + }; + }, []); + + const paginatedItems = rewardsData.slice( + page * resultsPerPage, + page * resultsPerPage + resultsPerPage + ); + + const pageCount = Math.ceil(rewardsData.length / resultsPerPage); + + // Calculate total rewards per referrer + const totalRewards = rewardsData.reduce( + (acc, reward) => { + if (!acc[reward.referrerAddress]) { + acc[reward.referrerAddress] = BigNumber(0); + } + acc[reward.referrerAddress] = acc[reward.referrerAddress].plus( + reward.amount + ); + return acc; + }, + {} as Record + ); + + // Group rewards by referrer address for easier navigation + const rewardsByReferrer = rewardsData.reduce( + (acc, reward) => { + if (!acc[reward.referrerAddress]) { + acc[reward.referrerAddress] = []; + } + acc[reward.referrerAddress].push(reward); + return acc; + }, + {} as Record + ); + + return ( + + + + + +
+ Referral Rewards +
+ +
+ {rewardsData.length === 0 ? +
No rewards found
+ :
+
+

+ Total Rewards by Referrer +

+
+ {Object.entries(totalRewards).map(([address, amount]) => ( +
+
+ + Referrer: + +
+ {shortenAddress(address, 10, 6)} + + {address} + +
+
+
+ + Total NAM: + + + {amount.toFormat(6)} + +
+
+ ))} +
+
+ +
+

Rewards by Epoch

+ + {/* Referrer selector tabs */} +
+
+ {Object.keys(rewardsByReferrer).map( + (referrerAddress, index) => ( + + ) + )} +
+
+ + +
+
+ } +
+
+
+ ); +}; diff --git a/apps/namadillo/src/App/Referrals/Referrals.tsx b/apps/namadillo/src/App/Referrals/Referrals.tsx new file mode 100644 index 0000000000..ddbd174af3 --- /dev/null +++ b/apps/namadillo/src/App/Referrals/Referrals.tsx @@ -0,0 +1,63 @@ +import { Panel } from "@namada/components"; +import { useEffect, useState } from "react"; +import { getReferralsFromSupabase } from "../../utils/supabase"; +import { ReferralsTable } from "./ReferralsTable"; + +export type Referral = { + id: number; + referrer_address: string; + referee_address: string; + start_epoch: number; + created_at?: string; +}; + +export const Referrals = (): JSX.Element => { + const [referrals, setReferrals] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchReferrals = async (): Promise => { + try { + setLoading(true); + const { success, data, error } = await getReferralsFromSupabase(); + + if (success && data) { + setReferrals(data as Referral[]); + } else { + console.error("Failed to fetch referrals:", error); + setError("Failed to fetch referrals"); + } + } catch (err) { + console.error("Error fetching referrals:", err); + setError("Error fetching referrals"); + } finally { + setLoading(false); + } + }; + + fetchReferrals(); + }, []); + + return ( + +
+

Referrals

+ + {loading && ( +
Loading referrals...
+ )} + + {error &&
{error}
} + + {!loading && !error && referrals.length === 0 && ( +
No referrals found
+ )} + + {!loading && !error && referrals.length > 0 && ( + + )} +
+
+ ); +}; diff --git a/apps/namadillo/src/App/Referrals/ReferralsTable.tsx b/apps/namadillo/src/App/Referrals/ReferralsTable.tsx new file mode 100644 index 0000000000..5c989ac212 --- /dev/null +++ b/apps/namadillo/src/App/Referrals/ReferralsTable.tsx @@ -0,0 +1,272 @@ +import { ActionButton, TableRow, Tooltip } from "@namada/components"; +import { shortenAddress } from "@namada/utils"; +import { TableWithPaginator } from "App/Common/TableWithPaginator"; +import { chainStatusAtom } from "atoms/chain"; +import BigNumber from "bignumber.js"; +import { useAtomValue } from "jotai"; +import { useCallback, useState } from "react"; +import { twMerge } from "tailwind-merge"; +import { Referral } from "./Referrals"; +import { ReferralSheetModal } from "./ReferralSheetModal"; + +export type ReferralsTableProps = { + id: string; + referrals: Referral[]; + resultsPerPage?: number; + initialPage?: number; + tableClassName?: string; +}; + +export type ValidatorInfo = { + address: string; + votingPower: string; + maxCommission: string; + commission: string; + state: string; + name: string; + email: string; + website: string; + description: string; + discordHandle: string | null; + avatar: string; + validatorId: string; + rank: string | null; +}; + +export type RewardData = { + minDenomAmount: string; + validator: ValidatorInfo; +}; + +export type ReferralReward = { + epoch: number; + referrerAddress: string; + refereeAddress: string; + amount: BigNumber; + validator: ValidatorInfo; +}; + +// Helper function to generate CSV data +const generateCsvContent = (rewardsData: ReferralReward[]): string => { + // CSV Headers + const headers = + "Referrer Address,Referee Address,Epoch,Reward (NAM),Validator Name\n"; + + // Format each reward as a CSV row + const csvData = rewardsData + .map((reward) => { + return [ + reward.referrerAddress, + reward.refereeAddress, + reward.epoch, + reward.amount.toFixed(6), + reward.validator.name, + ].join(","); + }) + .join("\n"); + + return headers + csvData; +}; + +// Helper function to download CSV +const downloadCsv = (data: string, filename: string): void => { + const csvContent = `data:text/csv;charset=utf-8,${data}`; + const encodedUri = encodeURI(csvContent); + + const link = document.createElement("a"); + link.setAttribute("href", encodedUri); + link.setAttribute("download", filename); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); +}; + +export const ReferralsTable = ({ + id, + referrals, + resultsPerPage = 10, + initialPage = 0, + tableClassName, +}: ReferralsTableProps): JSX.Element => { + const [page, setPage] = useState(initialPage); + const [isGenerating, setIsGenerating] = useState(false); + const [generationError, setGenerationError] = useState(null); + const [rewardsData, setRewardsData] = useState([]); + const [showModal, setShowModal] = useState(false); + const chainStatus = useAtomValue(chainStatusAtom); + + // Define table headers + const headers = [ + "Referrer Address", + "Referee Address", + "Start Epoch", + "Created At", + ]; + + const renderRow = useCallback((referral: Referral): TableRow => { + return { + key: `referral-${referral.id}`, + cells: [ + // Referrer address (truncated for display) +
+ {shortenAddress(referral.referrer_address, 10, 6)} + + {referral.referrer_address} + +
, + // Referee address (truncated for display) +
+ {shortenAddress(referral.referee_address, 10, 6)} + + {referral.referee_address} + +
, + // Epoch +
+ {referral.start_epoch} +
, + // Created at date +
+ {referral.created_at ? + new Date(referral.created_at).toLocaleString() + : "N/A"} +
, + ], + }; + }, []); + + const paginatedItems = referrals.slice( + page * resultsPerPage, + page * resultsPerPage + resultsPerPage + ); + + const pageCount = Math.ceil(referrals.length / resultsPerPage); + + const generateReferralSheet = async (): Promise => { + console.log(chainStatus, "chainStatus"); + if (!chainStatus?.epoch) { + setGenerationError("Chain status not available"); + return; + } + + setIsGenerating(true); + setGenerationError(null); + setRewardsData([]); + + try { + const rewards: ReferralReward[] = []; + + // Process each referral + for (const referral of referrals) { + const referrerAddress = referral.referrer_address; + const refereeAddress = referral.referee_address; + + // Get rewards for each epoch from start_epoch to current epoch + for ( + let epoch = referral.start_epoch; + epoch < chainStatus.epoch; + epoch++ + ) { + try { + console.log("yooo"); + const url = `${process.env.INDEXER_URL}/api/v1/pos/reward/${referrerAddress}/tnam1q8lhvxys53dlc8wzlg7dyqf9avd0vff6wvav4amt/${epoch}`; + + const response = await fetch(url); + + if (response.ok) { + const data = (await response.json()) as RewardData[]; + + if (data && data.length > 0) { + const rewardData = data[0]; + + // Convert minDenomAmount (uNam) to NAM (1 NAM = 1,000,000 uNam) + const amount = new BigNumber( + rewardData.minDenomAmount + ).dividedBy(1_000_000); + + rewards.push({ + epoch, + referrerAddress, + refereeAddress, + amount, + validator: rewardData.validator, + }); + } + } else { + console.error( + `Failed to fetch rewards for epoch ${epoch} for referrer ${referrerAddress}: ${response.statusText}` + ); + } + } catch (error) { + console.error( + `Error fetching rewards for epoch ${epoch} for referrer ${referrerAddress}:`, + error + ); + } + } + } + + setRewardsData(rewards); + + // Download CSV automatically + if (rewards.length > 0) { + const csvContent = generateCsvContent(rewards); + downloadCsv(csvContent, "referral_rewards.csv"); + } + + // Show modal + setShowModal(true); + } catch (error) { + console.error("Error generating referral sheet:", error); + setGenerationError("Failed to generate referral sheet"); + } finally { + setIsGenerating(false); + } + }; + + return ( +
+ + {generationError && ( +
{generationError}
+ )} + + {isGenerating ? "Generating..." : "Generate Referral Sheet"} + + + {showModal && rewardsData.length > 0 && ( + setShowModal(false)} + /> + )} +
+ ); +}; diff --git a/apps/namadillo/src/App/routes.ts b/apps/namadillo/src/App/routes.ts index b72e21797d..42ff2d6ad9 100644 --- a/apps/namadillo/src/App/routes.ts +++ b/apps/namadillo/src/App/routes.ts @@ -1,6 +1,9 @@ export const routes = { root: "/", + // Referrals + referrals: "/referrals", + // Staking staking: "/staking", stakingBondingIncrement: "/staking/bonding/increment", diff --git a/apps/namadillo/src/hooks/useTransactionCallbacks.tsx b/apps/namadillo/src/hooks/useTransactionCallbacks.tsx index 2e2d7f120e..91d94381d0 100644 --- a/apps/namadillo/src/hooks/useTransactionCallbacks.tsx +++ b/apps/namadillo/src/hooks/useTransactionCallbacks.tsx @@ -1,10 +1,12 @@ import { accountBalanceAtom, defaultAccountAtom } from "atoms/accounts"; import { shieldedBalanceAtom } from "atoms/balance/atoms"; +import { chainStatusAtom } from "atoms/chain"; import { shouldUpdateBalanceAtom, shouldUpdateProposalAtom } from "atoms/etc"; import { claimableRewardsAtom } from "atoms/staking"; import { useAtomValue, useSetAtom } from "jotai"; import { TransferStep, TransferTransactionData } from "types"; import { useTransactionEventListener } from "utils"; +import { saveReferralToSupabase } from "utils/supabase"; import { useTransactionActions } from "./useTransactionActions"; export const useTransactionCallback = (): void => { @@ -16,7 +18,7 @@ export const useTransactionCallback = (): void => { const { changeTransaction } = useTransactionActions(); const shouldUpdateProposal = useSetAtom(shouldUpdateProposalAtom); const shouldUpdateBalance = useSetAtom(shouldUpdateBalanceAtom); - + const chainStatus = useAtomValue(chainStatusAtom); const onBalanceUpdate = (): void => { // TODO: refactor this after event subscription is enabled on indexer shouldUpdateBalance(true); @@ -31,7 +33,22 @@ export const useTransactionCallback = (): void => { } }; - useTransactionEventListener("Bond.Success", onBalanceUpdate); + const successfulBond = async (): Promise => { + onBalanceUpdate(); + const referrerAddress = localStorage.getItem("referrerAddress"); + const refereeAddress = localStorage.getItem("refereeAddress"); + const epoch = chainStatus?.epoch; + await saveReferralToSupabase(referrerAddress!, refereeAddress!, epoch!); + + // After this write an authenticated page that allows Paul to grab all referrals from DB + // Then loop through them and check if the referee has received rewards in the epochs searched for. + // Once that's done display all the referrers, referees, amounts in a table. + // Remove all delegates that have been paid 0 on their last epoch from the DB. + // When he presses submit then do a batch transaction of all the rewards to be paid out. + // Make sure to add my 25% of the rewards in the transaction too. + }; + + useTransactionEventListener("Bond.Success", successfulBond); useTransactionEventListener("Unbond.Success", onBalanceUpdate); useTransactionEventListener("Withdraw.Success", onBalanceUpdate); useTransactionEventListener("Redelegate.Success", onBalanceUpdate); diff --git a/apps/namadillo/src/utils/supabase.ts b/apps/namadillo/src/utils/supabase.ts new file mode 100644 index 0000000000..b6ed18cc8d --- /dev/null +++ b/apps/namadillo/src/utils/supabase.ts @@ -0,0 +1,83 @@ +import { createClient } from "@supabase/supabase-js"; + +const supabaseUrl = process.env.SUPABASE_URL; +const supabaseKey = process.env.SUPABASE_ANON_KEY; +const supabase = createClient(supabaseUrl!, supabaseKey!); + +export const saveReferralToSupabase = async ( + referrerAddress: string, + refereeAddress: string, + epoch: number +): Promise<{ success: boolean; error?: unknown; data?: unknown }> => { + try { + // Make sure we have the required data + if (!referrerAddress || !refereeAddress || !epoch) { + console.error("Missing required referral data"); + return { success: false, error: "Missing required referral data" }; + } + + // First check if this referee already exists in the database + const { data: existingReferrals, error: fetchError } = await supabase + .from("referrals") + .select("*") + .eq("referee_address", refereeAddress) + .eq("referrer_address", referrerAddress) + .limit(1); + + if (fetchError) { + console.error("Error checking existing referral:", fetchError); + return { success: false, error: fetchError }; + } + + // If referee already exists, skip insertion + if (existingReferrals && existingReferrals.length > 0) { + console.log("Referee already exists in database, skipping insertion"); + return { success: true, data: existingReferrals[0] }; + } + + // Create referral record with timestamp + const referralData = { + referrer_address: referrerAddress, + referee_address: refereeAddress, + start_epoch: epoch, + active: true, + }; + + // Send data to Supabase + const { data, error } = await supabase + .from("referrals") + .insert([referralData]); + + if (error) { + console.error("Error saving referral:", error); + return { success: false, error }; + } + + console.log("Referral saved successfully:", data); + return { success: true, data }; + } catch (err) { + console.error("Unexpected error saving referral:", err); + return { success: false, error: err }; + } +}; + +export const getReferralsFromSupabase = async (): Promise<{ + success: boolean; + error?: unknown; + data?: unknown; +}> => { + try { + const { data, error } = await supabase.from("referrals").select("*"); + + if (error) { + console.error("Error fetching referrals:", error); + return { success: false, error }; + } + + console.log("Referrals fetched successfully:", data); + return { success: true, data }; + } catch (err) { + console.error("Unexpected error fetching referrals:", err); + return { success: false, error: err }; + } +}; diff --git a/apps/namadillo/vite.config.mjs b/apps/namadillo/vite.config.mjs index 671ffe3868..48e5cc6e3c 100644 --- a/apps/namadillo/vite.config.mjs +++ b/apps/namadillo/vite.config.mjs @@ -1,12 +1,14 @@ /* eslint-disable */ import react from "@vitejs/plugin-react"; -import { defineConfig } from "vite"; +import { defineConfig, loadEnv } from "vite"; import checker from "vite-plugin-checker"; import { nodePolyfills } from "vite-plugin-node-polyfills"; import { VitePWA } from "vite-plugin-pwa"; import tsconfigPaths from "vite-tsconfig-paths"; -export default defineConfig(() => { +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, process.cwd(), ""); + return { server: { headers: { @@ -44,6 +46,10 @@ export default defineConfig(() => { plugins: () => [tsconfigPaths()], format: "es", }, + define: { + "process.env.SUPABASE_URL": JSON.stringify(env.SUPABASE_URL), + "process.env.SUPABASE_ANON_KEY": JSON.stringify(env.SUPABASE_ANON_KEY), + }, optimizeDeps: { esbuildOptions: { // Node.js global to browser globalThis From 95ee95ac3776eda807055e2f2fb71c8eab5dd145 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Mon, 14 Apr 2025 14:26:04 +1200 Subject: [PATCH 02/18] fix: remove consoles --- apps/namadillo/src/App/Layout/TopNavigation.tsx | 6 +++--- apps/namadillo/src/App/Referrals/ReferralsTable.tsx | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/namadillo/src/App/Layout/TopNavigation.tsx b/apps/namadillo/src/App/Layout/TopNavigation.tsx index 27196014ca..1c41ae65fa 100644 --- a/apps/namadillo/src/App/Layout/TopNavigation.tsx +++ b/apps/namadillo/src/App/Layout/TopNavigation.tsx @@ -30,9 +30,9 @@ export const TopNavigation = (): JSX.Element => { const defaultAccount = useAtomValue(defaultAccountAtom); const location = useLocation(); const navigate = useNavigate(); - const isValidityOps = true; - // defaultAccount.data?.address === - // "tnam1q8lhvxys53dlc8wzlg7dyqf9avd0vff6wvav4amt"; + const isValidityOps = + defaultAccount.data?.address === + "tnam1q8lhvxys53dlc8wzlg7dyqf9avd0vff6wvav4amt"; if (!userHasAccount) { return ( diff --git a/apps/namadillo/src/App/Referrals/ReferralsTable.tsx b/apps/namadillo/src/App/Referrals/ReferralsTable.tsx index 5c989ac212..5c22d33b2a 100644 --- a/apps/namadillo/src/App/Referrals/ReferralsTable.tsx +++ b/apps/namadillo/src/App/Referrals/ReferralsTable.tsx @@ -149,7 +149,6 @@ export const ReferralsTable = ({ const pageCount = Math.ceil(referrals.length / resultsPerPage); const generateReferralSheet = async (): Promise => { - console.log(chainStatus, "chainStatus"); if (!chainStatus?.epoch) { setGenerationError("Chain status not available"); return; @@ -174,7 +173,6 @@ export const ReferralsTable = ({ epoch++ ) { try { - console.log("yooo"); const url = `${process.env.INDEXER_URL}/api/v1/pos/reward/${referrerAddress}/tnam1q8lhvxys53dlc8wzlg7dyqf9avd0vff6wvav4amt/${epoch}`; const response = await fetch(url); From 0c2528dce42ecc116b5c935d344d7eb7b34145fe Mon Sep 17 00:00:00 2001 From: neocybereth Date: Mon, 14 Apr 2025 14:26:38 +1200 Subject: [PATCH 03/18] fix: remove --- apps/namadillo/src/hooks/useTransactionCallbacks.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/apps/namadillo/src/hooks/useTransactionCallbacks.tsx b/apps/namadillo/src/hooks/useTransactionCallbacks.tsx index 91d94381d0..4c001297f3 100644 --- a/apps/namadillo/src/hooks/useTransactionCallbacks.tsx +++ b/apps/namadillo/src/hooks/useTransactionCallbacks.tsx @@ -39,13 +39,6 @@ export const useTransactionCallback = (): void => { const refereeAddress = localStorage.getItem("refereeAddress"); const epoch = chainStatus?.epoch; await saveReferralToSupabase(referrerAddress!, refereeAddress!, epoch!); - - // After this write an authenticated page that allows Paul to grab all referrals from DB - // Then loop through them and check if the referee has received rewards in the epochs searched for. - // Once that's done display all the referrers, referees, amounts in a table. - // Remove all delegates that have been paid 0 on their last epoch from the DB. - // When he presses submit then do a batch transaction of all the rewards to be paid out. - // Make sure to add my 25% of the rewards in the transaction too. }; useTransactionEventListener("Bond.Success", successfulBond); From 544ac996ba74d1243cfba01e5362156ad850054e Mon Sep 17 00:00:00 2001 From: neocybereth Date: Mon, 14 Apr 2025 14:28:25 +1200 Subject: [PATCH 04/18] fix: active true --- apps/namadillo/src/utils/supabase.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/namadillo/src/utils/supabase.ts b/apps/namadillo/src/utils/supabase.ts index b6ed18cc8d..c1731d028b 100644 --- a/apps/namadillo/src/utils/supabase.ts +++ b/apps/namadillo/src/utils/supabase.ts @@ -67,7 +67,10 @@ export const getReferralsFromSupabase = async (): Promise<{ data?: unknown; }> => { try { - const { data, error } = await supabase.from("referrals").select("*"); + const { data, error } = await supabase + .from("referrals") + .select("*") + .eq("active", true); if (error) { console.error("Error fetching referrals:", error); From a7cb1fbff5dbb19bc98ca5c62887e6b578b9ccdb Mon Sep 17 00:00:00 2001 From: neocybereth Date: Mon, 14 Apr 2025 14:34:41 +1200 Subject: [PATCH 05/18] fix: update --- .../namadillo/src/App/Layout/TopNavigation.tsx | 7 +++++-- apps/namadillo/src/App/Referrals/Referrals.tsx | 18 +++++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/apps/namadillo/src/App/Layout/TopNavigation.tsx b/apps/namadillo/src/App/Layout/TopNavigation.tsx index 1c41ae65fa..84b854e937 100644 --- a/apps/namadillo/src/App/Layout/TopNavigation.tsx +++ b/apps/namadillo/src/App/Layout/TopNavigation.tsx @@ -31,8 +31,11 @@ export const TopNavigation = (): JSX.Element => { const location = useLocation(); const navigate = useNavigate(); const isValidityOps = - defaultAccount.data?.address === - "tnam1q8lhvxys53dlc8wzlg7dyqf9avd0vff6wvav4amt"; + defaultAccount.data?.address && + [ + "tnam1q8lhvxys53dlc8wzlg7dyqf9avd0vff6wvav4amt", + "tnam1qr0e06vqhw9u0yqy9d5zmtq0q8ekckhe2vkqc3ky", + ].includes(defaultAccount.data?.address); if (!userHasAccount) { return ( diff --git a/apps/namadillo/src/App/Referrals/Referrals.tsx b/apps/namadillo/src/App/Referrals/Referrals.tsx index ddbd174af3..99e9606bb1 100644 --- a/apps/namadillo/src/App/Referrals/Referrals.tsx +++ b/apps/namadillo/src/App/Referrals/Referrals.tsx @@ -1,5 +1,9 @@ import { Panel } from "@namada/components"; +import { routes } from "App/routes"; +import { defaultAccountAtom } from "atoms/accounts"; +import { useAtomValue } from "jotai"; import { useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; import { getReferralsFromSupabase } from "../../utils/supabase"; import { ReferralsTable } from "./ReferralsTable"; @@ -15,7 +19,15 @@ export const Referrals = (): JSX.Element => { const [referrals, setReferrals] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const defaultAccount = useAtomValue(defaultAccountAtom); + const isValidityOps = + defaultAccount.data?.address && + [ + "tnam1q8lhvxys53dlc8wzlg7dyqf9avd0vff6wvav4amt", + "tnam1qr0e06vqhw9u0yqy9d5zmtq0q8ekckhe2vkqc3ky", + ].includes(defaultAccount.data?.address); + const navigate = useNavigate(); useEffect(() => { const fetchReferrals = async (): Promise => { try { @@ -35,10 +47,14 @@ export const Referrals = (): JSX.Element => { setLoading(false); } }; - + if (!isValidityOps) return navigate(routes.root); fetchReferrals(); }, []); + if (!isValidityOps) { + return
You are not authorized to view this page
; + } + return (
From 36763a11c01fe71e6a02f02732022abf6463181f Mon Sep 17 00:00:00 2001 From: neocybereth Date: Mon, 14 Apr 2025 14:39:39 +1200 Subject: [PATCH 06/18] fix: update --- apps/namadillo/src/App/Staking/IncrementBonding.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/namadillo/src/App/Staking/IncrementBonding.tsx b/apps/namadillo/src/App/Staking/IncrementBonding.tsx index 80b4b9eb2c..fac799124b 100644 --- a/apps/namadillo/src/App/Staking/IncrementBonding.tsx +++ b/apps/namadillo/src/App/Staking/IncrementBonding.tsx @@ -62,7 +62,7 @@ const IncrementBonding = (): JSX.Element => { const validatorLink = isNamadaAddress(searchParams.get("validator") ?? "") ? searchParams.get("validator") - : null; + : "ValidityOps#1"; const [filter, setFilter] = useState(validatorLink ?? ""); const [onlyMyValidators, setOnlyMyValidators] = useState(false); const [validatorFilter, setValidatorFilter] = From 03eaf689260494202f8c78d7695f8742f3634353 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Mon, 14 Apr 2025 14:48:44 +1200 Subject: [PATCH 07/18] fix: cleanup' --- .../src/App/Referrals/ReferralSheetModal.tsx | 132 +++++++++++++++++- apps/namadillo/src/atoms/staking/services.ts | 2 +- 2 files changed, 128 insertions(+), 6 deletions(-) diff --git a/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx b/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx index cfbc5e7a18..e4321bb94a 100644 --- a/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx +++ b/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx @@ -1,12 +1,15 @@ -import { Modal } from "@namada/components"; - -import { Tooltip } from "@namada/components"; - -import { TableRow } from "@namada/components"; +import { ActionButton, Modal, TableRow, Tooltip } from "@namada/components"; +import { TransparentTransferMsgValue } from "@namada/types"; import { shortenAddress } from "@namada/utils"; import { ModalTransition } from "App/Common/ModalTransition"; import { TableWithPaginator } from "App/Common/TableWithPaginator"; +import { defaultAccountAtom } from "atoms/accounts"; +import { chainAtom, nativeTokenAddressAtom } from "atoms/chain"; +import { createTransparentTransferAtom } from "atoms/transfer/atoms"; import BigNumber from "bignumber.js"; +import { useTransaction } from "hooks/useTransaction"; +import { useTransactionFee } from "hooks/useTransactionFee"; +import { useAtomValue } from "jotai"; import { useCallback, useState } from "react"; import { IoClose } from "react-icons/io5"; import { twMerge } from "tailwind-merge"; @@ -20,7 +23,14 @@ export const ReferralSheetModal = ({ onClose: () => void; }): JSX.Element => { const [page, setPage] = useState(0); + const [isProcessingPayout, setIsProcessingPayout] = useState(false); + const [payoutError, setPayoutError] = useState(null); + const [payoutSuccess, setPayoutSuccess] = useState(false); const resultsPerPage = 10; + const defaultAccount = useAtomValue(defaultAccountAtom); + const chain = useAtomValue(chainAtom); + const feeProps = useTransactionFee(["TransparentTransfer"], false); + const namTokenAddressQuery = useAtomValue(nativeTokenAddressAtom); // Define table headers const headers = [ @@ -99,6 +109,80 @@ export const ReferralSheetModal = ({ {} as Record ); + // Setup transaction + const { execute: executeBatchTransfer, isPending: isExecutingBatchTransfer } = + useTransaction({ + eventType: "TransparentTransfer", + createTxAtom: createTransparentTransferAtom, + params: [], + parsePendingTxNotification: () => ({ + title: "Referral payout in progress", + description: "Your referral payout transaction is being processed", + }), + parseErrorTxNotification: () => ({ + title: "Referral payout failed", + description: "An error occurred during the batch payment", + }), + onBroadcasted: () => { + setPayoutSuccess(true); + setIsProcessingPayout(false); + }, + onError: (error) => { + setPayoutError( + typeof error === "string" ? error + : error instanceof Error ? error.message + : "Unknown error" + ); + setIsProcessingPayout(false); + }, + }); + + // Handle payout button click + const handlePayoutReferrals = async (): Promise => { + try { + setIsProcessingPayout(true); + setPayoutError(null); + setPayoutSuccess(false); + + if (!defaultAccount.data?.address) { + throw new Error("No source account available"); + } + + const sourceAddress = defaultAccount.data.address; + // NAM token address - this should be configured appropriately for your environment + const tokenAddress = namTokenAddressQuery.data!; + + // Create batch transfer data structure - one per referrer + const msgValueData = Object.entries(totalRewards).map( + ([referrerAddress, amount]) => ({ + source: sourceAddress, + target: referrerAddress, + token: tokenAddress, + amount, + }) + ); + + const batchProps = [ + { + data: msgValueData, + }, + ]; + + // Execute the batch transfer + await executeBatchTransfer({ + params: batchProps, + gasConfig: feeProps.gasConfig, + account: defaultAccount.data, + }); + } catch (error) { + console.error("Payout error:", error); + setPayoutError( + error instanceof Error ? error.message : "Unknown error occurred" + ); + setIsProcessingPayout(false); + } + }; + return (
+ + {/* Payout Referrals button */} +
+

Process Payouts

+

+ This will create a batch payment transaction to pay out all + referrers their total rewards. The transaction will be sent to + your Namada extension for approval. +

+ + {payoutError && ( +
+ {payoutError} +
+ )} + + {payoutSuccess && ( +
+ Payout transaction successfully sent to the blockchain! +
+ )} + + + {isProcessingPayout || isExecutingBatchTransfer ? + "Processing..." + : "Payout Referrals"} + +
}
diff --git a/apps/namadillo/src/atoms/staking/services.ts b/apps/namadillo/src/atoms/staking/services.ts index 03bc739a7d..d4381370f5 100644 --- a/apps/namadillo/src/atoms/staking/services.ts +++ b/apps/namadillo/src/atoms/staking/services.ts @@ -83,6 +83,7 @@ export const createWithdrawTx = async ( gasConfig: GasConfig ): Promise> => { const sdk = await getSdkInstance(); + return await buildTx( sdk, account, @@ -92,7 +93,6 @@ export const createWithdrawTx = async ( sdk.tx.buildWithdraw ); }; - export const createClaimTx = async ( chain: ChainSettings, account: Account, From c1e5248653a0a5e3223ea751ec1bde5e904e5e16 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Mon, 14 Apr 2025 17:28:27 +1200 Subject: [PATCH 08/18] fix: update --- .../src/App/Referrals/ReferralSheetModal.tsx | 79 +++++++++---------- .../src/App/Referrals/ReferralsTable.tsx | 2 +- apps/namadillo/vite.config.mjs | 1 + 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx b/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx index e4321bb94a..78cca6c87f 100644 --- a/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx +++ b/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx @@ -203,6 +203,45 @@ export const ReferralSheetModal = ({ {rewardsData.length === 0 ?
No rewards found
:
+ {/* Payout Referrals button - separate from tables */} +
+

+ Process Payouts +

+

+ This will create a batch payment transaction to pay out all + referrers their total rewards. The transaction will be sent to + your Namada extension for approval. +

+ {payoutError && ( +
+ {payoutError} +
+ )} + {payoutSuccess && ( +
+ Payout transaction successfully sent to the blockchain! +
+ )} + + + {isProcessingPayout || isExecutingBatchTransfer ? + "Processing..." + : "Payout Referrals"} + +

Total Rewards by Referrer @@ -236,10 +275,8 @@ export const ReferralSheetModal = ({ ))}

-

Rewards by Epoch

- {/* Referrer selector tabs */}
@@ -279,44 +316,6 @@ export const ReferralSheetModal = ({ headProps={{ className: "text-neutral-500" }} />
- - {/* Payout Referrals button */} -
-

Process Payouts

-

- This will create a batch payment transaction to pay out all - referrers their total rewards. The transaction will be sent to - your Namada extension for approval. -

- - {payoutError && ( -
- {payoutError} -
- )} - - {payoutSuccess && ( -
- Payout transaction successfully sent to the blockchain! -
- )} - - - {isProcessingPayout || isExecutingBatchTransfer ? - "Processing..." - : "Payout Referrals"} - -
}
diff --git a/apps/namadillo/src/App/Referrals/ReferralsTable.tsx b/apps/namadillo/src/App/Referrals/ReferralsTable.tsx index 5c22d33b2a..5a4ce32ba6 100644 --- a/apps/namadillo/src/App/Referrals/ReferralsTable.tsx +++ b/apps/namadillo/src/App/Referrals/ReferralsTable.tsx @@ -169,7 +169,7 @@ export const ReferralsTable = ({ // Get rewards for each epoch from start_epoch to current epoch for ( let epoch = referral.start_epoch; - epoch < chainStatus.epoch; + epoch < chainStatus.epoch + 1; epoch++ ) { try { diff --git a/apps/namadillo/vite.config.mjs b/apps/namadillo/vite.config.mjs index 48e5cc6e3c..ff364c1fe4 100644 --- a/apps/namadillo/vite.config.mjs +++ b/apps/namadillo/vite.config.mjs @@ -49,6 +49,7 @@ export default defineConfig(({ mode }) => { define: { "process.env.SUPABASE_URL": JSON.stringify(env.SUPABASE_URL), "process.env.SUPABASE_ANON_KEY": JSON.stringify(env.SUPABASE_ANON_KEY), + "process.env.INDEXER_URL": JSON.stringify(env.INDEXER_URL), }, optimizeDeps: { esbuildOptions: { From d0e6511868d5eebce1bb2c5111bd6b990c128f99 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Mon, 14 Apr 2025 19:06:12 +1200 Subject: [PATCH 09/18] fix: upgrade --- apps/namadillo/src/App/Referrals/ReferralsTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/namadillo/src/App/Referrals/ReferralsTable.tsx b/apps/namadillo/src/App/Referrals/ReferralsTable.tsx index 5a4ce32ba6..5c22d33b2a 100644 --- a/apps/namadillo/src/App/Referrals/ReferralsTable.tsx +++ b/apps/namadillo/src/App/Referrals/ReferralsTable.tsx @@ -169,7 +169,7 @@ export const ReferralsTable = ({ // Get rewards for each epoch from start_epoch to current epoch for ( let epoch = referral.start_epoch; - epoch < chainStatus.epoch + 1; + epoch < chainStatus.epoch; epoch++ ) { try { From fcb794037dedd21b1e38eea500f15c9b0c5752ab Mon Sep 17 00:00:00 2001 From: neocybereth Date: Thu, 24 Apr 2025 23:25:45 +1200 Subject: [PATCH 10/18] fix: update to grab proper amounts --- apps/namadillo/namada_history_table.txt | 356 ++++++++++++++++++ .../src/App/Referrals/ReferralSheetModal.tsx | 77 ++-- .../src/App/Referrals/ReferralsTable.tsx | 215 ++++++----- 3 files changed, 523 insertions(+), 125 deletions(-) create mode 100644 apps/namadillo/namada_history_table.txt diff --git a/apps/namadillo/namada_history_table.txt b/apps/namadillo/namada_history_table.txt new file mode 100644 index 0000000000..ad18738430 --- /dev/null +++ b/apps/namadillo/namada_history_table.txt @@ -0,0 +1,356 @@ +BLOCK HEIGHT | TX KIND | AMOUNT | TX ID | EXIT CODE | VALIDATOR +==================================================================================================== +1704575 | bond | 119.550506 | 46188605...2affcde9 | applied | tnam1q8l...wvav4amt +1704575 | claimRewards | - | 715c16f2...43fd9ae6 | applied | tnam1q8l...wvav4amt +1697165 | claimRewards | - | 0064c2bf...c1f5056e | applied | tnam1q8l...wvav4amt +1697165 | bond | 159.293111 | 574426e9...67413452 | applied | tnam1q8l...wvav4amt +1682840 | claimRewards | - | 41a0c6a0...3ff07ae3 | applied | tnam1q8l...wvav4amt +1682840 | bond | 318.760436 | 24f40781...6ea51979 | applied | tnam1q8l...wvav4amt +1656858 | bond | 198.889127 | 34acb5aa...35645cfb | applied | tnam1q8l...wvav4amt +1656858 | claimRewards | - | ec1dc05b...83f4cb23 | applied | tnam1q8l...wvav4amt +1642971 | bond | 119.536340 | a9ca1774...91b4be68 | applied | tnam1q8l...wvav4amt +1642971 | claimRewards | - | 2213b3b5...4949962a | applied | tnam1q8l...wvav4amt +1632620 | bond | 1.000000 | 0f43a6e0...2bca0b70 | applied | tnam1q8l...wvav4amt +1632464 | bond | 1.000000 | 8dd82842...5e90806e | applied | tnam1q8l...wvav4amt +1632400 | bond | 1.000000 | dbce4dca...fb4f1c1a | applied | tnam1q8l...wvav4amt +1631953 | claimRewards | - | f8f9b55b...d37e0aad | applied | tnam1q8l...wvav4amt +1631953 | bond | 39.846789 | 5af57779...46144e49 | applied | tnam1q8l...wvav4amt +1629476 | claimRewards | - | e09756ba...8fa23533 | applied | tnam1q8l...wvav4amt +1629476 | bond | 79.658502 | fd4dd7eb...0a9ffe2c | applied | tnam1q8l...wvav4amt +1624164 | bond | 199.152027 | bfa902cc...3da85e6e | applied | tnam1q8l...wvav4amt +1624164 | claimRewards | - | 7d6867a2...153c7b35 | applied | tnam1q8l...wvav4amt +1607670 | claimRewards | - | 37a1ca5a...3f6631bc | applied | tnam1q8l...wvav4amt +1607670 | bond | 39.863134 | ada5fd46...f4f7e5b3 | applied | tnam1q8l...wvav4amt +1605327 | claimRewards | - | 6b4acef7...76b51800 | applied | tnam1q8l...wvav4amt +1605327 | bond | 159.299389 | aa90e204...7318c16d | applied | tnam1q8l...wvav4amt +1593038 | claimRewards | - | 9779bb7d...ec13a3bf | applied | tnam1q8l...wvav4amt +1593038 | bond | 159.115779 | f8d27fb2...bee8bac8 | applied | tnam1q8l...wvav4amt +1580320 | bond | 318.412817 | 656bb1b2...12f3413c | applied | tnam1q8l...wvav4amt +1580320 | claimRewards | - | ccae3c20...3f1aa1b7 | applied | tnam1q8l...wvav4amt +1555665 | claimRewards | - | 50154617...77da8dda | applied | tnam1q8l...wvav4amt +1555665 | bond | 119.378111 | b4b6524b...3b46c1db | applied | tnam1q8l...wvav4amt +1546063 | claimRewards | - | 93b7902a...4558a47a | applied | tnam1q8l...wvav4amt +1546063 | bond | 119.119812 | 0cfd7dd1...ffdc03d9 | applied | tnam1q8l...wvav4amt +1536806 | claimRewards | - | 442b4991...9fa1bc0e | applied | tnam1q8l...wvav4amt +1536806 | bond | 39.691468 | aff10238...18b2fe28 | applied | tnam1q8l...wvav4amt +1534704 | claimRewards | - | 9ca2850e...785ad7fc | applied | tnam1q8l...wvav4amt +1534704 | bond | 158.756843 | 212224e5...6f01ea0f | applied | tnam1q8l...wvav4amt +1520346 | claimRewards | - | 6ad89bd1...f2374443 | applied | tnam1q8l...wvav4amt +1520346 | bond | 198.392616 | 365a53d6...d7c091d5 | applied | tnam1q8l...wvav4amt +1506576 | bond | 158.709221 | 98c22454...2a5c267b | applied | tnam1q8l...wvav4amt +1506576 | claimRewards | - | b4c97f7d...3321c279 | applied | tnam1q8l...wvav4amt +1494220 | claimRewards | - | 5f04b3d8...3a7f571d | applied | tnam1q8l...wvav4amt +1494220 | bond | 158.670374 | 7cc8f9e1...491f90e5 | applied | tnam1q8l...wvav4amt +1480098 | bond | 118.955664 | 3f255aff...5592053c | applied | tnam1q8l...wvav4amt +1480098 | claimRewards | - | 72d64593...41aa50aa | applied | tnam1q8l...wvav4amt +1472840 | claimRewards | - | f879541c...700f6abf | applied | tnam1q8l...wvav4amt +1472840 | bond | 39.646647 | 63132476...02a8c23e | applied | tnam1q8l...wvav4amt +1467799 | bond | 79.284010 | 75366871...b2eb5b1d | applied | tnam1q8l...wvav4amt +1467799 | claimRewards | - | 1c81ff47...739778f8 | applied | tnam1q8l...wvav4amt +1460641 | claimRewards | - | 41934e84...e766acf3 | applied | tnam1q8l...wvav4amt +1460641 | bond | 79.255550 | eaef6604...50cddb76 | applied | tnam1q8l...wvav4amt +1457176 | bond | 118.859309 | ea372fff...ce36c208 | applied | tnam1q8l...wvav4amt +1457176 | claimRewards | - | 15261d98...a858108e | applied | tnam1q8l...wvav4amt +1445273 | bond | 118.856331 | 58a4f8b4...ff567bcc | applied | tnam1q8l...wvav4amt +1445273 | claimRewards | - | 0602a4dd...3847d59f | applied | tnam1q8l...wvav4amt +1438256 | bond | 158.410274 | 85c5bf2b...feec0032 | applied | tnam1q8l...wvav4amt +1438256 | claimRewards | - | 46869966...d3e739d4 | applied | tnam1q8l...wvav4amt +1424819 | claimRewards | - | 5b32370a...83053791 | applied | tnam1q8l...wvav4amt +1424819 | bond | 79.205057 | 238ce0d7...143a97b5 | applied | tnam1q8l...wvav4amt +1417770 | claimRewards | - | fbc307d5...ee5a0c69 | applied | tnam1q8l...wvav4amt +1417770 | bond | 79.157255 | 846b271b...f4a735e0 | applied | tnam1q8l...wvav4amt +1411310 | bond | 79.670239 | cbfe78c5...62f6713f | applied | tnam1q8l...wvav4amt +1411310 | claimRewards | - | 40f9b0ef...d1b5262b | applied | tnam1q8l...wvav4amt +1405578 | claimRewards | - | bb831833...1ed9a99b | applied | tnam1q8l...wvav4amt +1405578 | bond | 80.171619 | d5041d86...27324d80 | applied | tnam1q8l...wvav4amt +1399791 | bond | 39.618100 | 1a1676ee...232de6a6 | applied | tnam1q8l...wvav4amt +1399791 | claimRewards | - | 275d069d...4d2a0ec1 | applied | tnam1q8l...wvav4amt +1397272 | claimRewards | - | cd34c602...561c4841 | applied | tnam1q8l...wvav4amt +1397272 | bond | 39.628442 | 67d53de3...ef128544 | applied | tnam1q8l...wvav4amt +1393424 | claimRewards | - | 88b2aaa5...d917c9b0 | applied | tnam1q8l...wvav4amt +1393424 | bond | 278.645000 | 66600c61...7c05be1d | applied | tnam1q8l...wvav4amt +1371689 | bond | 40.021959 | 8004882b...3deba4e5 | applied | tnam1q8l...wvav4amt +1371689 | claimRewards | - | f209a886...caf90dcd | applied | tnam1q8l...wvav4amt +1369901 | bond | 478.337161 | 593ae656...dff45988 | applied | tnam1q8l...wvav4amt +1369901 | claimRewards | - | c3c21878...39c36acf | applied | tnam1q8l...wvav4amt +1331605 | bond | 316.229725 | 71cb33f8...0d703f3f | applied | tnam1q8l...wvav4amt +1331605 | claimRewards | - | a200fa1a...ab796d3c | applied | tnam1q8l...wvav4amt +1306829 | bond | 118.235864 | 7bf37670...5cace705 | applied | tnam1q8l...wvav4amt +1306829 | claimRewards | - | 970ab02d...1c7c9121 | applied | tnam1q8l...wvav4amt +1298749 | claimRewards | - | 259c344f...bfc91c2d | applied | tnam1q8l...wvav4amt +1298749 | bond | 39.413947 | 23c82e2d...d52e4944 | applied | tnam1q8l...wvav4amt +1295964 | bond | 78.807450 | 9702d2c4...fec0364e | applied | tnam1q8l...wvav4amt +1295964 | claimRewards | - | 58d2e3e2...f3d49993 | applied | tnam1q8l...wvav4amt +1287188 | bond | 78.686646 | a458cb78...475cd29b | applied | tnam1q8l...wvav4amt +1287188 | claimRewards | - | d8fe3587...f3100179 | applied | tnam1q8l...wvav4amt +1282366 | claimRewards | - | a7f0ed2d...db7c149d | applied | tnam1q8l...wvav4amt +1282366 | bond | 711.157814 | 972f0c9b...c280f633 | applied | tnam1q8l...wvav4amt +1225691 | bond | 198.514846 | 5b8b730d...3c49dd20 | applied | tnam1q8l...wvav4amt +1225691 | claimRewards | - | 5981517e...633901c4 | applied | tnam1q8l...wvav4amt +1209651 | transparentTransfer | - | 72847664...dce405ef | applied | - +1209422 | bond | 158.804181 | be3fb991...764d6677 | applied | tnam1q8l...wvav4amt +1209422 | claimRewards | - | e011a8d1...7e08c371 | applied | tnam1q8l...wvav4amt +1197777 | bond | 39.657933 | 429555c7...39f67b51 | applied | tnam1q8l...wvav4amt +1197777 | claimRewards | - | 40f38c6b...7227aab3 | applied | tnam1q8l...wvav4amt +1196142 | bond | 158.710059 | cca504a2...55f47af0 | applied | tnam1q8l...wvav4amt +1196142 | claimRewards | - | b0f9105e...90ae6218 | applied | tnam1q8l...wvav4amt +1183359 | claimRewards | - | 956483e4...fade8171 | applied | tnam1q8l...wvav4amt +1183359 | bond | 158.644817 | b814d405...fca322f7 | applied | tnam1q8l...wvav4amt +1170955 | claimRewards | - | 9771536e...dc3f0736 | applied | tnam1q8l...wvav4amt +1170955 | bond | 118.885931 | c49ddb02...99e8079d | applied | tnam1q8l...wvav4amt +1159417 | claimRewards | - | b062c4c7...f0d28b01 | applied | tnam1q8l...wvav4amt +1159417 | bond | 118.858618 | d4549920...80249dca | applied | tnam1q8l...wvav4amt +1151712 | claimRewards | - | 87e80be5...2c57c664 | applied | tnam1q8l...wvav4amt +1151712 | bond | 39.612859 | 61b370eb...b9397ae4 | applied | tnam1q8l...wvav4amt +1149256 | claimRewards | - | 82919ff1...3f712c33 | applied | tnam1q8l...wvav4amt +1149256 | bond | 39.615750 | de538428...7d2ca32d | applied | tnam1q8l...wvav4amt +1146459 | claimRewards | - | cb29dd62...70508d73 | applied | tnam1q8l...wvav4amt +1146459 | bond | 79.215214 | 3524505a...9f092c02 | applied | tnam1q8l...wvav4amt +1138534 | claimRewards | - | 9b1812b6...0a2c829a | applied | tnam1q8l...wvav4amt +1138534 | bond | 79.147320 | 31e15c39...adbe9da2 | applied | tnam1q8l...wvav4amt +1133132 | bond | 158.242482 | edd9cef1...ae2ac0b1 | applied | tnam1q8l...wvav4amt +1133132 | claimRewards | - | aa042c68...d7092ccf | applied | tnam1q8l...wvav4amt +1121382 | claimRewards | - | 8893ae47...7fe59550 | applied | tnam1q8l...wvav4amt +1121382 | bond | 78.981410 | 89555038...60a0b638 | applied | tnam1q8l...wvav4amt +1114100 | bond | 39.493765 | 851e1fea...9f28135c | applied | tnam1q8l...wvav4amt +1110790 | bond | 39.466625 | 5e7a468c...a76e92c7 | applied | tnam1q8l...wvav4amt +1107388 | bond | 78.975865 | 61edfce6...35652525 | applied | tnam1q8l...wvav4amt +1100912 | bond | 78.927224 | eadfedda...895f1ced | applied | tnam1q8l...wvav4amt +1095313 | bond | 78.909631 | dfa3526c...827eed53 | applied | tnam1q8l...wvav4amt +1088000 | bond | 78.916235 | 0257d3ad...78831936 | applied | tnam1q8l...wvav4amt +1083536 | bond | 118.322216 | 952c3009...43e7a3ee | applied | tnam1q8l...wvav4amt +1072666 | bond | 39.393564 | 0361916d...e37aed4b | applied | tnam1q8l...wvav4amt +1072666 | claimRewards | - | dbf75e49...9d6d7fec | applied | tnam1q8l...wvav4amt +1069982 | bond | 118.254126 | 1a55289f...a35f650a | applied | tnam1q8l...wvav4amt +1069982 | claimRewards | - | 7b9b22c9...b87a6032 | applied | tnam1q8l...wvav4amt +1060878 | bond | 39.467588 | d8e43d53...000db552 | applied | tnam1q8l...wvav4amt +1060878 | claimRewards | - | 29b359f0...d35456ee | applied | tnam1q8l...wvav4amt +1057817 | voteProposal | - | 1d86a342...f9cc8313 | applied | - +1057781 | voteProposal | - | c73850dc...978ae714 | applied | - +1057662 | bond | 157.811454 | a334ae00...10c013de | applied | tnam1q8l...wvav4amt +1057662 | claimRewards | - | e5691e10...7c9f0e0e | applied | tnam1q8l...wvav4amt +1046091 | bond | 157.534881 | b5b6ccd2...284de5f7 | applied | tnam1q8l...wvav4amt +1046091 | claimRewards | - | abc22e57...8a70f681 | applied | tnam1q8l...wvav4amt +1032561 | claimRewards | - | 113a3ac0...a327c76a | applied | tnam1q8l...wvav4amt +1032561 | bond | 78.675543 | 98171415...eb8a033a | applied | tnam1q8l...wvav4amt +1026608 | claimRewards | - | c14d788e...923c54a0 | applied | tnam1q8l...wvav4amt +1026608 | bond | 78.417091 | d0093d6c...cc752202 | applied | tnam1q8l...wvav4amt +1021166 | bond | 157.020879 | fe12fb4a...e0a73e41 | applied | tnam1q8l...wvav4amt +1021166 | claimRewards | - | 2f404429...40358e15 | applied | tnam1q8l...wvav4amt +1008674 | voteProposal | - | 0dcecf5e...8b479141 | applied | - +1008672 | voteProposal | - | 22c844ad...8bcdbcbc | applied | - +1008669 | voteProposal | - | 5c94d4b2...f7fad5f1 | applied | - +1008661 | bond | 393.816623 | cf54e38e...f9c2dfe2 | applied | tnam1q8l...wvav4amt +1008661 | claimRewards | - | cf7b8be6...e6f47b89 | applied | tnam1q8l...wvav4amt +975950 | claimRewards | - | c577a66e...e2518e97 | applied | tnam1q8l...wvav4amt +975950 | bond | 235.034733 | 9083ae8f...20afb811 | applied | tnam1q8l...wvav4amt +959160 | bond | 157.049808 | 1a2037fb...7f3d7fdc | applied | tnam1q8l...wvav4amt +959160 | claimRewards | - | f525a68c...3888d62d | applied | tnam1q8l...wvav4amt +946914 | bond | 117.495870 | cfd7f850...b6ad5e26 | applied | tnam1q8l...wvav4amt +946914 | claimRewards | - | fe64498b...b21fb96c | applied | tnam1q8l...wvav4amt +937883 | unbond | - | a483aad5...2584d322 | applied | tnam1q8l...wvav4amt +937847 | unbond | - | 609168cf...ed6cacab | rejected | tnam1q8l...wvav4amt +937807 | unbond | - | 18dc78c5...8241d1b2 | applied | tnam1q8l...wvav4amt +937787 | unbond | - | c6e5a76f...7cd9b906 | rejected | tnam1q8l...wvav4amt +937777 | unbond | - | 11a2305b...4a625a5d | rejected | tnam1q8l...wvav4amt +935599 | unbond | - | ffa03d34...2a1e3208 | rejected | tnam1q8l...wvav4amt +935194 | bond | 313.495697 | ceb7cb4d...850e2ea3 | applied | tnam1q8l...wvav4amt +935194 | claimRewards | - | f8a0a8f3...b98e45e3 | applied | tnam1q8l...wvav4amt +924965 | voteProposal | - | be5bb36d...81f9da4f | applied | - +922687 | unbond | - | a5895c8a...a0d5298f | rejected | tnam1q8l...wvav4amt +922661 | unbond | - | b6e1b29c...e09b29a0 | rejected | tnam1q8l...wvav4amt +922632 | unbond | - | 4346bada...a14c08a8 | rejected | tnam1q8l...wvav4amt +922628 | claimRewards | - | 53b04882...40cba88c | applied | tnam1q8l...wvav4amt +922628 | bond | 313.356581 | d7f75149...43b8b854 | applied | tnam1q8l...wvav4amt +912294 | unbond | - | 1ae80904...87c2621c | rejected | tnam1q8l...wvav4amt +911856 | unbond | - | 714b0f27...ed206c1e | rejected | tnam1q8l...wvav4amt +911849 | unbond | - | 5d2e89cf...087511c7 | rejected | tnam1q8l...wvav4amt +911837 | unbond | - | 5935f3a9...a0ba9b96 | rejected | tnam1q8l...wvav4amt +911832 | bond | 233.878096 | 7316719d...1c0b25fe | applied | tnam1q8l...wvav4amt +911832 | claimRewards | - | 290b0f1e...f04f946c | applied | tnam1q8l...wvav4amt +901692 | claimRewards | - | 0664e0fb...c4bcef8b | applied | tnam1q8l...wvav4amt +901692 | bond | 7464.142114 | fb867b07...ddf6fe2b | rejected | tnam1q8l...wvav4amt +901640 | bond | 7464.142114 | 42efad92...242bef61 | applied | tnam1q8l...wvav4amt +901640 | claimRewards | - | 05f0166e...f227ddc7 | rejected | tnam1q8l...wvav4amt +603472 | bond | 161.065611 | 4ffd0e37...fda5ec6f | applied | tnam1q8l...wvav4amt +603472 | claimRewards | - | 7a5b320c...6b43edfd | applied | tnam1q8l...wvav4amt +598609 | bond | 160.940537 | cf2abd68...ec577382 | applied | tnam1q8l...wvav4amt +598609 | claimRewards | - | 602ff9cd...61b28780 | applied | tnam1q8l...wvav4amt +592638 | claimRewards | - | f08bf3ed...fccbdd53 | applied | tnam1q8l...wvav4amt +592638 | bond | 80.560070 | b7ea704c...73c20e69 | applied | tnam1q8l...wvav4amt +589700 | claimRewards | - | d2449696...ac3a0f15 | applied | tnam1q8l...wvav4amt +589700 | bond | 241.796912 | ffd3d8ef...8ac75e12 | applied | tnam1q8l...wvav4amt +579926 | claimRewards | - | 74643f2f...e7e8b72f | applied | tnam1q8l...wvav4amt +579926 | bond | 161.092738 | bc127011...dc7cd41a | applied | tnam1q8l...wvav4amt +574692 | bond | 241.919504 | 3f7f22db...992986c8 | applied | tnam1q8l...wvav4amt +574692 | claimRewards | - | 2a21c809...6aba5ef1 | applied | tnam1q8l...wvav4amt +562898 | claimRewards | - | 13af0017...478faf41 | applied | tnam1q8l...wvav4amt +562898 | bond | 402.989378 | 04d306ed...94183c85 | applied | tnam1q8l...wvav4amt +547904 | bond | 161.108435 | 113af04d...3bac5bec | applied | tnam1q8l...wvav4amt +547904 | claimRewards | - | 76e45d24...75495e60 | applied | tnam1q8l...wvav4amt +541536 | bond | 402.775067 | 500de353...7cda8da4 | applied | tnam1q8l...wvav4amt +541536 | claimRewards | - | 0b6de0ee...e36191fa | applied | tnam1q8l...wvav4amt +525508 | bond | 80.569960 | f632d55e...e62d59fd | applied | tnam1q8l...wvav4amt +525508 | claimRewards | - | e54da943...6decd2a2 | applied | tnam1q8l...wvav4amt +523328 | claimRewards | - | 8727aa3a...a05d2f45 | applied | tnam1q8l...wvav4amt +523328 | bond | 161.015710 | cbee5158...d906023d | applied | tnam1q8l...wvav4amt +515814 | claimRewards | - | 365e93a3...dddff335 | applied | tnam1q8l...wvav4amt +515814 | bond | 80.489716 | c16d4456...321f21d7 | applied | tnam1q8l...wvav4amt +513568 | claimRewards | - | 036a80e9...3dbc62ca | applied | tnam1q8l...wvav4amt +513568 | bond | 80.755643 | 187c56f6...27aaa583 | applied | tnam1q8l...wvav4amt +511374 | bond | 80.746208 | d98dd7bb...0bdba248 | applied | tnam1q8l...wvav4amt +511374 | claimRewards | - | a6f79099...155cf074 | applied | tnam1q8l...wvav4amt +506491 | bond | 80.692849 | 8c6baff1...332fa9e2 | applied | tnam1q8l...wvav4amt +506491 | claimRewards | - | a78f9f82...86662ecd | applied | tnam1q8l...wvav4amt +503901 | bond | 80.726508 | 8623b445...a473da45 | applied | tnam1q8l...wvav4amt +503901 | claimRewards | - | 97c7c919...b0945630 | applied | tnam1q8l...wvav4amt +500297 | claimRewards | - | e61cb553...d0e14e30 | applied | tnam1q8l...wvav4amt +500297 | bond | 80.732276 | a4ce0b56...523adb84 | applied | tnam1q8l...wvav4amt +498925 | bond | 161.376700 | 120d59cb...7b9982c0 | applied | tnam1q8l...wvav4amt +498925 | claimRewards | - | 7d1fd950...0494d394 | applied | tnam1q8l...wvav4amt +491429 | claimRewards | - | 561a2c52...d02c7e80 | applied | tnam1q8l...wvav4amt +491429 | bond | 80.699862 | a35de8c0...893bd3fb | applied | tnam1q8l...wvav4amt +488021 | bond | 80.646106 | 5a84c02f...b8070cfd | applied | tnam1q8l...wvav4amt +488021 | claimRewards | - | 6ae2c97e...4731efa3 | applied | tnam1q8l...wvav4amt +485929 | claimRewards | - | 346324de...796efc3f | applied | tnam1q8l...wvav4amt +485929 | bond | 161.302445 | 1e5aa052...b640926d | applied | tnam1q8l...wvav4amt +479049 | bond | 80.684743 | c47a5688...0473ca61 | applied | tnam1q8l...wvav4amt +479049 | claimRewards | - | 707627ad...44a41cd7 | applied | tnam1q8l...wvav4amt +476532 | bond | 80.688064 | 029cb1d8...ac0520bf | applied | tnam1q8l...wvav4amt +476532 | claimRewards | - | 950577b9...04f7a338 | applied | tnam1q8l...wvav4amt +473617 | bond | 80.626737 | 2816e67c...8ca06f6b | applied | tnam1q8l...wvav4amt +473617 | claimRewards | - | 47aea4ed...a6ccd1ff | applied | tnam1q8l...wvav4amt +469922 | bond | 80.678913 | efa1b6ff...e1bd4f97 | applied | tnam1q8l...wvav4amt +469922 | claimRewards | - | b2ee2571...9f15877d | applied | tnam1q8l...wvav4amt +466543 | claimRewards | - | a4459a92...14593342 | applied | tnam1q8l...wvav4amt +466543 | bond | 80.638475 | 05655365...69ad9aa4 | applied | tnam1q8l...wvav4amt +462813 | claimRewards | - | 8a4d3325...76170582 | applied | tnam1q8l...wvav4amt +462813 | bond | 241.996302 | c339ce42...efc7d77d | applied | tnam1q8l...wvav4amt +453164 | claimRewards | - | efc6ff02...6ed1527a | applied | tnam1q8l...wvav4amt +453164 | bond | 80.649137 | f892231f...d91c4ff9 | applied | tnam1q8l...wvav4amt +450606 | bond | 80.599441 | 0f481f67...c768d7df | applied | tnam1q8l...wvav4amt +450606 | claimRewards | - | 0cb8b9cb...0bde7a9e | applied | tnam1q8l...wvav4amt +448876 | claimRewards | - | cd6da9f7...68cf4e04 | applied | tnam1q8l...wvav4amt +448876 | bond | 161.228486 | 41a77d4c...82b2d5cd | applied | tnam1q8l...wvav4amt +440995 | claimRewards | - | 9dda19a4...8581cc73 | applied | tnam1q8l...wvav4amt +440995 | bond | 80.604298 | d03e3784...01297dbf | applied | tnam1q8l...wvav4amt +438920 | bond | 82.660876 | 998d0d62...89f45b3f | applied | tnam1q8l...wvav4amt +438920 | claimRewards | - | 228b7ea6...59ad40af | applied | tnam1q8l...wvav4amt +435699 | claimRewards | - | 7b3842e8...63b5c539 | applied | tnam1q8l...wvav4amt +435699 | bond | 165.852706 | a08adf7b...6963f4a5 | applied | tnam1q8l...wvav4amt +428274 | claimRewards | - | 85bdca66...6d869e1e | applied | tnam1q8l...wvav4amt +428274 | bond | 83.989556 | a460357e...868dbcdf | applied | tnam1q8l...wvav4amt +425082 | bond | 84.013543 | 67c936de...6096ed9c | applied | tnam1q8l...wvav4amt +425082 | claimRewards | - | 65bdf4ac...5630f0e7 | applied | tnam1q8l...wvav4amt +423810 | bond | 84.007368 | 3f0a4a61...bb48db16 | applied | tnam1q8l...wvav4amt +423810 | claimRewards | - | 69afe28a...0257d92a | applied | tnam1q8l...wvav4amt +418890 | bond | 83.996031 | d2f7e60c...d17ec1bb | applied | tnam1q8l...wvav4amt +418890 | claimRewards | - | 3e33e04a...6c8e9f86 | applied | tnam1q8l...wvav4amt +416330 | bond | 83.991765 | 6c99d787...0798a21c | applied | tnam1q8l...wvav4amt +416330 | claimRewards | - | 696560a7...952d8089 | applied | tnam1q8l...wvav4amt +412709 | claimRewards | - | 905b9538...167af16d | applied | tnam1q8l...wvav4amt +412709 | bond | 84.076221 | 64b6769f...f5e1cb51 | applied | tnam1q8l...wvav4amt +410824 | claimRewards | - | 2f83590e...6236527c | applied | tnam1q8l...wvav4amt +410824 | bond | 83.944690 | dc1eb595...bd7cdc7f | applied | tnam1q8l...wvav4amt +406710 | claimRewards | - | 8c933ddd...fdf3f39b | applied | tnam1q8l...wvav4amt +406710 | bond | 83.930789 | a586f43f...1ae82747 | applied | tnam1q8l...wvav4amt +403517 | bond | 83.930627 | 24d75e47...17b3ba60 | applied | tnam1q8l...wvav4amt +403517 | claimRewards | - | 40b64f87...aebeb662 | applied | tnam1q8l...wvav4amt +400026 | bond | 83.921775 | 76082c70...6ee8022c | applied | tnam1q8l...wvav4amt +400026 | claimRewards | - | 0ff32666...2add8f71 | applied | tnam1q8l...wvav4amt +399022 | claimRewards | - | 9b71845f...295ec608 | applied | tnam1q8l...wvav4amt +399022 | bond | 83.909722 | 9dc8a7f5...e4497d9c | applied | tnam1q8l...wvav4amt +394070 | bond | 83.945728 | a8baf1c7...ef828cf4 | applied | tnam1q8l...wvav4amt +394070 | claimRewards | - | 0e5e5d8b...26710785 | applied | tnam1q8l...wvav4amt +390733 | claimRewards | - | dda2f690...6cd66e4b | applied | tnam1q8l...wvav4amt +390733 | bond | 83.943718 | c01ba0a7...157a4832 | applied | tnam1q8l...wvav4amt +389949 | bond | 83.992213 | 2291c59f...90976673 | applied | tnam1q8l...wvav4amt +389949 | claimRewards | - | cc0b29a2...f2f6485d | applied | tnam1q8l...wvav4amt +386065 | bond | 76.419950 | c580ee16...e3615941 | applied | tnam1q8l...wvav4amt +386065 | claimRewards | - | 4dc80002...54851903 | applied | tnam1q8l...wvav4amt +381831 | claimRewards | - | 67d9e57c...953f1ea8 | applied | tnam1q8l...wvav4amt +381831 | bond | 81.088015 | 36761c9d...7d97fc40 | applied | tnam1q8l...wvav4amt +380474 | bond | 90000.000000 | fd7017e1...29159d5a | applied | tnam1q8l...wvav4amt +378202 | bond | 81.083229 | bbc9c8ea...d5e8f4d9 | applied | tnam1q8l...wvav4amt +378202 | claimRewards | - | 7e5ffb75...96707a15 | applied | tnam1q8l...wvav4amt +375487 | bond | 81.076761 | 2d30e6a7...67b7ed61 | applied | tnam1q8l...wvav4amt +375487 | claimRewards | - | 264255b7...2ff524fe | applied | tnam1q8l...wvav4amt +373093 | bond | 45.236017 | c267c903...b3c4cd67 | applied | tnam1q8l...wvav4amt +373093 | claimRewards | - | 3846feba...05e3a311 | applied | tnam1q8l...wvav4amt +369270 | claimRewards | - | a6858a51...920f66e8 | applied | tnam1q8l...wvav4amt +369270 | bond | 45.229576 | 1a4104cc...697775de | applied | tnam1q8l...wvav4amt +367150 | bond | 400000.000000 | 91940b39...53d06767 | applied | tnam1q8l...wvav4amt +367141 | bond | 45.235478 | 259616a7...a4f3ccb3 | applied | tnam1q8l...wvav4amt +367141 | claimRewards | - | 53a25611...eae2c490 | applied | tnam1q8l...wvav4amt +363460 | bond | 45.198757 | 2e49f6dc...61e6e8a2 | applied | tnam1q8l...wvav4amt +363460 | claimRewards | - | b0fa1d48...7283abd5 | applied | tnam1q8l...wvav4amt +361825 | bond | 135.656943 | 4b199f85...4cabcb18 | applied | tnam1q8l...wvav4amt +361825 | claimRewards | - | 1e2daf4f...36fe3ba8 | applied | tnam1q8l...wvav4amt +352367 | bond | 45.212604 | b98bdd03...ed7d653d | applied | tnam1q8l...wvav4amt +352367 | claimRewards | - | 034359bc...bd451028 | applied | tnam1q8l...wvav4amt +349010 | bond | 135.597128 | 4a03d693...cb0555f3 | applied | tnam1q8l...wvav4amt +349010 | claimRewards | - | 74a78947...a9134edb | applied | tnam1q8l...wvav4amt +338163 | claimRewards | - | 872aa34a...6b1c4f42 | applied | tnam1q8l...wvav4amt +338163 | bond | 45.236022 | 84bd7940...cf027957 | applied | tnam1q8l...wvav4amt +336433 | claimRewards | - | a3dfe067...39c43bf8 | applied | tnam1q8l...wvav4amt +336433 | bond | 90.456647 | c0036f42...c0849feb | applied | tnam1q8l...wvav4amt +328287 | voteProposal | - | 37fcb832...813cf754 | applied | - +328280 | claimRewards | - | 2c6d334a...da6ca138 | applied | tnam1q8l...wvav4amt +328280 | bond | 90.431905 | 433845e2...f857a97f | applied | tnam1q8l...wvav4amt +324054 | claimRewards | - | 10ae2ac6...e4c76440 | applied | tnam1q8l...wvav4amt +324054 | bond | 90.370742 | 86a4966e...dbf32fc7 | applied | tnam1q8l...wvav4amt +317138 | claimRewards | - | f989849c...c23a29ef | applied | tnam1q8l...wvav4amt +317138 | bond | 45.192290 | 83336f56...49a3f4c1 | applied | tnam1q8l...wvav4amt +312919 | bond | 45.182541 | c98dfcd5...0724e102 | applied | tnam1q8l...wvav4amt +312919 | claimRewards | - | 195044d4...ee54b152 | applied | tnam1q8l...wvav4amt +311631 | bond | 135.505783 | f110955a...ed4e77c4 | applied | tnam1q8l...wvav4amt +311631 | claimRewards | - | 593a4831...49f749ab | applied | tnam1q8l...wvav4amt +300257 | claimRewards | - | 7f471f03...f70518e3 | applied | tnam1q8l...wvav4amt +300257 | bond | 45.142906 | 3421f799...68601486 | applied | tnam1q8l...wvav4amt +298849 | claimRewards | - | 6ae10fce...9c05eeb5 | applied | tnam1q8l...wvav4amt +298849 | bond | 90.222138 | 0f361583...fcde9542 | applied | tnam1q8l...wvav4amt +293587 | bond | 45.083837 | 209e2c0a...c5b8f8d1 | applied | tnam1q8l...wvav4amt +293587 | claimRewards | - | ec881cb8...6b09c575 | applied | tnam1q8l...wvav4amt +287763 | bond | 46.387573 | fba29803...61b77866 | applied | tnam1q8l...wvav4amt +287763 | claimRewards | - | f32811a9...349138d5 | applied | tnam1q8l...wvav4amt +286534 | bond | 92.742865 | 431ad62a...1f260bbe | applied | tnam1q8l...wvav4amt +286534 | claimRewards | - | a15fc543...bbe1430a | applied | tnam1q8l...wvav4amt +278451 | claimRewards | - | 402f4cb4...9739b084 | applied | tnam1q8l...wvav4amt +278451 | bond | 46.254208 | dac71aa5...bed1c9b0 | applied | tnam1q8l...wvav4amt +276399 | claimRewards | - | c026c247...34158a44 | applied | tnam1q8l...wvav4amt +276399 | bond | 46.280223 | 8ccac679...2e5a4f94 | applied | tnam1q8l...wvav4amt +274684 | bond | 941.356693 | b4865f2a...7c554432 | applied | tnam1q8l...wvav4amt +274684 | claimRewards | - | 51da944c...8164c578 | applied | tnam1q8l...wvav4amt +214170 | claimRewards | - | e73b6063...08728f6f | applied | tnam1q8l...wvav4amt +214170 | bond | 62.115373 | b6c3609c...9f767ef0 | applied | tnam1q8l...wvav4amt +211518 | bond | 124.265195 | b0429770...d6952bfe | applied | tnam1q8l...wvav4amt +211518 | claimRewards | - | cf1990a9...568da306 | applied | tnam1q8l...wvav4amt +205554 | claimRewards | - | 104b5ce3...cc290dc2 | applied | tnam1q8l...wvav4amt +205554 | bond | 63.371039 | 285267ab...8c7b08ec | applied | tnam1q8l...wvav4amt +200539 | bond | 50.000000 | 8336245b...53ef4abe | applied | tnam1q8l...wvav4amt +200535 | claimRewards | - | d1c0819d...e0953b14 | applied | tnam1q8l...wvav4amt +199462 | bond | 129.956286 | 12811faa...0ae5999c | applied | tnam1q8l...wvav4amt +199462 | claimRewards | - | da685a14...04d6a23d | applied | tnam1q8l...wvav4amt +191132 | claimRewards | - | d281cca5...0ddae2ac | applied | tnam1q8l...wvav4amt +191132 | bond | 67.087372 | 87140915...bda5ff8d | applied | tnam1q8l...wvav4amt +188541 | bond | 67.148211 | fb9ecf38...22c698dc | applied | tnam1q8l...wvav4amt +188541 | claimRewards | - | 449413fb...b74b451f | applied | tnam1q8l...wvav4amt +186595 | bond | 134.359882 | 53148b50...fc04d3d7 | applied | tnam1q8l...wvav4amt +186595 | claimRewards | - | f69afb07...843f5f4a | applied | tnam1q8l...wvav4amt +178688 | claimRewards | - | 5914d387...c7e5c123 | applied | tnam1q8l...wvav4amt +178688 | bond | 54.820056 | debd41e5...efa76724 | applied | tnam1q8l...wvav4amt +177453 | voteProposal | - | 86b5cd53...32eac77f | applied | - +175818 | claimRewards | - | 04a4ff71...3c11c8c7 | applied | tnam1q8l...wvav4amt +175818 | bond | 50.912146 | 08a49f84...0f92a3b3 | applied | tnam1q8l...wvav4amt +174733 | bond | 99999.000000 | 15b50b2f...10e2f4f1 | applied | tnam1q8l...wvav4amt +174679 | bond | 87.226604 | 807a4ff0...e69dc410 | applied | tnam1q8l...wvav4amt +174679 | claimRewards | - | 3979430f...eafcf9b5 | applied | tnam1q8l...wvav4amt +168834 | claimRewards | - | fed81252...c8303308 | applied | tnam1q8l...wvav4amt +168834 | bond | 119.753607 | 740cf122...909c289c | applied | tnam1q8l...wvav4amt +152863 | bond | 99899.850000 | dc86ba30...7b3ff4d4 | applied | tnam1q8l...wvav4amt +64654 | bond | 100.000000 | 1ea0797c...c87b3739 | applied | tnam1q8l...wvav4amt +19718 | bond | 100000.000000 | a23cdbb5...a23ad6e2 | applied | tnam1q8l...wvav4amt +2828 | bond | 99990.000000 | b474513f...3504f94f | applied | tnam1q8l...wvav4amt +1267 | bond | 99999.700000 | 60ae4b33...1e8419f5 | applied | tnam1q8l...wvav4amt +1225 | bond | 10.000000 | 8af323bd...66a5e454 | applied | tnam1q8l...wvav4amt +1225 | revealPk | - | 1772aa0e...09d42358 | applied | - diff --git a/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx b/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx index 78cca6c87f..8aa0f696c4 100644 --- a/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx +++ b/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx @@ -26,7 +26,7 @@ export const ReferralSheetModal = ({ const [isProcessingPayout, setIsProcessingPayout] = useState(false); const [payoutError, setPayoutError] = useState(null); const [payoutSuccess, setPayoutSuccess] = useState(false); - const resultsPerPage = 10; + const resultsPerPage = 100; const defaultAccount = useAtomValue(defaultAccountAtom); const chain = useAtomValue(chainAtom); const feeProps = useTransactionFee(["TransparentTransfer"], false); @@ -187,22 +187,26 @@ export const ReferralSheetModal = ({ - - - -
- Referral Rewards -
+ {/* Modal header with fixed position */} +
+ + + +
+ Referral Rewards +
+
-
+ {/* Modal content with scrollable area */} +
{rewardsData.length === 0 ?
No rewards found
- :
+ :
{/* Payout Referrals button - separate from tables */}

@@ -246,7 +250,7 @@ export const ReferralSheetModal = ({

Total Rewards by Referrer

-
+
{Object.entries(totalRewards).map(([address, amount]) => (
-
+

Rewards by Epoch

- {/* Referrer selector tabs */} -
-
+ {/* Referrer selector tabs with horizontal scroll */} +
+
{Object.keys(rewardsByReferrer).map( (referrerAddress, index) => (
- + {/* Table with horizontal scroll wrapper */} +
+ +
} diff --git a/apps/namadillo/src/App/Referrals/ReferralsTable.tsx b/apps/namadillo/src/App/Referrals/ReferralsTable.tsx index 5c22d33b2a..0eecf872f2 100644 --- a/apps/namadillo/src/App/Referrals/ReferralsTable.tsx +++ b/apps/namadillo/src/App/Referrals/ReferralsTable.tsx @@ -46,35 +46,27 @@ export type ReferralReward = { validator: ValidatorInfo; }; -// Helper function to generate CSV data const generateCsvContent = (rewardsData: ReferralReward[]): string => { - // CSV Headers const headers = "Referrer Address,Referee Address,Epoch,Reward (NAM),Validator Name\n"; - - // Format each reward as a CSV row const csvData = rewardsData - .map((reward) => { - return [ - reward.referrerAddress, - reward.refereeAddress, - reward.epoch, - reward.amount.toFixed(6), - reward.validator.name, - ].join(","); - }) + .map((r) => + [ + r.referrerAddress, + r.refereeAddress, + r.epoch, + r.amount.toFixed(6), + r.validator.name, + ].join(",") + ) .join("\n"); - return headers + csvData; }; -// Helper function to download CSV const downloadCsv = (data: string, filename: string): void => { - const csvContent = `data:text/csv;charset=utf-8,${data}`; - const encodedUri = encodeURI(csvContent); - + const uri = encodeURI(`data:text/csv;charset=utf-8,${data}`); const link = document.createElement("a"); - link.setAttribute("href", encodedUri); + link.setAttribute("href", uri); link.setAttribute("download", filename); document.body.appendChild(link); link.click(); @@ -95,7 +87,6 @@ export const ReferralsTable = ({ const [showModal, setShowModal] = useState(false); const chainStatus = useAtomValue(chainStatusAtom); - // Define table headers const headers = [ "Referrer Address", "Referee Address", @@ -103,49 +94,39 @@ export const ReferralsTable = ({ "Created At", ]; - const renderRow = useCallback((referral: Referral): TableRow => { - return { - key: `referral-${referral.id}`, + const renderRow = useCallback( + (ref: Referral): TableRow => ({ + key: `referral-${ref.id}`, cells: [ - // Referrer address (truncated for display)
- {shortenAddress(referral.referrer_address, 10, 6)} - - {referral.referrer_address} - + {shortenAddress(ref.referrer_address, 10, 6)} + {ref.referrer_address}
, - // Referee address (truncated for display)
- {shortenAddress(referral.referee_address, 10, 6)} - - {referral.referee_address} - + {shortenAddress(ref.referee_address, 10, 6)} + {ref.referee_address}
, - // Epoch
- {referral.start_epoch} + {ref.start_epoch}
, - // Created at date
- {referral.created_at ? - new Date(referral.created_at).toLocaleString() - : "N/A"} + {ref.created_at ? new Date(ref.created_at).toLocaleString() : "N/A"}
, ], - }; - }, []); + }), + [] + ); const paginatedItems = referrals.slice( page * resultsPerPage, page * resultsPerPage + resultsPerPage ); - const pageCount = Math.ceil(referrals.length / resultsPerPage); const generateReferralSheet = async (): Promise => { @@ -153,72 +134,126 @@ export const ReferralsTable = ({ setGenerationError("Chain status not available"); return; } - setIsGenerating(true); setGenerationError(null); setRewardsData([]); try { - const rewards: ReferralReward[] = []; - - // Process each referral - for (const referral of referrals) { - const referrerAddress = referral.referrer_address; - const refereeAddress = referral.referee_address; + type ReferralEpochMapItem = { + type: "current" | "previous"; + referrerAddress: string; + refereeAddress: string; + epoch: number; + }; + type ResponseData = ReferralEpochMapItem & { + data: RewardData[] | null; + success: boolean; + }; - // Get rewards for each epoch from start_epoch to current epoch - for ( - let epoch = referral.start_epoch; - epoch < chainStatus.epoch; - epoch++ - ) { - try { - const url = `${process.env.INDEXER_URL}/api/v1/pos/reward/${referrerAddress}/tnam1q8lhvxys53dlc8wzlg7dyqf9avd0vff6wvav4amt/${epoch}`; + // map: referralKey → { epoch: { amount, validator } } + const cumByReferral = new Map< + string, + Record + >(); + const fetchPromises: Promise[] = []; + const referralEpochMap: ReferralEpochMapItem[] = []; - const response = await fetch(url); + for (const { + referrer_address, + referee_address, + start_epoch, + } of referrals) { + const key = `${referrer_address}-${referee_address}`; + // fetch previous epoch cumulative + if (start_epoch > 0) { + referralEpochMap.push({ + type: "previous", + referrerAddress: referrer_address, + refereeAddress: referee_address, + epoch: start_epoch - 1, + }); + fetchPromises.push( + fetch( + `${process.env.INDEXER_URL}/api/v1/pos/reward/${referrer_address}/tnam1q8lhvxys53dlc8wzlg7dyqf9avd0vff6wvav4amt/${ + start_epoch - 1 + }` + ) + ); + } + // fetch current epochs + for (let epoch = start_epoch; epoch < chainStatus.epoch; epoch++) { + referralEpochMap.push({ + type: "current", + referrerAddress: referrer_address, + refereeAddress: referee_address, + epoch, + }); + fetchPromises.push( + fetch( + `${process.env.INDEXER_URL}/api/v1/pos/reward/${referrer_address}/tnam1q8lhvxys53dlc8wzlg7dyqf9avd0vff6wvav4amt/${epoch}` + ) + ); + } + } - if (response.ok) { - const data = (await response.json()) as RewardData[]; + const responses = await Promise.all(fetchPromises); + const responseData: ResponseData[] = await Promise.all( + responses.map(async (res, i) => { + if (res.ok) { + const data = (await res.json()) as RewardData[]; + return { ...referralEpochMap[i], data, success: true }; + } + return { ...referralEpochMap[i], data: null, success: false }; + }) + ); - if (data && data.length > 0) { - const rewardData = data[0]; + // build cumulative map + responseData.forEach((r) => { + if (!r.success || !r.data?.length) return; + const key = `${r.referrerAddress}-${r.refereeAddress}`; + const amt = new BigNumber(r.data[0].minDenomAmount).dividedBy(1e6); + if (!cumByReferral.has(key)) cumByReferral.set(key, {}); + cumByReferral.get(key)![r.epoch] = { + amount: amt, + validator: r.data[0].validator, + }; + }); - // Convert minDenomAmount (uNam) to NAM (1 NAM = 1,000,000 uNam) - const amount = new BigNumber( - rewardData.minDenomAmount - ).dividedBy(1_000_000); + // compute incremental rewards + const rewards: ReferralReward[] = []; + for (const [key, epochMap] of cumByReferral) { + const [referrerAddress, refereeAddress] = key.split("-"); + const startEpoch = referrals.find( + (r) => + r.referrer_address === referrerAddress && + r.referee_address === refereeAddress + )!.start_epoch; + const epochs = Object.keys(epochMap) + .map((n) => +n) + .filter((e) => e >= startEpoch) + .sort((a, b) => a - b); - rewards.push({ - epoch, - referrerAddress, - refereeAddress, - amount, - validator: rewardData.validator, - }); - } - } else { - console.error( - `Failed to fetch rewards for epoch ${epoch} for referrer ${referrerAddress}: ${response.statusText}` - ); - } - } catch (error) { - console.error( - `Error fetching rewards for epoch ${epoch} for referrer ${referrerAddress}:`, - error - ); + for (const epoch of epochs) { + const curr = epochMap[epoch]; + const prev = epochMap[epoch - 1]; + let delta = prev ? curr.amount.minus(prev.amount) : new BigNumber(0); + if (!prev || delta.isLessThan(0)) delta = curr.amount; + if (delta.isGreaterThan(0)) { + rewards.push({ + epoch, + referrerAddress, + refereeAddress, + amount: delta, + validator: curr.validator, + }); } } } setRewardsData(rewards); - - // Download CSV automatically if (rewards.length > 0) { - const csvContent = generateCsvContent(rewards); - downloadCsv(csvContent, "referral_rewards.csv"); + downloadCsv(generateCsvContent(rewards), "referral_rewards.csv"); } - - // Show modal setShowModal(true); } catch (error) { console.error("Error generating referral sheet:", error); From dcabaa5a330fa7411f38411c8b19732490b33c52 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Fri, 25 Apr 2025 12:08:14 +1200 Subject: [PATCH 11/18] fix: clean up referral data --- apps/namadillo/package.json | 3 +- .../namadillo/src/App/Referrals/Referrals.tsx | 15 +- .../src/App/Referrals/ReferralsTable.tsx | 247 ++++++++++++------ apps/namadillo/src/App/Referrals/excel.ts | 197 ++++++++++++++ apps/namadillo/src/App/Referrals/types.ts | 46 ++++ apps/namadillo/src/utils/supabase.ts | 2 +- 6 files changed, 412 insertions(+), 98 deletions(-) create mode 100644 apps/namadillo/src/App/Referrals/excel.ts create mode 100644 apps/namadillo/src/App/Referrals/types.ts diff --git a/apps/namadillo/package.json b/apps/namadillo/package.json index d3c01dc64d..6f15702278 100644 --- a/apps/namadillo/package.json +++ b/apps/namadillo/package.json @@ -43,7 +43,8 @@ "traverse": "^0.6.9", "vite-plugin-checker": "^0.8.0", "web-vitals": "^2.1.4", - "wonka": "^6.3.4" + "wonka": "^6.3.4", + "xlsx": "^0.18.5" }, "scripts": { "start:proxy": "node ./scripts/startProxies.js", diff --git a/apps/namadillo/src/App/Referrals/Referrals.tsx b/apps/namadillo/src/App/Referrals/Referrals.tsx index 99e9606bb1..283eddadf8 100644 --- a/apps/namadillo/src/App/Referrals/Referrals.tsx +++ b/apps/namadillo/src/App/Referrals/Referrals.tsx @@ -6,14 +6,7 @@ import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { getReferralsFromSupabase } from "../../utils/supabase"; import { ReferralsTable } from "./ReferralsTable"; - -export type Referral = { - id: number; - referrer_address: string; - referee_address: string; - start_epoch: number; - created_at?: string; -}; +import { Referral } from "./types"; export const Referrals = (): JSX.Element => { const [referrals, setReferrals] = useState([]); @@ -71,7 +64,11 @@ export const Referrals = (): JSX.Element => { )} {!loading && !error && referrals.length > 0 && ( - + )}
diff --git a/apps/namadillo/src/App/Referrals/ReferralsTable.tsx b/apps/namadillo/src/App/Referrals/ReferralsTable.tsx index 0eecf872f2..7c01ad26b9 100644 --- a/apps/namadillo/src/App/Referrals/ReferralsTable.tsx +++ b/apps/namadillo/src/App/Referrals/ReferralsTable.tsx @@ -1,82 +1,30 @@ -import { ActionButton, TableRow, Tooltip } from "@namada/components"; +import { ActionButton, TableRow } from "@namada/components"; import { shortenAddress } from "@namada/utils"; import { TableWithPaginator } from "App/Common/TableWithPaginator"; import { chainStatusAtom } from "atoms/chain"; import BigNumber from "bignumber.js"; import { useAtomValue } from "jotai"; import { useCallback, useState } from "react"; +import { IoIosCopy } from "react-icons/io"; +import { + IoCheckmarkCircleOutline, + IoCloseCircleOutline, +} from "react-icons/io5"; import { twMerge } from "tailwind-merge"; -import { Referral } from "./Referrals"; +import { downloadMultiSheetExcel } from "./excel"; import { ReferralSheetModal } from "./ReferralSheetModal"; - -export type ReferralsTableProps = { - id: string; - referrals: Referral[]; - resultsPerPage?: number; - initialPage?: number; - tableClassName?: string; -}; - -export type ValidatorInfo = { - address: string; - votingPower: string; - maxCommission: string; - commission: string; - state: string; - name: string; - email: string; - website: string; - description: string; - discordHandle: string | null; - avatar: string; - validatorId: string; - rank: string | null; -}; - -export type RewardData = { - minDenomAmount: string; - validator: ValidatorInfo; -}; - -export type ReferralReward = { - epoch: number; - referrerAddress: string; - refereeAddress: string; - amount: BigNumber; - validator: ValidatorInfo; -}; - -const generateCsvContent = (rewardsData: ReferralReward[]): string => { - const headers = - "Referrer Address,Referee Address,Epoch,Reward (NAM),Validator Name\n"; - const csvData = rewardsData - .map((r) => - [ - r.referrerAddress, - r.refereeAddress, - r.epoch, - r.amount.toFixed(6), - r.validator.name, - ].join(",") - ) - .join("\n"); - return headers + csvData; -}; - -const downloadCsv = (data: string, filename: string): void => { - const uri = encodeURI(`data:text/csv;charset=utf-8,${data}`); - const link = document.createElement("a"); - link.setAttribute("href", uri); - link.setAttribute("download", filename); - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); -}; +import { + Referral, + ReferralReward, + ReferralsTableProps, + RewardData, + ValidatorInfo, +} from "./types"; export const ReferralsTable = ({ id, referrals, - resultsPerPage = 10, + resultsPerPage = 50, initialPage = 0, tableClassName, }: ReferralsTableProps): JSX.Element => { @@ -92,6 +40,8 @@ export const ReferralsTable = ({ "Referee Address", "Start Epoch", "Created At", + "Is Active", + "Check On Chain", ]; const renderRow = useCallback( @@ -100,30 +50,73 @@ export const ReferralsTable = ({ cells: [
{shortenAddress(ref.referrer_address, 10, 6)} - {ref.referrer_address} +
,
{shortenAddress(ref.referee_address, 10, 6)} - {ref.referee_address} +
,
- {ref.start_epoch} + {ref.last_paid_epoch}
,
{ref.created_at ? new Date(ref.created_at).toLocaleString() : "N/A"}
, +
+ {ref.active ? + + : } +
, +
+ + Verify + +
, ], }), [] ); - const paginatedItems = referrals.slice( + // Sort referrals - active ones first + const sortedReferrals = [...referrals].sort((a, b) => { + // Sort by active status (active first) + if (a.active && !b.active) return -1; + if (!a.active && b.active) return 1; + // If both have same active status, sort by creation date (newest first) + if (a.created_at && b.created_at) { + return ( + new Date(b.created_at).getTime() - new Date(a.created_at).getTime() + ); + } + return 0; + }); + + const paginatedItems = sortedReferrals.slice( page * resultsPerPage, page * resultsPerPage + resultsPerPage ); @@ -161,27 +154,42 @@ export const ReferralsTable = ({ for (const { referrer_address, referee_address, - start_epoch, + last_paid_epoch, + active, } of referrals) { - const key = `${referrer_address}-${referee_address}`; + // Dont fetch inactive referrals + if (!active) continue; + + console.log( + `Fetching rewards for ${referrer_address}-${referee_address}, last paid: ${last_paid_epoch}` + ); + // fetch previous epoch cumulative - if (start_epoch > 0) { + if (last_paid_epoch > 0) { referralEpochMap.push({ type: "previous", referrerAddress: referrer_address, refereeAddress: referee_address, - epoch: start_epoch - 1, + epoch: last_paid_epoch - 1, }); fetchPromises.push( fetch( `${process.env.INDEXER_URL}/api/v1/pos/reward/${referrer_address}/tnam1q8lhvxys53dlc8wzlg7dyqf9avd0vff6wvav4amt/${ - start_epoch - 1 + last_paid_epoch - 1 }` ) ); } + + // Ensure we're fetching ALL epochs since last_paid_epoch + if (chainStatus.epoch > last_paid_epoch) { + console.log( + `Fetching ${chainStatus.epoch - last_paid_epoch} epochs for this referral` + ); + } + // fetch current epochs - for (let epoch = start_epoch; epoch < chainStatus.epoch; epoch++) { + for (let epoch = last_paid_epoch; epoch < chainStatus.epoch; epoch++) { referralEpochMap.push({ type: "current", referrerAddress: referrer_address, @@ -207,6 +215,9 @@ export const ReferralsTable = ({ }) ); + console.log("Processed response data count:", responseData.length); + console.log("Cumulative rewards map entries:", cumByReferral.size); + // build cumulative map responseData.forEach((r) => { if (!r.success || !r.data?.length) return; @@ -221,39 +232,96 @@ export const ReferralsTable = ({ // compute incremental rewards const rewards: ReferralReward[] = []; + + console.log("Processed response data count:", responseData.length); + console.log("Cumulative rewards map entries:", cumByReferral.size); + for (const [key, epochMap] of cumByReferral) { const [referrerAddress, refereeAddress] = key.split("-"); + console.log(`Processing pair: ${referrerAddress}-${refereeAddress}`); + console.log(`Epochs available:`, Object.keys(epochMap)); + const startEpoch = referrals.find( (r) => r.referrer_address === referrerAddress && r.referee_address === refereeAddress - )!.start_epoch; + )!.last_paid_epoch; + + console.log(`Starting epoch: ${startEpoch}`); + const epochs = Object.keys(epochMap) .map((n) => +n) .filter((e) => e >= startEpoch) .sort((a, b) => a - b); + console.log(`Filtered epochs to process: ${epochs.join(", ")}`); + + // Track number of rewards added for this referral pair + let rewardsAdded = 0; + for (const epoch of epochs) { const curr = epochMap[epoch]; const prev = epochMap[epoch - 1]; - let delta = prev ? curr.amount.minus(prev.amount) : new BigNumber(0); - if (!prev || delta.isLessThan(0)) delta = curr.amount; - if (delta.isGreaterThan(0)) { + + // Calculate delta between current and previous epoch + const delta = prev ? curr.amount.minus(prev.amount) : curr.amount; + + // Always add the reward, even if zero or negative (for debugging) + // In production you might want to filter these out again + if (true) { + // Changed from delta.isGreaterThan(0) + rewardsAdded++; rewards.push({ epoch, referrerAddress, refereeAddress, - amount: delta, + amount: delta.isLessThan(0) ? curr.amount : delta, // Use full amount if delta is negative validator: curr.validator, }); } } + + console.log(`Added ${rewardsAdded} rewards for this referral pair`); } + console.log(`Total rewards to be exported: ${rewards.length}`); + console.log( + `Unique epochs in rewards:`, + [...new Set(rewards.map((r) => r.epoch))].sort((a, b) => a - b) + ); + + // Group rewards by referrer address for the multi-tab file + const rewardsByReferrer: Record = {}; + rewards.forEach((reward) => { + if (!rewardsByReferrer[reward.referrerAddress]) { + rewardsByReferrer[reward.referrerAddress] = []; + } + rewardsByReferrer[reward.referrerAddress].push(reward); + }); + setRewardsData(rewards); + if (rewards.length > 0) { - downloadCsv(generateCsvContent(rewards), "referral_rewards.csv"); + // Log data to help troubleshoot generation + console.log( + `Generated ${rewards.length} reward entries for ${Object.keys(rewardsByReferrer).length} referrers` + ); + + // Group rewards by epoch to check how many rewards per epoch + const rewardsByEpoch = rewards.reduce( + (acc, reward) => { + acc[reward.epoch] = (acc[reward.epoch] || 0) + 1; + return acc; + }, + {} as Record + ); + + console.log("Rewards per epoch:", rewardsByEpoch); + + // Download the multi-sheet Excel file + downloadMultiSheetExcel(rewardsByReferrer); } + setShowModal(true); } catch (error) { console.error("Error generating referral sheet:", error); @@ -281,7 +349,12 @@ export const ReferralsTable = ({ tableClassName ), }} - headProps={{ className: "text-neutral-500" }} + headProps={{ + className: twMerge( + "[&_th:last-child]:text-center", + "text-neutral-500" + ), + }} /> {generationError && (
{generationError}
@@ -291,7 +364,7 @@ export const ReferralsTable = ({ onClick={generateReferralSheet} disabled={isGenerating} > - {isGenerating ? "Generating..." : "Generate Referral Sheet"} + {isGenerating ? "Generating..." : "Generate Referral Sheets"} {showModal && rewardsData.length > 0 && ( diff --git a/apps/namadillo/src/App/Referrals/excel.ts b/apps/namadillo/src/App/Referrals/excel.ts new file mode 100644 index 0000000000..86a000ee70 --- /dev/null +++ b/apps/namadillo/src/App/Referrals/excel.ts @@ -0,0 +1,197 @@ +import { shortenAddress } from "@namada/utils"; +import * as XLSX from "xlsx"; +import { ReferralReward } from "./types"; + +// Helper function to generate CSV data +const generateCsvContent = (rewardsData: ReferralReward[]): string => { + // CSV Headers + const headers = + "Referrer Address,Referee Address,Epoch,Reward (NAM),Validator Name\r\n"; + + if (!rewardsData || rewardsData.length === 0) { + console.warn("No rewards data to generate CSV"); + return headers; + } + + try { + // Format each reward as a CSV row and join with Windows-style line breaks (CRLF) + const csvData = rewardsData + .map((r) => { + // Check if we have all required data + if (!r || !r.validator) { + console.warn("Invalid reward data item", r); + return null; + } + + // Escape any commas in text fields with quotes + const validatorName = + r.validator.name ? + `"${r.validator.name.replace(/"/g, '""')}"` + : "Unknown"; + + return [ + `"${r.referrerAddress}"`, + `"${r.refereeAddress}"`, + r.epoch, + r.amount.toFixed(6), + validatorName, + ].join(","); + }) + .filter(Boolean) // Remove any null entries + .join("\r\n"); + + return headers + csvData; + } catch (error) { + console.error("Error generating CSV content:", error); + return headers + "Error generating CSV data"; + } +}; + +// Helper function to download CSV +const downloadCsv = (data: string, filename: string): void => { + // Use BOM (Byte Order Mark) to help Excel recognize UTF-8 + const BOM = "\uFEFF"; + const csvContent = BOM + data; + + // Create a blob with proper MIME type for CSV + const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8" }); + const url = URL.createObjectURL(blob); + + const link = document.createElement("a"); + link.setAttribute("href", url); + link.setAttribute("download", filename); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); +}; + +// Helper function to create a multi-sheet Excel file +export const downloadMultiSheetExcel = ( + rewardsByReferrer: Record +): void => { + try { + // Create a new workbook + const workbook = XLSX.utils.book_new(); + + // First, add a sheet with all rewards combined + const allRewards = Object.values(rewardsByReferrer).flat(); + if (allRewards.length > 0) { + // Convert rewards to array of plain objects for Excel + // Force numbers to be strings by converting with explicit toString() to ensure left alignment + const allRewardsData = allRewards.map((reward) => ({ + "Referrer Address": reward.referrerAddress, + "Referee Address": reward.refereeAddress, + Epoch: String(reward.epoch), // Convert to string to force left alignment + "Reward (NAM)": String(reward.amount.toFixed(6)), // Convert to string to force left alignment + "Validator Name": reward.validator.name || "Unknown", + })); + + // Create worksheet with formatting options + const worksheet = XLSX.utils.aoa_to_sheet([ + Object.keys(allRewardsData[0]), // Headers + ...allRewardsData.map((row) => Object.values(row)), // Data rows + ]); + + // Auto-size columns based on content + const columnWidths = fitToColumn(allRewardsData); + worksheet["!cols"] = columnWidths; + + XLSX.utils.book_append_sheet(workbook, worksheet, "All Rewards"); + } + + // Add a sheet for each referrer + for (const [referrerAddress, rewards] of Object.entries( + rewardsByReferrer + )) { + if (rewards.length > 0) { + // Convert rewards to array of plain objects for Excel + // Force numbers to be strings to ensure left alignment + const referrerRewardsData = rewards.map((reward) => ({ + "Referrer Address": reward.referrerAddress, + "Referee Address": reward.refereeAddress, + Epoch: String(reward.epoch), // Convert to string to force left alignment + "Reward (NAM)": String(reward.amount.toFixed(6)), // Convert to string to force left alignment + "Validator Name": reward.validator.name || "Unknown", + })); + + // Create worksheet - use shortened address for sheet name + const shortAddr = shortenAddress(referrerAddress, 8, 4); + + // Create worksheet with formatting options + const worksheet = XLSX.utils.aoa_to_sheet([ + Object.keys(referrerRewardsData[0]), // Headers + ...referrerRewardsData.map((row) => Object.values(row)), // Data rows + ]); + + // Auto-size columns based on content + const columnWidths = fitToColumn(referrerRewardsData); + worksheet["!cols"] = columnWidths; + + XLSX.utils.book_append_sheet(workbook, worksheet, shortAddr); + } + } + + // Generate Excel file with formatting options + const excelBuffer = XLSX.write(workbook, { + bookType: "xlsx", + type: "array", + cellStyles: true, + }); + + // Generate filename with current date and time + const now = new Date(); + const dateString = now.toISOString().split("T")[0]; + const filename = `Referral-Rewards-${dateString}.xlsx`; + + // Convert to Blob and download + const blob = new Blob([excelBuffer], { + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + }); + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = filename; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + } catch (error) { + console.error("Error creating Excel file:", error); + + // Fallback: Just download a single CSV with all data + const allRewards = Object.values(rewardsByReferrer).flat(); + if (allRewards.length > 0) { + downloadCsv(generateCsvContent(allRewards), "referral_rewards.csv"); + } + } +}; + +// Helper function to calculate column widths based on content +export const fitToColumn = ( + data: Record[] +): { wch: number }[] => { + if (!data || data.length === 0) return []; + + // Get all the keys from the first object + const columnNames = Object.keys(data[0]); + + // Initialize the width array with the column header lengths + const columnWidths = columnNames.map((name) => ({ + wch: Math.max(10, name.length * 1.2), // Base width on header with minimum of 10 + })); + + // Check the width needed for each cell value + data.forEach((row) => { + columnNames.forEach((col, i) => { + const value = row[col]?.toString() || ""; + const cellWidth = Math.min(50, value.length * 1.2); // Cap width at 50 chars + + if (cellWidth > columnWidths[i].wch) { + columnWidths[i].wch = cellWidth; + } + }); + }); + + return columnWidths; +}; diff --git a/apps/namadillo/src/App/Referrals/types.ts b/apps/namadillo/src/App/Referrals/types.ts new file mode 100644 index 0000000000..f90f7f79b8 --- /dev/null +++ b/apps/namadillo/src/App/Referrals/types.ts @@ -0,0 +1,46 @@ +import BigNumber from "bignumber.js"; + +export type Referral = { + id: number; + referrer_address: string; + referee_address: string; + last_paid_epoch: number; + active: boolean; + created_at?: string; +}; +export type ReferralsTableProps = { + id: string; + referrals: Referral[]; + resultsPerPage?: number; + initialPage?: number; + tableClassName?: string; +}; + +export type ValidatorInfo = { + address: string; + votingPower: string; + maxCommission: string; + commission: string; + state: string; + name: string; + email: string; + website: string; + description: string; + discordHandle: string | null; + avatar: string; + validatorId: string; + rank: string | null; +}; + +export type RewardData = { + minDenomAmount: string; + validator: ValidatorInfo; +}; + +export type ReferralReward = { + epoch: number; + referrerAddress: string; + refereeAddress: string; + amount: BigNumber; + validator: ValidatorInfo; +}; diff --git a/apps/namadillo/src/utils/supabase.ts b/apps/namadillo/src/utils/supabase.ts index c1731d028b..ccf29867ad 100644 --- a/apps/namadillo/src/utils/supabase.ts +++ b/apps/namadillo/src/utils/supabase.ts @@ -39,7 +39,7 @@ export const saveReferralToSupabase = async ( const referralData = { referrer_address: referrerAddress, referee_address: refereeAddress, - start_epoch: epoch, + last_paid_epoch: epoch, active: true, }; From 41568c9bf524de9463acedb709186fc4ce10cb9c Mon Sep 17 00:00:00 2001 From: neocybereth Date: Fri, 25 Apr 2025 13:18:03 +1200 Subject: [PATCH 12/18] fix: cleanup --- .../src/App/Referrals/ReferralSheetModal.tsx | 409 ++++++------------ .../src/App/Referrals/ReferralsTable.tsx | 4 +- 2 files changed, 131 insertions(+), 282 deletions(-) diff --git a/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx b/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx index 8aa0f696c4..a3cfb85b1c 100644 --- a/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx +++ b/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx @@ -1,19 +1,12 @@ -import { ActionButton, Modal, TableRow, Tooltip } from "@namada/components"; -import { TransparentTransferMsgValue } from "@namada/types"; +import { Modal, TableRow } from "@namada/components"; import { shortenAddress } from "@namada/utils"; import { ModalTransition } from "App/Common/ModalTransition"; import { TableWithPaginator } from "App/Common/TableWithPaginator"; -import { defaultAccountAtom } from "atoms/accounts"; -import { chainAtom, nativeTokenAddressAtom } from "atoms/chain"; -import { createTransparentTransferAtom } from "atoms/transfer/atoms"; import BigNumber from "bignumber.js"; -import { useTransaction } from "hooks/useTransaction"; -import { useTransactionFee } from "hooks/useTransactionFee"; -import { useAtomValue } from "jotai"; import { useCallback, useState } from "react"; import { IoClose } from "react-icons/io5"; import { twMerge } from "tailwind-merge"; -import { ReferralReward } from "./ReferralsTable"; +import { ReferralReward } from "./types"; export const ReferralSheetModal = ({ rewardsData, @@ -22,17 +15,8 @@ export const ReferralSheetModal = ({ rewardsData: ReferralReward[]; onClose: () => void; }): JSX.Element => { - const [page, setPage] = useState(0); - const [isProcessingPayout, setIsProcessingPayout] = useState(false); - const [payoutError, setPayoutError] = useState(null); - const [payoutSuccess, setPayoutSuccess] = useState(false); - const resultsPerPage = 100; - const defaultAccount = useAtomValue(defaultAccountAtom); - const chain = useAtomValue(chainAtom); - const feeProps = useTransactionFee(["TransparentTransfer"], false); - const namTokenAddressQuery = useAtomValue(nativeTokenAddressAtom); + const [selectedReferrer, setSelectedReferrer] = useState(null); - // Define table headers const headers = [ "Referrer Address", "Referee Address", @@ -40,293 +24,158 @@ export const ReferralSheetModal = ({ "Reward (NAM)", ]; - const renderRow = useCallback((reward: ReferralReward): TableRow => { - return { - key: `reward-${reward.referrerAddress}-${reward.refereeAddress}-${reward.epoch}`, + const renderRow = useCallback( + (r: ReferralReward): TableRow => ({ + key: `rw-${r.referrerAddress}-${r.refereeAddress}-${r.epoch}`, cells: [ - // Referrer address (truncated for display) -
- {shortenAddress(reward.referrerAddress, 10, 6)} - - {reward.referrerAddress} - -
, - // Referee address (truncated for display) -
- {shortenAddress(reward.refereeAddress, 10, 6)} - - {reward.refereeAddress} - -
, - // Epoch -
- {reward.epoch} -
, - // Reward amount in NAM -
- {reward.amount.toFormat(6)} -
, + , + , +
{r.epoch}
, +
{r.amount.toFormat(6)}
, ], - }; - }, []); - - const paginatedItems = rewardsData.slice( - page * resultsPerPage, - page * resultsPerPage + resultsPerPage - ); - - const pageCount = Math.ceil(rewardsData.length / resultsPerPage); - - // Calculate total rewards per referrer - const totalRewards = rewardsData.reduce( - (acc, reward) => { - if (!acc[reward.referrerAddress]) { - acc[reward.referrerAddress] = BigNumber(0); - } - acc[reward.referrerAddress] = acc[reward.referrerAddress].plus( - reward.amount - ); - return acc; - }, - {} as Record + }), + [] ); - // Group rewards by referrer address for easier navigation - const rewardsByReferrer = rewardsData.reduce( - (acc, reward) => { - if (!acc[reward.referrerAddress]) { - acc[reward.referrerAddress] = []; - } - acc[reward.referrerAddress].push(reward); - return acc; - }, - {} as Record + const byReferrer = rewardsData.reduce>( + (a, r) => ((a[r.referrerAddress] ??= []).push(r), a), + {} ); - // Setup transaction - const { execute: executeBatchTransfer, isPending: isExecutingBatchTransfer } = - useTransaction({ - eventType: "TransparentTransfer", - createTxAtom: createTransparentTransferAtom, - params: [], - parsePendingTxNotification: () => ({ - title: "Referral payout in progress", - description: "Your referral payout transaction is being processed", - }), - parseErrorTxNotification: () => ({ - title: "Referral payout failed", - description: "An error occurred during the batch payment", - }), - onBroadcasted: () => { - setPayoutSuccess(true); - setIsProcessingPayout(false); - }, - onError: (error) => { - setPayoutError( - typeof error === "string" ? error - : error instanceof Error ? error.message - : "Unknown error" - ); - setIsProcessingPayout(false); - }, - }); - - // Handle payout button click - const handlePayoutReferrals = async (): Promise => { - try { - setIsProcessingPayout(true); - setPayoutError(null); - setPayoutSuccess(false); - - if (!defaultAccount.data?.address) { - throw new Error("No source account available"); - } - - const sourceAddress = defaultAccount.data.address; - // NAM token address - this should be configured appropriately for your environment - const tokenAddress = namTokenAddressQuery.data!; + if (!selectedReferrer && Object.keys(byReferrer).length) + setSelectedReferrer(Object.keys(byReferrer)[0]); - // Create batch transfer data structure - one per referrer - const msgValueData = Object.entries(totalRewards).map( - ([referrerAddress, amount]) => ({ - source: sourceAddress, - target: referrerAddress, - token: tokenAddress, - amount, - }) - ); + const rows = + selectedReferrer ? (byReferrer[selectedReferrer] ?? []) : rewardsData; - const batchProps = [ - { - data: msgValueData, - }, - ]; - - // Execute the batch transfer - await executeBatchTransfer({ - params: batchProps, - gasConfig: feeProps.gasConfig, - account: defaultAccount.data, - }); - } catch (error) { - console.error("Payout error:", error); - setPayoutError( - error instanceof Error ? error.message : "Unknown error occurred" - ); - setIsProcessingPayout(false); - } - }; + const totals = rewardsData.reduce>((acc, r) => { + const amt = new BigNumber(r.amount); // ensure BigNumber + acc[r.referrerAddress] = (acc[r.referrerAddress] ?? new BigNumber(0)).plus( + amt + ); + return acc; + }, {}); return ( - {/* Modal header with fixed position */} + {/* header */}
- - - -
- Referral Rewards -
+ className="absolute top-1.5 right-6 text-3xl cursor-pointer hover:text-yellow" + /> +
Referral Rewards
- {/* Modal content with scrollable area */} -
- {rewardsData.length === 0 ? -
No rewards found
- :
- {/* Payout Referrals button - separate from tables */} -
-

- Process Payouts -

-

- This will create a batch payment transaction to pay out all - referrers their total rewards. The transaction will be sent to - your Namada extension for approval. -

- {payoutError && ( -
- {payoutError} -
- )} - {payoutSuccess && ( -
- Payout transaction successfully sent to the blockchain! -
- )} - - - {isProcessingPayout || isExecutingBatchTransfer ? - "Processing..." - : "Payout Referrals"} - -
-
-

- Total Rewards by Referrer -

-
- {Object.entries(totalRewards).map(([address, amount]) => ( -
-
- - Referrer: - -
- {shortenAddress(address, 10, 6)} - - {address} - -
-
-
- - Total NAM: - - - {amount.toFormat(6)} - -
-
- ))} + {/* body – single scroll */} +
+ {rows.length === 0 ? +
No rewards found
+ : <> + {/* totals */} +
+

Referrer Total

+
+ {selectedReferrer && totals[selectedReferrer] && ( + + )}
-
+ + {/* epoch table */} +

Rewards by Epoch

- {/* Referrer selector tabs with horizontal scroll */} -
-
- {Object.keys(rewardsByReferrer).map( - (referrerAddress, index) => ( - - ) - )} -
-
- {/* Table with horizontal scroll wrapper */} -
- + {/* referrer selector */} +
+ {Object.keys(byReferrer).map((addr) => ( + + ))}
+ + {}} /* no-op */ + tableProps={{ + className: twMerge( + "w-full min-w-[600px] [&_td]:px-3 [&_th]:px-3 [&_td]:h-[64px]", + "[&_td:first-child]:pl-4 [&_td:last-child]:pr-4", + "[&_td:first-child]:rounded-s-md [&_td:last-child]:rounded-e-md" + ), + }} + headProps={{ className: "text-neutral-500" }} + />
-
+ }
); }; + +const Addr = ({ value }: { value: string }): JSX.Element => ( +
+ {shortenAddress(value, 10, 6)} +
+); + +const TotalCard = ({ + addr, + amount, +}: { + addr: string; + amount: BigNumber; +}): JSX.Element => { + return ( +
+
+ Referrer: +
+ {shortenAddress(addr, 10, 6)} + +
+
+
+ Total NAM Referred: + {amount.toFormat(6)} +
+
+ Total NAM Owed: + + {amount.multipliedBy(0.05).toFormat(6)} + +
+
+ ); +}; diff --git a/apps/namadillo/src/App/Referrals/ReferralsTable.tsx b/apps/namadillo/src/App/Referrals/ReferralsTable.tsx index 7c01ad26b9..f32b24781b 100644 --- a/apps/namadillo/src/App/Referrals/ReferralsTable.tsx +++ b/apps/namadillo/src/App/Referrals/ReferralsTable.tsx @@ -174,7 +174,7 @@ export const ReferralsTable = ({ }); fetchPromises.push( fetch( - `${process.env.INDEXER_URL}/api/v1/pos/reward/${referrer_address}/tnam1q8lhvxys53dlc8wzlg7dyqf9avd0vff6wvav4amt/${ + `${process.env.INDEXER_URL}/api/v1/pos/reward/${referee_address}/tnam1q8lhvxys53dlc8wzlg7dyqf9avd0vff6wvav4amt/${ last_paid_epoch - 1 }` ) @@ -198,7 +198,7 @@ export const ReferralsTable = ({ }); fetchPromises.push( fetch( - `${process.env.INDEXER_URL}/api/v1/pos/reward/${referrer_address}/tnam1q8lhvxys53dlc8wzlg7dyqf9avd0vff6wvav4amt/${epoch}` + `${process.env.INDEXER_URL}/api/v1/pos/reward/${referee_address}/tnam1q8lhvxys53dlc8wzlg7dyqf9avd0vff6wvav4amt/${epoch}` ) ); } From 71ec6528a89f8076a89e767f7fb2aaeb14506154 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Fri, 25 Apr 2025 13:25:41 +1200 Subject: [PATCH 13/18] fix: remove txt --- apps/namadillo/namada_history_table.txt | 356 ------------------------ 1 file changed, 356 deletions(-) delete mode 100644 apps/namadillo/namada_history_table.txt diff --git a/apps/namadillo/namada_history_table.txt b/apps/namadillo/namada_history_table.txt deleted file mode 100644 index ad18738430..0000000000 --- a/apps/namadillo/namada_history_table.txt +++ /dev/null @@ -1,356 +0,0 @@ -BLOCK HEIGHT | TX KIND | AMOUNT | TX ID | EXIT CODE | VALIDATOR -==================================================================================================== -1704575 | bond | 119.550506 | 46188605...2affcde9 | applied | tnam1q8l...wvav4amt -1704575 | claimRewards | - | 715c16f2...43fd9ae6 | applied | tnam1q8l...wvav4amt -1697165 | claimRewards | - | 0064c2bf...c1f5056e | applied | tnam1q8l...wvav4amt -1697165 | bond | 159.293111 | 574426e9...67413452 | applied | tnam1q8l...wvav4amt -1682840 | claimRewards | - | 41a0c6a0...3ff07ae3 | applied | tnam1q8l...wvav4amt -1682840 | bond | 318.760436 | 24f40781...6ea51979 | applied | tnam1q8l...wvav4amt -1656858 | bond | 198.889127 | 34acb5aa...35645cfb | applied | tnam1q8l...wvav4amt -1656858 | claimRewards | - | ec1dc05b...83f4cb23 | applied | tnam1q8l...wvav4amt -1642971 | bond | 119.536340 | a9ca1774...91b4be68 | applied | tnam1q8l...wvav4amt -1642971 | claimRewards | - | 2213b3b5...4949962a | applied | tnam1q8l...wvav4amt -1632620 | bond | 1.000000 | 0f43a6e0...2bca0b70 | applied | tnam1q8l...wvav4amt -1632464 | bond | 1.000000 | 8dd82842...5e90806e | applied | tnam1q8l...wvav4amt -1632400 | bond | 1.000000 | dbce4dca...fb4f1c1a | applied | tnam1q8l...wvav4amt -1631953 | claimRewards | - | f8f9b55b...d37e0aad | applied | tnam1q8l...wvav4amt -1631953 | bond | 39.846789 | 5af57779...46144e49 | applied | tnam1q8l...wvav4amt -1629476 | claimRewards | - | e09756ba...8fa23533 | applied | tnam1q8l...wvav4amt -1629476 | bond | 79.658502 | fd4dd7eb...0a9ffe2c | applied | tnam1q8l...wvav4amt -1624164 | bond | 199.152027 | bfa902cc...3da85e6e | applied | tnam1q8l...wvav4amt -1624164 | claimRewards | - | 7d6867a2...153c7b35 | applied | tnam1q8l...wvav4amt -1607670 | claimRewards | - | 37a1ca5a...3f6631bc | applied | tnam1q8l...wvav4amt -1607670 | bond | 39.863134 | ada5fd46...f4f7e5b3 | applied | tnam1q8l...wvav4amt -1605327 | claimRewards | - | 6b4acef7...76b51800 | applied | tnam1q8l...wvav4amt -1605327 | bond | 159.299389 | aa90e204...7318c16d | applied | tnam1q8l...wvav4amt -1593038 | claimRewards | - | 9779bb7d...ec13a3bf | applied | tnam1q8l...wvav4amt -1593038 | bond | 159.115779 | f8d27fb2...bee8bac8 | applied | tnam1q8l...wvav4amt -1580320 | bond | 318.412817 | 656bb1b2...12f3413c | applied | tnam1q8l...wvav4amt -1580320 | claimRewards | - | ccae3c20...3f1aa1b7 | applied | tnam1q8l...wvav4amt -1555665 | claimRewards | - | 50154617...77da8dda | applied | tnam1q8l...wvav4amt -1555665 | bond | 119.378111 | b4b6524b...3b46c1db | applied | tnam1q8l...wvav4amt -1546063 | claimRewards | - | 93b7902a...4558a47a | applied | tnam1q8l...wvav4amt -1546063 | bond | 119.119812 | 0cfd7dd1...ffdc03d9 | applied | tnam1q8l...wvav4amt -1536806 | claimRewards | - | 442b4991...9fa1bc0e | applied | tnam1q8l...wvav4amt -1536806 | bond | 39.691468 | aff10238...18b2fe28 | applied | tnam1q8l...wvav4amt -1534704 | claimRewards | - | 9ca2850e...785ad7fc | applied | tnam1q8l...wvav4amt -1534704 | bond | 158.756843 | 212224e5...6f01ea0f | applied | tnam1q8l...wvav4amt -1520346 | claimRewards | - | 6ad89bd1...f2374443 | applied | tnam1q8l...wvav4amt -1520346 | bond | 198.392616 | 365a53d6...d7c091d5 | applied | tnam1q8l...wvav4amt -1506576 | bond | 158.709221 | 98c22454...2a5c267b | applied | tnam1q8l...wvav4amt -1506576 | claimRewards | - | b4c97f7d...3321c279 | applied | tnam1q8l...wvav4amt -1494220 | claimRewards | - | 5f04b3d8...3a7f571d | applied | tnam1q8l...wvav4amt -1494220 | bond | 158.670374 | 7cc8f9e1...491f90e5 | applied | tnam1q8l...wvav4amt -1480098 | bond | 118.955664 | 3f255aff...5592053c | applied | tnam1q8l...wvav4amt -1480098 | claimRewards | - | 72d64593...41aa50aa | applied | tnam1q8l...wvav4amt -1472840 | claimRewards | - | f879541c...700f6abf | applied | tnam1q8l...wvav4amt -1472840 | bond | 39.646647 | 63132476...02a8c23e | applied | tnam1q8l...wvav4amt -1467799 | bond | 79.284010 | 75366871...b2eb5b1d | applied | tnam1q8l...wvav4amt -1467799 | claimRewards | - | 1c81ff47...739778f8 | applied | tnam1q8l...wvav4amt -1460641 | claimRewards | - | 41934e84...e766acf3 | applied | tnam1q8l...wvav4amt -1460641 | bond | 79.255550 | eaef6604...50cddb76 | applied | tnam1q8l...wvav4amt -1457176 | bond | 118.859309 | ea372fff...ce36c208 | applied | tnam1q8l...wvav4amt -1457176 | claimRewards | - | 15261d98...a858108e | applied | tnam1q8l...wvav4amt -1445273 | bond | 118.856331 | 58a4f8b4...ff567bcc | applied | tnam1q8l...wvav4amt -1445273 | claimRewards | - | 0602a4dd...3847d59f | applied | tnam1q8l...wvav4amt -1438256 | bond | 158.410274 | 85c5bf2b...feec0032 | applied | tnam1q8l...wvav4amt -1438256 | claimRewards | - | 46869966...d3e739d4 | applied | tnam1q8l...wvav4amt -1424819 | claimRewards | - | 5b32370a...83053791 | applied | tnam1q8l...wvav4amt -1424819 | bond | 79.205057 | 238ce0d7...143a97b5 | applied | tnam1q8l...wvav4amt -1417770 | claimRewards | - | fbc307d5...ee5a0c69 | applied | tnam1q8l...wvav4amt -1417770 | bond | 79.157255 | 846b271b...f4a735e0 | applied | tnam1q8l...wvav4amt -1411310 | bond | 79.670239 | cbfe78c5...62f6713f | applied | tnam1q8l...wvav4amt -1411310 | claimRewards | - | 40f9b0ef...d1b5262b | applied | tnam1q8l...wvav4amt -1405578 | claimRewards | - | bb831833...1ed9a99b | applied | tnam1q8l...wvav4amt -1405578 | bond | 80.171619 | d5041d86...27324d80 | applied | tnam1q8l...wvav4amt -1399791 | bond | 39.618100 | 1a1676ee...232de6a6 | applied | tnam1q8l...wvav4amt -1399791 | claimRewards | - | 275d069d...4d2a0ec1 | applied | tnam1q8l...wvav4amt -1397272 | claimRewards | - | cd34c602...561c4841 | applied | tnam1q8l...wvav4amt -1397272 | bond | 39.628442 | 67d53de3...ef128544 | applied | tnam1q8l...wvav4amt -1393424 | claimRewards | - | 88b2aaa5...d917c9b0 | applied | tnam1q8l...wvav4amt -1393424 | bond | 278.645000 | 66600c61...7c05be1d | applied | tnam1q8l...wvav4amt -1371689 | bond | 40.021959 | 8004882b...3deba4e5 | applied | tnam1q8l...wvav4amt -1371689 | claimRewards | - | f209a886...caf90dcd | applied | tnam1q8l...wvav4amt -1369901 | bond | 478.337161 | 593ae656...dff45988 | applied | tnam1q8l...wvav4amt -1369901 | claimRewards | - | c3c21878...39c36acf | applied | tnam1q8l...wvav4amt -1331605 | bond | 316.229725 | 71cb33f8...0d703f3f | applied | tnam1q8l...wvav4amt -1331605 | claimRewards | - | a200fa1a...ab796d3c | applied | tnam1q8l...wvav4amt -1306829 | bond | 118.235864 | 7bf37670...5cace705 | applied | tnam1q8l...wvav4amt -1306829 | claimRewards | - | 970ab02d...1c7c9121 | applied | tnam1q8l...wvav4amt -1298749 | claimRewards | - | 259c344f...bfc91c2d | applied | tnam1q8l...wvav4amt -1298749 | bond | 39.413947 | 23c82e2d...d52e4944 | applied | tnam1q8l...wvav4amt -1295964 | bond | 78.807450 | 9702d2c4...fec0364e | applied | tnam1q8l...wvav4amt -1295964 | claimRewards | - | 58d2e3e2...f3d49993 | applied | tnam1q8l...wvav4amt -1287188 | bond | 78.686646 | a458cb78...475cd29b | applied | tnam1q8l...wvav4amt -1287188 | claimRewards | - | d8fe3587...f3100179 | applied | tnam1q8l...wvav4amt -1282366 | claimRewards | - | a7f0ed2d...db7c149d | applied | tnam1q8l...wvav4amt -1282366 | bond | 711.157814 | 972f0c9b...c280f633 | applied | tnam1q8l...wvav4amt -1225691 | bond | 198.514846 | 5b8b730d...3c49dd20 | applied | tnam1q8l...wvav4amt -1225691 | claimRewards | - | 5981517e...633901c4 | applied | tnam1q8l...wvav4amt -1209651 | transparentTransfer | - | 72847664...dce405ef | applied | - -1209422 | bond | 158.804181 | be3fb991...764d6677 | applied | tnam1q8l...wvav4amt -1209422 | claimRewards | - | e011a8d1...7e08c371 | applied | tnam1q8l...wvav4amt -1197777 | bond | 39.657933 | 429555c7...39f67b51 | applied | tnam1q8l...wvav4amt -1197777 | claimRewards | - | 40f38c6b...7227aab3 | applied | tnam1q8l...wvav4amt -1196142 | bond | 158.710059 | cca504a2...55f47af0 | applied | tnam1q8l...wvav4amt -1196142 | claimRewards | - | b0f9105e...90ae6218 | applied | tnam1q8l...wvav4amt -1183359 | claimRewards | - | 956483e4...fade8171 | applied | tnam1q8l...wvav4amt -1183359 | bond | 158.644817 | b814d405...fca322f7 | applied | tnam1q8l...wvav4amt -1170955 | claimRewards | - | 9771536e...dc3f0736 | applied | tnam1q8l...wvav4amt -1170955 | bond | 118.885931 | c49ddb02...99e8079d | applied | tnam1q8l...wvav4amt -1159417 | claimRewards | - | b062c4c7...f0d28b01 | applied | tnam1q8l...wvav4amt -1159417 | bond | 118.858618 | d4549920...80249dca | applied | tnam1q8l...wvav4amt -1151712 | claimRewards | - | 87e80be5...2c57c664 | applied | tnam1q8l...wvav4amt -1151712 | bond | 39.612859 | 61b370eb...b9397ae4 | applied | tnam1q8l...wvav4amt -1149256 | claimRewards | - | 82919ff1...3f712c33 | applied | tnam1q8l...wvav4amt -1149256 | bond | 39.615750 | de538428...7d2ca32d | applied | tnam1q8l...wvav4amt -1146459 | claimRewards | - | cb29dd62...70508d73 | applied | tnam1q8l...wvav4amt -1146459 | bond | 79.215214 | 3524505a...9f092c02 | applied | tnam1q8l...wvav4amt -1138534 | claimRewards | - | 9b1812b6...0a2c829a | applied | tnam1q8l...wvav4amt -1138534 | bond | 79.147320 | 31e15c39...adbe9da2 | applied | tnam1q8l...wvav4amt -1133132 | bond | 158.242482 | edd9cef1...ae2ac0b1 | applied | tnam1q8l...wvav4amt -1133132 | claimRewards | - | aa042c68...d7092ccf | applied | tnam1q8l...wvav4amt -1121382 | claimRewards | - | 8893ae47...7fe59550 | applied | tnam1q8l...wvav4amt -1121382 | bond | 78.981410 | 89555038...60a0b638 | applied | tnam1q8l...wvav4amt -1114100 | bond | 39.493765 | 851e1fea...9f28135c | applied | tnam1q8l...wvav4amt -1110790 | bond | 39.466625 | 5e7a468c...a76e92c7 | applied | tnam1q8l...wvav4amt -1107388 | bond | 78.975865 | 61edfce6...35652525 | applied | tnam1q8l...wvav4amt -1100912 | bond | 78.927224 | eadfedda...895f1ced | applied | tnam1q8l...wvav4amt -1095313 | bond | 78.909631 | dfa3526c...827eed53 | applied | tnam1q8l...wvav4amt -1088000 | bond | 78.916235 | 0257d3ad...78831936 | applied | tnam1q8l...wvav4amt -1083536 | bond | 118.322216 | 952c3009...43e7a3ee | applied | tnam1q8l...wvav4amt -1072666 | bond | 39.393564 | 0361916d...e37aed4b | applied | tnam1q8l...wvav4amt -1072666 | claimRewards | - | dbf75e49...9d6d7fec | applied | tnam1q8l...wvav4amt -1069982 | bond | 118.254126 | 1a55289f...a35f650a | applied | tnam1q8l...wvav4amt -1069982 | claimRewards | - | 7b9b22c9...b87a6032 | applied | tnam1q8l...wvav4amt -1060878 | bond | 39.467588 | d8e43d53...000db552 | applied | tnam1q8l...wvav4amt -1060878 | claimRewards | - | 29b359f0...d35456ee | applied | tnam1q8l...wvav4amt -1057817 | voteProposal | - | 1d86a342...f9cc8313 | applied | - -1057781 | voteProposal | - | c73850dc...978ae714 | applied | - -1057662 | bond | 157.811454 | a334ae00...10c013de | applied | tnam1q8l...wvav4amt -1057662 | claimRewards | - | e5691e10...7c9f0e0e | applied | tnam1q8l...wvav4amt -1046091 | bond | 157.534881 | b5b6ccd2...284de5f7 | applied | tnam1q8l...wvav4amt -1046091 | claimRewards | - | abc22e57...8a70f681 | applied | tnam1q8l...wvav4amt -1032561 | claimRewards | - | 113a3ac0...a327c76a | applied | tnam1q8l...wvav4amt -1032561 | bond | 78.675543 | 98171415...eb8a033a | applied | tnam1q8l...wvav4amt -1026608 | claimRewards | - | c14d788e...923c54a0 | applied | tnam1q8l...wvav4amt -1026608 | bond | 78.417091 | d0093d6c...cc752202 | applied | tnam1q8l...wvav4amt -1021166 | bond | 157.020879 | fe12fb4a...e0a73e41 | applied | tnam1q8l...wvav4amt -1021166 | claimRewards | - | 2f404429...40358e15 | applied | tnam1q8l...wvav4amt -1008674 | voteProposal | - | 0dcecf5e...8b479141 | applied | - -1008672 | voteProposal | - | 22c844ad...8bcdbcbc | applied | - -1008669 | voteProposal | - | 5c94d4b2...f7fad5f1 | applied | - -1008661 | bond | 393.816623 | cf54e38e...f9c2dfe2 | applied | tnam1q8l...wvav4amt -1008661 | claimRewards | - | cf7b8be6...e6f47b89 | applied | tnam1q8l...wvav4amt -975950 | claimRewards | - | c577a66e...e2518e97 | applied | tnam1q8l...wvav4amt -975950 | bond | 235.034733 | 9083ae8f...20afb811 | applied | tnam1q8l...wvav4amt -959160 | bond | 157.049808 | 1a2037fb...7f3d7fdc | applied | tnam1q8l...wvav4amt -959160 | claimRewards | - | f525a68c...3888d62d | applied | tnam1q8l...wvav4amt -946914 | bond | 117.495870 | cfd7f850...b6ad5e26 | applied | tnam1q8l...wvav4amt -946914 | claimRewards | - | fe64498b...b21fb96c | applied | tnam1q8l...wvav4amt -937883 | unbond | - | a483aad5...2584d322 | applied | tnam1q8l...wvav4amt -937847 | unbond | - | 609168cf...ed6cacab | rejected | tnam1q8l...wvav4amt -937807 | unbond | - | 18dc78c5...8241d1b2 | applied | tnam1q8l...wvav4amt -937787 | unbond | - | c6e5a76f...7cd9b906 | rejected | tnam1q8l...wvav4amt -937777 | unbond | - | 11a2305b...4a625a5d | rejected | tnam1q8l...wvav4amt -935599 | unbond | - | ffa03d34...2a1e3208 | rejected | tnam1q8l...wvav4amt -935194 | bond | 313.495697 | ceb7cb4d...850e2ea3 | applied | tnam1q8l...wvav4amt -935194 | claimRewards | - | f8a0a8f3...b98e45e3 | applied | tnam1q8l...wvav4amt -924965 | voteProposal | - | be5bb36d...81f9da4f | applied | - -922687 | unbond | - | a5895c8a...a0d5298f | rejected | tnam1q8l...wvav4amt -922661 | unbond | - | b6e1b29c...e09b29a0 | rejected | tnam1q8l...wvav4amt -922632 | unbond | - | 4346bada...a14c08a8 | rejected | tnam1q8l...wvav4amt -922628 | claimRewards | - | 53b04882...40cba88c | applied | tnam1q8l...wvav4amt -922628 | bond | 313.356581 | d7f75149...43b8b854 | applied | tnam1q8l...wvav4amt -912294 | unbond | - | 1ae80904...87c2621c | rejected | tnam1q8l...wvav4amt -911856 | unbond | - | 714b0f27...ed206c1e | rejected | tnam1q8l...wvav4amt -911849 | unbond | - | 5d2e89cf...087511c7 | rejected | tnam1q8l...wvav4amt -911837 | unbond | - | 5935f3a9...a0ba9b96 | rejected | tnam1q8l...wvav4amt -911832 | bond | 233.878096 | 7316719d...1c0b25fe | applied | tnam1q8l...wvav4amt -911832 | claimRewards | - | 290b0f1e...f04f946c | applied | tnam1q8l...wvav4amt -901692 | claimRewards | - | 0664e0fb...c4bcef8b | applied | tnam1q8l...wvav4amt -901692 | bond | 7464.142114 | fb867b07...ddf6fe2b | rejected | tnam1q8l...wvav4amt -901640 | bond | 7464.142114 | 42efad92...242bef61 | applied | tnam1q8l...wvav4amt -901640 | claimRewards | - | 05f0166e...f227ddc7 | rejected | tnam1q8l...wvav4amt -603472 | bond | 161.065611 | 4ffd0e37...fda5ec6f | applied | tnam1q8l...wvav4amt -603472 | claimRewards | - | 7a5b320c...6b43edfd | applied | tnam1q8l...wvav4amt -598609 | bond | 160.940537 | cf2abd68...ec577382 | applied | tnam1q8l...wvav4amt -598609 | claimRewards | - | 602ff9cd...61b28780 | applied | tnam1q8l...wvav4amt -592638 | claimRewards | - | f08bf3ed...fccbdd53 | applied | tnam1q8l...wvav4amt -592638 | bond | 80.560070 | b7ea704c...73c20e69 | applied | tnam1q8l...wvav4amt -589700 | claimRewards | - | d2449696...ac3a0f15 | applied | tnam1q8l...wvav4amt -589700 | bond | 241.796912 | ffd3d8ef...8ac75e12 | applied | tnam1q8l...wvav4amt -579926 | claimRewards | - | 74643f2f...e7e8b72f | applied | tnam1q8l...wvav4amt -579926 | bond | 161.092738 | bc127011...dc7cd41a | applied | tnam1q8l...wvav4amt -574692 | bond | 241.919504 | 3f7f22db...992986c8 | applied | tnam1q8l...wvav4amt -574692 | claimRewards | - | 2a21c809...6aba5ef1 | applied | tnam1q8l...wvav4amt -562898 | claimRewards | - | 13af0017...478faf41 | applied | tnam1q8l...wvav4amt -562898 | bond | 402.989378 | 04d306ed...94183c85 | applied | tnam1q8l...wvav4amt -547904 | bond | 161.108435 | 113af04d...3bac5bec | applied | tnam1q8l...wvav4amt -547904 | claimRewards | - | 76e45d24...75495e60 | applied | tnam1q8l...wvav4amt -541536 | bond | 402.775067 | 500de353...7cda8da4 | applied | tnam1q8l...wvav4amt -541536 | claimRewards | - | 0b6de0ee...e36191fa | applied | tnam1q8l...wvav4amt -525508 | bond | 80.569960 | f632d55e...e62d59fd | applied | tnam1q8l...wvav4amt -525508 | claimRewards | - | e54da943...6decd2a2 | applied | tnam1q8l...wvav4amt -523328 | claimRewards | - | 8727aa3a...a05d2f45 | applied | tnam1q8l...wvav4amt -523328 | bond | 161.015710 | cbee5158...d906023d | applied | tnam1q8l...wvav4amt -515814 | claimRewards | - | 365e93a3...dddff335 | applied | tnam1q8l...wvav4amt -515814 | bond | 80.489716 | c16d4456...321f21d7 | applied | tnam1q8l...wvav4amt -513568 | claimRewards | - | 036a80e9...3dbc62ca | applied | tnam1q8l...wvav4amt -513568 | bond | 80.755643 | 187c56f6...27aaa583 | applied | tnam1q8l...wvav4amt -511374 | bond | 80.746208 | d98dd7bb...0bdba248 | applied | tnam1q8l...wvav4amt -511374 | claimRewards | - | a6f79099...155cf074 | applied | tnam1q8l...wvav4amt -506491 | bond | 80.692849 | 8c6baff1...332fa9e2 | applied | tnam1q8l...wvav4amt -506491 | claimRewards | - | a78f9f82...86662ecd | applied | tnam1q8l...wvav4amt -503901 | bond | 80.726508 | 8623b445...a473da45 | applied | tnam1q8l...wvav4amt -503901 | claimRewards | - | 97c7c919...b0945630 | applied | tnam1q8l...wvav4amt -500297 | claimRewards | - | e61cb553...d0e14e30 | applied | tnam1q8l...wvav4amt -500297 | bond | 80.732276 | a4ce0b56...523adb84 | applied | tnam1q8l...wvav4amt -498925 | bond | 161.376700 | 120d59cb...7b9982c0 | applied | tnam1q8l...wvav4amt -498925 | claimRewards | - | 7d1fd950...0494d394 | applied | tnam1q8l...wvav4amt -491429 | claimRewards | - | 561a2c52...d02c7e80 | applied | tnam1q8l...wvav4amt -491429 | bond | 80.699862 | a35de8c0...893bd3fb | applied | tnam1q8l...wvav4amt -488021 | bond | 80.646106 | 5a84c02f...b8070cfd | applied | tnam1q8l...wvav4amt -488021 | claimRewards | - | 6ae2c97e...4731efa3 | applied | tnam1q8l...wvav4amt -485929 | claimRewards | - | 346324de...796efc3f | applied | tnam1q8l...wvav4amt -485929 | bond | 161.302445 | 1e5aa052...b640926d | applied | tnam1q8l...wvav4amt -479049 | bond | 80.684743 | c47a5688...0473ca61 | applied | tnam1q8l...wvav4amt -479049 | claimRewards | - | 707627ad...44a41cd7 | applied | tnam1q8l...wvav4amt -476532 | bond | 80.688064 | 029cb1d8...ac0520bf | applied | tnam1q8l...wvav4amt -476532 | claimRewards | - | 950577b9...04f7a338 | applied | tnam1q8l...wvav4amt -473617 | bond | 80.626737 | 2816e67c...8ca06f6b | applied | tnam1q8l...wvav4amt -473617 | claimRewards | - | 47aea4ed...a6ccd1ff | applied | tnam1q8l...wvav4amt -469922 | bond | 80.678913 | efa1b6ff...e1bd4f97 | applied | tnam1q8l...wvav4amt -469922 | claimRewards | - | b2ee2571...9f15877d | applied | tnam1q8l...wvav4amt -466543 | claimRewards | - | a4459a92...14593342 | applied | tnam1q8l...wvav4amt -466543 | bond | 80.638475 | 05655365...69ad9aa4 | applied | tnam1q8l...wvav4amt -462813 | claimRewards | - | 8a4d3325...76170582 | applied | tnam1q8l...wvav4amt -462813 | bond | 241.996302 | c339ce42...efc7d77d | applied | tnam1q8l...wvav4amt -453164 | claimRewards | - | efc6ff02...6ed1527a | applied | tnam1q8l...wvav4amt -453164 | bond | 80.649137 | f892231f...d91c4ff9 | applied | tnam1q8l...wvav4amt -450606 | bond | 80.599441 | 0f481f67...c768d7df | applied | tnam1q8l...wvav4amt -450606 | claimRewards | - | 0cb8b9cb...0bde7a9e | applied | tnam1q8l...wvav4amt -448876 | claimRewards | - | cd6da9f7...68cf4e04 | applied | tnam1q8l...wvav4amt -448876 | bond | 161.228486 | 41a77d4c...82b2d5cd | applied | tnam1q8l...wvav4amt -440995 | claimRewards | - | 9dda19a4...8581cc73 | applied | tnam1q8l...wvav4amt -440995 | bond | 80.604298 | d03e3784...01297dbf | applied | tnam1q8l...wvav4amt -438920 | bond | 82.660876 | 998d0d62...89f45b3f | applied | tnam1q8l...wvav4amt -438920 | claimRewards | - | 228b7ea6...59ad40af | applied | tnam1q8l...wvav4amt -435699 | claimRewards | - | 7b3842e8...63b5c539 | applied | tnam1q8l...wvav4amt -435699 | bond | 165.852706 | a08adf7b...6963f4a5 | applied | tnam1q8l...wvav4amt -428274 | claimRewards | - | 85bdca66...6d869e1e | applied | tnam1q8l...wvav4amt -428274 | bond | 83.989556 | a460357e...868dbcdf | applied | tnam1q8l...wvav4amt -425082 | bond | 84.013543 | 67c936de...6096ed9c | applied | tnam1q8l...wvav4amt -425082 | claimRewards | - | 65bdf4ac...5630f0e7 | applied | tnam1q8l...wvav4amt -423810 | bond | 84.007368 | 3f0a4a61...bb48db16 | applied | tnam1q8l...wvav4amt -423810 | claimRewards | - | 69afe28a...0257d92a | applied | tnam1q8l...wvav4amt -418890 | bond | 83.996031 | d2f7e60c...d17ec1bb | applied | tnam1q8l...wvav4amt -418890 | claimRewards | - | 3e33e04a...6c8e9f86 | applied | tnam1q8l...wvav4amt -416330 | bond | 83.991765 | 6c99d787...0798a21c | applied | tnam1q8l...wvav4amt -416330 | claimRewards | - | 696560a7...952d8089 | applied | tnam1q8l...wvav4amt -412709 | claimRewards | - | 905b9538...167af16d | applied | tnam1q8l...wvav4amt -412709 | bond | 84.076221 | 64b6769f...f5e1cb51 | applied | tnam1q8l...wvav4amt -410824 | claimRewards | - | 2f83590e...6236527c | applied | tnam1q8l...wvav4amt -410824 | bond | 83.944690 | dc1eb595...bd7cdc7f | applied | tnam1q8l...wvav4amt -406710 | claimRewards | - | 8c933ddd...fdf3f39b | applied | tnam1q8l...wvav4amt -406710 | bond | 83.930789 | a586f43f...1ae82747 | applied | tnam1q8l...wvav4amt -403517 | bond | 83.930627 | 24d75e47...17b3ba60 | applied | tnam1q8l...wvav4amt -403517 | claimRewards | - | 40b64f87...aebeb662 | applied | tnam1q8l...wvav4amt -400026 | bond | 83.921775 | 76082c70...6ee8022c | applied | tnam1q8l...wvav4amt -400026 | claimRewards | - | 0ff32666...2add8f71 | applied | tnam1q8l...wvav4amt -399022 | claimRewards | - | 9b71845f...295ec608 | applied | tnam1q8l...wvav4amt -399022 | bond | 83.909722 | 9dc8a7f5...e4497d9c | applied | tnam1q8l...wvav4amt -394070 | bond | 83.945728 | a8baf1c7...ef828cf4 | applied | tnam1q8l...wvav4amt -394070 | claimRewards | - | 0e5e5d8b...26710785 | applied | tnam1q8l...wvav4amt -390733 | claimRewards | - | dda2f690...6cd66e4b | applied | tnam1q8l...wvav4amt -390733 | bond | 83.943718 | c01ba0a7...157a4832 | applied | tnam1q8l...wvav4amt -389949 | bond | 83.992213 | 2291c59f...90976673 | applied | tnam1q8l...wvav4amt -389949 | claimRewards | - | cc0b29a2...f2f6485d | applied | tnam1q8l...wvav4amt -386065 | bond | 76.419950 | c580ee16...e3615941 | applied | tnam1q8l...wvav4amt -386065 | claimRewards | - | 4dc80002...54851903 | applied | tnam1q8l...wvav4amt -381831 | claimRewards | - | 67d9e57c...953f1ea8 | applied | tnam1q8l...wvav4amt -381831 | bond | 81.088015 | 36761c9d...7d97fc40 | applied | tnam1q8l...wvav4amt -380474 | bond | 90000.000000 | fd7017e1...29159d5a | applied | tnam1q8l...wvav4amt -378202 | bond | 81.083229 | bbc9c8ea...d5e8f4d9 | applied | tnam1q8l...wvav4amt -378202 | claimRewards | - | 7e5ffb75...96707a15 | applied | tnam1q8l...wvav4amt -375487 | bond | 81.076761 | 2d30e6a7...67b7ed61 | applied | tnam1q8l...wvav4amt -375487 | claimRewards | - | 264255b7...2ff524fe | applied | tnam1q8l...wvav4amt -373093 | bond | 45.236017 | c267c903...b3c4cd67 | applied | tnam1q8l...wvav4amt -373093 | claimRewards | - | 3846feba...05e3a311 | applied | tnam1q8l...wvav4amt -369270 | claimRewards | - | a6858a51...920f66e8 | applied | tnam1q8l...wvav4amt -369270 | bond | 45.229576 | 1a4104cc...697775de | applied | tnam1q8l...wvav4amt -367150 | bond | 400000.000000 | 91940b39...53d06767 | applied | tnam1q8l...wvav4amt -367141 | bond | 45.235478 | 259616a7...a4f3ccb3 | applied | tnam1q8l...wvav4amt -367141 | claimRewards | - | 53a25611...eae2c490 | applied | tnam1q8l...wvav4amt -363460 | bond | 45.198757 | 2e49f6dc...61e6e8a2 | applied | tnam1q8l...wvav4amt -363460 | claimRewards | - | b0fa1d48...7283abd5 | applied | tnam1q8l...wvav4amt -361825 | bond | 135.656943 | 4b199f85...4cabcb18 | applied | tnam1q8l...wvav4amt -361825 | claimRewards | - | 1e2daf4f...36fe3ba8 | applied | tnam1q8l...wvav4amt -352367 | bond | 45.212604 | b98bdd03...ed7d653d | applied | tnam1q8l...wvav4amt -352367 | claimRewards | - | 034359bc...bd451028 | applied | tnam1q8l...wvav4amt -349010 | bond | 135.597128 | 4a03d693...cb0555f3 | applied | tnam1q8l...wvav4amt -349010 | claimRewards | - | 74a78947...a9134edb | applied | tnam1q8l...wvav4amt -338163 | claimRewards | - | 872aa34a...6b1c4f42 | applied | tnam1q8l...wvav4amt -338163 | bond | 45.236022 | 84bd7940...cf027957 | applied | tnam1q8l...wvav4amt -336433 | claimRewards | - | a3dfe067...39c43bf8 | applied | tnam1q8l...wvav4amt -336433 | bond | 90.456647 | c0036f42...c0849feb | applied | tnam1q8l...wvav4amt -328287 | voteProposal | - | 37fcb832...813cf754 | applied | - -328280 | claimRewards | - | 2c6d334a...da6ca138 | applied | tnam1q8l...wvav4amt -328280 | bond | 90.431905 | 433845e2...f857a97f | applied | tnam1q8l...wvav4amt -324054 | claimRewards | - | 10ae2ac6...e4c76440 | applied | tnam1q8l...wvav4amt -324054 | bond | 90.370742 | 86a4966e...dbf32fc7 | applied | tnam1q8l...wvav4amt -317138 | claimRewards | - | f989849c...c23a29ef | applied | tnam1q8l...wvav4amt -317138 | bond | 45.192290 | 83336f56...49a3f4c1 | applied | tnam1q8l...wvav4amt -312919 | bond | 45.182541 | c98dfcd5...0724e102 | applied | tnam1q8l...wvav4amt -312919 | claimRewards | - | 195044d4...ee54b152 | applied | tnam1q8l...wvav4amt -311631 | bond | 135.505783 | f110955a...ed4e77c4 | applied | tnam1q8l...wvav4amt -311631 | claimRewards | - | 593a4831...49f749ab | applied | tnam1q8l...wvav4amt -300257 | claimRewards | - | 7f471f03...f70518e3 | applied | tnam1q8l...wvav4amt -300257 | bond | 45.142906 | 3421f799...68601486 | applied | tnam1q8l...wvav4amt -298849 | claimRewards | - | 6ae10fce...9c05eeb5 | applied | tnam1q8l...wvav4amt -298849 | bond | 90.222138 | 0f361583...fcde9542 | applied | tnam1q8l...wvav4amt -293587 | bond | 45.083837 | 209e2c0a...c5b8f8d1 | applied | tnam1q8l...wvav4amt -293587 | claimRewards | - | ec881cb8...6b09c575 | applied | tnam1q8l...wvav4amt -287763 | bond | 46.387573 | fba29803...61b77866 | applied | tnam1q8l...wvav4amt -287763 | claimRewards | - | f32811a9...349138d5 | applied | tnam1q8l...wvav4amt -286534 | bond | 92.742865 | 431ad62a...1f260bbe | applied | tnam1q8l...wvav4amt -286534 | claimRewards | - | a15fc543...bbe1430a | applied | tnam1q8l...wvav4amt -278451 | claimRewards | - | 402f4cb4...9739b084 | applied | tnam1q8l...wvav4amt -278451 | bond | 46.254208 | dac71aa5...bed1c9b0 | applied | tnam1q8l...wvav4amt -276399 | claimRewards | - | c026c247...34158a44 | applied | tnam1q8l...wvav4amt -276399 | bond | 46.280223 | 8ccac679...2e5a4f94 | applied | tnam1q8l...wvav4amt -274684 | bond | 941.356693 | b4865f2a...7c554432 | applied | tnam1q8l...wvav4amt -274684 | claimRewards | - | 51da944c...8164c578 | applied | tnam1q8l...wvav4amt -214170 | claimRewards | - | e73b6063...08728f6f | applied | tnam1q8l...wvav4amt -214170 | bond | 62.115373 | b6c3609c...9f767ef0 | applied | tnam1q8l...wvav4amt -211518 | bond | 124.265195 | b0429770...d6952bfe | applied | tnam1q8l...wvav4amt -211518 | claimRewards | - | cf1990a9...568da306 | applied | tnam1q8l...wvav4amt -205554 | claimRewards | - | 104b5ce3...cc290dc2 | applied | tnam1q8l...wvav4amt -205554 | bond | 63.371039 | 285267ab...8c7b08ec | applied | tnam1q8l...wvav4amt -200539 | bond | 50.000000 | 8336245b...53ef4abe | applied | tnam1q8l...wvav4amt -200535 | claimRewards | - | d1c0819d...e0953b14 | applied | tnam1q8l...wvav4amt -199462 | bond | 129.956286 | 12811faa...0ae5999c | applied | tnam1q8l...wvav4amt -199462 | claimRewards | - | da685a14...04d6a23d | applied | tnam1q8l...wvav4amt -191132 | claimRewards | - | d281cca5...0ddae2ac | applied | tnam1q8l...wvav4amt -191132 | bond | 67.087372 | 87140915...bda5ff8d | applied | tnam1q8l...wvav4amt -188541 | bond | 67.148211 | fb9ecf38...22c698dc | applied | tnam1q8l...wvav4amt -188541 | claimRewards | - | 449413fb...b74b451f | applied | tnam1q8l...wvav4amt -186595 | bond | 134.359882 | 53148b50...fc04d3d7 | applied | tnam1q8l...wvav4amt -186595 | claimRewards | - | f69afb07...843f5f4a | applied | tnam1q8l...wvav4amt -178688 | claimRewards | - | 5914d387...c7e5c123 | applied | tnam1q8l...wvav4amt -178688 | bond | 54.820056 | debd41e5...efa76724 | applied | tnam1q8l...wvav4amt -177453 | voteProposal | - | 86b5cd53...32eac77f | applied | - -175818 | claimRewards | - | 04a4ff71...3c11c8c7 | applied | tnam1q8l...wvav4amt -175818 | bond | 50.912146 | 08a49f84...0f92a3b3 | applied | tnam1q8l...wvav4amt -174733 | bond | 99999.000000 | 15b50b2f...10e2f4f1 | applied | tnam1q8l...wvav4amt -174679 | bond | 87.226604 | 807a4ff0...e69dc410 | applied | tnam1q8l...wvav4amt -174679 | claimRewards | - | 3979430f...eafcf9b5 | applied | tnam1q8l...wvav4amt -168834 | claimRewards | - | fed81252...c8303308 | applied | tnam1q8l...wvav4amt -168834 | bond | 119.753607 | 740cf122...909c289c | applied | tnam1q8l...wvav4amt -152863 | bond | 99899.850000 | dc86ba30...7b3ff4d4 | applied | tnam1q8l...wvav4amt -64654 | bond | 100.000000 | 1ea0797c...c87b3739 | applied | tnam1q8l...wvav4amt -19718 | bond | 100000.000000 | a23cdbb5...a23ad6e2 | applied | tnam1q8l...wvav4amt -2828 | bond | 99990.000000 | b474513f...3504f94f | applied | tnam1q8l...wvav4amt -1267 | bond | 99999.700000 | 60ae4b33...1e8419f5 | applied | tnam1q8l...wvav4amt -1225 | bond | 10.000000 | 8af323bd...66a5e454 | applied | tnam1q8l...wvav4amt -1225 | revealPk | - | 1772aa0e...09d42358 | applied | - From 1289e2c8419dd456347f7625a838efcdbbace684 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Fri, 25 Apr 2025 13:29:59 +1200 Subject: [PATCH 14/18] fix: update notes --- apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx b/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx index a3cfb85b1c..ddd1ad67a4 100644 --- a/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx +++ b/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx @@ -8,6 +8,14 @@ import { IoClose } from "react-icons/io5"; import { twMerge } from "tailwind-merge"; import { ReferralReward } from "./types"; +// TODO: +// - Once we pay a user we need to update their entry in the DB to last_paid_epoch +// - After this we should send a confirmation email to Paul et al to let them know: +// - A user has been paid +// - The amount paid +// - The address of the user paid +// - The epoch at which the user was paid + export const ReferralSheetModal = ({ rewardsData, onClose, From 3de46951d8586c11631b5222c95ed3befe594921 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Fri, 25 Apr 2025 13:30:51 +1200 Subject: [PATCH 15/18] fix: linting --- apps/namadillo/src/App/Staking/AllValidatorsTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/namadillo/src/App/Staking/AllValidatorsTable.tsx b/apps/namadillo/src/App/Staking/AllValidatorsTable.tsx index 5c0b856944..93f4e2d0a4 100644 --- a/apps/namadillo/src/App/Staking/AllValidatorsTable.tsx +++ b/apps/namadillo/src/App/Staking/AllValidatorsTable.tsx @@ -29,7 +29,7 @@ export const AllValidatorsTable = ({ initialPage = 0, }: AllValidatorsProps): JSX.Element => { const validators = useAtomValue(allValidatorsAtom); - const [searchTerm, setSearchTerm] = useState(""); + const [_, setSearchTerm] = useState(""); const userHasAccount = useUserHasAccount(); const filteredValidators = useValidatorFilter({ From 7fd7fc980a4a902e3fd3de4276ad595c05119750 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Fri, 25 Apr 2025 13:47:56 +1200 Subject: [PATCH 16/18] fix: cleanup --- .../src/App/Referrals/ReferralsTable.tsx | 9 +- apps/namadillo/src/App/Referrals/excel.ts | 175 +++++++++++++----- 2 files changed, 139 insertions(+), 45 deletions(-) diff --git a/apps/namadillo/src/App/Referrals/ReferralsTable.tsx b/apps/namadillo/src/App/Referrals/ReferralsTable.tsx index f32b24781b..c789ea365d 100644 --- a/apps/namadillo/src/App/Referrals/ReferralsTable.tsx +++ b/apps/namadillo/src/App/Referrals/ReferralsTable.tsx @@ -299,7 +299,14 @@ export const ReferralsTable = ({ rewardsByReferrer[reward.referrerAddress].push(reward); }); - setRewardsData(rewards); + // Sort rewards by epoch so entries with the same epoch are grouped together + const rewardsSortedByEpoch = [...rewards].sort((a, b) => { + // First sort by epoch + if (a.epoch !== b.epoch) return a.epoch - b.epoch; + // Then by referrer address for consistent ordering within the same epoch + return a.referrerAddress.localeCompare(b.referrerAddress); + }); + setRewardsData(rewardsSortedByEpoch); if (rewards.length > 0) { // Log data to help troubleshoot generation diff --git a/apps/namadillo/src/App/Referrals/excel.ts b/apps/namadillo/src/App/Referrals/excel.ts index 86a000ee70..fba88dd124 100644 --- a/apps/namadillo/src/App/Referrals/excel.ts +++ b/apps/namadillo/src/App/Referrals/excel.ts @@ -14,33 +14,51 @@ const generateCsvContent = (rewardsData: ReferralReward[]): string => { } try { + // Sort rewards by referee address to group them + const sortedRewards = [...rewardsData].sort( + (a, b) => + a.refereeAddress.localeCompare(b.refereeAddress) || a.epoch - b.epoch + ); + // Format each reward as a CSV row and join with Windows-style line breaks (CRLF) - const csvData = rewardsData - .map((r) => { - // Check if we have all required data - if (!r || !r.validator) { - console.warn("Invalid reward data item", r); - return null; - } + let currentRefereeAddress: string | null = null; + const csvRows: string[] = []; + + sortedRewards.forEach((r) => { + // Check if we have all required data + if (!r || !r.validator) { + console.warn("Invalid reward data item", r); + return; + } + + // Add a blank row when referee address changes + if ( + currentRefereeAddress !== null && + currentRefereeAddress !== r.refereeAddress + ) { + csvRows.push(""); // Add blank row + } + + currentRefereeAddress = r.refereeAddress; - // Escape any commas in text fields with quotes - const validatorName = - r.validator.name ? - `"${r.validator.name.replace(/"/g, '""')}"` - : "Unknown"; + // Escape any commas in text fields with quotes + const validatorName = + r.validator.name ? + `"${r.validator.name.replace(/"/g, '""')}"` + : "Unknown"; - return [ + csvRows.push( + [ `"${r.referrerAddress}"`, `"${r.refereeAddress}"`, r.epoch, r.amount.toFixed(6), validatorName, - ].join(","); - }) - .filter(Boolean) // Remove any null entries - .join("\r\n"); + ].join(",") + ); + }); - return headers + csvData; + return headers + csvRows.join("\r\n"); } catch (error) { console.error("Error generating CSV content:", error); return headers + "Error generating CSV data"; @@ -77,24 +95,58 @@ export const downloadMultiSheetExcel = ( // First, add a sheet with all rewards combined const allRewards = Object.values(rewardsByReferrer).flat(); if (allRewards.length > 0) { - // Convert rewards to array of plain objects for Excel - // Force numbers to be strings by converting with explicit toString() to ensure left alignment - const allRewardsData = allRewards.map((reward) => ({ - "Referrer Address": reward.referrerAddress, - "Referee Address": reward.refereeAddress, - Epoch: String(reward.epoch), // Convert to string to force left alignment - "Reward (NAM)": String(reward.amount.toFixed(6)), // Convert to string to force left alignment - "Validator Name": reward.validator.name || "Unknown", - })); + // Sort all rewards by referee address to group them together + const sortedAllRewards = [...allRewards].sort( + (a, b) => + a.refereeAddress.localeCompare(b.refereeAddress) || a.epoch - b.epoch + ); + + // Convert rewards to array format with blank rows between referee addresses + const allRewardsFormatted: (Record | null)[] = []; + let currentRefereeAddress: string | null = null; + + sortedAllRewards.forEach((reward) => { + // Add a blank row when referee address changes + if ( + currentRefereeAddress !== null && + currentRefereeAddress !== reward.refereeAddress + ) { + allRewardsFormatted.push(null); // Add blank row + } + + currentRefereeAddress = reward.refereeAddress; + + allRewardsFormatted.push({ + "Referrer Address": reward.referrerAddress, + "Referee Address": reward.refereeAddress, + Epoch: String(reward.epoch), + "Reward (NAM)": String(reward.amount.toFixed(6)), + "Validator Name": reward.validator.name || "Unknown", + }); + }); + + // Convert to AOA format with null rows becoming empty arrays (blank rows) + const headers = [ + "Referrer Address", + "Referee Address", + "Epoch", + "Reward (NAM)", + "Validator Name", + ]; + const dataRows = allRewardsFormatted.map((row) => + row ? Object.values(row) : Array(headers.length).fill("") + ); // Create worksheet with formatting options const worksheet = XLSX.utils.aoa_to_sheet([ - Object.keys(allRewardsData[0]), // Headers - ...allRewardsData.map((row) => Object.values(row)), // Data rows + headers, // Headers + ...dataRows, // Data rows with blank rows ]); - // Auto-size columns based on content - const columnWidths = fitToColumn(allRewardsData); + // Auto-size columns based on content (filter out null entries for fitToColumn) + const columnWidths = fitToColumn( + allRewardsFormatted.filter(Boolean) as Record[] + ); worksheet["!cols"] = columnWidths; XLSX.utils.book_append_sheet(workbook, worksheet, "All Rewards"); @@ -105,27 +157,62 @@ export const downloadMultiSheetExcel = ( rewardsByReferrer )) { if (rewards.length > 0) { - // Convert rewards to array of plain objects for Excel - // Force numbers to be strings to ensure left alignment - const referrerRewardsData = rewards.map((reward) => ({ - "Referrer Address": reward.referrerAddress, - "Referee Address": reward.refereeAddress, - Epoch: String(reward.epoch), // Convert to string to force left alignment - "Reward (NAM)": String(reward.amount.toFixed(6)), // Convert to string to force left alignment - "Validator Name": reward.validator.name || "Unknown", - })); + // Sort rewards by referee address to group them together + const sortedRewards = [...rewards].sort( + (a, b) => + a.refereeAddress.localeCompare(b.refereeAddress) || + a.epoch - b.epoch + ); + + // Convert rewards to array format with blank rows between referee addresses + const referrerRewardsFormatted: (Record | null)[] = []; + let currentRefereeAddress: string | null = null; + + sortedRewards.forEach((reward) => { + // Add a blank row when referee address changes + if ( + currentRefereeAddress !== null && + currentRefereeAddress !== reward.refereeAddress + ) { + referrerRewardsFormatted.push(null); // Add blank row + } + + currentRefereeAddress = reward.refereeAddress; + + referrerRewardsFormatted.push({ + "Referrer Address": reward.referrerAddress, + "Referee Address": reward.refereeAddress, + Epoch: String(reward.epoch), + "Reward (NAM)": String(reward.amount.toFixed(6)), + "Validator Name": reward.validator.name || "Unknown", + }); + }); // Create worksheet - use shortened address for sheet name const shortAddr = shortenAddress(referrerAddress, 8, 4); + // Convert to AOA format with null rows becoming empty arrays (blank rows) + const headers = [ + "Referrer Address", + "Referee Address", + "Epoch", + "Reward (NAM)", + "Validator Name", + ]; + const dataRows = referrerRewardsFormatted.map((row) => + row ? Object.values(row) : Array(headers.length).fill("") + ); + // Create worksheet with formatting options const worksheet = XLSX.utils.aoa_to_sheet([ - Object.keys(referrerRewardsData[0]), // Headers - ...referrerRewardsData.map((row) => Object.values(row)), // Data rows + headers, // Headers + ...dataRows, // Data rows with blank rows ]); - // Auto-size columns based on content - const columnWidths = fitToColumn(referrerRewardsData); + // Auto-size columns based on content (filter out null entries for fitToColumn) + const columnWidths = fitToColumn( + referrerRewardsFormatted.filter(Boolean) as Record[] + ); worksheet["!cols"] = columnWidths; XLSX.utils.book_append_sheet(workbook, worksheet, shortAddr); From ce2a625a1f8e15d3f0d05500f0544c655c64046f Mon Sep 17 00:00:00 2001 From: neocybereth Date: Fri, 25 Apr 2025 13:50:50 +1200 Subject: [PATCH 17/18] fix: clean --- yarn.lock | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/yarn.lock b/yarn.lock index beb4c80fd3..bcee655934 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3829,6 +3829,7 @@ __metadata: "@namada/chain-registry": "npm:^1.0.0" "@namada/indexer-client": "npm:2.4.4" "@playwright/test": "npm:^1.24.1" + "@supabase/supabase-js": "npm:^2.49.4" "@svgr/webpack": "npm:^6.5.1" "@tailwindcss/container-queries": "npm:^0.1.1" "@tanstack/query-core": "npm:^5.40.0" @@ -3908,6 +3909,7 @@ __metadata: vite-tsconfig-paths: "npm:^5.0.1" web-vitals: "npm:^2.1.4" wonka: "npm:^6.3.4" + xlsx: "npm:^0.18.5" languageName: unknown linkType: soft @@ -4739,6 +4741,77 @@ __metadata: languageName: node linkType: hard +"@supabase/auth-js@npm:2.69.1": + version: 2.69.1 + resolution: "@supabase/auth-js@npm:2.69.1" + dependencies: + "@supabase/node-fetch": "npm:^2.6.14" + checksum: 10c0/efc08fc6be48769efc84105617f2cb681791641ba86480d29a316ae83beac8cf4f747bf00b4947c32992f9b7b0b40e3c2bf74013f01e070c38558169ab68b6f1 + languageName: node + linkType: hard + +"@supabase/functions-js@npm:2.4.4": + version: 2.4.4 + resolution: "@supabase/functions-js@npm:2.4.4" + dependencies: + "@supabase/node-fetch": "npm:^2.6.14" + checksum: 10c0/35871341ca96c35a416d81a6f035bd0d594d278f5cbe4492173766b3d6b9acfc52374b0a2b50e31a900a8e3a9dcb131d1eadf3808a9a9e1c10bbab7a2045d2d3 + languageName: node + linkType: hard + +"@supabase/node-fetch@npm:2.6.15, @supabase/node-fetch@npm:^2.6.14": + version: 2.6.15 + resolution: "@supabase/node-fetch@npm:2.6.15" + dependencies: + whatwg-url: "npm:^5.0.0" + checksum: 10c0/98d25cab2eba53c93c59e730d52d50065b1a7fe216c65224471e83e2064ebd45ae51ad09cb39ec263c3cb59e3d41870fc2e789ea2e9587480d7ba212b85daf38 + languageName: node + linkType: hard + +"@supabase/postgrest-js@npm:1.19.4": + version: 1.19.4 + resolution: "@supabase/postgrest-js@npm:1.19.4" + dependencies: + "@supabase/node-fetch": "npm:^2.6.14" + checksum: 10c0/1d14adc841f720e0035045f8b06cf2cb9f3b0a83ac903e268d5afb80b64d240ae64cb24372e0e9c857420b07010bfdb9a806f024fe60ac13468fd791ada2eb7f + languageName: node + linkType: hard + +"@supabase/realtime-js@npm:2.11.2": + version: 2.11.2 + resolution: "@supabase/realtime-js@npm:2.11.2" + dependencies: + "@supabase/node-fetch": "npm:^2.6.14" + "@types/phoenix": "npm:^1.5.4" + "@types/ws": "npm:^8.5.10" + ws: "npm:^8.18.0" + checksum: 10c0/1e91c8e70d4bf2cd25ed9d7d8c75c0fcc2ef4f53d03647cbaac790cf9f359295b5aa6ce0876f12e7e15804cfe9979398cd57fc92f19a43ee4e691d29abbe8e14 + languageName: node + linkType: hard + +"@supabase/storage-js@npm:2.7.1": + version: 2.7.1 + resolution: "@supabase/storage-js@npm:2.7.1" + dependencies: + "@supabase/node-fetch": "npm:^2.6.14" + checksum: 10c0/bcaa8bd275c59b8c5f6f00b9590ef54f008b63aacdcd8bf1747cb73f61ea7bd321bb816314ae0cf1bb318cd4d398515f9a135bde84ef960c19ac3c11e38d00fd + languageName: node + linkType: hard + +"@supabase/supabase-js@npm:^2.49.4": + version: 2.49.4 + resolution: "@supabase/supabase-js@npm:2.49.4" + dependencies: + "@supabase/auth-js": "npm:2.69.1" + "@supabase/functions-js": "npm:2.4.4" + "@supabase/node-fetch": "npm:2.6.15" + "@supabase/postgrest-js": "npm:1.19.4" + "@supabase/realtime-js": "npm:2.11.2" + "@supabase/storage-js": "npm:2.7.1" + checksum: 10c0/43f5500b4cee89fa975ef13036846e6406d7719c79e1c9563b9e6a54529aa7b8d17e86bd0cd2cd2394ce6894e85628f43a29809572a438d2fc493175bb25ac25 + languageName: node + linkType: hard + "@surma/rollup-plugin-off-main-thread@npm:^2.2.3": version: 2.2.3 resolution: "@surma/rollup-plugin-off-main-thread@npm:2.2.3" @@ -5638,6 +5711,13 @@ __metadata: languageName: node linkType: hard +"@types/phoenix@npm:^1.5.4": + version: 1.6.6 + resolution: "@types/phoenix@npm:1.6.6" + checksum: 10c0/4dfcb3fd36341ed5500de030291af14163c599857e00d2d4ff065d4c4600317d5d20aa170913fb9609747a09436e3add44db7d0c709bdf80f36cddcc67a42021 + languageName: node + linkType: hard + "@types/postcss-modules-local-by-default@npm:^4.0.2": version: 4.0.2 resolution: "@types/postcss-modules-local-by-default@npm:4.0.2" @@ -5913,6 +5993,15 @@ __metadata: languageName: node linkType: hard +"@types/ws@npm:^8.5.10": + version: 8.18.1 + resolution: "@types/ws@npm:8.18.1" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/61aff1129143fcc4312f083bc9e9e168aa3026b7dd6e70796276dcfb2c8211c4292603f9c4864fae702f2ed86e4abd4d38aa421831c2fd7f856c931a481afbab + languageName: node + linkType: hard + "@types/ws@npm:^8.5.5": version: 8.5.13 resolution: "@types/ws@npm:8.5.13" @@ -6483,6 +6572,13 @@ __metadata: languageName: node linkType: hard +"adler-32@npm:~1.3.0": + version: 1.3.1 + resolution: "adler-32@npm:1.3.1" + checksum: 10c0/c1b7185526ee1bbe0eac8ed414d5226af4cd02a0540449a72ec1a75f198c5e93352ba4d7b9327231eea31fd83c2d080d13baf16d8ed5710fb183677beb85f612 + languageName: node + linkType: hard + "adm-zip@npm:~0.5.x": version: 0.5.16 resolution: "adm-zip@npm:0.5.16" @@ -7827,6 +7923,16 @@ __metadata: languageName: node linkType: hard +"cfb@npm:~1.2.1": + version: 1.2.2 + resolution: "cfb@npm:1.2.2" + dependencies: + adler-32: "npm:~1.3.0" + crc-32: "npm:~1.2.0" + checksum: 10c0/87f6d9c3878268896ed6ca29dfe32a2aa078b12d0f21d8405c95911b74ab6296823d7312bbf5e18326d00b16cc697f587e07a17018c5edf7a1ba31dd5bc6da36 + languageName: node + linkType: hard + "chain-registry@npm:^1.63.100": version: 1.69.89 resolution: "chain-registry@npm:1.69.89" @@ -8113,6 +8219,13 @@ __metadata: languageName: node linkType: hard +"codepage@npm:~1.15.0": + version: 1.15.0 + resolution: "codepage@npm:1.15.0" + checksum: 10c0/2455b482302cb784b46dea60a8ee83f0c23e794bdd979556bdb107abe681bba722af62a37f5c955ff4efd68fdb9688c3986e719b4fd536c0e06bb25bc82abea3 + languageName: node + linkType: hard + "collect-v8-coverage@npm:^1.0.0": version: 1.0.2 resolution: "collect-v8-coverage@npm:1.0.2" @@ -8569,6 +8682,15 @@ __metadata: languageName: node linkType: hard +"crc-32@npm:~1.2.0, crc-32@npm:~1.2.1": + version: 1.2.2 + resolution: "crc-32@npm:1.2.2" + bin: + crc32: bin/crc32.njs + checksum: 10c0/11dcf4a2e77ee793835d49f2c028838eae58b44f50d1ff08394a610bfd817523f105d6ae4d9b5bef0aad45510f633eb23c903e9902e4409bed1ce70cb82b9bf0 + languageName: node + linkType: hard + "create-ecdh@npm:^4.0.4": version: 4.0.4 resolution: "create-ecdh@npm:4.0.4" @@ -11081,6 +11203,13 @@ __metadata: languageName: node linkType: hard +"frac@npm:~1.1.2": + version: 1.1.2 + resolution: "frac@npm:1.1.2" + checksum: 10c0/640740eb58b590eb38c78c676955bee91cd22d854f5876241a15c49d4495fa53a84898779dcf7eca30aabfe1c1a4a705752b5f224934257c5dda55c545413ba7 + languageName: node + linkType: hard + "fraction.js@npm:^4.3.7": version: 4.3.7 resolution: "fraction.js@npm:4.3.7" @@ -18980,6 +19109,15 @@ __metadata: languageName: node linkType: hard +"ssf@npm:~0.11.2": + version: 0.11.2 + resolution: "ssf@npm:0.11.2" + dependencies: + frac: "npm:~1.1.2" + checksum: 10c0/c3fd24a90dc37a9dc5c4154cb4121e27507c33ebfeee3532aaf03625756b2c006cf79c0a23db0ba16c4a6e88e1349455327867e03453fc9d54b32c546bc18ca6 + languageName: node + linkType: hard + "sshpk@npm:^1.7.0": version: 1.18.0 resolution: "sshpk@npm:1.18.0" @@ -21804,6 +21942,13 @@ __metadata: languageName: node linkType: hard +"wmf@npm:~1.0.1": + version: 1.0.2 + resolution: "wmf@npm:1.0.2" + checksum: 10c0/3fa5806f382632cadfe65d4ef24f7a583b0c0720171edb00e645af5248ad0bb6784e8fcee1ccd9f475a1a12a7523e2512e9c063731fbbdae14dc469e1c033d93 + languageName: node + linkType: hard + "wonka@npm:^6.3.4": version: 6.3.4 resolution: "wonka@npm:6.3.4" @@ -21818,6 +21963,13 @@ __metadata: languageName: node linkType: hard +"word@npm:~0.3.0": + version: 0.3.0 + resolution: "word@npm:0.3.0" + checksum: 10c0/c6da2a9f7a0d81a32fa6768a638d21b153da2be04f94f3964889c7cc1365d74b6ecb43b42256c3f926cd59512d8258206991c78c21000c3da96d42ff1238b840 + languageName: node + linkType: hard + "wordwrap@npm:^1.0.0": version: 1.0.0 resolution: "wordwrap@npm:1.0.0" @@ -22193,6 +22345,23 @@ __metadata: languageName: node linkType: hard +"xlsx@npm:^0.18.5": + version: 0.18.5 + resolution: "xlsx@npm:0.18.5" + dependencies: + adler-32: "npm:~1.3.0" + cfb: "npm:~1.2.1" + codepage: "npm:~1.15.0" + crc-32: "npm:~1.2.1" + ssf: "npm:~0.11.2" + wmf: "npm:~1.0.1" + word: "npm:~0.3.0" + bin: + xlsx: bin/xlsx.njs + checksum: 10c0/787cfa77034a3e86fdcde21572f1011c8976f87823a5e0ee5057f13b2f6e48f17a1710732a91b8ae15d7794945c7cba8a3ca904ea7150e028260b0ab8e1158c8 + languageName: node + linkType: hard + "xml-name-validator@npm:^4.0.0": version: 4.0.0 resolution: "xml-name-validator@npm:4.0.0" From 67abe2092bf2c540bd5de48e3fccf950ab01ed46 Mon Sep 17 00:00:00 2001 From: neocybereth Date: Fri, 25 Apr 2025 16:13:19 +1200 Subject: [PATCH 18/18] fix: add cut split --- .../src/App/Referrals/ReferralSheetModal.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx b/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx index ddd1ad67a4..c36f5e7063 100644 --- a/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx +++ b/apps/namadillo/src/App/Referrals/ReferralSheetModal.tsx @@ -158,6 +158,9 @@ const TotalCard = ({ addr: string; amount: BigNumber; }): JSX.Element => { + const validatorCut = amount.multipliedBy(0.05); + const referrerCut = validatorCut.multipliedBy(0.25); + const toolBuilderCut = referrerCut; return (
@@ -179,9 +182,15 @@ const TotalCard = ({ {amount.toFormat(6)}
- Total NAM Owed: + Validator Cut: - {amount.multipliedBy(0.05).toFormat(6)} + {validatorCut.minus(referrerCut).minus(toolBuilderCut).toFormat(6)} + +
+
+ Referrer Cut: + + {referrerCut.toFormat(6)}