forked from DA0-DA0/dao-dao-ui
-
Notifications
You must be signed in to change notification settings - Fork 1
/
actions.ts
340 lines (309 loc) · 11 KB
/
actions.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
// eslint-disable-next-line regex/invalid
import { Chain } from '@chain-registry/types'
import { ComponentType } from 'react'
import { FieldErrors } from 'react-hook-form'
import { TFunction } from 'react-i18next'
import {
ConfiguredChainContext,
IChainContext,
SupportedChainContext,
} from './chain'
import { CosmosMsgFor_Empty } from './contracts/common'
import { DaoInfo } from './dao'
import { AllGovParams } from './gov'
export enum ActionCategoryKey {
Authorizations = 'authorizations',
ChainGovernance = 'chainGovernance',
DaoAppearance = 'daoAppearance',
DaoGovernance = 'daoGovernance',
SmartContracting = 'smartContracting',
Treasury = 'treasury',
Nfts = 'nfts',
Press = 'press',
Advanced = 'advanced',
}
// TODO: Refactor adapter action key system, since a DAO may have multiple proposal modules of the same type, which would lead to duplicate keys.
export enum ActionKey {
Spend = 'spend',
CommunityPoolTransfer = 'communityPoolTransfer',
CommunityPoolDeposit = 'communityPoolDeposit',
ManageStaking = 'manageStaking',
ManageCw20 = 'manageCw20',
ManageCw721 = 'manageCw721',
CreateNftCollection = 'createNftCollection',
TransferNft = 'transferNft',
MintNft = 'mintNft',
BurnNft = 'burnNft',
ManageSubDaos = 'manageSubDaos',
UpdateInfo = 'updateInfo',
Instantiate = 'instantiate',
Instantiate2 = 'instantiate2',
Execute = 'execute',
Migrate = 'migrate',
UpdateAdmin = 'updateAdmin',
AuthzGrantRevoke = 'authzGrantRevoke',
AuthzExec = 'authzExec',
ValidatorActions = 'validatorActions',
Custom = 'custom',
BulkImport = 'bulkImport',
PerformTokenSwap = 'performTokenSwap',
WithdrawTokenSwap = 'withdrawTokenSwap',
ManageStorageItems = 'manageStorageItems',
GovernanceVote = 'governanceVote',
GovernanceProposal = 'governanceProposal',
GovernanceDeposit = 'governanceDeposit',
UpgradeV1ToV2 = 'upgradeV1ToV2',
EnableVestingPayments = 'enableVestingPayments',
EnableRetroactiveCompensation = 'enableRetroactiveCompensation',
DaoAdminExec = 'daoAdminExec',
EnableMultipleChoice = 'enableMultipleChoice',
ManageWidgets = 'manageWidgets',
FeeShare = 'feeShare',
ManageMembers = 'manageMembers',
Mint = 'mint',
UpdateMinterAllowance = 'updateMinterAllowance',
ManageVesting = 'manageVesting',
CreateCrossChainAccount = 'createCrossChainAccount',
CrossChainExecute = 'crossChainExecute',
UpdateStakingConfig = 'updateStakingConfig',
CreateIca = 'createIca',
IcaExecute = 'icaExecute',
ManageIcas = 'manageIcas',
// DaoProposalSingle
UpdatePreProposeSingleConfig = 'updatePreProposeSingleConfig',
UpdateProposalSingleConfig = 'updateProposalSingleConfig',
// DaoProposalMultiple
UpdatePreProposeMultipleConfig = 'updatePreProposeMultipleConfig',
UpdateProposalMultipleConfig = 'updateProposalMultipleConfig',
// Press
CreatePost = 'createPost',
UpdatePost = 'updatePost',
DeletePost = 'deletePost',
}
export type CategorizedActionAndData = {
category: ActionCategoryWithLabel
action: Action
data: any
}
export type PartialCategorizedActionAndData = Partial<CategorizedActionAndData>
export type CategorizedActionKeyAndData = {
// Add an ID field to prevent unnecessary re-renders when things move around.
// This should be handled by react-hook-form's `useFieldArray`, but it only
// works internally for that specific hook call, and we need to use it in many
// different components.
_id: string
categoryKey: ActionCategoryKey
actionKey: ActionKey
data: any
}
export type PartialCategorizedActionKeyAndDataNoId = Partial<
Omit<CategorizedActionKeyAndData, '_id'>
>
export type PartialCategorizedActionKeyAndData =
PartialCategorizedActionKeyAndDataNoId &
Pick<CategorizedActionKeyAndData, '_id'>
export type CategorizedAction = {
category: ActionCategoryWithLabel
action: Action
}
// A component which will render an action's input form.
export type ActionComponentProps<O = undefined, D = any> = {
fieldNamePrefix: string
allActionsWithData: PartialCategorizedActionKeyAndData[]
index: number
data: D
} & (
| {
isCreating: true
errors: FieldErrors
// Adds a new action to the form.
addAction: (
action: Partial<PartialCategorizedActionKeyAndData>,
// If omitted, the action will be appened to the end of the list.
insertIndex?: number
) => void
// Removes this action from the form.
remove: () => void
}
| {
isCreating: false
errors?: undefined
addAction?: undefined
remove?: undefined
}
) &
(O extends undefined ? {} : { options: O })
// eslint-disable-next-line regex/invalid
export type ActionComponent<O = undefined, D = any> = ComponentType<
ActionComponentProps<O, D>
>
/**
* A hook that returns the default values for an action. If it returns an error,
* the action should not be added because some critical data failed to load. If
* it returns undefined, the action is loading and should not allowed to be
* added until the default values are loaded.
*/
export type UseDefaults<D extends {} = any> = () => D | Error | undefined
export type UseTransformToCosmos<D extends {} = any> = () => (
data: D
) => CosmosMsgFor_Empty | undefined
export interface DecodeCosmosMsgNoMatch {
match: false
data?: never
}
export interface DecodeCosmosMsgMatch<D extends {} = any> {
match: true
data: D
}
export type UseDecodedCosmosMsg<D extends {} = any> = (
msg: Record<string, any>
) => DecodeCosmosMsgNoMatch | DecodeCosmosMsgMatch<D>
export type UseHideFromPicker = () => boolean
export interface Action<Data extends {} = any, Options extends {} = any> {
key: ActionKey
Icon: ComponentType
label: string
description: string
// Optional keywords to improve search results.
keywords?: string[]
Component: ActionComponent<Options, Data>
// This determines if the action should be hidden from creation. If true, the
// action will not be shown in the list of actions to create, but it will
// still match and render in existing contexts. This is used to conditionally
// show the upgrade actions while still allowing them to render in existing
// proposals and be added programmatically during creation.
hideFromPicker?: boolean
// Whether or not this action is reusable. Defaults to false. If true, when
// editing the action, the add and remove button in the group will be removed,
// and the action will be hidden from future category picker selections. Some
// actions, like 'Spend', make sense to use multiple times, while others, like
// 'Update Info' or any configuration updater, should only be used once at a
// time. We should prevent users from adding multiple of these actions.
notReusable?: boolean
// Programmatic actions cannot be chosen or removed by the user. This is used
// for actions should only be controlled by code. The user should not be able
// to modify it at all, which also means the user cannot pick this action or
// go back to the category action picker. This includes both `hideFromPicker`
// and `notReusable`, while also preventing the user from going back to the
// category action picker or removing the action.
programmaticOnly?: boolean
// Hook to get default fields for form display.
useDefaults: UseDefaults<Data>
// Hook to make function to convert action data to CosmosMsgFor_Empty.
useTransformToCosmos: UseTransformToCosmos<Data>
// Hook to make function to convert decoded msg to form display fields.
useDecodedCosmosMsg: UseDecodedCosmosMsg<Data>
// Hook to determine whether or not the action should be hidden from the
// creation picker. If true, the action will not be shown in the list of
// actions to create, but it will still match and render in existing contexts.
// This is a hook version of the `hideFromPicker` option above. If either is
// true, it will be hidden.
useHideFromPicker?: UseHideFromPicker
}
export type ActionCategory = {
// If many categories exist with the same key, they will be merged. The first
// defined label and description will be used. This allows additional modules
// to add actions to the same category without changing any metadata.
key: ActionCategoryKey
label?: string
description?: string
// Optional keywords to improve search results.
keywords?: string[]
actions: Action[]
}
export type ActionCategoryWithLabel = Omit<ActionCategory, 'label'> & {
label: string
}
export enum ActionContextType {
Dao = 'dao',
Wallet = 'wallet',
// x/gov chain governance
Gov = 'gov',
}
export type ActionContext =
| {
type: ActionContextType.Dao
info: DaoInfo
}
| {
type: ActionContextType.Wallet
}
| {
type: ActionContextType.Gov
params: AllGovParams
}
export enum ActionChainContextType {
/**
* Any chain, not configured.
*/
Any = 'any',
/**
* Configured chain, no DAO DAO deployment.
*/
Configured = 'configured',
/**
* Supported chain with a DAO DAO deployment.
*/
Supported = 'supported',
}
export type ActionChainContext =
| ({
type: ActionChainContextType.Any
} & IChainContext)
| ({
type: ActionChainContextType.Configured
} & ConfiguredChainContext)
| ({
type: ActionChainContextType.Supported
} & SupportedChainContext)
export type ActionOptions<ExtraOptions extends {} = {}> = ExtraOptions & {
t: TFunction
chain: Chain
chainContext: ActionChainContext
// The address of the sender/actor.
// DAO core address if context.type === Dao
// Wallet address if context.type === Wallet
// x/gov module address if context.type === Gov
address: string
context: ActionContext
}
export type ActionMaker<Data extends {} = any, ExtraOptions extends {} = {}> = (
options: ActionOptions<ExtraOptions>
) => Action<Data> | null
// A category maker can return null to indicate that the category should not be
// included. It can also return either actions, action makers, or both. This is
// convience to avoid every category maker needing the same boilerplate action
// maker code. `actionMakers` will be made into actions and merged with
// `actions`. If no actions exist after all are made, the category will be
// ignored.
export type ActionCategoryMaker<ExtraOptions extends {} = {}> = (
options: ActionOptions<ExtraOptions>
) =>
| (Omit<ActionCategory, 'actions'> & {
actions?: Action[]
actionMakers?: ActionMaker[]
})
| null
// React context/provider system for actions.
export type IActionsContext = {
options: ActionOptions
categories: ActionCategoryWithLabel[]
}
export type UseActionsOptions = {
// If true, the actions will be filtered to only include those which are
// allowed to be created. This is used to hide the upgrade actions from the
// list of actions to create.
isCreating?: boolean
}
export type LoadedAction = {
category: ActionCategoryWithLabel
action: Action
transform: ReturnType<UseTransformToCosmos>
defaults: ReturnType<UseDefaults>
}
export type LoadedActions = Partial<Record<ActionKey, LoadedAction>>
export type NestedActionsEditorFormData = {
msgs: CosmosMsgFor_Empty[]
// Internal action data so that errors are added to main form.
_actionData?: PartialCategorizedActionKeyAndData[]
}