Proxy implementation (#216)
* Proxy implementation * Support one1 addresses * Add Read as proxy and Write as proxy features * Improve verification UI * Fix input width * Minor lifecycle fixpull/225/head
parent
a27920c894
commit
43fb25e464
@ -0,0 +1,196 @@ |
||||
import React, { useEffect, useState } from "react"; |
||||
import {Box, Heading, Select, Spinner, Text, TextArea, TextInput, Tip} from "grommet"; |
||||
import { Alert, StatusGood } from "grommet-icons"; |
||||
import { useHistory } from "react-router-dom"; |
||||
import useQuery from "../../hooks/useQuery"; |
||||
import { |
||||
assignProxyImplementation, |
||||
getProxyImplementation |
||||
} from "../../api/client"; |
||||
import {Address, BaseContainer, BasePage, Button} from "../../components/ui"; |
||||
import styled from "styled-components"; |
||||
import {getAddress} from "../../utils"; |
||||
import {ISourceCode, loadSourceCode} from "../../api/explorerV1"; |
||||
|
||||
export const ActionButton = styled(Button)` |
||||
font-size: 14px; |
||||
padding: 8px 8px 5px 8px; |
||||
font-weight: 500; |
||||
`;
|
||||
|
||||
export function VerifyProxyContract() { |
||||
const query = useQuery(); |
||||
const history = useHistory(); |
||||
const queryAddress = query.get('a') || ''; |
||||
|
||||
const [isLoading, setIsLoading] = useState(false) |
||||
const [contractAddress, setContractAddress] = useState(queryAddress) |
||||
const [implementationAddress, setImplementationAddress] = useState('') |
||||
const [implementationCode, setImplementationCode] = useState<ISourceCode>() |
||||
const [contractCode, setContractCode] = useState<ISourceCode>() |
||||
const [verificationError, setVerificationError] = useState('') |
||||
const [isVerified, setIsVerified] = useState(false) |
||||
|
||||
const enrichAddress = (address: string) => { |
||||
let a = address.trim().toLowerCase() |
||||
if(address.startsWith('one1')) { // convert one1 to 0x before send request to backend
|
||||
try { |
||||
a = getAddress(address).basicHex |
||||
} catch (e) {} |
||||
} |
||||
return a |
||||
} |
||||
|
||||
const setDefaultState = () => { |
||||
setImplementationAddress('') |
||||
setVerificationError('') |
||||
setIsVerified(false) |
||||
} |
||||
|
||||
const validateAddress = (address: string) => { |
||||
if(address.length === 0) { |
||||
return '' |
||||
} else { |
||||
if(!address.startsWith('0x') && !address.startsWith('one1')) { |
||||
return 'Address should start with 0x or one1' |
||||
} |
||||
if(address.length != 42) { |
||||
return 'Address must be 42 characters long' |
||||
} |
||||
} |
||||
return '' |
||||
} |
||||
|
||||
const verifyContract = async () => { |
||||
setImplementationAddress('') |
||||
const err = validateAddress(contractAddress) |
||||
if(err) { |
||||
setVerificationError(err) |
||||
} else { |
||||
try { |
||||
setIsLoading(true) |
||||
setVerificationError('') |
||||
const formattedContractAddress = enrichAddress(contractAddress) |
||||
const implContract = await getProxyImplementation([0, formattedContractAddress]); |
||||
if(implContract) { |
||||
try { |
||||
const code = await loadSourceCode(formattedContractAddress, 0) |
||||
const implCode = await loadSourceCode(implContract.address, 0) |
||||
setContractCode(code) |
||||
setImplementationCode(implCode) |
||||
} catch (e) { |
||||
setVerificationError('Proxy or implementation contract source code is not verified. Please verify and publish the contract source before proceeding with this proxy verification.') |
||||
} |
||||
setImplementationAddress(implContract.address) |
||||
} else { |
||||
setImplementationAddress('') |
||||
setVerificationError('This contract does not look like it contains any delegatecall opcode sequence.') |
||||
} |
||||
} catch (e) { |
||||
console.error('Unable to verify contract', e) |
||||
setVerificationError('Cannot verify contract address, try again later') |
||||
} finally { |
||||
setIsLoading(false) |
||||
} |
||||
} |
||||
} |
||||
|
||||
const applyImplementationAddress = async () => { |
||||
const contract = await assignProxyImplementation([0, enrichAddress(contractAddress), implementationAddress]) |
||||
setDefaultState() |
||||
setIsVerified(true) |
||||
} |
||||
|
||||
const onChangeInput = (value: string) => { |
||||
setContractAddress(value) |
||||
setDefaultState() |
||||
} |
||||
|
||||
return ( |
||||
<BaseContainer pad={{ horizontal: "0" }}> |
||||
<Heading size="small" margin={{ bottom: "medium", top: "0" }}> |
||||
Proxy Contract Verification |
||||
</Heading> |
||||
<BasePage pad={"small"} style={{ overflow: "inherit" }}> |
||||
<Box pad={'16px'} gap={'16px'} width={'600px'}> |
||||
<Box direction="row" justify={'start'} wrap> |
||||
<Box margin={'0'}> |
||||
<Text size={'small'}>Please enter the Proxy Contract Address you would like to verify</Text> |
||||
<Box direction="row"> |
||||
<Box width={'600px'}> |
||||
<TextInput |
||||
placeholder={"Contract address"} |
||||
onChange={(evt: React.ChangeEvent<HTMLInputElement>) => onChangeInput(evt.currentTarget.value)} |
||||
value={contractAddress} |
||||
disabled={false} |
||||
/> |
||||
</Box> |
||||
</Box> |
||||
</Box> |
||||
</Box> |
||||
{!implementationAddress && |
||||
<Box direction={'column'} gap={'16px'}> |
||||
{verificationError && |
||||
<Box round={'8px'} background={'backgroundError'} pad={'16px'}> |
||||
<Text color={'text'} size={'small'}> |
||||
{verificationError} |
||||
</Text> |
||||
</Box> |
||||
} |
||||
{isVerified && |
||||
<Box round={'8px'} background={'backgroundSuccess'} pad={'16px'}> |
||||
<Text color={'successText'} size={'small'}> |
||||
Successfully saved. Feel free to return to the address <Address address={contractAddress} hideCopyBtn={true} /> to view updates. |
||||
</Text> |
||||
</Box> |
||||
} |
||||
<Box direction={'row'} gap={'16px'}> |
||||
<Box width={'small'}> |
||||
<ActionButton |
||||
disabled={!contractAddress || isLoading} |
||||
onClick={verifyContract}> |
||||
Verify |
||||
</ActionButton> |
||||
</Box> |
||||
<Box width={'small'}> |
||||
<ActionButton |
||||
disabled={!contractAddress} |
||||
onClick={() => { |
||||
setDefaultState() |
||||
setContractAddress('') |
||||
} |
||||
}>Clear</ActionButton> |
||||
</Box> |
||||
</Box> |
||||
</Box> |
||||
} |
||||
{implementationAddress && |
||||
<Box direction={'column'} gap={'16px'}> |
||||
<Box> |
||||
<Text size={'small'}>The proxy's implementation contract is found at:</Text> |
||||
<Text size={'small'} weight={'bold'} color={'successText'}> |
||||
<Address address={implementationAddress} /> |
||||
</Text> |
||||
</Box> |
||||
{verificationError && |
||||
<Box round={'8px'} background={'backgroundError'} pad={'16px'}> |
||||
<Text color={'text'} size={'small'}> |
||||
{verificationError} |
||||
</Text> |
||||
</Box> |
||||
} |
||||
<Box direction={'row'} gap={'16px'}> |
||||
<Box width={'small'}> |
||||
<ActionButton |
||||
disabled={isLoading || !implementationCode || !contractCode} |
||||
onClick={applyImplementationAddress}>Save</ActionButton> |
||||
</Box> |
||||
<Box width={'small'}><ActionButton onClick={setDefaultState}>Clear</ActionButton></Box> |
||||
</Box> |
||||
</Box> |
||||
} |
||||
</Box> |
||||
</BasePage> |
||||
</BaseContainer> |
||||
) |
||||
} |
Loading…
Reference in new issue