Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
I
interface
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
LuckySwap
interface
Commits
5dd1249d
Unverified
Commit
5dd1249d
authored
Jun 13, 2021
by
Noah Zinsmeister
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: quick fix for new governor
parent
9eb20624
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
124 additions
and
106 deletions
+124
-106
addresses.ts
src/constants/addresses.ts
+6
-4
governance.ts
src/constants/governance.ts
+3
-22
index.ts
src/constants/proposals/index.ts
+0
-6
useContract.ts
src/hooks/useContract.ts
+6
-3
index.tsx
src/pages/Vote/index.tsx
+3
-2
hooks.ts
src/state/governance/hooks.ts
+106
-69
No files found.
src/constants/addresses.ts
View file @
5dd1249d
...
@@ -16,10 +16,12 @@ export const V2_ROUTER_ADDRESS: AddressMap = constructSameAddressMap(
...
@@ -16,10 +16,12 @@ export const V2_ROUTER_ADDRESS: AddressMap = constructSameAddressMap(
'
0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
'
,
'
0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
'
,
false
false
)
)
export
const
GOVERNANCE_ADDRESS
:
AddressMap
=
constructSameAddressMap
(
export
const
GOVERNANCE_ADDRESSES
:
AddressMap
[]
=
[
'
0x5e4be8Bc9637f0EAA1A755019e06A68ce081D58F
'
,
constructSameAddressMap
(
'
0x5e4be8Bc9637f0EAA1A755019e06A68ce081D58F
'
,
false
),
false
{
)
[
SupportedChainId
.
MAINNET
]:
'
0xC4e172459f1E7939D522503B81AFAaC1014CE6F6
'
,
},
]
export
const
TIMELOCK_ADDRESS
:
AddressMap
=
constructSameAddressMap
(
'
0x1a9C8182C09F50C8318d769245beA52c32BE35BC
'
,
false
)
export
const
TIMELOCK_ADDRESS
:
AddressMap
=
constructSameAddressMap
(
'
0x1a9C8182C09F50C8318d769245beA52c32BE35BC
'
,
false
)
export
const
MERKLE_DISTRIBUTOR_ADDRESS
:
AddressMap
=
{
export
const
MERKLE_DISTRIBUTOR_ADDRESS
:
AddressMap
=
{
[
SupportedChainId
.
MAINNET
]:
'
0x090D4613473dEE047c3f2706764f49E0821D256e
'
,
[
SupportedChainId
.
MAINNET
]:
'
0x090D4613473dEE047c3f2706764f49E0821D256e
'
,
...
...
src/constants/governance.ts
View file @
5dd1249d
import
{
GOVERNANCE_ADDRESS
,
TIMELOCK_ADDRESS
,
UNI_ADDRESS
}
from
'
./addresses
'
import
{
GOVERNANCE_ADDRESS
ES
,
TIMELOCK_ADDRESS
,
UNI_ADDRESS
}
from
'
./addresses
'
export
const
COMMON_CONTRACT_NAMES
:
{
[
chainId
:
number
]:
{
[
address
:
string
]:
string
}
}
=
{
export
const
COMMON_CONTRACT_NAMES
:
{
[
chainId
:
number
]:
{
[
address
:
string
]:
string
}
}
=
{
[
1
]:
{
[
1
]:
{
[
UNI_ADDRESS
[
1
]]:
'
UNI
'
,
[
UNI_ADDRESS
[
1
]]:
'
UNI
'
,
[
GOVERNANCE_ADDRESS
[
1
]]:
'
Governance
'
,
[
GOVERNANCE_ADDRESSES
[
0
][
1
]]:
'
Governance (V0)
'
,
[
GOVERNANCE_ADDRESSES
[
1
][
1
]]:
'
Governance
'
,
[
TIMELOCK_ADDRESS
[
1
]]:
'
Timelock
'
,
[
TIMELOCK_ADDRESS
[
1
]]:
'
Timelock
'
,
},
},
[
4
]:
{
[
UNI_ADDRESS
[
4
]]:
'
Rinkeby UNI
'
,
[
GOVERNANCE_ADDRESS
[
4
]]:
'
Rinkeby Governance
'
,
[
TIMELOCK_ADDRESS
[
4
]]:
'
Rinkeby Timelock
'
,
},
[
3
]:
{
[
UNI_ADDRESS
[
3
]]:
'
Ropsten UNI
'
,
[
GOVERNANCE_ADDRESS
[
3
]]:
'
Ropsten Governance
'
,
[
TIMELOCK_ADDRESS
[
3
]]:
'
Ropsten Timelock
'
,
},
[
42
]:
{
[
UNI_ADDRESS
[
42
]]:
'
Kovan UNI
'
,
[
GOVERNANCE_ADDRESS
[
42
]]:
'
Kovan Governance
'
,
[
TIMELOCK_ADDRESS
[
42
]]:
'
Kovan Timelock
'
,
},
[
5
]:
{
[
UNI_ADDRESS
[
5
]]:
'
Goerli UNI
'
,
[
GOVERNANCE_ADDRESS
[
5
]]:
'
Goerli Governance
'
,
[
TIMELOCK_ADDRESS
[
5
]]:
'
Goerli Timelock
'
,
},
}
}
export
const
DEFAULT_AVERAGE_BLOCK_TIME_IN_SECS
=
13
export
const
DEFAULT_AVERAGE_BLOCK_TIME_IN_SECS
=
13
...
...
src/constants/proposals/index.ts
deleted
100644 → 0
View file @
9eb20624
import
{
UNISWAP_GRANTS_PROPOSAL_DESCRIPTION
}
from
'
./uniswap_grants_proposal_description
'
// Proposals are 0-indexed
export
const
PROPOSAL_DESCRIPTION_TEXT
:
{
[
proposalId
:
number
]:
string
}
=
{
[
2
]:
UNISWAP_GRANTS_PROPOSAL_DESCRIPTION
,
}
src/hooks/useContract.ts
View file @
5dd1249d
...
@@ -27,7 +27,7 @@ import {
...
@@ -27,7 +27,7 @@ import {
V3_CORE_FACTORY_ADDRESSES
,
V3_CORE_FACTORY_ADDRESSES
,
V3_MIGRATOR_ADDRESSES
,
V3_MIGRATOR_ADDRESSES
,
ARGENT_WALLET_DETECTOR_ADDRESS
,
ARGENT_WALLET_DETECTOR_ADDRESS
,
GOVERNANCE_ADDRESS
,
GOVERNANCE_ADDRESS
ES
,
MERKLE_DISTRIBUTOR_ADDRESS
,
MERKLE_DISTRIBUTOR_ADDRESS
,
MULTICALL2_ADDRESSES
,
MULTICALL2_ADDRESSES
,
V2_ROUTER_ADDRESS
,
V2_ROUTER_ADDRESS
,
...
@@ -116,8 +116,11 @@ export function useMerkleDistributorContract() {
...
@@ -116,8 +116,11 @@ export function useMerkleDistributorContract() {
return
useContract
(
MERKLE_DISTRIBUTOR_ADDRESS
,
MERKLE_DISTRIBUTOR_ABI
,
true
)
return
useContract
(
MERKLE_DISTRIBUTOR_ADDRESS
,
MERKLE_DISTRIBUTOR_ABI
,
true
)
}
}
export
function
useGovernanceContract
()
{
export
function
useGovernanceContracts
():
(
Contract
|
null
)[]
{
return
useContract
(
GOVERNANCE_ADDRESS
,
GOVERNANCE_ABI
,
true
)
return
[
useContract
(
GOVERNANCE_ADDRESSES
[
0
],
GOVERNANCE_ABI
,
false
),
useContract
(
GOVERNANCE_ADDRESSES
[
1
],
GOVERNANCE_ABI
,
true
),
]
}
}
export
function
useUniContract
()
{
export
function
useUniContract
()
{
...
...
src/pages/Vote/index.tsx
View file @
5dd1249d
...
@@ -120,7 +120,8 @@ export default function Vote() {
...
@@ -120,7 +120,8 @@ export default function Vote() {
const
toggleDelegateModal
=
useToggleDelegateModal
()
const
toggleDelegateModal
=
useToggleDelegateModal
()
// get data to list all proposals
// get data to list all proposals
const
allProposals
:
ProposalData
[]
=
useAllProposalData
()
// TODO don't hardcode for first gov alpha
const
allProposals
:
ProposalData
[]
=
useAllProposalData
()[
0
]
// user data
// user data
const
availableVotes
:
CurrencyAmount
<
Token
>
|
undefined
=
useUserVotes
()
const
availableVotes
:
CurrencyAmount
<
Token
>
|
undefined
=
useUserVotes
()
...
@@ -248,7 +249,7 @@ export default function Vote() {
...
@@ -248,7 +249,7 @@ export default function Vote() {
</
TYPE
.
subHeader
>
</
TYPE
.
subHeader
>
</
EmptyProposals
>
</
EmptyProposals
>
)
}
)
}
{
allProposals
?.
map
((
p
:
ProposalData
,
i
)
=>
{
{
allProposals
?.
reverse
()?.
map
((
p
:
ProposalData
,
i
)
=>
{
return
(
return
(
<
Proposal
as=
{
Link
}
to=
{
'
/vote/
'
+
p
.
id
}
key=
{
i
}
>
<
Proposal
as=
{
Link
}
to=
{
'
/vote/
'
+
p
.
id
}
key=
{
i
}
>
<
ProposalNumber
>
{
p
.
id
}
</
ProposalNumber
>
<
ProposalNumber
>
{
p
.
id
}
</
ProposalNumber
>
...
...
src/state/governance/hooks.ts
View file @
5dd1249d
import
{
CurrencyAmount
,
Token
}
from
'
@uniswap/sdk-core
'
import
{
CurrencyAmount
,
Token
}
from
'
@uniswap/sdk-core
'
import
{
isAddress
}
from
'
ethers/lib/utils
'
import
{
isAddress
}
from
'
ethers/lib/utils
'
import
{
PROPOSAL_DESCRIPTION_TEXT
}
from
'
../../constants/proposals
'
import
{
UNI
}
from
'
../../constants/tokens
'
import
{
UNI
}
from
'
../../constants/tokens
'
import
{
useGovernanceContract
,
useUniContract
}
from
'
../../hooks/useContract
'
import
{
useGovernanceContract
s
,
useUniContract
}
from
'
../../hooks/useContract
'
import
{
calculateGasMargin
}
from
'
../../utils/calculateGasMargin
'
import
{
calculateGasMargin
}
from
'
../../utils/calculateGasMargin
'
import
{
useSingleCallResult
,
useSingleContractMultipleData
}
from
'
../multicall/hooks
'
import
{
useSingleCallResult
,
useSingleContractMultipleData
}
from
'
../multicall/hooks
'
import
{
useActiveWeb3React
}
from
'
../../hooks/web3
'
import
{
useActiveWeb3React
}
from
'
../../hooks/web3
'
...
@@ -11,6 +10,7 @@ import { TransactionResponse } from '@ethersproject/providers'
...
@@ -11,6 +10,7 @@ import { TransactionResponse } from '@ethersproject/providers'
import
{
useTransactionAdder
}
from
'
../transactions/hooks
'
import
{
useTransactionAdder
}
from
'
../transactions/hooks
'
import
{
useState
,
useEffect
,
useCallback
,
useMemo
}
from
'
react
'
import
{
useState
,
useEffect
,
useCallback
,
useMemo
}
from
'
react
'
import
{
abi
as
GOV_ABI
}
from
'
@uniswap/governance/build/GovernorAlpha.json
'
import
{
abi
as
GOV_ABI
}
from
'
@uniswap/governance/build/GovernorAlpha.json
'
import
{
UNISWAP_GRANTS_PROPOSAL_DESCRIPTION
}
from
'
constants/proposals/uniswap_grants_proposal_description
'
interface
ProposalDetail
{
interface
ProposalDetail
{
target
:
string
target
:
string
...
@@ -43,10 +43,9 @@ export enum ProposalState {
...
@@ -43,10 +43,9 @@ export enum ProposalState {
Executed
,
Executed
,
}
}
// get count of all proposals made
// get count of all proposals made on the given governor alpha
export
function
useProposalCount
():
number
|
undefined
{
function
useProposalCount
(
govContract
:
ethers
.
Contract
|
null
):
number
|
undefined
{
const
gov
=
useGovernanceContract
()
const
res
=
useSingleCallResult
(
govContract
,
'
proposalCount
'
)
const
res
=
useSingleCallResult
(
gov
,
'
proposalCount
'
)
if
(
res
.
result
&&
!
res
.
loading
)
{
if
(
res
.
result
&&
!
res
.
loading
)
{
return
parseInt
(
res
.
result
[
0
])
return
parseInt
(
res
.
result
[
0
])
}
}
...
@@ -58,15 +57,15 @@ export function useProposalCount(): number | undefined {
...
@@ -58,15 +57,15 @@ export function useProposalCount(): number | undefined {
* new proposal event.
* new proposal event.
*/
*/
const
eventParser
=
new
ethers
.
utils
.
Interface
(
GOV_ABI
)
const
eventParser
=
new
ethers
.
utils
.
Interface
(
GOV_ABI
)
export
function
useDataFromEventLogs
(
)
{
function
useDataFromEventLogs
(
govContract
:
ethers
.
Contract
|
null
)
{
const
{
library
,
chainId
}
=
useActiveWeb3React
()
const
{
library
,
chainId
}
=
useActiveWeb3React
()
const
[
formattedEvents
,
setFormattedEvents
]
=
const
[
formattedEvents
,
setFormattedEvents
]
=
useState
<
{
description
:
string
;
details
:
{
target
:
string
;
functionSig
:
string
;
callData
:
string
}[]
}[]
>
()
useState
<
{
description
:
string
;
details
:
{
target
:
string
;
functionSig
:
string
;
callData
:
string
}[]
}[]
>
()
const
govContract
=
useGovernanceContract
()
// create filter for these specific events
// create filter for these specific events
const
filter
=
useMemo
(
const
filter
=
useMemo
(
()
=>
(
govContract
?
{
...
govContract
.
filters
.
ProposalCreated
(),
fromBlock
:
0
,
toBlock
:
'
latest
'
}
:
undefined
),
()
=>
govContract
?
{
...
govContract
.
filters
.
ProposalCreated
(),
fromBlock
:
10861678
,
toBlock
:
'
latest
'
}
:
undefined
,
[
govContract
]
[
govContract
]
)
)
...
@@ -79,28 +78,25 @@ export function useDataFromEventLogs() {
...
@@ -79,28 +78,25 @@ export function useDataFromEventLogs() {
.
getLogs
(
filter
)
.
getLogs
(
filter
)
.
then
((
proposalEvents
)
=>
{
.
then
((
proposalEvents
)
=>
{
if
(
stale
)
return
if
(
stale
)
return
// reverse events to get them from newest to odlest
const
formattedEventData
=
proposalEvents
?.
map
((
event
)
=>
{
const
formattedEventData
=
proposalEvents
const
eventParsed
=
eventParser
.
parseLog
(
event
).
args
?.
map
((
event
)
=>
{
return
{
const
eventParsed
=
eventParser
.
parseLog
(
event
).
args
description
:
eventParsed
.
description
,
return
{
details
:
eventParsed
.
targets
.
map
((
target
:
string
,
i
:
number
)
=>
{
description
:
eventParsed
.
description
,
const
signature
=
eventParsed
.
signatures
[
i
]
details
:
eventParsed
.
targets
.
map
((
target
:
string
,
i
:
number
)
=>
{
const
[
name
,
types
]
=
signature
.
substr
(
0
,
signature
.
length
-
1
).
split
(
'
(
'
)
const
signature
=
eventParsed
.
signatures
[
i
]
const
[
name
,
types
]
=
signature
.
substr
(
0
,
signature
.
length
-
1
).
split
(
'
(
'
)
const
calldata
=
eventParsed
.
calldatas
[
i
]
const
decoded
=
utils
.
defaultAbiCoder
.
decode
(
types
.
split
(
'
,
'
),
calldata
)
const
calldata
=
eventParsed
.
calldatas
[
i
]
const
decoded
=
utils
.
defaultAbiCoder
.
decode
(
types
.
split
(
'
,
'
),
calldata
)
return
{
target
,
return
{
functionSig
:
name
,
target
,
callData
:
decoded
.
join
(
'
,
'
),
functionSig
:
name
,
}
callData
:
decoded
.
join
(
'
,
'
),
}),
}
}
}),
})
}
})
.
reverse
()
setFormattedEvents
(
formattedEventData
)
setFormattedEvents
(
formattedEventData
)
})
})
.
catch
((
error
)
=>
{
.
catch
((
error
)
=>
{
...
@@ -118,55 +114,93 @@ export function useDataFromEventLogs() {
...
@@ -118,55 +114,93 @@ export function useDataFromEventLogs() {
}
}
// get data for all past and active proposals
// get data for all past and active proposals
export
function
useAllProposalData
()
{
export
function
useAllProposalData
()
:
ProposalData
[][]
{
const
proposalCount
=
useProposalCount
()
// fetch all governance contracts
const
govContract
=
useGovernanceContract
()
const
govContract
s
=
useGovernanceContracts
()
const
proposalIndexes
=
[]
// fetch the proposal count on the active contract
for
(
let
i
=
1
;
i
<=
(
proposalCount
??
0
);
i
++
)
{
const
proposalCount
=
useProposalCount
(
govContracts
[
govContracts
.
length
-
1
])
proposalIndexes
.
push
([
i
])
}
// get metadata from past events
// get all proposals for all contracts
const
formattedEvents
=
useDataFromEventLogs
()
const
proposalsIndicesByGovContract
=
[
[
1
,
2
,
3
,
4
],
// hardcoded for first governor alpha
typeof
proposalCount
===
'
number
'
?
new
Array
(
proposalCount
).
fill
(
0
).
map
((
_
,
i
)
=>
i
+
1
)
:
[],
// dynamic for current governor alpha
]
// get all proposal entities
// get all proposal entities
const
allProposals
=
useSingleContractMultipleData
(
govContract
,
'
proposals
'
,
proposalIndexes
)
const
allProposalsByGovContract
=
[
useSingleContractMultipleData
(
govContracts
[
0
],
'
proposals
'
,
proposalsIndicesByGovContract
[
0
].
map
((
i
)
=>
[
i
])
),
useSingleContractMultipleData
(
govContracts
[
1
],
'
proposals
'
,
proposalsIndicesByGovContract
[
1
].
map
((
i
)
=>
[
i
])
),
]
// get all proposal states
// get all proposal states
const
allProposalStates
=
useSingleContractMultipleData
(
govContract
,
'
state
'
,
proposalIndexes
)
const
allProposalStatesByGovContract
=
[
useSingleContractMultipleData
(
govContracts
[
0
],
'
state
'
,
proposalsIndicesByGovContract
[
0
].
map
((
i
)
=>
[
i
])
),
useSingleContractMultipleData
(
govContracts
[
1
],
'
state
'
,
proposalsIndicesByGovContract
[
1
].
map
((
i
)
=>
[
i
])
),
]
if
(
formattedEvents
&&
allProposals
&&
allProposalStates
)
{
// get metadata from past events
allProposals
.
reverse
()
const
formattedEventsByGovContract
=
[
useDataFromEventLogs
(
govContracts
[
0
]),
useDataFromEventLogs
(
govContracts
[
1
])]
allProposalStates
.
reverse
()
return
allProposals
const
returnData
:
ProposalData
[][]
=
[]
.
filter
((
p
,
i
)
=>
{
return
Boolean
(
p
.
result
)
&&
Boolean
(
allProposalStates
[
i
]?.
result
)
&&
Boolean
(
formattedEvents
[
i
])
for
(
let
governorIndex
=
0
;
governorIndex
<
allProposalsByGovContract
.
length
;
governorIndex
++
)
{
})
const
allProposals
=
allProposalsByGovContract
[
governorIndex
]
.
map
((
p
,
i
)
=>
{
const
allProposalStates
=
allProposalStatesByGovContract
[
governorIndex
]
const
description
=
PROPOSAL_DESCRIPTION_TEXT
[
allProposals
.
length
-
i
-
1
]
||
formattedEvents
[
i
].
description
const
formattedEvents
=
formattedEventsByGovContract
[
governorIndex
]
const
formattedProposal
:
ProposalData
=
{
id
:
allProposals
[
i
]?.
result
?.
id
.
toString
(),
if
(
title
:
description
?.
split
(
/# |
\n
/g
)[
1
]
||
'
Untitled
'
,
allProposals
?.
every
((
p
)
=>
Boolean
(
p
.
result
))
&&
description
:
description
||
'
No description.
'
,
allProposalStates
?.
every
((
p
)
=>
Boolean
(
p
.
result
))
&&
proposer
:
allProposals
[
i
]?.
result
?.
proposer
,
formattedEvents
?.
every
((
p
)
=>
Boolean
(
p
))
status
:
allProposalStates
[
i
]?.
result
?.[
0
]
??
ProposalState
.
Undetermined
,
)
{
forCount
:
parseFloat
(
ethers
.
utils
.
formatUnits
(
allProposals
[
i
]?.
result
?.
forVotes
.
toString
(),
18
)),
returnData
.
push
(
againstCount
:
parseFloat
(
ethers
.
utils
.
formatUnits
(
allProposals
[
i
]?.
result
?.
againstVotes
.
toString
(),
18
)),
allProposals
.
map
((
proposal
,
i
):
ProposalData
=>
{
startBlock
:
parseInt
(
allProposals
[
i
]?.
result
?.
startBlock
?.
toString
()),
let
description
=
formattedEvents
[
i
].
description
endBlock
:
parseInt
(
allProposals
[
i
]?.
result
?.
endBlock
?.
toString
()),
// overwrite broken description
details
:
formattedEvents
[
i
].
details
,
if
(
governorIndex
===
0
&&
i
===
2
)
description
=
UNISWAP_GRANTS_PROPOSAL_DESCRIPTION
}
return
formattedProposal
return
{
})
id
:
proposal
?.
result
?.
id
.
toString
(),
}
else
{
title
:
description
?.
split
(
/# |
\n
/g
)[
1
]
??
'
Untitled
'
,
return
[]
description
:
description
??
'
No description.
'
,
proposer
:
proposal
?.
result
?.
proposer
,
status
:
allProposalStates
[
i
]?.
result
?.[
0
]
??
ProposalState
.
Undetermined
,
forCount
:
parseFloat
(
ethers
.
utils
.
formatUnits
(
proposal
?.
result
?.
forVotes
.
toString
(),
18
)),
againstCount
:
parseFloat
(
ethers
.
utils
.
formatUnits
(
proposal
?.
result
?.
againstVotes
.
toString
(),
18
)),
startBlock
:
parseInt
(
proposal
?.
result
?.
startBlock
?.
toString
()),
endBlock
:
parseInt
(
proposal
?.
result
?.
endBlock
?.
toString
()),
details
:
formattedEvents
[
i
].
details
,
}
})
)
}
else
{
returnData
.
push
([])
}
}
}
return
returnData
}
}
export
function
useProposalData
(
id
:
string
):
ProposalData
|
undefined
{
export
function
useProposalData
(
id
:
string
):
ProposalData
|
undefined
{
const
allProposalData
=
useAllProposalData
()
// TODO don't hardcode for first gov alpha
const
allProposalData
=
useAllProposalData
()[
0
]
return
allProposalData
?.
find
((
p
)
=>
p
.
id
===
id
)
return
allProposalData
?.
find
((
p
)
=>
p
.
id
===
id
)
}
}
...
@@ -232,7 +266,10 @@ export function useVoteCallback(): {
...
@@ -232,7 +266,10 @@ export function useVoteCallback(): {
}
{
}
{
const
{
account
}
=
useActiveWeb3React
()
const
{
account
}
=
useActiveWeb3React
()
const
govContract
=
useGovernanceContract
()
// we only care about voting on the active governance contract
const
govContracts
=
useGovernanceContracts
()
const
govContract
=
govContracts
[
govContracts
.
length
-
1
]
const
addTransaction
=
useTransactionAdder
()
const
addTransaction
=
useTransactionAdder
()
const
voteCallback
=
useCallback
(
const
voteCallback
=
useCallback
(
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment