diff --git a/scripts/dev.ts b/scripts/dev.ts index 964681f36a..22df50d816 100644 --- a/scripts/dev.ts +++ b/scripts/dev.ts @@ -9,23 +9,47 @@ import { const hre = require('hardhat'); const moment = require('moment'); +const accounts = [ + '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + '0xc73480525e9d1198d448ece4a01daea851f72a9d', + '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + '0xaf1a6415202453d79b84d8a9d055b8f9141f696b', + '0x02803e2cdff171d1910d178dac948c711937bd3f', + '0x797c62953ef866a1ece855c4033cc5dc3c11290b', + '0x016f70459e4ba98e0d60a5f5855e117e8ff39cae', + '0x073f4fdc12f805b8d98e58551fc10d0a71bbc7db', + '0x6829556f30899d70403947590ffe8900e8c0d0d7', + '0x2b410bcb3b8096164fa8c6a06220a66bfb77058d', + '0x309f75c54a57937a7a0c6eb9b36eb1dbca82407e', + '0xec9d2d34ad6acda19ab8afe71595051b206e3e4d', + '0x40c23c536bad1fe206ce911114f2c70309a7e487', + '0x28d254f2ddb522c43a21d338e337fd8d2f820db2', + '0xaf7386ce842cc0cffef91361059b0ca9ae48d6a0', + '0x46c18451aaead6a2cb888b5bd6193c0f2c402329', + '0xc707c8143a6e1274ae7f637946f685870925261f', + '0x5b14a88dbbb04abcb6e5bf6384491be8d939cf57', + '0x92d356240dda25d050aa441690b92b2fa0011b84', + '0x5a485c203d9537095a6be2acc5a7ad83805d301d', +]; + async function main() { const web3 = hre.web3; const PermissionRegistry = await hre.artifacts.require('PermissionRegistry'); const ERC20Guild = await hre.artifacts.require('ERC20Guild'); + const SnapshotERC20Guild = await hre.artifacts.require('SnapshotERC20Guild'); const deployconfig = { reputation: [ { - address: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + address: accounts[0], amount: 6000, }, { - address: '0xc73480525e9d1198d448ece4a01daea851f72a9d', + address: accounts[1], amount: 4000, }, { - address: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + address: accounts[2], amount: 1000, }, ], @@ -36,15 +60,15 @@ async function main() { symbol: 'DXD', distribution: [ { - address: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + address: accounts[0], amount: web3.utils.toWei('220'), }, { - address: '0xc73480525e9d1198d448ece4a01daea851f72a9d', + address: accounts[1], amount: web3.utils.toWei('50'), }, { - address: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + address: accounts[2], amount: web3.utils.toWei('10'), }, ], @@ -54,15 +78,15 @@ async function main() { symbol: 'RGT', distribution: [ { - address: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + address: accounts[0], amount: web3.utils.toWei('200'), }, { - address: '0xc73480525e9d1198d448ece4a01daea851f72a9d', + address: accounts[1], amount: web3.utils.toWei('50'), }, { - address: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + address: accounts[2], amount: web3.utils.toWei('10'), }, ], @@ -72,15 +96,15 @@ async function main() { symbol: 'SGT', distribution: [ { - address: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + address: accounts[0], amount: web3.utils.toWei('200'), }, { - address: '0xc73480525e9d1198d448ece4a01daea851f72a9d', + address: accounts[1], amount: web3.utils.toWei('40'), }, { - address: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + address: accounts[2], amount: web3.utils.toWei('10'), }, ], @@ -248,7 +272,7 @@ async function main() { { token: 'SGT', contractName: 'SnapshotERC20Guild', - name: 'SnapshotGuild', + name: 'SnapshotERC20Guild', proposalTime: moment.duration(5, 'minutes').asSeconds(), timeForExecution: moment.duration(2, 'minutes').asSeconds(), votingPowerForProposalExecution: '50', @@ -265,7 +289,7 @@ async function main() { actions: [ { type: 'transfer', - from: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + from: accounts[0], data: { asset: ZERO_ADDRESS, address: 'Avatar', @@ -274,7 +298,7 @@ async function main() { }, { type: 'transfer', - from: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + from: accounts[0], data: { asset: 'DXD', address: 'Avatar', @@ -284,7 +308,7 @@ async function main() { { type: 'transfer', - from: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + from: accounts[0], data: { asset: ZERO_ADDRESS, address: 'DXDGuild', @@ -293,7 +317,7 @@ async function main() { }, { type: 'transfer', - from: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + from: accounts[0], data: { asset: 'DXD', address: 'DXDGuild', @@ -303,7 +327,26 @@ async function main() { { type: 'transfer', - from: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + from: accounts[1], + data: { + asset: ZERO_ADDRESS, + address: 'SnapshotERC20Guild', + amount: web3.utils.toWei('10'), + }, + }, + { + type: 'transfer', + from: accounts[1], + data: { + asset: 'SGT', + address: 'SnapshotERC20Guild', + amount: web3.utils.toWei('10'), + }, + }, + + { + type: 'transfer', + from: accounts[0], data: { asset: ZERO_ADDRESS, address: 'REPGuild', @@ -313,7 +356,7 @@ async function main() { { type: 'proposal', - from: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + from: accounts[2], data: { to: ['PermissionRegistry'], callData: [ @@ -337,7 +380,7 @@ async function main() { }, { type: 'stake', - from: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + from: accounts[2], data: { proposal: '0', decision: '1', @@ -347,7 +390,7 @@ async function main() { { type: 'vote', time: moment.duration(1, 'minutes').asSeconds(), - from: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + from: accounts[2], data: { proposal: '0', decision: '1', @@ -357,14 +400,14 @@ async function main() { { type: 'execute', time: moment.duration(3, 'minutes').asSeconds(), - from: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + from: accounts[2], data: { proposal: '0', }, }, { type: 'redeem', - from: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + from: accounts[2], data: { proposal: '0', }, @@ -372,7 +415,7 @@ async function main() { { type: 'proposal', - from: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + from: accounts[2], data: { to: ['QuickWalletScheme'], callData: ['0x0'], @@ -385,7 +428,7 @@ async function main() { }, { type: 'stake', - from: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + from: accounts[2], data: { proposal: '1', decision: '1', @@ -395,7 +438,7 @@ async function main() { { type: 'vote', time: moment.duration(1, 'minutes').asSeconds(), - from: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + from: accounts[2], data: { proposal: '1', decision: '1', @@ -404,7 +447,7 @@ async function main() { }, { type: 'vote', - from: '0xc73480525e9d1198d448ece4a01daea851f72a9d', + from: accounts[1], data: { proposal: '1', decision: '2', @@ -414,9 +457,9 @@ async function main() { { type: 'proposal', - from: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + from: accounts[2], data: { - to: ['0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351'], + to: [accounts[2]], callData: ['0x0'], value: [web3.utils.toWei('1.5')], title: 'Proposal Test #2', @@ -429,7 +472,7 @@ async function main() { { type: 'approve', - from: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + from: accounts[0], data: { asset: 'DXD', address: 'DXDGuild-vault', @@ -438,7 +481,7 @@ async function main() { }, { type: 'guild-lockTokens', - from: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + from: accounts[0], data: { guildName: 'DXDGuild', amount: web3.utils.toWei('100'), @@ -446,7 +489,7 @@ async function main() { }, { type: 'guild-withdrawTokens', - from: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + from: accounts[0], data: { guildName: 'DXDGuild', amount: web3.utils.toWei('10'), @@ -455,7 +498,7 @@ async function main() { }, { type: 'guild-createProposal', - from: '0x79706c8e413cdaee9e63f282507287b9ea9c0928', + from: accounts[0], data: { guildName: 'DXDGuild', to: ['DXDGuild'], @@ -479,7 +522,7 @@ async function main() { }, { type: 'guild-voteProposal', - from: '0xc73480525e9d1198d448ece4a01daea851f72a9d', + from: accounts[1], data: { guildName: 'DXDGuild', proposal: 0, @@ -490,18 +533,43 @@ async function main() { { time: moment.duration(10, 'minutes').asSeconds(), type: 'guild-endProposal', - from: '0xc73480525e9d1198d448ece4a01daea851f72a9d', + from: accounts[1], data: { guildName: 'DXDGuild', proposal: 0, }, }, + { + type: 'guild-createProposal', + from: accounts[1], + data: { + guildName: 'SnapshotERC20Guild', + to: ['SnapshotERC20Guild'], + callData: [ + new web3.eth.Contract(SnapshotERC20Guild.abi).methods + .setPermission( + [ZERO_ADDRESS], + [ANY_ADDRESS], + [ANY_FUNC_SIGNATURE], + [web3.utils.toWei('5').toString()], + [true] + ) + .encodeABI(), + ], + value: ['0'], + totalActions: '1', + title: 'Proposal Test #1 to SnapshotERC20Guild', + description: + 'Allow call any address and function and send a max of 5 ETH per proposal', + }, + }, + { type: 'proposal', - from: '0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351', + from: accounts[2], data: { - to: ['0x3f943f38b2fbe1ee5daf0516cecfe4e0f8734351'], + to: [accounts[2]], callData: ['0x0'], value: [web3.utils.toWei('1.5')], title: 'Proposal Test #3', diff --git a/src/components/Guilds/ProposalPage/ProposalVoteCard/index.tsx b/src/components/Guilds/ProposalPage/ProposalVoteCard/index.tsx index b84009e26e..992058c69f 100644 --- a/src/components/Guilds/ProposalPage/ProposalVoteCard/index.tsx +++ b/src/components/Guilds/ProposalPage/ProposalVoteCard/index.tsx @@ -101,7 +101,6 @@ const ProposalVoteCard = () => { contract.setVote(proposalId, selectedAction, userVotingPower) ); }; - return ( { [guildAddress, 'getVotingPowerForProposalExecution'], [guildAddress, 'getTokenVault'], [guildAddress, 'getLockTime'], - [guildAddress, 'getTotalLocked'], ] : [], { @@ -40,6 +40,8 @@ export const useGuildConfig = (guildAddress: string) => { } ); + const { data: totalLocked } = useTotalLocked(guildAddress); + // TODO: Move this into a SWR middleware const transformedData: GuildConfig = useMemo(() => { if (!data) return undefined; @@ -55,7 +57,6 @@ export const useGuildConfig = (guildAddress: string) => { votingPowerForProposalExecution, tokenVault, lockTime, - totalLocked, ] = data; return { @@ -73,9 +74,9 @@ export const useGuildConfig = (guildAddress: string) => { ), tokenVault, lockTime: BigNumber.from(lockTime), - totalLocked: BigNumber.from(totalLocked), + totalLocked: totalLocked, }; - }, [data]); + }, [data, totalLocked]); return { error, diff --git a/src/hooks/Guilds/ether-swr/guild/useSnapshotId.ts b/src/hooks/Guilds/ether-swr/guild/useSnapshotId.ts new file mode 100644 index 0000000000..0b8f20e520 --- /dev/null +++ b/src/hooks/Guilds/ether-swr/guild/useSnapshotId.ts @@ -0,0 +1,24 @@ +import { BigNumber } from 'ethers'; +import { SWRResponse } from 'swr'; +import useEtherSWR from '../useEtherSWR'; +import SnapshotERC20Guild from 'contracts/SnapshotERC20Guild.json'; + +interface UseSnapshotIdProps { + contractAddress: string; + proposalId: string; +} + +type UseSnapshotIdHook = (args: UseSnapshotIdProps) => SWRResponse; + +const useSnapshotId: UseSnapshotIdHook = ({ contractAddress, proposalId }) => { + return useEtherSWR( + proposalId && contractAddress + ? [contractAddress, 'getProposalSnapshotId', proposalId] + : [], + { + ABIs: new Map([[contractAddress, SnapshotERC20Guild.abi]]), + } + ); +}; + +export default useSnapshotId; diff --git a/src/hooks/Guilds/ether-swr/guild/useTotalLocked.ts b/src/hooks/Guilds/ether-swr/guild/useTotalLocked.ts new file mode 100644 index 0000000000..8ed376415d --- /dev/null +++ b/src/hooks/Guilds/ether-swr/guild/useTotalLocked.ts @@ -0,0 +1,40 @@ +import { useParams } from 'react-router-dom'; +import ERC20GuildContract from 'contracts/ERC20Guild.json'; +import useEtherSWR from '../useEtherSWR'; +import useTotalLockedAt from 'hooks/Guilds/ether-swr/guild/useTotalLockedAt'; +import useSnapshotId from 'hooks/Guilds/ether-swr/guild/useSnapshotId'; +import useGuildImplementationType from 'hooks/Guilds/guild/useGuildImplementationType'; + +const useTotalLocked = (guildAddress: string) => { + // Hooks call + const { proposal_id: proposalId } = useParams<{ proposal_id?: string }>(); + + const { data: snapshotId } = useSnapshotId({ + contractAddress: guildAddress, + proposalId, + }); + + const { isSnapshotGuild, isRepGuild, isSnapshotRepGuild } = + useGuildImplementationType(guildAddress); + + const totalLockedResponse = useEtherSWR( + guildAddress ? [guildAddress, 'getTotalLocked'] : [], + { + ABIs: new Map([[guildAddress, ERC20GuildContract.abi]]), + refreshInterval: 0, + } + ); + + const totalLockedAtProposalSnapshotResponse = useTotalLockedAt({ + contractAddress: guildAddress, + snapshotId: snapshotId?.toString(), + }); + + // Return response based on implementation type + if (isSnapshotGuild) return totalLockedAtProposalSnapshotResponse; + if (isRepGuild) return totalLockedResponse; // TODO: replace with rep implementation totalLocked call + if (isSnapshotRepGuild) return totalLockedResponse; // TODO: replace with rep implementation totalLocked call + return totalLockedResponse; +}; + +export default useTotalLocked; diff --git a/src/hooks/Guilds/ether-swr/guild/useTotalLockedAt.ts b/src/hooks/Guilds/ether-swr/guild/useTotalLockedAt.ts new file mode 100644 index 0000000000..f5c3f5bae2 --- /dev/null +++ b/src/hooks/Guilds/ether-swr/guild/useTotalLockedAt.ts @@ -0,0 +1,33 @@ +import { BigNumber } from 'ethers'; +import { SWRResponse } from 'swr'; +import useEtherSWR from '../useEtherSWR'; +// import useSnapshotId from './useSnapshotId'; +import SnapshotERC20Guild from 'contracts/SnapshotERC20Guild.json'; + +interface UseTotalLockedAtProps { + contractAddress: string; + snapshotId: string; +} + +type UseTotalLockedAtHook = ( + args: UseTotalLockedAtProps +) => SWRResponse; + +/** + * Get the total locked amount at snapshot + */ +const useTotalLockedAt: UseTotalLockedAtHook = ({ + contractAddress, + snapshotId, +}) => { + return useEtherSWR( + snapshotId && contractAddress + ? [contractAddress, 'totalLockedAt', snapshotId] + : [], + { + ABIs: new Map([[contractAddress, SnapshotERC20Guild.abi]]), + } + ); +}; + +export default useTotalLockedAt; diff --git a/src/hooks/Guilds/ether-swr/guild/useVotingPowerOfAt.ts b/src/hooks/Guilds/ether-swr/guild/useVotingPowerOfAt.ts new file mode 100644 index 0000000000..bb979b1290 --- /dev/null +++ b/src/hooks/Guilds/ether-swr/guild/useVotingPowerOfAt.ts @@ -0,0 +1,31 @@ +import { BigNumber } from 'ethers'; +import { SWRResponse } from 'swr'; +import useEtherSWR from '../useEtherSWR'; +import SnapshotERC20Guild from 'contracts/SnapshotERC20Guild.json'; + +interface useVotingPowerOfAtProps { + contractAddress: string; + userAddress: string; + snapshotId: string; +} + +type useVotingPowerOfAtHook = ( + args: useVotingPowerOfAtProps +) => SWRResponse; + +/** + * Get the voting power of an account at snapshot id + */ +export const useVotingPowerOfAt: useVotingPowerOfAtHook = ({ + contractAddress, + userAddress, + snapshotId, +}) => + useEtherSWR( + contractAddress && snapshotId && userAddress + ? [contractAddress, 'votingPowerOf', userAddress, snapshotId] + : [], + { + ABIs: new Map([[contractAddress, SnapshotERC20Guild.abi]]), + } + ); diff --git a/src/hooks/Guilds/guild/useGuildImplementationType.ts b/src/hooks/Guilds/guild/useGuildImplementationType.ts index 1ccd2e2d2e..4098514539 100644 --- a/src/hooks/Guilds/guild/useGuildImplementationType.ts +++ b/src/hooks/Guilds/guild/useGuildImplementationType.ts @@ -21,14 +21,19 @@ interface ImplementationTypeConfig { interface ImplementationTypeConfigReturn extends ImplementationTypeConfig { isRepGuild: boolean; isSnapshotGuild: boolean; + isSnapshotRepGuild: boolean; } const parseConfig = ( config: ImplementationTypeConfig ): ImplementationTypeConfigReturn => { return { ...config, - isRepGuild: config.features.includes('REP'), - isSnapshotGuild: config.features.includes('SNAPSHOT'), + isRepGuild: + config.features.includes('REP') && !config.features.includes('SNAPSHOT'), + isSnapshotGuild: + config.features.includes('SNAPSHOT') && !config.features.includes('REP'), + isSnapshotRepGuild: + config.features.includes('SNAPSHOT') && config.features.includes('REP'), }; };