Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Become SubDAO #1779

Merged
merged 10 commits into from
Jul 18, 2024
Merged
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
13 changes: 13 additions & 0 deletions packages/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
"rebalancerLabel": "Rebalancer",
"smartContractingDescription": "Create, execute, and manage smart contracts.",
"smartContractingLabel": "Smart Contracting",
"subDaosDescription": "Manage SubDAOs or become a SubDAO.",
"subDaosLabel": "SubDAOs",
"treasuryDescription_dao": "Manage this DAO's treasury (for example, send payments, stake or swap tokens, etc.).",
"treasuryDescription_gov": "Manage the community pool (for example, send payments, stake or swap tokens, etc.).",
"treasuryDescription_wallet": "Manage your tokens (for example, send payments, stake or swap tokens, etc.).",
Expand Down Expand Up @@ -315,6 +317,7 @@
"cameraWithFlash": "Camera with flash",
"chains": "Chains",
"chart": "Chart",
"check": "Check",
"clock": "Clock",
"closedLockWithKey": "Closed lock with key",
"computerDisk": "Computer disk",
Expand Down Expand Up @@ -537,6 +540,7 @@
"unsupportedWallet": "Unsupported wallet. Please use a different wallet."
},
"form": {
"acceptSubDaoAddressInputLabel": "SubDAO to accept",
"addAnImage": "Add an image",
"address": "Address",
"addressInputPlaceholder_any": "Search profile or DAO name or enter an address...",
Expand Down Expand Up @@ -567,6 +571,7 @@
"automaticallyAddTokensTitle": "Automatically add tokens",
"automaticallyAddTokensTooltip": "Should tokens sent to the DAO get added to the treasury?",
"baseToken": "Base token",
"becomeSubDaoAdminInputLabel": "New parent DAO",
"blocksToPauseFor": "Blocks to pause for",
"buttonLabel": "Button label",
"calls": "Calls",
Expand Down Expand Up @@ -895,6 +900,9 @@
},
"info": {
"abstainVote": "Abstain",
"acceptSubDaoActionDescription": "Accept a pending SubDAO's ownership transfer request. Be sure that the SubDAO-to-be has already requested to join your DAO through the \"Become SubDAO\" action.",
"acceptSubDaoActionOtherActionsAdded": "Other actions that are required to register a new SubDAO have been added to the proposal automatically.",
"acceptSubDaoDescription": "Accept a DAO's request to become a SubDAO.",
"acknowledgeServiceFee": "We acknowledge that {{fee}} will be charged from the treasury for registration to the rebalancer.",
"acknowledgeServiceFee_none": "We acknowledge that there is no fee to register for the rebalancer.",
"actionLibraryDescription": "Choose one or more actions to perform upon execution.",
Expand Down Expand Up @@ -944,6 +952,9 @@
"available": "available",
"availableBalance": "Available balance",
"availableHistoryLoaded": "All available history has been loaded.",
"becomeSubDaoActionDescription": "Provide the address of a DAO to request to become its SubDAO. The DAO will then need to accept your request to complete the ownership transfer and become your parent. Once the transfer is complete, only your new parent DAO can transfer ownership in the future. Keep in mind that your parent DAO will have full authority to execute anything it wants to on behalf of this DAO.",
"becomeSubDaoActionDescription_created": "The parent DAO needs to accept this request to complete the ownership transfer and become your parent. Once the transfer is complete, only your new parent DAO can transfer ownership in the future. Keep in mind that your parent DAO will have full authority to execute anything it wants to on behalf of this DAO.",
"becomeSubDaoDescription": "Request to become a SubDAO of a DAO.",
"betterNonPfmIbcPathAvailable": "A better path exists that unwinds the IBC token properly, but it requires hops that cannot be performed in a single proposal due to the configuration of one or more of the chains involved. You may safely use the direct path above, though it likely results in an undesirable IBC token on the final chain that will eventually need to be unwound. If you wish to unwind it manually, follow the path below by creating an individual proposal for the first hop, then a second proposal once the first one is passed and executed, and so on.",
"betterPfmIbcPathAvailable_one": "A better path exists that unwinds the IBC token properly, but it requires you to have an account on {{chains}}. An account must exist on each intermediary chain in a multi-hop transaction to act as a failsafe in the event of catastrophic failure (unlikely). Either a cross-chain or ICA account works and will be automatically detected once created. You may safely use the direct path above, though it likely results in an undesirable IBC token on the final chain that will eventually need to be unwound.",
"betterPfmIbcPathAvailable_other": "A better path exists that unwinds the IBC token properly, but it requires you to have accounts on the following chains: {{chains}}. An account must exist on each intermediary chain in a multi-hop transaction to act as a failsafe in the event of catastrophic failure (unlikely). Either a cross-chain or ICA account works and will be automatically detected once created. You may safely use the direct path above, though it likely results in an undesirable IBC token on the final chain that will eventually need to be unwound.",
Expand Down Expand Up @@ -1564,6 +1575,7 @@
"title": {
"404": "404: Not Found",
"500": "500: Error",
"acceptSubDao": "Accept SubDAO Request",
"accepted": "Accepted",
"acceptingSubmissions": "Accepting submissions",
"account": "Account",
Expand All @@ -1590,6 +1602,7 @@
"averages": "Averages",
"balance": "Balance",
"balances": "Balances",
"becomeSubDao": "Become SubDAO",
"beforeYouEnter": "Before you enter",
"beginVesting": "Begin vesting",
"blockCompleted": "Block completed",
Expand Down
2 changes: 0 additions & 2 deletions packages/stateful/actions/core/dao_governance/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { makeDaoAdminExecAction } from './DaoAdminExec'
import { makeEnableMultipleChoiceAction } from './EnableMultipleChoice'
import { makeManageStorageItemsAction } from './ManageStorageItems'
import { makeManageSubDaoPauseAction } from './ManageSubDaoPause'
import { makeManageSubDaosAction } from './ManageSubDaos'
import { makeManageVetoableDaosAction } from './ManageVetoableDaos'
import { makeNeutronOverruleSubDaoProposalAction } from './NeutronOverruleSubDaoProposal'
import { makeSetUpApproverAction } from './SetUpApprover'
Expand All @@ -26,7 +25,6 @@ export const makeDaoGovernanceActionCategory: ActionCategoryMaker = ({
}),
actionMakers: [
makeEnableMultipleChoiceAction,
makeManageSubDaosAction,
makeManageStorageItemsAction,
makeDaoAdminExecAction,
makeUpgradeV1ToV2Action,
Expand Down
2 changes: 2 additions & 0 deletions packages/stateful/actions/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { makeDaoAppearanceActionCategory } from './dao_appearance'
import { makeDaoGovernanceActionCategory } from './dao_governance'
import { makeManageNftsActionCategory } from './nfts'
import { makeSmartContractingActionCategory } from './smart_contracting'
import { makeSubDaosActionCategory } from './subdaos'
import { makeTreasuryActionCategory } from './treasury'
import { makeValenceActionCategory } from './valence'

Expand All @@ -23,6 +24,7 @@ export const getCoreActionCategoryMakers = (): ActionCategoryMaker[] => [
makeCommonlyUsedCategory,
makeTreasuryActionCategory,
makeDaoGovernanceActionCategory,
makeSubDaosActionCategory,
makeDaoAppearanceActionCategory,
makeManageNftsActionCategory,
makeSmartContractingActionCategory,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
import { useActionOptions } from '../../../react'
import { UpdateAdminComponent as StatelessUpdateAdminComponent } from './Component'

interface UpdateAdminData {
export type UpdateAdminData = {
chainId: string
contract: string
newAdmin: string
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ComponentMeta, ComponentStory } from '@storybook/react'

import { ReactHookFormDecorator } from '@dao-dao/storybook'

import { AddressInput } from '../../../../components'
import { AcceptSubDaoComponent } from './Component'

export default {
title:
'DAO DAO / packages / stateful / actions / core / subdaos / AcceptSubDao',
component: AcceptSubDaoComponent,
decorators: [ReactHookFormDecorator],
} as ComponentMeta<typeof AcceptSubDaoComponent>

const Template: ComponentStory<typeof AcceptSubDaoComponent> = (args) => (
<AcceptSubDaoComponent {...args} />
)

export const Default = Template.bind({})
Default.args = {
fieldNamePrefix: '',
isCreating: true,
errors: {},
options: {
AddressInput,
},
}
181 changes: 181 additions & 0 deletions packages/stateful/actions/core/subdaos/AcceptSubDao/Component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import { ComponentType, useEffect, useState } from 'react'
import { useFormContext } from 'react-hook-form'
import { useTranslation } from 'react-i18next'

import { InputErrorMessage, InputLabel } from '@dao-dao/stateless'
import { AddressInputProps } from '@dao-dao/types'
import { ActionComponent, ActionKey } from '@dao-dao/types/actions'
import {
isValidBech32Address,
makeValidateAddress,
validateRequired,
} from '@dao-dao/utils'

import { useActionOptions } from '../../../react'
import { DaoAdminExecData } from '../../dao_governance/DaoAdminExec/Component'
import { UpdateAdminData } from '../../smart_contracting/UpdateAdmin'
import { ManageSubDaosData } from '../ManageSubDaos/Component'

export type AcceptSubDaoData = {
address: string
}

type AcceptSubDaoDataOptions = {
AddressInput: ComponentType<AddressInputProps<AcceptSubDaoData>>
}

export const AcceptSubDaoComponent: ActionComponent<
AcceptSubDaoDataOptions,
AcceptSubDaoData
> = ({
fieldNamePrefix,
errors,
isCreating,
allActionsWithData,
index,
addAction,
options: { AddressInput },
}) => {
const { t } = useTranslation()
const {
address: currentAddress,
chain: { chain_id: chainId, bech32_prefix: bech32Prefix },
} = useActionOptions()

const { watch, register, setValue, getValues } =
useFormContext<AcceptSubDaoData>()

const addressFieldName = (fieldNamePrefix + 'address') as 'address'

const address = watch(addressFieldName)
const isValid = !!address && isValidBech32Address(address, bech32Prefix)
const [otherActionsAdded, setOtherActionsAdded] = useState(false)
useEffect(() => {
if (!isCreating || !isValid) {
return
}

const existingUpdateAdminIndex = allActionsWithData.findIndex(
(a, i) =>
i > index &&
a.actionKey === ActionKey.DaoAdminExec &&
(a.data as DaoAdminExecData)?._actionData?.length === 1 &&
(a.data as DaoAdminExecData)._actionData![0].actionKey ===
ActionKey.UpdateAdmin &&
(a.data as DaoAdminExecData)._actionData![0].data.newAdmin ===
currentAddress
)
const existingManageSubDaosIndex = allActionsWithData.findIndex(
(a, i) => i > index && a.actionKey === ActionKey.ManageSubDaos
)

if (existingUpdateAdminIndex === -1) {
addAction(
{
actionKey: ActionKey.DaoAdminExec,
data: {
coreAddress: address,
_actionData: [
{
actionKey: ActionKey.UpdateAdmin,
data: {
chainId,
contract: address,
newAdmin: currentAddress,
} as UpdateAdminData,
},
],
} as DaoAdminExecData,
},
// After current action.
index + 1
)
} else {
// Path to the address field on the update admin sub-action of the DAO
// admin exec action.
const existingAddressFieldName = fieldNamePrefix.replace(
new RegExp(`${index}\\.data.$`),
`${existingUpdateAdminIndex}.data._actionData.0.data.contract`
)

// If the address isn't correct, update the existing one.
if (getValues(existingAddressFieldName as any) !== address) {
setValue(existingAddressFieldName as any, address)
}
}

if (existingManageSubDaosIndex === -1) {
addAction(
{
actionKey: ActionKey.ManageSubDaos,
data: {
toAdd: [
{
addr: address,
},
],
toRemove: [],
} as ManageSubDaosData,
},
// After DAO admin exec / update admin action.
index + 2
)
} else {
// Path to the address field on the manage subDAOs action.
const existingAddressFieldName = fieldNamePrefix.replace(
new RegExp(`${index}\\.data.$`),
`${existingManageSubDaosIndex}.data.toAdd.0.addr`
)

// If the address isn't correct, update the existing one.
if (getValues(existingAddressFieldName as any) !== address) {
setValue(existingAddressFieldName as any, address)
}
}

setOtherActionsAdded(true)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
isCreating,
isValid,
address,
index,
addAction,
currentAddress,
chainId,
fieldNamePrefix,
getValues,
setValue,
])

return (
<>
<div className="space-y-3">
{isCreating && (
<p className="max-w-prose">
{t('info.acceptSubDaoActionDescription')}
</p>
)}

<div className="space-y-1">
<InputLabel name={t('form.acceptSubDaoAddressInputLabel')} />
<AddressInput
disabled={!isCreating}
error={errors?.address}
fieldName={addressFieldName}
register={register}
type="contract"
validation={[validateRequired, makeValidateAddress(bech32Prefix)]}
/>
<InputErrorMessage error={errors?.address} />
</div>

{otherActionsAdded && (
<p className="max-w-prose">
{t('info.acceptSubDaoActionOtherActionsAdded')}
</p>
)}
</div>
</>
)
}
20 changes: 20 additions & 0 deletions packages/stateful/actions/core/subdaos/AcceptSubDao/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# AcceptSubDao

Accept SubDAO join request and complete its ownership transfer.

## Bulk import format

This is relevant when bulk importing actions, as described in [this
guide](https://github.com/DA0-DA0/dao-dao-ui/wiki/Bulk-importing-actions).

### Key

`acceptSubDao`

### Data format

```json
{
"address": "<SUBDAO ADDRESS>"
}
```
Loading
Loading