Skip to content

Commit

Permalink
Merge pull request #531 from HausDAO/develop
Browse files Browse the repository at this point in the history
deploy
  • Loading branch information
skuhlmann authored Oct 21, 2024
2 parents 073fb53 + 2d22202 commit 0552423
Show file tree
Hide file tree
Showing 17 changed files with 306 additions and 32 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ contact_links:
url: https://daohaus.club/
about: Learn more about DAOhaus
- name: DAOhaus Discord
url: https://discord.gg/daohaus
url: https://discord.gg/kJaVkXtsXA
about: 'Join our discord and report issues in #support'
- name: DAOhaus Developer Docs and Contribution Guidelines
url: https://docs.daohaus.club/
Expand Down
2 changes: 1 addition & 1 deletion libs/form-builder/src/FormBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export const FormBuilder = ({
onSubmit={handleSubmit}
footer={
<FormFooter
submitButtonText={submitButtonText}
submitButtonText={form.submitButtonText || submitButtonText}
status={status}
txHash={txHash}
/>
Expand Down
2 changes: 1 addition & 1 deletion libs/keychain-utils/src/contractKeychains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export const CONTRACT_KEYCHAINS: Record<string, Keychain> = {
'0xaa36a7': '0x000000000000aDdB49795b0f9bA5BC298cDda236',
'0x64': '0x00000000000DC7F163742Eb4aBEf650037b1f588',
'0x89': '0x00000000000DC7F163742Eb4aBEf650037b1f588',
'0xa': '0xC22834581EbC8527d974F8a1c97E1bEA4EF910BC',
'0xa': '0x00000000000DC7F163742Eb4aBEf650037b1f588',
'0xa4b1': '0x00000000000DC7F163742Eb4aBEf650037b1f588',
'0x2105': '0x000000000000aDdB49795b0f9bA5BC298cDda236',
},
Expand Down
2 changes: 2 additions & 0 deletions libs/moloch-v3-fields/src/config/fieldConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { SafeSelect } from '../fields';
import { MultisendActions } from '../fields';
import { AddressesAndAmounts } from '../fields';
import { EpochDatePicker } from '../fields';
import { TransferTokens } from '../fields';

export const MolochFields = {
...CoreFieldLookup,
Expand All @@ -41,6 +42,7 @@ export const MolochFields = {
addressesAndAmounts: AddressesAndAmounts,
epochDatePicker: EpochDatePicker,
markdownField: MarkdownField,
transferTokens: TransferTokens,
};

export type MolochFieldLego = FieldLegoBase<typeof MolochFields>;
Expand Down
24 changes: 14 additions & 10 deletions libs/moloch-v3-fields/src/fields/RequestERC20.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { useEffect, useMemo } from 'react';
import { RegisterOptions, useFormContext } from 'react-hook-form';

import {
formatValueTo,
handleBaseUnits,
ignoreEmptyVal,
toWholeUnits,
truncValue,
ValidateField,
} from '@daohaus/utils';
import { isValidNetwork } from '@daohaus/keychain-utils';
Expand Down Expand Up @@ -74,17 +74,21 @@ export const RequestERC20 = (
return erc20s.find(({ address }) => address === paymentTokenAddr);
}, [paymentTokenAddr, erc20s]);

const tokenBalance = selectedToken?.daoBalance
? formatValueTo({
value: toWholeUnits(selectedToken?.daoBalance, selectedToken?.decimals),
decimals: 6,
format: 'number',
})
: '0';
const displayBalance = useMemo(() => {
if (!selectedToken || BigInt(selectedToken.daoBalance) === BigInt(0))
return '0';
return truncValue(
toWholeUnits(selectedToken.daoBalance, selectedToken.decimals),
6
);
}, [selectedToken]);

const setMax = () => {
if (!selectedToken) return;
setValue(amtId, tokenBalance.trim());
setValue(
amtId,
toWholeUnits(selectedToken?.daoBalance || '0', selectedToken?.decimals)
);
};

const newRules: RegisterOptions = {
Expand Down Expand Up @@ -116,7 +120,7 @@ export const RequestERC20 = (
options={selectOptions || []}
rightAddon={
<Button color="secondary" size="sm" onClick={setMax}>
Max: {tokenBalance}
Max: {displayBalance}
</Button>
}
rules={newRules}
Expand Down
17 changes: 11 additions & 6 deletions libs/moloch-v3-fields/src/fields/RequestNativeToken.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useMemo } from 'react';
import { RegisterOptions, useFormContext } from 'react-hook-form';

import { toWholeUnits, handleBaseUnits } from '@daohaus/utils';
import { handleBaseUnits, toWholeUnits, truncValue } from '@daohaus/utils';
import { Buildable, Button, WrappedInput } from '@daohaus/ui';
import { isValidNetwork } from '@daohaus/keychain-utils';
import { useDaoData, useCurrentDao } from '@daohaus/moloch-v3-hooks';
Expand Down Expand Up @@ -29,6 +29,15 @@ export const RequestNativeToken = (
return getNetworkToken(dao, daoChain, safeAddress);
}, [dao, daoChain, safeAddress]);

const displayBalance = useMemo(() => {
if (!networkTokenData || BigInt(networkTokenData.daoBalance) === BigInt(0))
return '0';
return truncValue(
toWholeUnits(networkTokenData.daoBalance, networkTokenData.decimals),
6
);
}, [networkTokenData]);

const label = networkTokenData?.name
? `Request ${networkTokenData.name}`
: `Request Network Token`;
Expand Down Expand Up @@ -56,11 +65,7 @@ export const RequestNativeToken = (
defaultValue="0"
rightAddon={
<Button color="secondary" size="sm" onClick={setMax}>
Max:{' '}
{toWholeUnits(
networkTokenData?.daoBalance || '0',
networkTokenData?.decimals
)}
Max: {displayBalance}
</Button>
}
rules={newRules}
Expand Down
159 changes: 159 additions & 0 deletions libs/moloch-v3-fields/src/fields/TransferTokens.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { RegisterOptions, useFormContext } from 'react-hook-form';

import { LOCAL_ABI } from '@daohaus/abis';
import { ValidNetwork } from '@daohaus/keychain-utils';
import { MolochV3Dao } from '@daohaus/moloch-v3-data';
import {
useConnectedMember,
useCurrentDao,
useDaoData,
} from '@daohaus/moloch-v3-hooks';
import {
Buildable,
Button,
Field,
WrappedInput,
WrappedInputSelect,
} from '@daohaus/ui';
import {
createViemClient,
handleBaseUnits,
ignoreEmptyVal,
toWholeUnits,
ValidateField,
ZERO_ADDRESS,
} from '@daohaus/utils';

type Token = {
name: string;
value: string;
paused: boolean;
decimals: number;
};

export const TransferTokens = (props: Buildable<Field>) => {
const { connectedMember } = useConnectedMember();
const { daoChain } = useCurrentDao();
const { dao } = useDaoData();
const { register, setValue, watch } = useFormContext();
const [selectOptions, setSelectOptions] = useState<Array<Token>>([]);

const selectedTokenId = 'paymentTokenAddress';
const selectedTokenAddr = watch(selectedTokenId);
const tokenErrorMsg = 'Token is non-transferrable';

const recipientId = 'recipientAddress';

register(recipientId);
register(selectedTokenId);

useEffect(() => {
const getDAOTokens = async (_dao: MolochV3Dao, chainId: ValidNetwork) => {
const client = createViemClient({
chainId,
});
const sharesDecimals = await client.readContract({
abi: LOCAL_ABI.ERC20,
address: _dao.sharesAddress as `0x${string}`,
functionName: 'decimals',
});
const lootDecimals = await client.readContract({
abi: LOCAL_ABI.ERC20,
address: _dao.lootAddress as `0x${string}`,
functionName: 'decimals',
});
setSelectOptions([
{
name: `${_dao.shareTokenName} (Voting Tokens)`,
value: _dao.sharesAddress,
paused: _dao.sharesPaused,
decimals: Number(sharesDecimals),
},
{
name: `${_dao.lootTokenName} (Non-Voting Tokens)`,
value: _dao.lootAddress,
paused: _dao.lootPaused,
decimals: Number(lootDecimals),
},
]);
};
if (!dao || !daoChain) return;
getDAOTokens(dao, daoChain);
}, [dao, daoChain]);

const selectedToken = useMemo(() => {
if (!selectOptions || !selectedTokenAddr) return;
return selectOptions.find((opt) => opt.value === selectedTokenAddr);
}, [selectOptions, selectedTokenAddr]);

const tokenBalance = useMemo(() => {
if (!connectedMember || !dao || !selectedToken) return '0';
if (selectedToken.value === dao.sharesAddress)
return toWholeUnits(connectedMember.shares, selectedToken.decimals);
return toWholeUnits(connectedMember.loot, selectedToken.decimals);
}, [connectedMember, dao, selectedToken]);

const setMax = useCallback(() => {
console.log('selectedToken', selectedToken);
if (!selectedToken) return;
setValue(props.id, tokenBalance.trim());
}, [selectedToken]);

Check warning on line 101 in libs/moloch-v3-fields/src/fields/TransferTokens.tsx

View workflow job for this annotation

GitHub Actions / build

React Hook useCallback has missing dependencies: 'props.id', 'setValue', and 'tokenBalance'. Either include them or remove the dependency array

const newRules: RegisterOptions = useMemo(() => {
return {
setValueAs: (value) => handleBaseUnits(value, selectedToken?.decimals),
validate: {
number: (value) => ignoreEmptyVal(value, ValidateField.number),
isTransferrable: () => (selectedToken?.paused ? tokenErrorMsg : true),
tokenSelected: () => !!selectedToken,
maxValue: (value) =>
Number(value) <= Number(handleBaseUnits(tokenBalance, 18)) ||
'Cannot exceed current token balance',
nonZero: (value) =>
Number(value) > 0 || 'Value must be greater than zero',
},
...props.rules,
};
}, [selectedToken, tokenBalance]);

Check warning on line 118 in libs/moloch-v3-fields/src/fields/TransferTokens.tsx

View workflow job for this annotation

GitHub Actions / build

React Hook useMemo has a missing dependency: 'props.rules'. Either include it or remove the dependency array

return (
<>
<WrappedInputSelect
{...props}
placeholder="0"
error={
selectedToken?.paused
? { type: 'error', message: tokenErrorMsg }
: undefined
}
selectId={selectedTokenId}
selectPlaceholder="--"
options={selectOptions || []}
rightAddon={
<Button color="secondary" size="sm" onClick={setMax}>
Max: {tokenBalance}
</Button>
}
rules={newRules}
/>
<WrappedInput
address
disabled={selectedToken?.paused}
full
id={recipientId}
label="Recipient"
placeholder="0x..."
rules={{
required: true,
validate: {
ethAddress: (value) =>
ignoreEmptyVal(value, ValidateField.ethAddress),
nonZeroAddress: (value) =>
value !== ZERO_ADDRESS || 'Cannot send to the Zero Address',
},
}}
/>
</>
);
};
1 change: 1 addition & 0 deletions libs/moloch-v3-fields/src/fields/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ export * from './TagsInput';
export * from './TributeInput';
export * from './WalletConnectLink';
export * from './EpochDatePicker';
export * from './TransferTokens';
5 changes: 5 additions & 0 deletions libs/moloch-v3-legos/src/fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,9 @@ export const FIELD: Record<string, MolochFieldLego> = {
type: 'addressesAndAmounts',
label: 'Addresses & Amounts',
},
TRANSFER_TOKENS: {
id: 'transferTokens',
type: 'transferTokens',
label: 'Transfer DAO Tokens',
},
};
11 changes: 11 additions & 0 deletions libs/moloch-v3-legos/src/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,17 @@ export const COMMON_FORMS: Record<string, MolochFormLego> = {
submitButtonText: 'Update Delegate',
tx: TX.MANAGE_DELEGATE,
},
MANAGE_TOKENS: {
id: 'MANAGE_TOKENS',
fields: [FIELD.TRANSFER_TOKENS],
requiredFields: {
transferTokens: true,
paymentTokenAddress: true,
recipientAddress: true,
},
submitButtonText: 'Transfer Tokens',
tx: TX.TRANSFER_TOKENS,
},
RAGEQUIT: {
id: 'RAGEQUIT',
title: 'Ragequit',
Expand Down
6 changes: 6 additions & 0 deletions libs/moloch-v3-legos/src/tx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,12 @@ export const TX: Record<string, TXLego> = {
method: 'delegate',
args: ['.formValues.delegatingTo'],
},
TRANSFER_TOKENS: {
id: 'TRANSFER_TOKENS',
contract: CONTRACT.ERC_20_FUNDING,
method: 'transfer',
args: ['.formValues.recipientAddress', '.formValues.transferTokens'],
},
RAGEQUIT: {
id: 'RAGEQUIT',
contract: CONTRACT.CURRENT_DAO,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useDHConnect } from '@daohaus/connect';
import { FormBuilder } from '@daohaus/form-builder';
import { ValidNetwork } from '@daohaus/keychain-utils';
import { MolochFields } from '@daohaus/moloch-v3-fields';
import {
useConnectedMember,
useDaoData,
useDaoMembers,
} from '@daohaus/moloch-v3-hooks';
import { COMMON_FORMS } from '@daohaus/moloch-v3-legos';

type ManageTokensProps = {
daoChain: ValidNetwork;
daoId: string;
};

export const ManageTokens = ({ daoChain, daoId }: ManageTokensProps) => {
const { refetch } = useDaoData();
const { refetch: refetchMembers } = useDaoMembers();
const { address } = useDHConnect();
const { refetch: refetchMember } = useConnectedMember({
daoChain,
daoId,
memberAddress: address as string,
});

const onFormComplete = () => {
refetch?.();
refetchMembers?.();
refetchMember?.();
};

return (
<FormBuilder
form={COMMON_FORMS.MANAGE_TOKENS}
customFields={MolochFields}
lifeCycleFns={{
onPollSuccess: () => {
onFormComplete();
},
}}
targetNetwork={daoChain}
/>
);
};
Loading

0 comments on commit 0552423

Please sign in to comment.