Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: wireup verifications #1232

Merged
merged 11 commits into from
Feb 14, 2025
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Features

- [#1232](https://github.com/alleslabs/celatone-frontend/pull/1232) Support EVM verification with multiparts and standard JSON input for both Solidity and Vyper
- [#1225](https://github.com/alleslabs/celatone-frontend/pull/1225) Validate EVM verification form options
- [#1226](https://github.com/alleslabs/celatone-frontend/pull/1226) Show nonce on EVM tx details
- [#1224](https://github.com/alleslabs/celatone-frontend/pull/1224) Support Vyper verification with contract code
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export const OptimizerConfiguration = <T extends FieldValues>({
<ControllerInput
width={125}
type="number"
name={`${name}.optimizerConfig.runs` as FieldPath<T>}
name={`${name}.runs` as FieldPath<T>}
isDisabled={!enabled}
control={control}
size="md"
Expand Down
16 changes: 16 additions & 0 deletions src/lib/services/types/verification/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,26 @@ type SubmitEvmVerifyBaseArgs = {
export type SubmitEvmVerifySolidityContractCodeArgs = SubmitEvmVerifyBaseArgs &
EvmContractVerifyForm["verifyForm"]["solidityContractCode"];

// MARK - Solidity Json Input
export type SubmitEvmVerifySolidityJsonInputArgs = SubmitEvmVerifyBaseArgs &
EvmContractVerifyForm["verifyForm"]["solidityJsonInput"];

// MARK - Solidity Upload Files
export type SubmitEvmVerifySolidityUploadFilesArgs = SubmitEvmVerifyBaseArgs &
EvmContractVerifyForm["verifyForm"]["solidityUploadFiles"];

// MARK - Vyper Contract Code
export type SubmitEvmVerifyVyperContractCodeArgs = SubmitEvmVerifyBaseArgs &
EvmContractVerifyForm["verifyForm"]["vyperContractCode"];

// MARK - Vyper Json Input
export type SubmitEvmVerifyVyperJsonInputArgs = SubmitEvmVerifyBaseArgs &
EvmContractVerifyForm["verifyForm"]["vyperJsonInput"];

// MARK - Vyper Upload File
export type SubmitEvmVerifyVyperUploadFilesArgs = SubmitEvmVerifyBaseArgs &
EvmContractVerifyForm["verifyForm"]["vyperUploadFile"];

export interface SubmitEvmVerifyArgs
extends Omit<SubmitEvmVerifyBaseArgs, "verifierUrl"> {
option: EvmVerifyOptions;
Expand Down
166 changes: 165 additions & 1 deletion src/lib/services/verification/evm/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import { CELATONE_VERIFICATION_API } from "env";
import {
SubmitEvmVerifyArgs,
SubmitEvmVerifySolidityContractCodeArgs,
SubmitEvmVerifySolidityJsonInputArgs,
SubmitEvmVerifySolidityUploadFilesArgs,
SubmitEvmVerifyVyperContractCodeArgs,
SubmitEvmVerifyVyperJsonInputArgs,
SubmitEvmVerifyVyperUploadFilesArgs,
} from "lib/services/types";
import {
EvmVerifyOptions,
Expand Down Expand Up @@ -37,6 +41,7 @@ export const getEvmVerifyInfo = async (
// Prepares data for Solidity & Vyper verification.
// ============================================

// ============== Solidity ==============
const submitEvmVerifySolidityContractCode = async ({
verifierUrl,
contractAddress,
Expand Down Expand Up @@ -93,6 +98,59 @@ const submitEvmVerifySolidityContractCode = async ({
);
};

const submitEvmVerifySolidityUploadFiles = async ({
verifierUrl,
evmVersion,
contractAddress,
chainId,
compilerVersion,
licenseType,
constructorArgs,
files,
optimizerConfig,
}: SubmitEvmVerifySolidityUploadFilesArgs) => {
if (files.length === 0)
throw new Error(
"At least one file is required (submitEvmVerifySolidityUploadFiles)"
);

const formData = new FormData();

formData.append("license", licenseType);
formData.append("language", "Solidity");
formData.append("bytecode_type", BYTECODE_TYPE);
formData.append("compiler_version", compilerVersion);
formData.append("constructor_arguments", constructorArgs.value);
formData.append(
"metadata",
JSON.stringify({
chain_id: chainId,
contract_address: contractAddress,
})
);
formData.append(
"settings",
JSON.stringify({
evmVersion,
optimizer: {
enabled: optimizerConfig.enabled,
runs: Number(optimizerConfig.runs),
},
})
);

files.forEach((file) => {
formData.append("files", file.file);
});

return axios.post(verifierUrl, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
});
};

// ============== Vyper ==============
const submitEvmVerifyVyperContractCode = async ({
verifierUrl,
contractAddress,
Expand Down Expand Up @@ -128,6 +186,89 @@ const submitEvmVerifyVyperContractCode = async ({
}
);

const submitEvmVerifyVyperUploadFiles = async ({
verifierUrl,
evmVersion,
contractAddress,
chainId,
compilerVersion,
licenseType,
constructorArgs,
file,
}: SubmitEvmVerifyVyperUploadFilesArgs) => {
if (!file)
throw new Error("File is required (submitEvmVerifyVyperUploadFiles)");

const formData = new FormData();

formData.append("license", licenseType);
formData.append("language", "Vyper");
formData.append("bytecode_type", BYTECODE_TYPE);
formData.append("compiler_version", compilerVersion);
formData.append("constructor_arguments", constructorArgs.value);
formData.append(
"metadata",
JSON.stringify({
chain_id: chainId,
contract_address: contractAddress,
})
);
formData.append(
"settings",
JSON.stringify({
evmVersion,
})
);
formData.append("files", file);

return axios.post(verifierUrl, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
});
};

// ============= JSON Input For Both ==============
const submitEvmVerifyJsonInput = async ({
verifierUrl,
contractAddress,
chainId,
compilerVersion,
licenseType,
jsonFile,
constructorArgs,
}:
| SubmitEvmVerifySolidityJsonInputArgs
| SubmitEvmVerifyVyperJsonInputArgs) => {
if (!jsonFile)
throw new Error("JSON file is required (submitEvmVerifyJsonInput)");
const reader = new FileReader();
const jsonContent = await new Promise<string>((resolve, reject) => {
reader.onload = () => resolve(reader.result as string);
reader.onerror = () => reject(reader.error);
reader.readAsText(jsonFile);
});
return axios.post(
verifierUrl,
{
license: licenseType,
bytecode_type: BYTECODE_TYPE,
compiler_version: compilerVersion,
constructor_arguments: constructorArgs.value,
metadata: {
chain_id: chainId,
contract_address: contractAddress,
},
input: jsonContent,
},
{
headers: {
"Content-Type": "application/json",
},
}
);
};

export const submitEvmVerify = async ({
option,
verifyForm,
Expand All @@ -142,13 +283,36 @@ export const submitEvmVerify = async ({
...rest,
...verifyForm.solidityContractCode,
});
case EvmVerifyOptions.SolidityJsonInput:
return submitEvmVerifyJsonInput({
verifierUrl,
...rest,
...verifyForm.solidityJsonInput,
});
case EvmVerifyOptions.SolidityUploadFiles:
return submitEvmVerifySolidityUploadFiles({
verifierUrl,
...rest,
...verifyForm.solidityUploadFiles,
});
case EvmVerifyOptions.VyperJsonInput:
return submitEvmVerifyJsonInput({
verifierUrl,
...rest,
...verifyForm.vyperJsonInput,
});
case EvmVerifyOptions.VyperContractCode:
return submitEvmVerifyVyperContractCode({
verifierUrl,
...rest,
...verifyForm.vyperContractCode,
});
// TODO: Implement other options
case EvmVerifyOptions.VyperUploadFile:
return submitEvmVerifyVyperUploadFiles({
verifierUrl,
...rest,
...verifyForm.vyperUploadFile,
});
default:
throw new Error(`Unsupported option: ${option}`);
}
Expand Down
3 changes: 2 additions & 1 deletion src/lib/services/verification/evm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { CELATONE_QUERY_KEYS } from "lib/app-provider/env";
import { getEvmVerifyConfig, getEvmVerifyInfo, submitEvmVerify } from "./api";
import { HexAddr20, Option } from "lib/types";
import { useCurrentChain } from "lib/app-provider";
import { isHex20Bytes } from "lib/utils";

export const useEvmVerifyConfig = () =>
useQuery({
Expand All @@ -24,7 +25,7 @@ export const useEvmVerifyInfo = (contractAddress: Option<HexAddr20>) => {
throw new Error("contractAddress is undefined (useEvmVerifyInfo)");
return getEvmVerifyInfo(chainId, contractAddress);
},
enabled: !!contractAddress,
enabled: !!contractAddress && isHex20Bytes(contractAddress),
refetchOnWindowFocus: false,
retry: 1,
staleTime: Infinity,
Expand Down