diff --git a/src/apollo/client.js b/src/apollo/client.js
index d10bbbdc..1eaf790f 100644
--- a/src/apollo/client.js
+++ b/src/apollo/client.js
@@ -57,3 +57,11 @@ export const ensClient = new ApolloClient({
cache: new InMemoryCache(),
shouldBatch: true,
})
+
+export const candleClient = new ApolloClient({
+ link: new HttpLink({
+ uri: 'https://api.thegraph.com/subgraphs/name/candleplatforms/candlegovernor',
+ }),
+ cache: new InMemoryCache(),
+ shouldBatch: true,
+})
diff --git a/src/assets/images/candle-logo.svg b/src/assets/images/candle-logo.svg
new file mode 100644
index 00000000..b20e0af2
--- /dev/null
+++ b/src/assets/images/candle-logo.svg
@@ -0,0 +1,1437 @@
+
+
diff --git a/src/assets/images/candle-logo.svg:Zone.Identifier b/src/assets/images/candle-logo.svg:Zone.Identifier
new file mode 100644
index 00000000..053d1127
--- /dev/null
+++ b/src/assets/images/candle-logo.svg:Zone.Identifier
@@ -0,0 +1,3 @@
+[ZoneTransfer]
+ZoneId=3
+HostUrl=about:internet
diff --git a/src/components/governance/ProposalList.tsx b/src/components/governance/ProposalList.tsx
index 28806b5a..15e9c1a8 100644
--- a/src/components/governance/ProposalList.tsx
+++ b/src/components/governance/ProposalList.tsx
@@ -75,7 +75,11 @@ export default function ProposalList({ allProposals }: { allProposals: { [id: st
- {p.id + '.'}
+
+ {activeProtocol.id === 'candle'
+ ? p.id.slice(0, 3) + '...' + p.id.slice(-3) + '.'
+ : p.id + '.'}
+
{p.title}
diff --git a/src/constants/abis/openzeppelin-governor.json b/src/constants/abis/openzeppelin-governor.json
new file mode 100644
index 00000000..2151fa4c
--- /dev/null
+++ b/src/constants/abis/openzeppelin-governor.json
@@ -0,0 +1,509 @@
+[
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "proposalId",
+ "type": "uint256"
+ }
+ ],
+ "name": "ProposalCanceled",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "proposalId",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "address",
+ "name": "proposer",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "address[]",
+ "name": "targets",
+ "type": "address[]"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256[]",
+ "name": "values",
+ "type": "uint256[]"
+ },
+ {
+ "indexed": false,
+ "internalType": "string[]",
+ "name": "signatures",
+ "type": "string[]"
+ },
+ {
+ "indexed": false,
+ "internalType": "bytes[]",
+ "name": "calldatas",
+ "type": "bytes[]"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "startBlock",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "endBlock",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "string",
+ "name": "description",
+ "type": "string"
+ }
+ ],
+ "name": "ProposalCreated",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "proposalId",
+ "type": "uint256"
+ }
+ ],
+ "name": "ProposalExecuted",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "voter",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "proposalId",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint8",
+ "name": "support",
+ "type": "uint8"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "weight",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "string",
+ "name": "reason",
+ "type": "string"
+ }
+ ],
+ "name": "VoteCast",
+ "type": "event"
+ },
+ {
+ "inputs": [],
+ "name": "COUNTING_MODE",
+ "outputs": [
+ {
+ "internalType": "string",
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "stateMutability": "pure",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "proposalId",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint8",
+ "name": "support",
+ "type": "uint8"
+ }
+ ],
+ "name": "castVote",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "balance",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "proposalId",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint8",
+ "name": "support",
+ "type": "uint8"
+ },
+ {
+ "internalType": "uint8",
+ "name": "v",
+ "type": "uint8"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "r",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "s",
+ "type": "bytes32"
+ }
+ ],
+ "name": "castVoteBySig",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "balance",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "proposalId",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint8",
+ "name": "support",
+ "type": "uint8"
+ },
+ {
+ "internalType": "string",
+ "name": "reason",
+ "type": "string"
+ }
+ ],
+ "name": "castVoteWithReason",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "balance",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address[]",
+ "name": "targets",
+ "type": "address[]"
+ },
+ {
+ "internalType": "uint256[]",
+ "name": "values",
+ "type": "uint256[]"
+ },
+ {
+ "internalType": "bytes[]",
+ "name": "calldatas",
+ "type": "bytes[]"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "descriptionHash",
+ "type": "bytes32"
+ }
+ ],
+ "name": "execute",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "proposalId",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "account",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "blockNumber",
+ "type": "uint256"
+ }
+ ],
+ "name": "getVotes",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "proposalId",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "account",
+ "type": "address"
+ }
+ ],
+ "name": "hasVoted",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address[]",
+ "name": "targets",
+ "type": "address[]"
+ },
+ {
+ "internalType": "uint256[]",
+ "name": "values",
+ "type": "uint256[]"
+ },
+ {
+ "internalType": "bytes[]",
+ "name": "calldatas",
+ "type": "bytes[]"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "descriptionHash",
+ "type": "bytes32"
+ }
+ ],
+ "name": "hashProposal",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "pure",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "name",
+ "outputs": [
+ {
+ "internalType": "string",
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "proposalId",
+ "type": "uint256"
+ }
+ ],
+ "name": "proposalDeadline",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "proposalId",
+ "type": "uint256"
+ }
+ ],
+ "name": "proposalSnapshot",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "address[]",
+ "name": "targets",
+ "type": "address[]"
+ },
+ {
+ "internalType": "uint256[]",
+ "name": "values",
+ "type": "uint256[]"
+ },
+ {
+ "internalType": "bytes[]",
+ "name": "calldatas",
+ "type": "bytes[]"
+ },
+ {
+ "internalType": "string",
+ "name": "description",
+ "type": "string"
+ }
+ ],
+ "name": "propose",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "proposalId",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "blockNumber",
+ "type": "uint256"
+ }
+ ],
+ "name": "quorum",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "proposalId",
+ "type": "uint256"
+ }
+ ],
+ "name": "state",
+ "outputs": [
+ {
+ "internalType": "enum IGovernor.ProposalState",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "version",
+ "outputs": [
+ {
+ "internalType": "string",
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "votingDelay",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
+ "name": "votingPeriod",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ }
+]
diff --git a/src/data/proposalStates/index.ts b/src/data/proposalStates/index.ts
index 5e3a4f14..d1dad4f9 100644
--- a/src/data/proposalStates/index.ts
+++ b/src/data/proposalStates/index.ts
@@ -1,9 +1,10 @@
import { abi as GOVERNANCE_ABI } from '@uniswap/governance/build/GovernorAlpha.json'
-import { useGovernanceContractBravo, useIsAave } from 'hooks/useContract'
+import { useGovernanceContractBravo, useGovernanceContractOpenZeppelin, useIsAave } from 'hooks/useContract'
import { useActiveProtocol } from 'state/governance/hooks'
import { useState, useEffect, useMemo } from 'react'
import { useGenericAlphaProposalCounts, useGenericBravoProposalCount } from 'data/proposalCount.ts'
import { useMultipleContractMultipleData, NEVER_RELOAD, useSingleContractMultipleData } from 'state/multicall/hooks'
+import { useAllProposals } from 'state/governance/hooks'
import { Interface } from '@ethersproject/abi'
import GOVERNANCE_AAVE_ABI from '../../constants/abis/aave-governance.json'
@@ -102,3 +103,44 @@ export function useGenericBravoProposalStates(): number[] | undefined {
return statuses
}
+
+export function useGenericOpenZeppelinStates(): number[] | undefined {
+ // const ids = [
+ // ['110464326887736665124431317636588996956479993036702271717961959788835407961802'],
+ // ['3997076801006050911366480014433799584238981404972673116103611075834769525735'],
+ // ]
+
+ const [statuses, setStatuses] = useState()
+ const govContractOpenZeppelin = useGovernanceContractOpenZeppelin()
+
+ const proposalData = useAllProposals()
+
+ let ids: string[][] = []
+ if (proposalData) {
+ ids = Array.from(Object.keys(proposalData), (k) => [k])
+ }
+ const proposalCount = ids.length
+
+ const openZeppelinStates = useSingleContractMultipleData(
+ proposalCount ? govContractOpenZeppelin : undefined,
+ 'state',
+ ids,
+ NEVER_RELOAD
+ )
+
+ const formattedOpenZeppelin = openZeppelinStates?.map((res) => {
+ if (!res.loading && res.valid) {
+ return res.result?.[0]
+ } else {
+ return undefined
+ }
+ })
+
+ useEffect(() => {
+ if (!statuses && proposalCount) {
+ setStatuses(formattedOpenZeppelin)
+ }
+ }, [statuses, proposalCount, formattedOpenZeppelin])
+
+ return formattedOpenZeppelin
+}
diff --git a/src/hooks/useContract.ts b/src/hooks/useContract.ts
index d25572eb..65b6293f 100644
--- a/src/hooks/useContract.ts
+++ b/src/hooks/useContract.ts
@@ -4,6 +4,7 @@ import { abi as UNI_ABI } from '@uniswap/governance/build/Uni.json'
import { ChainId, WETH } from '@uniswap/sdk'
import { useMemo } from 'react'
import GOVERNANCE_AAVE_ABI from '../constants/abis/aave-governance.json'
+import GOVERNANCE_OPENZEPPELIN_ABI from '../constants/abis/openzeppelin-governor.json'
import AAVE_ABI from '../constants/abis/aave-token.json'
import {
ARGENT_WALLET_DETECTOR_ABI,
@@ -127,6 +128,16 @@ export function useGovernanceContractBravo(): Contract | null {
)
}
+export function useGovernanceContractOpenZeppelin(): Contract | null {
+ const [activeProtocol] = useActiveProtocol()
+
+ return useContract(
+ activeProtocol ? activeProtocol.governanceAddressOpenZeppelin : undefined,
+ GOVERNANCE_OPENZEPPELIN_ABI,
+ true
+ )
+}
+
export function useIsAave(): boolean {
const [activeProtocol] = useActiveProtocol()
return activeProtocol?.id === AAVE_GOVERNANCE.id
diff --git a/src/state/application/hooks.ts b/src/state/application/hooks.ts
index 19b70e0f..de498431 100644
--- a/src/state/application/hooks.ts
+++ b/src/state/application/hooks.ts
@@ -12,6 +12,7 @@ import {
RADICLE_GOVERNANCE,
NOUNS_GOVERNANCE,
ENS_GOVERNANCE,
+ CANDLE_GOVERNANCE,
} from '../governance/reducer'
import {
uniswapClient,
@@ -21,6 +22,7 @@ import {
radicleClient,
nounsClient,
ensClient,
+ candleClient,
} from '../../apollo/client'
export function useBlockNumber(): number | undefined {
@@ -127,5 +129,9 @@ export function useSubgraphClient() {
return ensClient
}
+ if (activeProtocol?.id === CANDLE_GOVERNANCE.id) {
+ return candleClient
+ }
+
return undefined
}
diff --git a/src/state/governance/hooks.ts b/src/state/governance/hooks.ts
index 1de2b243..4821d6ef 100644
--- a/src/state/governance/hooks.ts
+++ b/src/state/governance/hooks.ts
@@ -11,7 +11,14 @@ import {
} from './actions'
import { AppDispatch, AppState } from './../index'
import { useDispatch, useSelector } from 'react-redux'
-import { GovernanceInfo, GlobaData, COMPOUND_GOVERNANCE, NOUNS_GOVERNANCE, UNISWAP_GOVERNANCE } from './reducer'
+import {
+ GovernanceInfo,
+ GlobaData,
+ COMPOUND_GOVERNANCE,
+ NOUNS_GOVERNANCE,
+ UNISWAP_GOVERNANCE,
+ CANDLE_GOVERNANCE,
+} from './reducer'
import { useState, useEffect, useCallback } from 'react'
import { useGovernanceContract, useGovTokenContract, useIsAave } from '../../hooks/useContract'
import { useSingleCallResult } from '../multicall/hooks'
@@ -25,7 +32,11 @@ import { deserializeToken } from '../user/hooks'
import { useIsEOA } from '../../hooks/useIsEOA'
import { AUTONOMOUS_PROPOSAL_BYTECODE } from '../../constants/proposals'
import usePrevious from '../../hooks/usePrevious'
-import { useGenericAlphaProposalStates, useGenericBravoProposalStates } from 'data/proposalStates'
+import {
+ useGenericAlphaProposalStates,
+ useGenericBravoProposalStates,
+ useGenericOpenZeppelinStates,
+} from 'data/proposalStates'
export interface DelegateData {
id: string
@@ -187,6 +198,11 @@ export function useAllProposalStates(): number[] | undefined {
const alphaStates = useGenericAlphaProposalStates()
const bravoStates = useGenericBravoProposalStates()
+ const openZeppelinStates = useGenericOpenZeppelinStates()
+
+ if (activeProtocol === CANDLE_GOVERNANCE) {
+ return openZeppelinStates
+ }
if (
activeProtocol === COMPOUND_GOVERNANCE ||
diff --git a/src/state/governance/reducer.ts b/src/state/governance/reducer.ts
index ce0e7e8d..0998c54b 100644
--- a/src/state/governance/reducer.ts
+++ b/src/state/governance/reducer.ts
@@ -17,6 +17,7 @@ import PoolLogo from '../../assets/images/pooltogether-icon.png'
import RadicleLogo from '../../assets/images/radicle-logo.svg'
import NounsLogo from '../../assets/images/nouns-logo.png'
import ENSLogo from '../../assets/images/ens.jpeg'
+import CandleLogo from '../../assets/images/candle-logo.svg'
import AddAccount from '../../assets/images/AddAccount.png'
import { serializeToken } from '../user/hooks'
@@ -30,6 +31,7 @@ export interface GovernanceInfo {
token: SerializedToken
governanceAlphaAddresses: string[]
governanceAddressBravo?: string
+ governanceAddressOpenZeppelin?: string
migrationProposalId?: number
social: string
emoji?: string
@@ -143,17 +145,6 @@ export const ENS_GOVERNANCE: GovernanceInfo = {
emoji: '🌱',
}
-export const CONNECT_CONFIG: GovernanceInfo = {
- id: 'connect',
- name: 'Connect Social Profile', // placeholder
- logo: AddAccount, // placeholder
- primaryColor: '#5284ff', // placeholder
- secondaryColor: '#cfddff', // placeholder
- token: serializeToken(ENS), //placeholder
- governanceAlphaAddresses: [ENS_GOVERNANCE_ADDRESS], //placeholder
- social: '@twitter', // placeholder
-}
-
export const NOUNS_GOVERNANCE_ADDRESS_BRAVO = '0x6f3E6272A167e8AcCb32072d08E0957F9c79223d'
export const NOUNS_ADDRESS = '0x9C8fF314C9Bc7F6e59A9d9225Fb22946427eDC03'
const NOUN = new Token(ChainId.MAINNET, NOUNS_ADDRESS, 0, 'NOUN', 'Nouns')
@@ -172,6 +163,33 @@ export const NOUNS_GOVERNANCE: GovernanceInfo = {
emoji: EMOJIS[Math.floor(Math.random() * EMOJIS.length)],
}
+export const CANDLE_GOVERNANCE_ADDRESS = '0xB80Be29667021AE0B617AC9eFe0a3A1a58033681'
+export const CNDL_ADDRESS = '0xbc138bD20C98186CC0342C8e380953aF0cb48BA8'
+const CNDL = new Token(ChainId.MAINNET, CNDL_ADDRESS, 18, 'CNDL', 'Candle')
+export const CANDLE_GOVERNANCE: GovernanceInfo = {
+ id: 'candle',
+ name: 'Candle Platforms',
+ logo: CandleLogo,
+ primaryColor: '#6fbeff',
+ secondaryColor: '#ebfeff',
+ token: serializeToken(CNDL),
+ governanceAlphaAddresses: [],
+ governanceAddressOpenZeppelin: CANDLE_GOVERNANCE_ADDRESS,
+ social: '@cndlcoin',
+ emoji: '🕯️',
+}
+
+export const CONNECT_CONFIG: GovernanceInfo = {
+ id: 'connect',
+ name: 'Connect Social Profile', // placeholder
+ logo: AddAccount, // placeholder
+ primaryColor: '#5284ff', // placeholder
+ secondaryColor: '#cfddff', // placeholder
+ token: serializeToken(ENS), //placeholder
+ governanceAlphaAddresses: [ENS_GOVERNANCE_ADDRESS], //placeholder
+ social: '@twitter', // placeholder
+}
+
// #/connect or #/delegates/connect
// show only identity flow e.g. link to twitter
export function identityOnlyPath(pathname: string) {
@@ -187,6 +205,7 @@ export const SUPPORTED_PROTOCOLS: { [id: string]: GovernanceInfo } = {
radicle: RADICLE_GOVERNANCE,
nouns: NOUNS_GOVERNANCE,
ens: ENS_GOVERNANCE,
+ candle: CANDLE_GOVERNANCE,
connect: CONNECT_CONFIG,
}