import { CSSProperties, ComponentType, ReactNode } from 'react' import { FieldPath, FieldValues } from 'react-hook-form' import { RecoilValueReadOnly } from 'recoil' import { ActionKeyAndData, ActionMaker } from './actions' import { AnyChain } from './chain' import { IProposalModuleBase } from './clients' import { DaoInfoCard, LinkWrapperProps, SelfRelayExecuteModalProps, } from './components' import { Expiration } from './contracts' import { CheckedDepositInfo, Duration, ProposalStatus, UnifiedCosmosMsg, } from './contracts/common' import { MultipleChoiceOptions, MultipleChoiceVote, } from './contracts/DaoProposalMultiple' import { Vote as SingleChoiceVote } from './contracts/DaoProposalSingle.v2' import { DaoCreationGetInstantiateInfo, DaoCreationVotingConfigItem, ProposalDraft, } from './dao' import { LoadingData, LoadingDataWithError } from './misc' import { ProposalCreatedCardProps, ProposalExecutionMetadata, ProposalTimestampInfo, } from './proposal' import { IQueryClient } from './query' export type IProposalModuleAdapterCommon = { // Fields fields: { // Make this a function so it doesn't return the same instance of the form // data each time. makeDefaultNewProposalForm: () => FormData newProposalFormTitleKey: FieldPath updateConfigActionMaker: ActionMaker updatePreProposeConfigActionMaker?: ActionMaker } // Selectors selectors: { proposalCount: ProposalCountSelector reverseProposalInfos: ReverseProposalInfosSelector reversePreProposePendingProposalInfos?: ReversePreProposePendingProposalInfosSelector reversePreProposeCompletedProposalInfos?: ReversePreProposeCompletedProposalInfosSelector depositInfo: DepositInfoSelector maxVotingPeriod: MaxVotingPeriodSelector } // Hooks hooks: { useProposalDaoInfoCards: () => DaoInfoCard[] } // Components components: { NewProposal: ComponentType } } export type IProposalModuleAdapter< Vote extends unknown = any, ApprovalProposal extends unknown = any, > = { // Functions functions: { getProposalInfo: () => Promise } // Hooks hooks: { useProposalRefreshers: () => ProposalRefreshers useLoadingProposalExecutionTxHash: () => LoadingDataWithError useLoadingProposalStatus: () => LoadingData<{ status: ProposalStatus isVotingOpen: boolean }> useLoadingVoteOptions: () => LoadingData[]> // Return when no wallet connected. useLoadingWalletVoteInfo: () => | undefined | LoadingData> useCastVote: (onSuccess?: () => void | Promise) => { castVote: (vote: Vote) => Promise castingVote: boolean } useLoadingApprovalProposal: () => LoadingData< ApprovalProposalWithMetadata | undefined > } // Components components: { ProposalStatusAndInfo: ComponentType ProposalVoter: ComponentType ProposalInnerContentDisplay: ComponentType ProposalWalletVote: ComponentType> ProposalVotes: ComponentType ProposalVoteTally: ComponentType ProposalLine: ComponentType ApprovalProposalStatusAndInfo?: ComponentType ApprovalProposalInnerContentDisplay?: ComponentType ApprovalProposalLine?: ComponentType } } export type ProposalModuleAdapter< DaoCreationExtraVotingConfig extends FieldValues = any, Vote extends unknown = any, FormData extends FieldValues = any, ApprovalProposal extends unknown = any, > = { id: string contractNames: string[] loadCommon: ( options: IProposalModuleAdapterCommonOptions ) => IProposalModuleAdapterCommon load: ( options: IProposalModuleAdapterOptions ) => IProposalModuleAdapter queries: { proposalCount: { indexerFormula?: string cosmWasmQuery: Record } } daoCreation: { // Voting config added to the common voting config. extraVotingConfig?: { default: DaoCreationExtraVotingConfig items?: DaoCreationVotingConfigItem[] advancedItems?: DaoCreationVotingConfigItem[] advancedWarningI18nKeys?: string[] } getInstantiateInfo: DaoCreationGetInstantiateInfo } } export type IProposalModuleAdapterCommonOptions = { proposalModule: IProposalModuleBase } export type IProposalModuleAdapterOptions = { /** * The query client. */ queryClient: IQueryClient /** * The DAO's native chain. */ chain: AnyChain /** * The DAO's core contract address. */ coreAddress: string /** * The proposal module. */ proposalModule: IProposalModuleBase /** * The proposal ID unique across all proposal modules. They include the * proposal module's prefix, the proposal number within the proposal module, * and potentially an asterisk in the middle to indicate a * pre-propose-approval proposal. */ proposalId: string /** * The proposal number used by the proposal module to identify this proposal. */ proposalNumber: number /** * Whether or not this refers to a pre-propose-approval proposal. If this is * true, the proposal ID should contain an asterisk (*) between the proposal * module prefix and proposal number. */ isApprovalProposal: boolean } export type IProposalModuleAdapterInitialOptions = Omit< IProposalModuleAdapterOptions, 'proposalModule' | 'proposalId' | 'proposalNumber' | 'isApprovalProposal' > /** * Proposal module adapter context. This is specific to a single proposal in a * single proposal module. */ export type IProposalModuleContext = { id: string options: IProposalModuleAdapterOptions adapter: IProposalModuleAdapter common: IProposalModuleAdapterCommon proposalModule: IProposalModuleBase } /** * Common proposal module adapter context. This is not specific to any single * proposal, but is specific to a single proposal module. This could be used to * retrieve the current proposal module's config for example. */ export type IProposalModuleCommonContext = { id: string options: IProposalModuleAdapterCommonOptions common: IProposalModuleAdapterCommon } // Internal Adapter Types export type ReverseProposalInfosSelector = (data: { startBefore: number | undefined limit: number | undefined }) => RecoilValueReadOnly export type ProposalCountSelector = RecoilValueReadOnly export type ReversePreProposePendingProposalInfosSelector = (data: { startBefore: number | undefined limit: number | undefined }) => RecoilValueReadOnly export type ReversePreProposeCompletedProposalInfosSelector = (data: { startBefore: number | undefined limit: number | undefined }) => RecoilValueReadOnly export type DepositInfoSelector = RecoilValueReadOnly< CheckedDepositInfo | undefined > export type MaxVotingPeriodSelector = RecoilValueReadOnly export type CommonProposalListInfo = { id: string proposalNumber: number timestamp: Date | undefined status: ProposalStatus /** * Whether or not this proposal is executable early when in veto timelock. */ executableEarly?: boolean // If true, will be not be shown in the proposal list. This is used for // example to hide completed pre-propose proposals that were approved, since // those show up as normal proposals. No need to double count. hideFromList?: boolean } export type CommonProposalInfo = { id: string title: string description: string expiration: Expiration | null createdAtEpoch: number | null createdByAddress: string } export type BaseProposalStatusAndInfoProps = { inline?: boolean // Open self-relay modal to execute a proposal and relay IBC packets. openSelfRelayExecute: ( props: Pick< SelfRelayExecuteModalProps, 'uniqueId' | 'chainIds' | 'crossChainPackets' | 'transaction' > ) => void onExecuteSuccess: () => void | Promise onCloseSuccess: () => void | Promise onVetoSuccess: () => void | Promise } & { voter: BaseProposalVoterProps } export type BaseProposalVoterProps = { onVoteSuccess: () => void | Promise } export type BaseProposalVotesProps = { /** * An optional class name. */ className?: string } export type BasePreProposeProposalStatusAndInfoProps = Pick< BaseProposalStatusAndInfoProps, 'inline' > export type BaseProposalInnerContentDisplayProps< FormData extends FieldValues = any, > = { // Once proposal messages are loaded, the inner component is responsible for // setting the duplicate form data for the duplicate button in the header. setDuplicateFormData?: (data: FormData) => void } export type BaseApprovalProposalInnerContentDisplayProps = BaseProposalInnerContentDisplayProps export type BaseProposalWalletVoteProps = { vote: T | undefined fallback: 'pending' | 'hasNoVote' } export type BaseProposalLineProps = { href: string onClick?: () => void openInNewTab?: boolean LinkWrapper: ComponentType } export type BaseNewProposalProps = { onCreateSuccess: (props: ProposalCreatedCardProps) => void draft?: ProposalDraft saveDraft: () => void drafts: ProposalDraft[] loadDraft?: (index: number) => void unloadDraft: () => void draftSaving: boolean deleteDraft: (index: number) => void proposalModuleSelector: ReactNode // If true, will display actions as read only. This is useful when prompting a // proposal to be created from preset actions. Default: false. actionsReadOnlyMode?: boolean /** * The ProposalDaoInfoCards stateful component that renders the proposal * module's config cards. */ ProposalDaoInfoCards: ComponentType } export type WalletVoteInfo = { // Present if voted. vote: T | undefined couldVote: boolean canVote: boolean /** * Whether or not the wallet is registered as a delegate. */ isDelegate: boolean /** * The voting power percentage of the total voting power. Includes all voting * power (both individual and unvoted delegated). */ votingPowerPercent: number /** * The voting power owned by the voter as a percentage of the total voting * power. Excludes delegated power. * * Since delegations were added in v2.7.0, this equals `votingPowerPercent` * for earlier versions. */ individualVotingPowerPercent: number /** * The voting power delegated to the voter by other members of the DAO who * have not yet voted on the proposal as a percentage of the total voting * power. * * Since delegations were added in v2.7.0, this is 0 for earlier versions. */ unvotedDelegatedVotingPowerPercent: number } export type ProposalRefreshers = { refreshProposalId: number refreshProposal: () => void refreshProposalAndAll: () => void refreshing: boolean } export type ProposalVoteOption = { Icon: ComponentType<{ className: string; style?: CSSProperties }> label: string value: Vote color?: string } export type PercentOrMajorityValue = { /** * Whether or not to use majority instead of percent. */ majority: boolean /** * The percent to use when `majority` is false. */ value: number } export type ApprovalProposalWithMetadata< ApprovalProposal extends unknown = any, > = ApprovalProposal & { timestampDisplay: ProposalTimestampInfo['display'] // If this pre-propose-approval proposal is being approved by a // pre-propose-approver proposal in another DAO, this is the approval // proposal ID. approverProposalId?: string } export type SingleChoiceNewProposalForm = { title: string description: string actionData: ActionKeyAndData[] metadata?: ProposalExecutionMetadata vote?: SingleChoiceVote } export type SingleChoiceNewProposalData = { title: string description: string msgs: UnifiedCosmosMsg[] vote?: SingleChoiceVote } export type MultipleChoiceOptionFormData = { title: string description: string actionData: ActionKeyAndData[] metadata?: ProposalExecutionMetadata } export type MultipleChoiceNewProposalForm = { title: string description: string choices: MultipleChoiceOptionFormData[] vote?: MultipleChoiceVote } export type MultipleChoiceNewProposalData = { title: string description: string choices: MultipleChoiceOptions vote?: MultipleChoiceVote }