Skip to content

Commit

Permalink
combine configure rebalancer and create valence account action
Browse files Browse the repository at this point in the history
  • Loading branch information
NoahSaso committed Nov 5, 2024
1 parent 31119cf commit e02cd02
Show file tree
Hide file tree
Showing 11 changed files with 591 additions and 197 deletions.
1 change: 1 addition & 0 deletions packages/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@
"insufficientBalance": "Insufficient balance of {{amount}} ${{tokenSymbol}}.",
"insufficientFunds": "Insufficient funds.",
"insufficientFundsWarning": "You currently have {{amount}} ${{tokenSymbol}}, which may not be sufficient unless another action transfers funds to the DAO before this one.",
"insufficientFundsWarningMinusServiceFee": "You currently have {{amount}} ${{tokenSymbol}} available (accounting for the service fee), which may not be sufficient unless another action transfers funds to the DAO before this one.",
"insufficientWalletBalance": "Insufficient wallet balance of {{amount}} ${{tokenSymbol}}.",
"invalidAccount": "At least one of the specified accounts is invalid.",
"invalidActionKeys": "Invalid action keys found: {{keys}}",
Expand Down
10 changes: 9 additions & 1 deletion packages/state/recoil/selectors/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ export const allBalancesSelector = selectorFamily<
includeAccountTypes?: AccountType[]
// Exclude these account types.
excludeAccountTypes?: AccountType[]
// Include only these chain IDs.
includeChainIds?: string[]
}>
>({
key: 'allBalances',
Expand All @@ -94,6 +96,7 @@ export const allBalancesSelector = selectorFamily<
ignoreStaked,
includeAccountTypes,
excludeAccountTypes = [AccountType.Valence],
includeChainIds,
}) =>
({ get }) => {
const allAccounts = get(
Expand All @@ -102,10 +105,15 @@ export const allBalancesSelector = selectorFamily<
address: mainAddress,
includeIcaChains,
})
).filter(({ type }) => {
).filter(({ chainId, type }) => {
if (includeChainIds && !includeChainIds.includes(chainId)) {
return false
}

if (includeAccountTypes) {
return includeAccountTypes.includes(type)
}

if (excludeAccountTypes) {
return !excludeAccountTypes.includes(type)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ export default {
component: ConfigureRebalancerComponent,
decorators: [
makeReactHookFormDecorator<ConfigureRebalancerData>({
newValenceAccount: {
creating: true,
funds: [],
},
chainId: ChainId.NeutronMainnet,
baseDenom: getNativeIbcUsdc(ChainId.NeutronMainnet)!.denomOrAddress,
tokens: [
Expand Down Expand Up @@ -98,6 +102,54 @@ Default.args = {
},
],
},
serviceFee: {
loading: false,
errored: false,
data: null,
},
currentValenceBalances: {
loading: false,
data: [
{
token: getNativeTokenForChainId(ChainId.NeutronMainnet),
balance: '46252349169321',
},
{
token: {
chainId: ChainId.NeutronMainnet,
type: TokenType.Native,
denomOrAddress: getNativeIbcUsdc(ChainId.NeutronMainnet)!
.denomOrAddress,
decimals: 6,
symbol: 'USDC',
imageUrl: '',
source: {
chainId: ChainId.NeutronMainnet,
type: TokenType.Native,
denomOrAddress: getNativeIbcUsdc(ChainId.NeutronMainnet)!
.denomOrAddress,
},
},
balance: '102948124125',
},
{
token: {
chainId: ChainId.NeutronMainnet,
type: TokenType.Native,
denomOrAddress: 'uatom',
decimals: 6,
symbol: 'ATOM',
imageUrl: '',
source: {
chainId: ChainId.NeutronMainnet,
type: TokenType.Native,
denomOrAddress: 'uatom',
},
},
balance: '1284135723893',
},
],
},
denomWhitelistTokens: {
loading: false,
data: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import { HugeDecimal } from '@dao-dao/math'
import {
Button,
ErrorPage,
FormCheckbox,
IconButton,
InputErrorMessage,
InputLabel,
Loader,
MarkdownRenderer,
NativeCoinSelector,
NumericInput,
RebalancerProjector,
RebalancerProjectorAsset,
Expand All @@ -29,12 +31,12 @@ import {
GenericTokenWithUsdPrice,
LoadingData,
LoadingDataWithError,
ValenceAccount,
} from '@dao-dao/types'
import { ActionComponent } from '@dao-dao/types/actions'
import { TargetOverrideStrategy } from '@dao-dao/types/contracts/ValenceRebalancer'
import {
formatPercentOf100,
getNativeTokenForChainId,
makeValidateAddress,
validatePositive,
validateRequired,
Expand Down Expand Up @@ -68,10 +70,17 @@ export const pidPresets: {
]

export type ConfigureRebalancerData = {
// Will be set when a valence account is found so the transformation function
// has the address.
valenceAccount?: ValenceAccount
chainId: string
newValenceAccount: {
creating: boolean
funds: {
denom: string
amount: string
// Will multiply `amount` by 10^decimals when generating the message.
decimals: number
}[]
acknowledgedServiceFee?: boolean
}
trustee?: string
baseDenom: string
tokens: {
Expand All @@ -97,6 +106,8 @@ export type ConfigureRebalancerData = {

export type ConfigureRebalancerOptions = {
nativeBalances: LoadingData<GenericTokenBalance[]>
serviceFee: LoadingDataWithError<GenericTokenBalance | null>
currentValenceBalances: LoadingData<GenericTokenBalance[]>
baseDenomWhitelistTokens: LoadingData<GenericToken[]>
denomWhitelistTokens: LoadingData<GenericToken[]>
prices: LoadingDataWithError<GenericTokenWithUsdPrice[]>
Expand All @@ -112,6 +123,8 @@ export const ConfigureRebalancerComponent: ActionComponent<
isCreating,
options: {
nativeBalances,
serviceFee,
currentValenceBalances,
baseDenomWhitelistTokens,
denomWhitelistTokens,
prices,
Expand All @@ -133,6 +146,7 @@ export const ConfigureRebalancerComponent: ActionComponent<
clearErrors,
setError,
} = useFormContext<ConfigureRebalancerData>()

const {
fields: tokensFields,
append: appendToken,
Expand All @@ -142,6 +156,17 @@ export const ConfigureRebalancerComponent: ActionComponent<
name: (fieldNamePrefix + 'tokens') as 'tokens',
})

const {
fields: newValenceAccountFunds,
append: appendNewValenceAccountFund,
remove: removeNewValenceAccountFund,
} = useFieldArray({
control,
name: (fieldNamePrefix +
'newValenceAccount.funds') as 'newValenceAccount.funds',
})

const chainId = watch((fieldNamePrefix + 'chainId') as 'chainId')
const baseDenom = watch((fieldNamePrefix + 'baseDenom') as 'baseDenom')
const targetOverrideStrategy = watch(
(fieldNamePrefix + 'targetOverrideStrategy') as 'targetOverrideStrategy'
Expand All @@ -152,6 +177,10 @@ export const ConfigureRebalancerComponent: ActionComponent<
const showCustomPid = watch(
(fieldNamePrefix + 'showCustomPid') as 'showCustomPid'
)
const creatingNewValenceAccount = !!watch(
(fieldNamePrefix +
'newValenceAccount.creating') as 'newValenceAccount.creating'
)

// Get selected whitelist tokens.
const denomWhitelistTokensSelected = denomWhitelistTokens.loading
Expand Down Expand Up @@ -198,8 +227,152 @@ export const ConfigureRebalancerComponent: ActionComponent<
preset.kp === pid.kp && preset.ki === pid.ki && preset.kd === pid.kd
)?.preset

const acknowledgedServiceFee = watch(
(fieldNamePrefix +
'newValenceAccount.acknowledgedServiceFee') as 'newValenceAccount.acknowledgedServiceFee'
)

useEffect(() => {
if (!isCreating) {
return
}

if (acknowledgedServiceFee) {
if (errors?.newValenceAccount?.acknowledgedServiceFee) {
clearErrors(
(fieldNamePrefix +
'newValenceAccount.acknowledgedServiceFee') as 'newValenceAccount.acknowledgedServiceFee'
)
}
} else {
if (!errors?.newValenceAccount?.acknowledgedServiceFee) {
setError(
(fieldNamePrefix +
'newValenceAccount.acknowledgedServiceFee') as 'newValenceAccount.acknowledgedServiceFee',
{
type: 'required',
message: t('error.acknowledgeServiceFee'),
}
)
}
}
}, [
isCreating,
acknowledgedServiceFee,
fieldNamePrefix,
clearErrors,
setError,
t,
errors?.newValenceAccount?.acknowledgedServiceFee,
])

return (
<>
{creatingNewValenceAccount && (
<div className="flex flex-col gap-2 mb-2">
<InputLabel name={t('form.initialBalances')} primary />

{newValenceAccountFunds.map(({ id }, index) => (
<NativeCoinSelector
key={id + index}
chainId={chainId}
errors={errors?.newValenceAccount?.funds?.[index]}
fieldNamePrefix={
fieldNamePrefix + `newValenceAccount.funds.${index}.`
}
isCreating={isCreating}
onRemove={
// Don't allow removing the first token.
isCreating && newValenceAccountFunds.length > 1
? () => removeNewValenceAccountFund(index)
: undefined
}
overrideInsufficientFundsWarning={(amount, tokenSymbol) =>
t('error.insufficientFundsWarningMinusServiceFee', {
amount,
tokenSymbol,
})
}
tokens={nativeBalances}
/>
))}

{!isCreating && newValenceAccountFunds.length === 0 && (
<p className="-mt-1 text-xs italic text-text-tertiary">
{t('info.none')}
</p>
)}

{isCreating && (
<>
<Button
className="self-start"
onClick={() =>
appendNewValenceAccountFund({
amount: '1',
denom: getNativeTokenForChainId(chainId).denomOrAddress,
decimals: getNativeTokenForChainId(chainId).decimals,
})
}
variant="secondary"
>
{t('button.addToken')}
</Button>

<div className="flex flex-row gap-2 items-center mt-4">
<FormCheckbox
fieldName={
(fieldNamePrefix +
'newValenceAccount.acknowledgedServiceFee') as 'newValenceAccount.acknowledgedServiceFee'
}
setValue={setValue}
size="sm"
value={acknowledgedServiceFee}
/>

<p
className="body-text cursor-pointer"
onClick={() =>
setValue(
(fieldNamePrefix +
'newValenceAccount.acknowledgedServiceFee') as 'newValenceAccount.acknowledgedServiceFee',
!acknowledgedServiceFee
)
}
>
{t('info.acknowledgeServiceFee', {
fee: serviceFee.loading
? '...'
: serviceFee.errored
? '<error>'
: serviceFee.data
? t('format.token', {
amount: HugeDecimal.from(
serviceFee.data.balance
).toInternationalizedHumanReadableString({
decimals: serviceFee.data.token.decimals,
}),
symbol: serviceFee.data.token.symbol,
})
: '',
context:
serviceFee.loading ||
serviceFee.errored ||
serviceFee.data
? undefined
: 'none',
})}
</p>
</div>

<InputErrorMessage
error={errors?.newValenceAccount?.acknowledgedServiceFee}
/>
</>
)}
</div>
)}

<div className="flex max-w-prose flex-col gap-5">
<div className="flex flex-col gap-2">
<InputLabel name={t('form.baseToken')} primary />
Expand Down Expand Up @@ -598,7 +771,7 @@ export const ConfigureRebalancerComponent: ActionComponent<
numericValue
register={register}
setValue={setValue}
sizing="auto"
sizing="md"
step={0.01}
unit="%"
validation={[validateRequired, validatePositive]}
Expand Down Expand Up @@ -629,7 +802,7 @@ export const ConfigureRebalancerComponent: ActionComponent<
</p>
</div>

{nativeBalances.loading ||
{currentValenceBalances.loading ||
prices.loading ||
denomWhitelistTokens.loading ? (
<Loader />
Expand All @@ -643,7 +816,7 @@ export const ConfigureRebalancerComponent: ActionComponent<
({ denomOrAddress }) => denomOrAddress === denom
)
const { balance: _balance } =
nativeBalances.data.find(
currentValenceBalances.data.find(
({ token }) => token.denomOrAddress === denom
) ?? {}
const balance = HugeDecimal.from(_balance || 0)
Expand Down
Loading

0 comments on commit e02cd02

Please sign in to comment.