mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
Embed verify.sourcify.dev as Sourcify verification method (#3027)
* Embed verify.sourcify.dev as Sourcify verification method * Rephrase Sourcify option inside verification method selector * Mock iframe content in test * Fix test * Update screenshot for test
This commit is contained in:
@@ -3,7 +3,6 @@ import React from 'react';
|
||||
import type { SmartContractVerificationConfig } from 'types/client/contract';
|
||||
|
||||
import { ENVS_MAP } from 'playwright/fixtures/mockEnvs';
|
||||
import * as socketServer from 'playwright/fixtures/socketServer';
|
||||
import { test, expect } from 'playwright/lib';
|
||||
|
||||
import ContractVerificationForm from './ContractVerificationForm';
|
||||
@@ -102,49 +101,26 @@ test('standard input json method', async({ render, page }) => {
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test.describe('sourcify', () => {
|
||||
test.describe.configure({ mode: 'serial', timeout: 20_000 });
|
||||
|
||||
test('with multiple contracts', async({ render, page, createSocket }) => {
|
||||
const component = await render(
|
||||
<ContractVerificationForm config={ formConfig } hash={ hash }/>,
|
||||
{ hooksConfig },
|
||||
{ withSocket: true },
|
||||
);
|
||||
|
||||
// select method
|
||||
await component.locator('button').filter({ hasText: 'Verification method' }).click();
|
||||
await page.getByRole('option', { name: 'Solidity (Sourcify)' }).click();
|
||||
|
||||
await page.getByText(/drop files/i).click();
|
||||
await page.locator('input[name="sources"]').setInputFiles([
|
||||
'./playwright/mocks/file_mock_1.json',
|
||||
'./playwright/mocks/file_mock_2.json',
|
||||
'./playwright/mocks/file_mock_with_very_long_name.json',
|
||||
]);
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
|
||||
const socket = await createSocket();
|
||||
const channel = await socketServer.joinChannel(socket, `addresses:${ hash.toLowerCase() }`);
|
||||
|
||||
await page.getByRole('button', { name: /verify/i }).click();
|
||||
|
||||
socketServer.sendMessage(socket, channel, 'verification_result', {
|
||||
status: 'error',
|
||||
errors: {
|
||||
// eslint-disable-next-line max-len
|
||||
files: [ 'Detected 5 contracts (ERC20, IERC20, IERC20Metadata, Context, MockERC20), but can only verify 1 at a time. Please choose a main contract and click Verify again.' ],
|
||||
},
|
||||
test('sourcify method', async({ render, page }) => {
|
||||
await page.route('https://verify.sourcify.dev/widget**', (route) => {
|
||||
return route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'text/html',
|
||||
path: './playwright/mocks/page.html',
|
||||
});
|
||||
|
||||
await component.locator('button').filter({ hasText: 'Contract name*' }).click();
|
||||
const contractNameOption = page.getByRole('option', { name: 'MockERC20' });
|
||||
|
||||
await expect(contractNameOption).toBeVisible();
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
const component = await render(
|
||||
<ContractVerificationForm config={ formConfig } hash={ hash }/>,
|
||||
{ hooksConfig },
|
||||
{ withSocket: true },
|
||||
);
|
||||
|
||||
// select method
|
||||
await component.locator('button').filter({ hasText: 'Verification method' }).click();
|
||||
await page.getByRole('option', { name: 'Sourcify (Solidity or Vyper)' }).click();
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test('multi-part files method', async({ render, page }) => {
|
||||
|
||||
@@ -205,7 +205,7 @@ const ContractVerificationForm = ({ method: methodFromQuery, config, hash }: Pro
|
||||
</Grid>
|
||||
{ content }
|
||||
{ formState.errors.root?.message && <Text color="text.error" mt={ 4 } fontSize="sm" whiteSpace="pre-wrap">{ formState.errors.root.message }</Text> }
|
||||
{ Boolean(method) && methodValue !== 'solidity-hardhat' && methodValue !== 'solidity-foundry' && (
|
||||
{ Boolean(method) && methodValue !== 'solidity-hardhat' && methodValue !== 'solidity-foundry' && methodValue !== 'sourcify' && (
|
||||
<Button
|
||||
size="md"
|
||||
type="submit"
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
@@ -1,61 +0,0 @@
|
||||
import { createListCollection } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
|
||||
import type { FormFields } from '../types';
|
||||
|
||||
import type { SelectOption } from 'toolkit/chakra/select';
|
||||
import { FormFieldSelect } from 'toolkit/components/forms/fields/FormFieldSelect';
|
||||
import { useUpdateEffect } from 'toolkit/hooks/useUpdateEffect';
|
||||
|
||||
import ContractVerificationFormRow from '../ContractVerificationFormRow';
|
||||
|
||||
const SOURCIFY_ERROR_REGEXP = /\(([^()]*)\)/;
|
||||
|
||||
const ContractVerificationFieldContractIndex = () => {
|
||||
const [ options, setOptions ] = React.useState<Array<SelectOption>>([]);
|
||||
const { formState, watch } = useFormContext<FormFields>();
|
||||
|
||||
const sources = watch('sources');
|
||||
const sourcesError = 'sources' in formState.errors ? formState.errors.sources?.message : undefined;
|
||||
|
||||
useUpdateEffect(() => {
|
||||
if (!sourcesError) {
|
||||
return;
|
||||
}
|
||||
|
||||
const matchResult = sourcesError.match(SOURCIFY_ERROR_REGEXP);
|
||||
const parsedMethods = matchResult?.[1].split(',');
|
||||
if (!Array.isArray(parsedMethods) || parsedMethods.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newOptions = parsedMethods.map((option, index) => ({ label: option, value: String(index + 1) }));
|
||||
setOptions(newOptions);
|
||||
}, [ sourcesError ]);
|
||||
|
||||
useUpdateEffect(() => {
|
||||
setOptions([]);
|
||||
}, [ sources ]);
|
||||
|
||||
const collection = React.useMemo(() => {
|
||||
return createListCollection({ items: options });
|
||||
}, [ options ]);
|
||||
|
||||
if (options.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ContractVerificationFormRow>
|
||||
<FormFieldSelect<FormFields, 'contract_index'>
|
||||
name="contract_index"
|
||||
placeholder="Contract name"
|
||||
collection={ collection }
|
||||
required
|
||||
/>
|
||||
</ContractVerificationFormRow>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ContractVerificationFieldContractIndex);
|
||||
@@ -1,22 +1,27 @@
|
||||
import React from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
|
||||
import type { FormFields } from '../types';
|
||||
|
||||
import config from 'configs/app';
|
||||
|
||||
import ContractVerificationMethod from '../ContractVerificationMethod';
|
||||
import ContractVerificationFieldContractIndex from '../fields/ContractVerificationFieldContractIndex';
|
||||
import ContractVerificationFieldSources from '../fields/ContractVerificationFieldSources';
|
||||
|
||||
const FILE_TYPES = [ '.json' as const, '.sol' as const ];
|
||||
|
||||
const ContractVerificationSourcify = () => {
|
||||
const { watch } = useFormContext<FormFields>();
|
||||
const address = watch('address');
|
||||
|
||||
const iframeUrl = `https://verify.sourcify.dev/widget?chainId=${ config.chain.id }&address=${ address }`;
|
||||
|
||||
return (
|
||||
<ContractVerificationMethod title="Contract verification via Solidity (Sourcify)">
|
||||
<ContractVerificationFieldSources
|
||||
fileTypes={ FILE_TYPES }
|
||||
multiple
|
||||
required
|
||||
title="Sources and Metadata JSON"
|
||||
hint="Upload all Solidity contract source files and JSON metadata file(s) created during contract compilation."
|
||||
<ContractVerificationMethod title="Contract verification via Sourcify (Solidity or Vyper)">
|
||||
<iframe
|
||||
src={ iframeUrl }
|
||||
sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"
|
||||
referrerPolicy="no-referrer"
|
||||
width="100%"
|
||||
height="840"
|
||||
/>
|
||||
<ContractVerificationFieldContractIndex/>
|
||||
</ContractVerificationMethod>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,7 +5,6 @@ import type {
|
||||
FormFields,
|
||||
FormFieldsFlattenSourceCode,
|
||||
FormFieldsMultiPartFile,
|
||||
FormFieldsSourcify,
|
||||
FormFieldsStandardInput,
|
||||
FormFieldsStandardInputZk,
|
||||
FormFieldsStylusGitHubRepo,
|
||||
@@ -38,7 +37,7 @@ export const SUPPORTED_VERIFICATION_METHODS: Array<SmartContractVerificationMeth
|
||||
export const METHOD_LABELS: Record<SmartContractVerificationMethod, string> = {
|
||||
'flattened-code': 'Solidity (Single file)',
|
||||
'standard-input': 'Solidity (Standard JSON input)',
|
||||
sourcify: 'Solidity (Sourcify)',
|
||||
sourcify: 'Sourcify (Solidity or Vyper)',
|
||||
'multi-part': 'Solidity (Multi-part files)',
|
||||
'vyper-code': 'Vyper (Contract)',
|
||||
'vyper-multi-part': 'Vyper (Multi-part files)',
|
||||
@@ -237,16 +236,6 @@ export function prepareRequestBody(data: FormFields): FetchParams['body'] {
|
||||
return body;
|
||||
}
|
||||
|
||||
case 'sourcify': {
|
||||
const _data = data as FormFieldsSourcify;
|
||||
const body = new FormData();
|
||||
addFilesToFormData(body, _data.sources, 'files');
|
||||
body.set('chosen_contract_index', _data.contract_index?.value ?? '0');
|
||||
_data.license_type && body.set('license_type', _data.license_type?.[0] ?? defaultLicenseType);
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
case 'multi-part': {
|
||||
const _data = data as FormFieldsMultiPartFile;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user