0xweb
Version:
Contract package manager and other web3 tools
215 lines (192 loc) • 8.38 kB
text/typescript
import { File } from 'atma-io';
import { $require } from '@dequanto/utils/$require';
import { TEth } from '@dequanto/models/TEth';
import { IVerifier } from './IVerifier';
import { IBlockchainTransferEvent } from '../IBlockchainExplorer';
import { $platform } from '@dequanto/utils/$platform';
import { TPlatform } from '@dequanto/models/TPlatform';
import { TExplorer, TExplorerDefinition } from '@dequanto/models/TExplorer';
const PATH_ROOT = `./data/0xc/verification`;
const PATH_TEMPLATE = `${PATH_ROOT}/template.html`;
const PATH_TEMPLATE_PROXY = `${PATH_ROOT}/template-proxy.html`;
const TEMPLATE_DEFAULT = `
<h1>__name__</h1>
<form method="post" action="__host__">
<input type="hidden" name="apikey" value="__apikey__" />
<input type="hidden" name="module" value="contract" />
<input type="hidden" name="action" value="verifysourcecode" />
<input
type="hidden"
name="contractaddress"
value="__contractaddress__"
/>
<input type="hidden" name="sourceCode" />
<input type="hidden" name="codeformat" value="solidity-standard-json-input" />
<input
type="hidden"
name="contractname"
value="__contractname__"
/>
<input type="hidden" name="compilerversion" value="v0.8.20+commit.a1b79de6" />
<input type="hidden" name="optimizationUsed" value="1" />
<input type="hidden" name="runs" value="200" />
<input type="hidden" name="constructorArguements" value="__constructorArguements__" />
<input type="submit" value="Submit" />
</form>
<script>
var data = __JSON__;
document.querySelector('input[name="sourceCode"]').value = JSON.stringify(data);
</script>
`;
const TEMPLATE_PROXY_DEFAULT = `
<h1>__name__</h1>
<form method="post" action="__host__">
<input type="hidden" name="apikey" value="__apikey__" />
<input type="hidden" name="module" value="contract" />
<input type="hidden" name="action" value="verifyproxycontract" />
<input
type="hidden"
name="address"
value="__contractaddress__"
/>
<input
type="hidden"
name="expectedimplementation"
value="__expectedImplementation__"
/>
<input type="submit" value="Submit" />
</form>
`;
export class FsHtmlVerifier implements IVerifier {
private key: string;
private enabled: boolean;
constructor (public platform: TPlatform, public config: TExplorer) {
$require.notEmpty(platform, `Argument platform is required`);
$require.notNull(config, `Config is required for ${platform}`);
this.enabled = Boolean(config.api);
this.key = $platform.toPath(platform ?? /** fallback */ 'eth');
}
async submitContractVerification(contractData: {
address: `0x${string}`;
sourceCode: any;
contractName: any;
compilerVersion: any;
optimizer?: { enabled?: boolean; runs: number; };
arguments?: `0x${string}`;
}): Promise<string> {
if (this.enabled) {
await this.saveContractVerification(contractData);
}
return null;
}
private async saveContractVerification(contractData: {
address: `0x${string}`;
sourceCode: any;
contractName: any;
compilerVersion: any;
optimizer?: { enabled?: boolean; runs: number; };
arguments?: `0x${string}`;
}): Promise<string> {
let name = this.extractContractName(contractData);
let hostKey = this.extractHostKey(this.config);
let filename = `${PATH_ROOT}/${this.key}/${name}-${contractData.address}-${hostKey}.html`;
let filenameSources = filename.replace(/html$/, 'json');
let template = await File.existsAsync(PATH_TEMPLATE)
? await File.readAsync<string>(PATH_TEMPLATE)
: TEMPLATE_DEFAULT;
let html = template
.replace('__host__', this.config.api.url)
.replace('__name__', contractData.contractName)
.replace('__JSON__', contractData.sourceCode)
.replace('__contractaddress__', contractData.address)
.replace('__contractname__', contractData.contractName)
.replace('__apikey__', this.config.api.key ?? '')
.replace('__constructorArguements__', contractData.arguments?.replace(/^0x/, '') || '');
if (contractData.contractName.includes('Utils/Proxy')) {
// fix Proxy, rename contract duplicates
html = html.replaceAll('contracts/Utils/Proxy.sol:Proxy', 'contracts/Utils/TUProxy.sol:TUProxy');
html = html.replace('Utils/Proxy.sol":', 'Utils/TUProxy.sol":');
html = html.replace('contract Proxy is ', 'contract TUProxy is ');
}
await File.writeAsync(filename, html);
await File.writeAsync(filenameSources, contractData.sourceCode);
return null;
}
async checkContractVerificationSubmission(submission: { guid: any; }): Promise<string> {
return `verified`;
}
async submitContractProxyVerification(contractData: { address: `0x${string}`; expectedImplementation?: `0x${string}`; }): Promise<string> {
if (this.enabled) {
await this.saveProxyVerification(contractData);
}
return null;
}
getContractSource (address: TEth.Address): Promise<{
SourceCode: {
contractName: string
files: {
[filename: string]: {
content: string
}
}
}
ContractName: string
ABI: string
}> {
return null;
}
private async saveProxyVerification (contractData: { address: `0x${string}`; expectedImplementation?: `0x${string}`; }) {
let hostKey = this.extractHostKey(this.config);
let template = await await File.existsAsync(PATH_TEMPLATE_PROXY)
? await File.readAsync<string>(PATH_TEMPLATE_PROXY)
: TEMPLATE_PROXY_DEFAULT;
let name = this.extractContractName(contractData, 'proxy');
let filename = `${PATH_ROOT}/${this.key}/${name}-${contractData.address}-${hostKey}.html`;
let html = template
.replace('__host__', this.config.api.url)
.replace('__contractaddress__', contractData.address)
.replace('__apikey__', this.config.api.key ?? '')
.replace('__expectedImplementation__', contractData.expectedImplementation ?? '')
;
await File.writeAsync(filename, html);
return "guid";
}
async checkContractProxyVerificationSubmission(submission: { guid: any; }): Promise<string> {
return `verified`;
}
getTransactions(address: `0x${string}`, params?: { fromBlockNumber?: number; page?: number; size?: number; }): Promise<TEth.DataLike<TEth.Tx>[]> {
throw new Error('Method not implemented.');
}
getTransactionsAll(address: `0x${string}`): Promise<TEth.DataLike<TEth.Tx>[]> {
throw new Error('Method not implemented.');
}
getInternalTransactions(address: `0x${string}`, params?: { fromBlockNumber?: number; page?: number; size?: number; }): Promise<TEth.DataLike<TEth.Tx>[]> {
throw new Error('Method not implemented.');
}
getInternalTransactionsAll(address: `0x${string}`): Promise<TEth.DataLike<TEth.Tx>[]> {
throw new Error('Method not implemented.');
}
getErc20Transfers(address: `0x${string}`, fromBlockNumber?: number): Promise<IBlockchainTransferEvent[]> {
throw new Error('Method not implemented.');
}
getErc20TransfersAll(address: `0x${string}`, fromBlockNumber?: number): Promise<IBlockchainTransferEvent[]> {
throw new Error('Method not implemented.');
}
registerAbi(abis: { name: any; address: any; abi: any; }[]) {
throw new Error('Method not implemented.');
}
private extractHostKey (config: TExplorer) {
let hostKey = /(?<hostKey>[^\.]+)\.\w+($|\/)/.exec(config.api?.url ?? config.url).groups.hostKey;
return hostKey;
}
private extractContractName (contractData: { address: `0x${string}`; contractName?: string; }, defaultPfx?: string) {
if (contractData.contractName) {
let name = /:(\w+)$/.exec(contractData.contractName)[1];
if (name) {
return name;
}
}
let pfx = defaultPfx ? (`${defaultPfx}-`) : '';
return `${pfx}${contractData.address}`;
}
}