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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,5 @@ next-env.d.ts
.claude
.vscode
.zed
.tempor
.agents
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
} from '@aragon/gov-ui-kit';
import { useCallback, useRef } from 'react';
import { useFormContext, useWatch } from 'react-hook-form';
import { CapitalFlowDaoSlotId } from '@/modules/capitalFlow/constants/moduleDaoSlots';
import { CapitalDistributorDaoSlotId } from '@/actions/capitalDistributor/constants/capitalDistributorDaoSlotId';
import type { IAsset } from '@/modules/finance/api/financeService';
import { AssetInput } from '@/modules/finance/components/assetInput';
import { useDao } from '@/shared/api/daoService';
Expand Down Expand Up @@ -251,7 +251,7 @@ export const CapitalDistributorCreateCampaignActionCreateForm: React.FC<
dao={dao}
pluginId={dao.id}
slotId={
CapitalFlowDaoSlotId.CAPITAL_DISTRIBUTOR_MEMBERS_FILE_DOWNLOAD
CapitalDistributorDaoSlotId.CAPITAL_DISTRIBUTOR_MEMBERS_FILE_DOWNLOAD
}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export enum CapitalFlowDaoSlotId {
export enum CapitalDistributorDaoSlotId {
CAPITAL_DISTRIBUTOR_MEMBERS_FILE_DOWNLOAD = 'CAPITAL_DISTRIBUTOR_MEMBERS_FILE_DOWNLOAD',
}

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

15 changes: 0 additions & 15 deletions src/daos/capitalDistributorTest/index.ts

This file was deleted.

8 changes: 8 additions & 0 deletions src/daos/cryptex/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { CapitalDistributorDaoSlotId } from '@/actions/capitalDistributor/constants/capitalDistributorDaoSlotId';
import { DashboardDaoSlotId } from '@/modules/dashboard/constants/moduleDaoSlots';
import { pluginRegistryUtils } from '@/shared/utils/pluginRegistryUtils';
import { CryptexPageHeader } from './components/cryptexPageHeader';
import { cryptex } from './constants/cryptex';
import { CryptexRewardsMembersFileDownload } from './rewards';

export const initialiseCryptex = () => {
pluginRegistryUtils
Expand All @@ -11,5 +13,11 @@ export const initialiseCryptex = () => {
slotId: DashboardDaoSlotId.DASHBOARD_DAO_HEADER,
pluginId: cryptex.id,
component: CryptexPageHeader,
})

.registerSlotComponent({
slotId: CapitalDistributorDaoSlotId.CAPITAL_DISTRIBUTOR_MEMBERS_FILE_DOWNLOAD,
pluginId: cryptex.id,
component: CryptexRewardsMembersFileDownload,
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type {
ITokenRewardDistribution,
ITokenRewardEntry,
} from './tokenReward';
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* A single reward entry in the token reward distribution response.
*/
export interface ITokenRewardEntry {
/**
* Staker address receiving the reward.
*/
address: string;
/**
* Reward amount in wei.
*/
amount: string;
}

/**
* Response from the GET /v2/token/rewards/:pluginAddress/:network endpoint.
* Array of reward entries where Σ amount === totalAmount from the request.
*/
export type ITokenRewardDistribution = ITokenRewardEntry[];
5 changes: 5 additions & 0 deletions src/daos/cryptex/rewards/api/tokenRewardService/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type * from './domain';
export * from './queries';
export { tokenRewardService } from './tokenRewardService';
export type * from './tokenRewardService.api';
export { tokenRewardServiceKeys } from './tokenRewardServiceKeys';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useTokenRewardDistribution';
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export {
tokenRewardDistributionOptions,
useTokenRewardDistribution,
} from './useTokenRewardDistribution';
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useQuery } from '@tanstack/react-query';
import type {
QueryOptions,
SharedQueryOptions,
} from '@/shared/types/queryOptions';
import type { ITokenRewardDistribution } from '../../domain';
import { tokenRewardService } from '../../tokenRewardService';
import type { IGetTokenRewardDistributionParams } from '../../tokenRewardService.api';
import { tokenRewardServiceKeys } from '../../tokenRewardServiceKeys';

export const tokenRewardDistributionOptions = (
params: IGetTokenRewardDistributionParams,
options?: QueryOptions<ITokenRewardDistribution>,
): SharedQueryOptions<ITokenRewardDistribution> => ({
queryKey: tokenRewardServiceKeys.rewardDistribution(params),
queryFn: () => tokenRewardService.getRewardDistribution(params),
...options,
});

export const useTokenRewardDistribution = (
params: IGetTokenRewardDistributionParams,
options?: QueryOptions<ITokenRewardDistribution>,
) => useQuery(tokenRewardDistributionOptions(params, options));
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { Hex } from 'viem';
import type { Network } from '@/shared/api/daoService';
import type { IRequestUrlQueryParams } from '@/shared/api/httpService';

export interface IGetTokenRewardDistributionUrlParams {
/**
* Token voting governance plugin address.
*/
pluginAddress: Hex;
/**
* Network of the DAO.
*/
network: Network;
}

export interface IGetTokenRewardDistributionQueryParams {
/**
* Total amount to distribute in wei.
*/
rewardTotalAmount: string;
/**
* ISO date string (e.g. "2026-01-15"). Backend includes proposals
* with endDate >= lookbackDate and endDate <= now.
*/
lookbackDate: string;
}

export interface IGetTokenRewardDistributionParams
extends IRequestUrlQueryParams<
IGetTokenRewardDistributionUrlParams,
IGetTokenRewardDistributionQueryParams
> {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { AragonBackendService } from '@/shared/api/aragonBackendService';
import type { ITokenRewardDistribution } from './domain';
import type { IGetTokenRewardDistributionParams } from './tokenRewardService.api';

class TokenRewardService extends AragonBackendService {
private urls = {
rewardDistribution: '/v2/token/rewards/:pluginAddress/:network',
};

/**
* Computes the token voting reward distribution for a given plugin and lookback period.
* Returns per-staker reward entries based on governance participation.
*/
getRewardDistribution = async (
params: IGetTokenRewardDistributionParams,
): Promise<ITokenRewardDistribution> => {
const result = await this.request<ITokenRewardDistribution>(
this.urls.rewardDistribution,
params,
);
return result;
};
}

export const tokenRewardService = new TokenRewardService();
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { IGetTokenRewardDistributionParams } from './tokenRewardService.api';

export enum TokenRewardServiceKey {
TOKEN_REWARD_DISTRIBUTION = 'TOKEN_REWARD_DISTRIBUTION',
}

export const tokenRewardServiceKeys = {
rewardDistribution: (params: IGetTokenRewardDistributionParams) => [
TokenRewardServiceKey.TOKEN_REWARD_DISTRIBUTION,
params,
],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { IAsset } from '@/modules/finance/api/financeService';
import type { IDao } from '@/shared/api/daoService';

export interface ICryptexRewardsMembersFileDownloadProps {
/**
* DAO to display in the header.
*/
dao: IDao;
/**
* Asset selected in the campaign creation form, passed via PluginSingleComponent.
*/
asset?: IAsset;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
'use client';

import { Button, Card, IconType, InputContainer } from '@aragon/gov-ui-kit';
import { useState } from 'react';
import type { Hex } from 'viem';
import type { IDaoPlugin } from '@/shared/api/daoService';
import { PluginInterfaceType } from '@/shared/api/daoService';
import { useDialogContext } from '@/shared/components/dialogProvider';
import { useDaoPlugins } from '@/shared/hooks/useDaoPlugins';
import { CryptexRewardsDialogId } from '../../constants/cryptexRewardsDialogId';
import type { ICryptexRewardsMembersFileDownloadDialogParams } from '../../dialogs/cryptexRewardsMembersFileDownloadDialog';
import type { ICryptexRewardsMembersFileDownloadProps } from './cryptexRewardsMembersFileDownload.api';

export const CryptexRewardsMembersFileDownload: React.FC<
ICryptexRewardsMembersFileDownloadProps
> = (props) => {
const { dao, asset } = props;

const { open } = useDialogContext();

const [downloadedFileName, setDownloadedFileName] = useState<string | null>(
null,
);

const governancePlugins = useDaoPlugins({
daoId: dao.id,
interfaceType: PluginInterfaceType.TOKEN_VOTING,
includeSubPlugins: false,
});
const governancePlugin = governancePlugins?.[0]?.meta as
| IDaoPlugin
| undefined;

const handleClick = () => {
if (governancePlugin?.address == null) {
return;
}

const params: ICryptexRewardsMembersFileDownloadDialogParams = {
pluginAddress: governancePlugin.address as Hex,
network: dao.network,
asset,
onDownload: setDownloadedFileName,
};

open(CryptexRewardsDialogId.CRYPTEX_REWARDS_MEMBERS_FILE_DOWNLOAD, {
params,
disableOutsideClick: true,
});
};

return (
<InputContainer
helpText="Generate and download a rewards JSON based on governance participation"
id="cryptex-rewards-members-file"
label="Members file"
useCustomWrapper={true}
>
{downloadedFileName && (
<Card className="border border-neutral-100 px-6 py-2 shadow-neutral-sm">
<p className="text-neutral-400 text-sm">
{downloadedFileName}
</p>
</Card>
)}
<Button
className="w-fit"
disabled={governancePlugin?.address == null}
iconLeft={IconType.PLUS}
onClick={handleClick}
size="md"
variant="tertiary"
>
Generate rewards file
</Button>
</InputContainer>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { CryptexRewardsMembersFileDownload } from './cryptexRewardsMembersFileDownload';
export type { ICryptexRewardsMembersFileDownloadProps } from './cryptexRewardsMembersFileDownload.api';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export enum CryptexRewardsDialogId {
CRYPTEX_REWARDS_MEMBERS_FILE_DOWNLOAD = 'CRYPTEX_REWARDS_MEMBERS_FILE_DOWNLOAD',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { IDialogComponentDefinitions } from '@/shared/components/dialogProvider';
import { CryptexRewardsMembersFileDownloadDialog } from '../dialogs/cryptexRewardsMembersFileDownloadDialog';
import { CryptexRewardsDialogId } from './cryptexRewardsDialogId';

export const cryptexRewardsDialogsDefinitions: Record<
CryptexRewardsDialogId,
IDialogComponentDefinitions
> = {
[CryptexRewardsDialogId.CRYPTEX_REWARDS_MEMBERS_FILE_DOWNLOAD]: {
Component: CryptexRewardsMembersFileDownloadDialog,
},
};
Loading
Loading