1- import { ReactElement , useEffect , useMemo , useState } from 'react'
1+ import { ReactElement , useCallback , useEffect , useMemo , useState } from 'react'
22import { useSelector } from 'react-redux'
33import styled from 'styled-components'
4- import { Table , TableHeader , TableRow , Text } from '@gnosis.pm/safe-react-components'
4+ import { ButtonLink , Table , TableHeader , TableRow , Text } from '@gnosis.pm/safe-react-components'
55
66import Block from 'src/components/layout/Block'
77import Heading from 'src/components/layout/Heading'
@@ -10,7 +10,12 @@ import { lg } from 'src/theme/variables'
1010import { currentSafeWithNames } from 'src/logic/safe/store/selectors'
1111import { getChainInfo } from 'src/config'
1212import { checksumAddress } from 'src/utils/checksumAddress'
13+ import { getWeb3 } from 'src/logic/wallets/getWeb3'
14+ import { AddDelegateModal } from 'src/routes/safe/components/Settings/Delegates/AddDelegateModal'
15+ import { userAccountSelector } from 'src/logic/wallets/store/selectors'
16+ import { keccak256 , fromAscii } from 'web3-utils'
1317
18+ // TODO: these types will come from the Client GW SDK once #72 is merged
1419type Page < T > = {
1520 next ?: string
1621 previous ?: string
@@ -37,8 +42,10 @@ const StyledHeading = styled(Heading)`
3742
3843const Delegates = ( ) : ReactElement => {
3944 const { address : safeAddress } = useSelector ( currentSafeWithNames )
40- const [ delegatesList , setDelegatesList ] = useState < DelegateResponse [ 'results' ] > ( [ ] )
45+ const userAccount = useSelector ( userAccountSelector )
4146 const { transactionService } = getChainInfo ( )
47+ const [ delegatesList , setDelegatesList ] = useState < DelegateResponse [ 'results' ] > ( [ ] )
48+ const [ addDelegateModalOpen , setAddDelegateModalOpen ] = useState < boolean > ( false )
4249
4350 const headerCells : TableHeader [ ] = useMemo (
4451 ( ) => [
@@ -50,9 +57,7 @@ const Delegates = (): ReactElement => {
5057 )
5158 const rows : TableRow [ ] = useMemo ( ( ) => [ ] , [ ] )
5259
53- useEffect ( ( ) => {
54- if ( ! safeAddress || ! transactionService ) return
55-
60+ const fetchDelegates = useCallback ( ( ) => {
5661 const url = `${ transactionService } /api/v1/safes/${ safeAddress } /delegates/`
5762 fetch ( url )
5863 . then ( ( response ) => response . json ( ) )
@@ -61,6 +66,22 @@ const Delegates = (): ReactElement => {
6166 } )
6267 } , [ safeAddress , transactionService ] )
6368
69+ const getSignature = async ( delegate ) => {
70+ const totp = Math . floor ( Date . now ( ) / 1000 / 3600 )
71+ const web3 = getWeb3 ( )
72+ const msg = checksumAddress ( delegate ) + totp
73+
74+ const hashMessage = keccak256 ( fromAscii ( msg ) )
75+ const signature = await web3 . eth . sign ( hashMessage , userAccount )
76+
77+ return signature
78+ }
79+
80+ useEffect ( ( ) => {
81+ if ( ! safeAddress || ! transactionService ) return
82+ fetchDelegates ( )
83+ } , [ fetchDelegates , safeAddress , transactionService ] )
84+
6485 useEffect ( ( ) => {
6586 if ( delegatesList . length ) {
6687 let index = 0
@@ -77,12 +98,54 @@ const Delegates = (): ReactElement => {
7798 }
7899 } , [ delegatesList , rows ] )
79100
101+ const handleAddDelegate = async ( { address, label } ) => {
102+ // close Add delegate modal
103+ setAddDelegateModalOpen ( false )
104+
105+ const delegate = checksumAddress ( address )
106+
107+ const signature = await getSignature ( delegate )
108+ const requestOptions = {
109+ method : 'POST' ,
110+ headers : { 'Content-type' : 'application/json' } ,
111+ body : JSON . stringify ( {
112+ safe : safeAddress ,
113+ delegate : delegate ,
114+ signature : signature ,
115+ label : label ,
116+ } ) ,
117+ }
118+
119+ const url = `${ transactionService } /api/v1/safes/${ safeAddress } /delegates/`
120+ fetch ( url , requestOptions )
121+ . then ( ( response ) => response . json ( ) )
122+ . then ( ( ) => {
123+ fetchDelegates ( )
124+ } )
125+ }
126+
80127 return (
81128 < StyledBlock >
82129 < StyledHeading tag = "h2" > Manage Safe Delegates</ StyledHeading >
83130 < Paragraph > Get, add and delete delegates.</ Paragraph >
131+ < ButtonLink
132+ onClick = { ( ) => {
133+ setAddDelegateModalOpen ( true )
134+ } }
135+ color = "primary"
136+ iconType = "add"
137+ iconSize = "sm"
138+ textSize = "xl"
139+ >
140+ Add delegate
141+ </ ButtonLink >
84142 < pre > { JSON . stringify ( delegatesList , undefined , 2 ) } </ pre >
85143 < Table headers = { headerCells } rows = { rows } />
144+ < AddDelegateModal
145+ isOpen = { addDelegateModalOpen }
146+ onClose = { ( ) => setAddDelegateModalOpen ( false ) }
147+ onSubmit = { handleAddDelegate }
148+ />
86149 </ StyledBlock >
87150 )
88151}
0 commit comments