From b978931a0d78f492507f8f3ab62885efc5c9a52c Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Wed, 8 Jan 2025 15:50:28 +0100 Subject: [PATCH 1/7] commit to create branch --- packages/lib-sourcify/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/lib-sourcify/README.md b/packages/lib-sourcify/README.md index c02ca7fe4..67c50356f 100644 --- a/packages/lib-sourcify/README.md +++ b/packages/lib-sourcify/README.md @@ -1,3 +1,5 @@ +// TODO delete this + # lib-sourcify [![codecov](https://codecov.io/gh/ethereum/sourcify/branch/staging/graph/badge.svg?token=eN6XDAwWfV&flag=lib-sourcify)](https://codecov.io/gh/ethereum/sourcify) From e80c6557d6d922ec346ffc9306d2f6f3e3240736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kaan=20Uzdo=C4=9Fan?= Date: Wed, 15 Jan 2025 14:21:55 +0100 Subject: [PATCH 2/7] Add Validation classes and types (#1851) * Init class * Init SolidityMetadataContracts, split zipUtils and processFiles * SolidityMetadataContract: create dummy Compilation, add fetchMissing methods * Add tests for zipUtils, Fix directories being in `files` array * Add fetchUtils tests * Run tests both under test/*.spec.ts and in subfolders test/**/*.spec.ts * Remove `outputSelection` to be handled by the Compilation class * Fetch missing before creating the Compilation * Rename everything `check..` to `create...` in validation ie. SolidityMetadataContract * Try first assembling the contract without variations, only try after if it doesn't work * Rename metadata2Provided to metadataPathToProvidedFilePath --- packages/lib-sourcify/package.json | 2 +- .../Validation/SolidityMetadataContract.ts | 383 ++++++++++++++++++ .../lib-sourcify/src/Validation/fetchUtils.ts | 136 +++++++ .../src/Validation/processFiles.ts | 243 +++++++++++ .../lib-sourcify/src/Validation/zipUtils.ts | 74 ++++ packages/lib-sourcify/src/lib/types.ts | 2 +- .../test/Validation-draft/fetchUtils.spec.ts | 126 ++++++ .../test/Validation-draft/zipUtils.spec.ts | 114 ++++++ 8 files changed, 1078 insertions(+), 2 deletions(-) create mode 100644 packages/lib-sourcify/src/Validation/SolidityMetadataContract.ts create mode 100644 packages/lib-sourcify/src/Validation/fetchUtils.ts create mode 100644 packages/lib-sourcify/src/Validation/processFiles.ts create mode 100644 packages/lib-sourcify/src/Validation/zipUtils.ts create mode 100644 packages/lib-sourcify/test/Validation-draft/fetchUtils.spec.ts create mode 100644 packages/lib-sourcify/test/Validation-draft/zipUtils.spec.ts diff --git a/packages/lib-sourcify/package.json b/packages/lib-sourcify/package.json index 9909b6c76..600a16eba 100644 --- a/packages/lib-sourcify/package.json +++ b/packages/lib-sourcify/package.json @@ -18,7 +18,7 @@ "check": "run-s check:*", "check:eslint": "eslint . --ext .ts", "check:prettier": "prettier \"./**/*.ts\" --check", - "test": "c8 --reporter=none mocha -r ts-node/register test/**/*.spec.ts --no-timeout --exit", + "test": "c8 --reporter=none mocha -r ts-node/register test/**/*.spec.ts test/*.spec.ts --no-timeout --exit", "check-cli": "run-s test diff-integration-tests check-integration-tests", "check-integration-tests": "run-s check-integration-test:*", "diff-integration-tests": "mkdir -p diff && rm -rf diff/test && cp -r test diff/test && rm -rf diff/test/test-*/.git && cd diff && git init --quiet && git add -A && git commit --quiet --no-verify --allow-empty -m 'WIP' && echo '\\n\\nCommitted most recent integration test output in the \"diff\" directory. Review the changes with \"cd diff && git diff HEAD\" or your preferred git diff viewer.'", diff --git a/packages/lib-sourcify/src/Validation/SolidityMetadataContract.ts b/packages/lib-sourcify/src/Validation/SolidityMetadataContract.ts new file mode 100644 index 000000000..231eac7ae --- /dev/null +++ b/packages/lib-sourcify/src/Validation/SolidityMetadataContract.ts @@ -0,0 +1,383 @@ +import { + InvalidSources, + ISolidityCompiler, + IVyperCompiler, + Metadata, + MissingSources, + PathContent, + StringMap, + VyperJsonInput, + JsonInput, + Settings, + Libraries, +} from '..'; +import { id as keccak256str } from 'ethers'; +import semver from 'semver'; +import { getIpfsGateway, performFetch } from './fetchUtils'; + +const CONTENT_VARIATORS = [ + (content: string) => content.replace(/\r?\n/g, '\r\n'), + (content: string) => content.replace(/\r\n/g, '\n'), +]; + +const ENDING_VARIATORS = [ + (content: string) => content.trimEnd(), + (content: string) => content.trimEnd() + '\n', + (content: string) => content.trimEnd() + '\r\n', + (content: string) => content + '\n', + (content: string) => content + '\r\n', +]; + +// Dummy Compilation class +class Compilation { + compiler: ISolidityCompiler | IVyperCompiler; + compilerVersion: string; + compilationTarget: string; + jsonInput: JsonInput | VyperJsonInput; + constructor( + compiler: ISolidityCompiler | IVyperCompiler, + compilerVersion: string, + compilationTarget: string, + jsonInput: JsonInput | VyperJsonInput, + ) { + this.compiler = compiler; + this.compilerVersion = compilerVersion; + this.compilationTarget = compilationTarget; + this.jsonInput = jsonInput; + } +} + +export class SolidityMetadataContract { + metadata: Metadata; + name: string; + path: string; + providedSources: PathContent[]; + providedSourcesByHash: Map; + foundSources: StringMap; + missingSources: MissingSources; + invalidSources: InvalidSources; + unusedSourceFiles: string[]; + metadataPathToProvidedFilePath: StringMap; // maps the file path as in metadata.sources to the path of the provided by the user. E.g. metadata can have "contracts/1_Storage.sol" but the user provided "/Users/user/project/contracts/1_Storage.sol" + compilation: Compilation | null; + solcJsonInput: JsonInput | null; + + constructor(metadata: Metadata, providedSources: PathContent[]) { + this.metadata = metadata; + // These are assigned in `assembleContract` + this.foundSources = {}; + this.missingSources = {}; + this.invalidSources = {}; + this.unusedSourceFiles = []; + this.metadataPathToProvidedFilePath = {}; + this.compilation = null; + this.solcJsonInput = null; + + const compilationTargetPath = Object.keys( + metadata.settings.compilationTarget, + )[0]; + this.path = compilationTargetPath; + this.name = metadata.settings.compilationTarget[compilationTargetPath]; + this.providedSources = providedSources; + this.providedSourcesByHash = this.storeByHash(providedSources); + this.assembleContract(); + if (this.isCompilable()) { + this.#createJsonInputFromMetadata(); + } + } + + async createCompilation(compiler: ISolidityCompiler) { + if (!this.solcJsonInput) { + throw new Error( + `No JsonInput found, cannot create compilation for SolidityMetadataContract: ${this.path}:${this.name}`, + ); + } + + if (Object.keys(this.missingSources).length > 0) { + await this.fetchMissing(); + } + + this.compilation = new Compilation( + compiler, + this.metadata.compiler.version, + this.path + ':' + this.name, + this.solcJsonInput, + ); + + return this.compilation; + } + + /** + * Generates a map of files indexed by the keccak hash of their content. + * + */ + storeByHash(files: PathContent[]): Map { + const byHash: Map = new Map(); + + for (const pathContent of files) { + const calculatedHash = keccak256str(pathContent.content); + byHash.set(calculatedHash, pathContent); + } + + return byHash; + } + + generateSourceVariations() { + for (const pathContent of this.providedSources) { + const variations = this.generateVariations(pathContent); + for (const variation of variations) { + const calculatedHash = keccak256str(variation.content); + this.providedSourcesByHash.set(calculatedHash, variation); + } + } + } + + /** + * Assembles the contract by checking the sources in the metadata and finding them with the hash in the sourcesByHash map. + * First optimistically tries to find the source in the provided sources. If not all found, it will generate variations of source files and try again. + * Marks missing sources and invalid sources. + */ + private assembleContract() { + for (const sourcePath in this.metadata.sources) { + const sourceInfoFromMetadata = this.metadata.sources[sourcePath]; + let file: PathContent | undefined = undefined; + const expectedHash: string = sourceInfoFromMetadata.keccak256; + if (sourceInfoFromMetadata.content) { + // Source content already in metadata + file = { + content: sourceInfoFromMetadata.content, + path: sourcePath, + }; + const contentHash = keccak256str(file.content); + if (contentHash != expectedHash) { + this.invalidSources[sourcePath] = { + expectedHash: expectedHash, + calculatedHash: contentHash, + msg: `The keccak256 given in the metadata and the calculated keccak256 of the source content in metadata don't match`, + }; + continue; + } + } else { + // Get source from input files by hash + const pathContent = this.providedSourcesByHash.get(expectedHash); + if (pathContent) { + file = pathContent; + this.metadataPathToProvidedFilePath[sourcePath] = pathContent.path; + } // else: no file has the hash that was searched for + } + + if (file && file.content) { + this.foundSources[sourcePath] = file.content; + } else { + this.missingSources[sourcePath] = { + keccak256: expectedHash, + urls: sourceInfoFromMetadata.urls!, + }; + } + } + + // If there are still missing sources, generate variations of the provided sources and try again. We optimistically tried to find the source in the provided sources first. + if (Object.keys(this.missingSources).length > 0) { + this.generateSourceVariations(); + for (const missingSource in this.missingSources) { + const missingKeccak = this.missingSources[missingSource].keccak256; + const pathContent = this.providedSourcesByHash.get(missingKeccak); + if (pathContent) { + this.foundSources[missingSource] = pathContent.content; + this.metadataPathToProvidedFilePath[missingSource] = pathContent.path; + delete this.missingSources[missingSource]; + } + } + } + + // Finally, extract unused source files + const usedFilePaths = Object.values(this.metadataPathToProvidedFilePath); + const usedFilesSet = new Set(usedFilePaths); + this.unusedSourceFiles = this.providedSources + .map((pc) => pc.path) + .filter((file) => !usedFilesSet.has(file)); + } + + /** + * Asynchronously attempts to fetch the missing sources of this contract. An error is thrown in case of a failure. + * + * @param log log object + */ + async fetchMissing(): Promise { + const IPFS_PREFIX = 'dweb:/ipfs/'; + + for (const fileName in this.missingSources) { + const file = this.missingSources[fileName]; + const hash = file.keccak256; + + let retrievedContent = null; + + // Sometimes file paths are github urls, try to fetch from there + if (fileName.includes('github.com')) { + const githubUrl = fileName + .replace('github.com', 'raw.githubusercontent.com') + .replace('/blob/', '/'); + retrievedContent = await performFetch(githubUrl, hash, fileName); + } else { + for (const url of file.urls) { + if (url.startsWith(IPFS_PREFIX)) { + const ipfsCID = url.slice(IPFS_PREFIX.length); + const ipfsGateway = getIpfsGateway(); + const ipfsUrl = ipfsGateway.url + ipfsCID; + retrievedContent = await performFetch( + ipfsUrl, + hash, + fileName, + ipfsGateway.headers, + ); + if (retrievedContent) { + break; + } + } + } + } + + if (retrievedContent) { + this.foundSources[fileName] = retrievedContent; + delete this.missingSources[fileName]; + } + } + + if (Object.keys(this.missingSources).length) { + const error = new Error( + `Resource missing; unsuccessful fetching: ${Object.keys(this.missingSources).join(', ')}`, + ); + throw error; + } + + this.#createJsonInputFromMetadata(); + } + + private generateVariations(pathContent: PathContent): PathContent[] { + const variations: { + content: string; + contentVariator: number; + endingVariator: number; + }[] = []; + const original = pathContent.content; + for (const [ + CONTENT_VARIATORS_INDEX, + contentVariator, + ] of CONTENT_VARIATORS.entries()) { + const variatedContent = contentVariator(original); + for (const [ + ENDING_VARIATORS_INDEX, + endingVariator, + ] of ENDING_VARIATORS.entries()) { + const variation = endingVariator(variatedContent); + variations.push({ + content: variation, + contentVariator: CONTENT_VARIATORS_INDEX, + endingVariator: ENDING_VARIATORS_INDEX, + }); + } + } + + return variations.map(({ content, contentVariator, endingVariator }) => { + return { + content, + path: pathContent.path, + variation: contentVariator + '.' + endingVariator, + }; + }); + } + + #createJsonInputFromMetadata() { + if ( + Object.keys(this.missingSources).length > 0 || + Object.keys(this.invalidSources).length > 0 + ) { + throw new Error( + `Can't create JsonInput from metadata: Missing or invalid sources in metadata: ${JSON.stringify( + this.missingSources, + )}`, + ); + } + + this.solcJsonInput = {} as JsonInput; + // Clone the settings object to avoid mutating the original metadata + this.solcJsonInput.settings = JSON.parse( + JSON.stringify(this.metadata.settings), + ) as Settings; + + if ( + !this.metadata.settings || + !this.metadata.settings.compilationTarget || + Object.keys(this.metadata.settings.compilationTarget).length != 1 + ) { + throw new Error( + `Can't create JsonInput from metadata: Invalid compilationTarget in metadata: ${ + this.metadata?.settings?.compilationTarget + }`, + ); + } + + this.handleInlinerBug(); + // Standard JSON does not have compilationTarget, only in metadata.json + delete this.solcJsonInput?.settings?.compilationTarget; + + this.solcJsonInput.sources = {}; + for (const source in this.metadata.sources) { + this.solcJsonInput.sources[source] = { + content: this.foundSources[source], + }; + } + + this.solcJsonInput.language = this.metadata.language; + + // Convert the libraries from the metadata format to the compiler_settings format + // metadata format: "contracts/1_Storage.sol:Journal": "0x7d53f102f4d4aa014db4e10d6deec2009b3cda6b" + // settings format: "contracts/1_Storage.sol": { Journal: "0x7d53f102f4d4aa014db4e10d6deec2009b3cda6b" } + const metadataLibraries = this.metadata.settings?.libraries || {}; + this.solcJsonInput.settings.libraries = Object.keys( + metadataLibraries || {}, + ).reduce((libraries, libraryKey) => { + // Before Solidity v0.7.5: { "ERC20": "0x..."} + if (!libraryKey.includes(':')) { + if (!libraries['']) { + libraries[''] = {}; + } + // try using the global method, available for pre 0.7.5 versions + libraries[''][libraryKey] = metadataLibraries[libraryKey]; + return libraries; + } + + // After Solidity v0.7.5: { "ERC20.sol:ERC20": "0x..."} + const [contractPath, contractName] = libraryKey.split(':'); + if (!libraries[contractPath]) { + libraries[contractPath] = {}; + } + libraries[contractPath][contractName] = metadataLibraries[libraryKey]; + return libraries; + }, {} as Libraries); + } + + handleInlinerBug() { + // Check inliner bug for below versions https://github.com/ethereum/sourcify/issues/640 + const affectedVersions = ['0.8.2', '0.8.3', '0.8.4']; + // Normalize the version e.g. 0.8.2+commit.6615895f -> 0.8.2 + const coercedVersion = semver.coerce( + this.metadata.compiler.version, + )?.version; + + const isAffected = affectedVersions.some((version) => + semver.eq(version, coercedVersion || ''), + ); + if (isAffected) { + if (this.solcJsonInput?.settings?.optimizer?.details?.inliner) { + delete this.solcJsonInput.settings.optimizer.details.inliner; + } + } + } + + isCompilable() { + return ( + Object.keys(this.missingSources).length === 0 && + Object.keys(this.invalidSources).length === 0 + ); + } +} diff --git a/packages/lib-sourcify/src/Validation/fetchUtils.ts b/packages/lib-sourcify/src/Validation/fetchUtils.ts new file mode 100644 index 000000000..e6e7bbe1b --- /dev/null +++ b/packages/lib-sourcify/src/Validation/fetchUtils.ts @@ -0,0 +1,136 @@ +import { logError, logInfo, logDebug } from '../lib/logger'; +import { id as keccak256str } from 'ethers'; +import { IpfsGateway } from '../lib/types'; + +export async function performFetch( + url: string, + hash?: string, + fileName?: string, + headers: HeadersInit = {}, +): Promise { + logInfo('Fetching file', { + url, + hash, + fileName, + }); + const res = await fetchWithBackoff(url, headers).catch((err) => { + logError(err); + }); + + if (res) { + if (res.status === 200) { + const content = await res.text(); + if (hash && keccak256str(content) !== hash) { + logError("The calculated and the provided hash don't match."); + return null; + } + + logInfo('Fetched the file', { + fileName, + url, + hash, + }); + return content; + } else { + logError('Failed to fetch the file', { + url, + hash, + fileName, + status: res.status, + }); + return null; + } + } + return null; +} + +/** + * Fetches a resource with an exponential timeout. + * 1) Send req, wait backoff * 2^0 ms, abort if doesn't resolve + * 2) Send req, wait backoff * 2^1 ms, abort if doesn't resolve + * 3) Send req, wait backoff * 2^2 ms, abort if doesn't resolve... + * ... + * ... + */ +export async function fetchWithBackoff( + resource: string, + headers: HeadersInit = {}, + backoff: number = 10000, + retries: number = 4, +) { + let timeout = backoff; + + for (let attempt = 0; attempt <= retries; attempt++) { + try { + logDebug('Start fetchWithBackoff', { + resource, + headers, + timeout, + attempt, + }); + const controller = new AbortController(); + const id = setTimeout(() => { + logDebug('Aborting request', { resource, headers, timeout, attempt }); + controller.abort(); + }, timeout); + const response = await fetch(resource, { + signal: controller.signal, + headers, + }); + logDebug('Success fetchWithBackoff', { resource, timeout, attempt }); + clearTimeout(id); + return response; + } catch (error) { + if (attempt === retries) { + logError('Failed fetchWithBackoff', { + resource, + headers, + attempt, + retries, + timeout, + error, + }); + throw new Error(`Failed fetching ${resource}: ${error}`); + } else { + timeout *= 2; // exponential backoff + logDebug('Retrying fetchWithBackoff', { + resource, + headers, + attempt, + timeout, + error, + }); + continue; + } + } + } + throw new Error(`Failed fetching ${resource}`); +} + +/** + * Because the gateway might change across tests, don't set it to a variable but look for env variable. + * Otherwise fall back to the default ipfs.io. + * + * This will likely moved to server or somewhere else. But keep it here for now. + */ +export function getIpfsGateway(): IpfsGateway { + let ipfsGatewaysHeaders; + if (process.env.IPFS_GATEWAY_HEADERS) { + try { + ipfsGatewaysHeaders = JSON.parse(process.env.IPFS_GATEWAY_HEADERS); + } catch (error) { + logError('Error while parsing IPFS_GATEWAY_HEADERS option', { error }); + throw new Error('Error while parsing IPFS_GATEWAY_HEADERS option'); + } + } + + const ipfsGatewayUrl = process.env.IPFS_GATEWAY || 'https://ipfs.io/ipfs/'; + const urlWithTrailingSlash = ipfsGatewayUrl.endsWith('/') + ? ipfsGatewayUrl + : `${ipfsGatewayUrl}/`; + + return { + url: urlWithTrailingSlash, + headers: ipfsGatewaysHeaders, + }; +} diff --git a/packages/lib-sourcify/src/Validation/processFiles.ts b/packages/lib-sourcify/src/Validation/processFiles.ts new file mode 100644 index 000000000..c6f491a41 --- /dev/null +++ b/packages/lib-sourcify/src/Validation/processFiles.ts @@ -0,0 +1,243 @@ +// Tools to assemble SolidityMetadataContract(s) from files. + +import { logDebug, logInfo } from '../lib/logger'; +import { Metadata, PathBuffer, PathContent } from '../lib/types'; +import { SolidityMetadataContract } from './SolidityMetadataContract'; +import { unzipFiles } from './zipUtils'; +import fs from 'fs'; +import Path from 'path'; +/** + * Regular expression matching metadata nested within another string. + * Assumes metadata's first key is "compiler" and the last key is "version". + * This is true for the metadata files generated by the compiler as it's canonicalized by sorting the keys. + */ +const NESTED_METADATA_REGEX = + /"{\\"compiler\\":{\\"version\\".*?},\\"version\\":1}"/; +const HARDHAT_OUTPUT_FORMAT_REGEX = /"hh-sol-build-info-1"/; + +export function createMetadataContractsFromPaths( + paths: string[], + ignoring?: string[], +) { + const files: PathBuffer[] = []; + paths.forEach((path) => { + if (fs.existsSync(path)) { + traversePathRecursively(path, (filePath) => { + const fullPath = Path.resolve(filePath); + const file = { buffer: fs.readFileSync(filePath), path: fullPath }; + files.push(file); + }); + } else if (ignoring) { + ignoring.push(path); + } + }); + + return createMetadataContractsFromFiles(files); +} + +export async function createMetadataContractsFromFiles(files: PathBuffer[]) { + logInfo('Creating metadata contracts from files', { + numberOfFiles: files.length, + }); + await unzipFiles(files); + const parsedFiles: PathContent[] = files.map((pathBuffer) => ({ + content: pathBuffer.buffer.toString(), + path: pathBuffer.path, + })); + const { metadataFiles, sourceFiles } = splitFiles(parsedFiles); + + const metadataContracts: SolidityMetadataContract[] = []; + + metadataFiles.forEach((metadata) => { + if (metadata.language === 'Solidity') { + const metadataContract = new SolidityMetadataContract( + metadata, + sourceFiles, + ); + metadataContracts.push(metadataContract); + } else if (metadata.language === 'Vyper') { + throw new Error('Can only handle Solidity metadata files'); + } else { + throw new Error('Unsupported language'); + } + }); + + logInfo('SolidityMetadataContracts', { + contracts: metadataContracts.map((c) => c.name), + }); + return metadataContracts; +} + +/** + * Splits the files into metadata and source files using heuristics. + */ +function splitFiles(files: PathContent[]): { + metadataFiles: Metadata[]; + sourceFiles: PathContent[]; +} { + const metadataFiles: Metadata[] = []; + const sourceFiles: PathContent[] = []; + + for (const file of files) { + // If hardhat output file, extract source and metadatas. + if (file.content.match(HARDHAT_OUTPUT_FORMAT_REGEX)) { + logDebug('Found a hardhat output file', { path: file.path }); + const { hardhatMetadataFiles, hardhatSourceFiles } = + extractHardhatMetadataAndSources(file); + sourceFiles.push(...hardhatSourceFiles); + metadataFiles.push(...hardhatMetadataFiles); + continue; + } + + let metadata = extractMetadataFromString(file.content); + + if (metadata) { + assertCompilationTarget(metadata); + metadataFiles.push(metadata); + continue; + } + + // If metadata is nested within another string, extract it. + const matchRes = file.content.match(NESTED_METADATA_REGEX); + if (matchRes) { + metadata = extractMetadataFromString(matchRes[0]); + if (metadata) { + assertCompilationTarget(metadata); + metadataFiles.push(metadata); + continue; + } + } + + // Otherwise, assume it's a source file. + sourceFiles.push(file); + } + + logDebug('Split files', { + metadataFilesCount: metadataFiles.length, + sourceFilesCount: sourceFiles.length, + }); + + return { metadataFiles, sourceFiles }; +} + +export function extractHardhatMetadataAndSources(hardhatFile: PathContent) { + const hardhatMetadataFiles: any[] = []; + const hardhatSourceFiles: PathContent[] = []; + + logDebug('Processing Hardhat file', { + hardhatFileSize: new TextEncoder().encode(hardhatFile.content).length, + path: hardhatFile.path, + }); + const startTime = Date.now(); + // TODO: Test how long it takes to parse a large hardhat file. If it's too slow, + // we should use a streaming parser. + const hardhatJson = JSON.parse(hardhatFile.content); + const endTime = Date.now(); + logDebug(`Parsing hardhat file took ${endTime - startTime} milliseconds.`); + + // Extract source files + const hardhatSourceFilesObject = hardhatJson.input.sources; + for (const path in hardhatSourceFilesObject) { + if (hardhatSourceFilesObject[path].content) { + hardhatSourceFiles.push({ + path: path, + content: hardhatSourceFilesObject[path].content, + }); + } + } + + // Extract metadata files + const contractsObject = hardhatJson.output.contracts; + for (const path in contractsObject) { + for (const contractName in contractsObject[path]) { + if (contractsObject[path][contractName].metadata) { + const metadataObj = extractMetadataFromString( + contractsObject[path][contractName].metadata, + ); + if (metadataObj) { + hardhatMetadataFiles.push(metadataObj); + } + } + } + } + + logDebug('Extracted metadata and sources from hardhat file', { + metadataFilesCount: hardhatMetadataFiles.length, + sourceFilesCount: hardhatSourceFiles.length, + }); + return { hardhatMetadataFiles, hardhatSourceFiles }; +} + +function extractMetadataFromString(file: string): Metadata | null { + try { + let obj = JSON.parse(file); + if (isMetadata(obj)) { + return obj as Metadata; + } + + // if the input string originates from a file where it was double encoded (e.g. truffle) + obj = JSON.parse(obj); + if (isMetadata(obj)) { + return obj as Metadata; + } + } catch (err) { + // Fail silently. + } + + return null; +} +/** + * Applies the provided worker function to the provided path recursively. + * + * @param path the path to be traversed + * @param worker the function to be applied on each file that is not a directory + * @param afterDir the function to be applied on the directory after traversing its children + */ +function traversePathRecursively( + path: string, + worker: (filePath: string) => void, + afterDirectory?: (filePath: string) => void, +) { + if (!fs.existsSync(path)) { + const msg = `Encountered a nonexistent path: ${path}`; + const error = new Error(msg); + throw error; + } + + const fileStat = fs.lstatSync(path); + if (fileStat.isFile()) { + worker(path); + } else if (fileStat.isDirectory()) { + fs.readdirSync(path).forEach((nestedName) => { + const nestedPath = Path.join(path, nestedName); + traversePathRecursively(nestedPath, worker, afterDirectory); + }); + + if (afterDirectory) { + afterDirectory(path); + } + } +} + +function isMetadata(obj: any): boolean { + return ( + (obj?.language === 'Vyper' || obj?.language === 'Solidity') && + !!obj?.settings?.compilationTarget && + !!obj?.version && + !!obj?.output?.abi && + !!obj?.output?.userdoc && + !!obj?.output?.devdoc && + !!obj?.sources + ); +} + +function assertCompilationTarget(metadata: Metadata) { + if (!metadata.settings.compilationTarget) { + throw new Error(`Compilation target not found for the metadata file`); + } + if (Object.keys(metadata.settings.compilationTarget).length !== 1) { + throw new Error( + `Metadata must have exactly one compilation target. Found: ${Object.keys(metadata.settings.compilationTarget).join(', ')}`, + ); + } +} diff --git a/packages/lib-sourcify/src/Validation/zipUtils.ts b/packages/lib-sourcify/src/Validation/zipUtils.ts new file mode 100644 index 000000000..728640b89 --- /dev/null +++ b/packages/lib-sourcify/src/Validation/zipUtils.ts @@ -0,0 +1,74 @@ +import JSZip from 'jszip'; +import { logDebug } from '../lib/logger'; +import { PathBuffer } from '../lib/types'; + +export async function unzipFiles(files: PathBuffer[]) { + const allUnzipped: PathBuffer[] = []; + for (let i = 0; i < files.length; i++) { + const file = files[i]; + if (isZip(file.buffer)) { + logDebug('Unzipping file', { + path: file.path, + size: file.buffer.length, + unzippedFilesCount: allUnzipped.length, + }); + const unzipped = await unzip(file); + allUnzipped.push(...unzipped); + + logDebug('Unzipped file', { + path: file.path, + size: file.buffer.length, + unzippedFilesCount: allUnzipped.length, + }); + // Remove the zip file from the array and decrement the index to check the next file. + files.splice(i, 1); + i--; + } + } + // Add unzipped at the end to not check again if the extracted files are zips. + files.push(...allUnzipped); +} + +function isZip(file: Buffer): boolean { + // How is-zip-file checks https://github.com/luthraG/is-zip-file/blob/master/index.js + // Also according to this: https://stackoverflow.com/a/18194946/6528944 + const response = + file[0] === 0x50 && + file[1] === 0x4b && + (file[2] === 0x03 || file[2] === 0x05 || file[2] === 0x07) && + (file[3] === 0x04 || file[3] === 0x06 || file[3] === 0x08); + return response; +} + +/** + * Unzips the provided file buffer to the provided array. + * + * @param zippedFile the buffer containin the zipped file to be unpacked + * @returns the unzipped files as an array + */ +async function unzip(zippedFile: PathBuffer) { + const zip = new JSZip(); + const unzipped: PathBuffer[] = []; + try { + await zip.loadAsync(zippedFile.buffer); + for (const filePath in zip.files) { + const file = zip.files[filePath]; + if (file.dir) { + continue; + } + // Exclude Mac specific files + if (filePath.includes('__MACOSX')) { + continue; + } + + const buffer = await file.async('nodebuffer'); + unzipped.push({ + path: filePath, + buffer, + }); + } + } catch (e: any) { + throw new Error(`Error while unzipping ${zippedFile.path}: ${e.message}`); + } + return unzipped; +} diff --git a/packages/lib-sourcify/src/lib/types.ts b/packages/lib-sourcify/src/lib/types.ts index da1229da1..4a8bb9e06 100644 --- a/packages/lib-sourcify/src/lib/types.ts +++ b/packages/lib-sourcify/src/lib/types.ts @@ -475,7 +475,7 @@ interface ModelChecker { timeout?: number; } -interface Settings { +export interface Settings { stopAfter?: string; remappings?: string[]; optimizer?: Optimizer; diff --git a/packages/lib-sourcify/test/Validation-draft/fetchUtils.spec.ts b/packages/lib-sourcify/test/Validation-draft/fetchUtils.spec.ts new file mode 100644 index 000000000..05ea23b6d --- /dev/null +++ b/packages/lib-sourcify/test/Validation-draft/fetchUtils.spec.ts @@ -0,0 +1,126 @@ +import { expect } from 'chai'; +import { describe, it, beforeEach, afterEach } from 'mocha'; +import { + performFetch, + fetchWithBackoff, + getIpfsGateway, +} from '../../src/Validation/fetchUtils'; +import { id as keccak256str } from 'ethers'; + +describe('fetchUtils', () => { + let originalFetch: typeof fetch; + let originalEnv: NodeJS.ProcessEnv; + + beforeEach(() => { + originalFetch = global.fetch; + originalEnv = process.env; + process.env = { ...process.env }; + }); + + afterEach(() => { + global.fetch = originalFetch; + process.env = originalEnv; + }); + + describe('performFetch', () => { + it('should successfully fetch content', async () => { + const content = 'test content'; + const hash = keccak256str(content); + global.fetch = async () => new Response(content, { status: 200 }); + + const result = await performFetch('https://test.com', hash, 'test.txt'); + expect(result).to.equal(content); + }); + + it('should return null if hash does not match', async () => { + const content = 'test content'; + const wrongHash = keccak256str('wrong content'); + global.fetch = async () => new Response(content, { status: 200 }); + + const result = await performFetch( + 'https://test.com', + wrongHash, + 'test.txt', + ); + expect(result).to.be.null; + }); + + it('should return null on failed fetch', async () => { + global.fetch = async () => new Response('Not Found', { status: 404 }); + + const result = await performFetch('https://test.com'); + expect(result).to.be.null; + }); + }); + + describe('fetchWithBackoff', () => { + it('should succeed on first attempt', async () => { + global.fetch = async () => new Response('success', { status: 200 }); + + const response = await fetchWithBackoff('https://test.com'); + expect(response.status).to.equal(200); + const text = await response.text(); + expect(text).to.equal('success'); + }); + + it('should retry on failure and succeed eventually', async () => { + let attempts = 0; + global.fetch = async () => { + attempts++; + if (attempts < 2) throw new Error('Temporary failure'); + return new Response('success', { status: 200 }); + }; + + const response = await fetchWithBackoff('https://test.com', {}, 100, 2); + expect(response.status).to.equal(200); + expect(attempts).to.equal(2); + }); + + it('should throw after max retries', async () => { + global.fetch = async () => { + throw new Error('Persistent failure'); + }; + + try { + await fetchWithBackoff('https://test.com', {}, 100, 2); + expect.fail('Should have thrown'); + } catch (error: any) { + expect(error.message).to.include('Failed fetching'); + } + }); + }); + + describe('getIpfsGateway', () => { + it('should return default gateway when no env variables set', () => { + delete process.env.IPFS_GATEWAY; + delete process.env.IPFS_GATEWAY_HEADERS; + + const gateway = getIpfsGateway(); + expect(gateway.url).to.equal('https://ipfs.io/ipfs/'); + expect(gateway.headers).to.be.undefined; + }); + + it('should use custom gateway from env', () => { + process.env.IPFS_GATEWAY = 'https://custom.gateway/ipfs'; + + const gateway = getIpfsGateway(); + expect(gateway.url).to.equal('https://custom.gateway/ipfs/'); + }); + + it('should parse headers from env', () => { + const headers = { Authorization: 'Bearer token' }; + process.env.IPFS_GATEWAY_HEADERS = JSON.stringify(headers); + + const gateway = getIpfsGateway(); + expect(gateway.headers).to.deep.equal(headers); + }); + + it('should throw on invalid headers JSON', () => { + process.env.IPFS_GATEWAY_HEADERS = 'invalid json'; + + expect(() => getIpfsGateway()).to.throw( + 'Error while parsing IPFS_GATEWAY_HEADERS option', + ); + }); + }); +}); diff --git a/packages/lib-sourcify/test/Validation-draft/zipUtils.spec.ts b/packages/lib-sourcify/test/Validation-draft/zipUtils.spec.ts new file mode 100644 index 000000000..4aba7ff77 --- /dev/null +++ b/packages/lib-sourcify/test/Validation-draft/zipUtils.spec.ts @@ -0,0 +1,114 @@ +import { expect } from 'chai'; +import { unzipFiles } from '../../src/Validation/zipUtils'; +import JSZip from 'jszip'; +import { PathBuffer } from '../../src/lib/types'; + +describe('zipUtils', () => { + describe('isZip', () => { + it('should identify valid ZIP files', async () => { + // Create a simple ZIP file + const zip = new JSZip(); + zip.file('test.txt', 'Hello World'); + const zipBuffer = await zip.generateAsync({ type: 'nodebuffer' }); + + const files: PathBuffer[] = [ + { + path: 'test.zip', + buffer: zipBuffer, + }, + ]; + + // The unzipFiles function should process this as a ZIP + await unzipFiles(files); + + // Original ZIP file should be removed and replaced with contents + expect(files.length).to.equal(1); + expect(files[0].path).to.equal('test.txt'); + expect(files[0].buffer.toString()).to.equal('Hello World'); + }); + + it('should not process non-ZIP files', async () => { + const nonZipBuffer = Buffer.from('This is not a ZIP file'); + const files: PathBuffer[] = [ + { + path: 'test.txt', + buffer: nonZipBuffer, + }, + ]; + + // The file should remain unchanged + await unzipFiles(files); + expect(files.length).to.equal(1); + expect(files[0].path).to.equal('test.txt'); + expect(files[0].buffer.toString()).to.equal('This is not a ZIP file'); + }); + }); + + describe('unzipFiles', () => { + it('should handle multiple files in ZIP', async () => { + const zip = new JSZip(); + zip.file('file1.txt', 'Content 1'); + zip.file('file2.txt', 'Content 2'); + zip.file('subfolder/file3.txt', 'Content 3'); + // write file + const zipBuffer = await zip.generateAsync({ type: 'nodebuffer' }); + + const files: PathBuffer[] = [ + { + path: 'test.zip', + buffer: zipBuffer, + }, + ]; + + await unzipFiles(files); + + expect(files.length).to.equal(3); + const paths = files.map((f) => f.path).sort(); + expect(paths).to.deep.equal([ + 'file1.txt', + 'file2.txt', + 'subfolder/file3.txt', + ]); + + const file1 = files.find((f) => f.path === 'file1.txt'); + expect(file1?.buffer.toString()).to.equal('Content 1'); + }); + + it('should skip __MACOSX files', async () => { + const zip = new JSZip(); + zip.file('file1.txt', 'Content 1'); + zip.file('__MACOSX/._file1.txt', 'Mac Resource Fork'); + const zipBuffer = await zip.generateAsync({ type: 'nodebuffer' }); + + const files: PathBuffer[] = [ + { + path: 'test.zip', + buffer: zipBuffer, + }, + ]; + + await unzipFiles(files); + + expect(files.length).to.equal(1); + expect(files[0].path).to.equal('file1.txt'); + expect(files[0].buffer.toString()).to.equal('Content 1'); + }); + + it('should throw error for corrupted ZIP files', async () => { + const corruptedZipBuffer = Buffer.from([0x50, 0x4b, 0x03, 0x04, 0x00]); // Invalid ZIP + const files: PathBuffer[] = [ + { + path: 'corrupted.zip', + buffer: corruptedZipBuffer, + }, + ]; + + try { + await unzipFiles(files); + expect.fail('Should have thrown an error'); + } catch (error: any) { + expect(error.message).to.include('Error while unzipping corrupted.zip'); + } + }); + }); +}); From f5d3e11387b472940f2025514943229d0c22372b Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Wed, 15 Jan 2025 16:22:55 +0100 Subject: [PATCH 3/7] Add Compilation classes and types (#1849) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add Compilation classes and types * Add tests * Refactor compilation classes to unify compile methods and remove deprecated properties - Renamed `recompile` methods to `compile` across `AbstractCompilation`, `SolidityCompilation`, and `VyperCompilation` classes for consistency. - Removed unused properties such as `metadataRaw`, `creationBytecode`, and `runtimeBytecode` from `AbstractCompilation`. - Updated related methods to utilize new `getCreationBytecode` and `getRuntimeBytecode` methods. - Cleaned up `CompilationTypes` by removing `RecompilationResult` interface. - Adjusted tests to reflect changes in method names and ensure functionality remains intact. * Refactor AbstractCompilation to improve error handling and logging * always add 0x to get...Bytecode( functions * fix PR suggestions * Update packages/lib-sourcify/src/Compilation/AbstractCompilation.ts Co-authored-by: Kaan Uzdoğan * fix tests * enable all tests --------- Co-authored-by: Kaan Uzdoğan --- .../src/Compilation/AbstractCompilation.ts | 129 +++++++++ .../src/Compilation/CompilationTypes.ts | 145 ++++++++++ .../src/Compilation/SolidityCompilation.ts | 214 ++++++++++++++ .../src/Compilation/SolidityTypes.ts | 214 ++++++++++++++ .../src/Compilation/VyperCompilation.ts | 231 +++++++++++++++ .../src/Compilation/VyperTypes.ts | 98 +++++++ .../src/Compilation/auxdataUtils.ts | 142 ++++++++++ .../Compilation/SolidityCompilation.spec.ts | 266 ++++++++++++++++++ .../test/Compilation/VyperCompilation.spec.ts | 184 ++++++++++++ .../test/compiler/solidityCompiler.ts | 7 +- packages/lib-sourcify/test/utils.ts | 16 +- 11 files changed, 1635 insertions(+), 11 deletions(-) create mode 100644 packages/lib-sourcify/src/Compilation/AbstractCompilation.ts create mode 100644 packages/lib-sourcify/src/Compilation/CompilationTypes.ts create mode 100644 packages/lib-sourcify/src/Compilation/SolidityCompilation.ts create mode 100644 packages/lib-sourcify/src/Compilation/SolidityTypes.ts create mode 100644 packages/lib-sourcify/src/Compilation/VyperCompilation.ts create mode 100644 packages/lib-sourcify/src/Compilation/VyperTypes.ts create mode 100644 packages/lib-sourcify/src/Compilation/auxdataUtils.ts create mode 100644 packages/lib-sourcify/test/Compilation/SolidityCompilation.spec.ts create mode 100644 packages/lib-sourcify/test/Compilation/VyperCompilation.spec.ts diff --git a/packages/lib-sourcify/src/Compilation/AbstractCompilation.ts b/packages/lib-sourcify/src/Compilation/AbstractCompilation.ts new file mode 100644 index 000000000..6294c7f1b --- /dev/null +++ b/packages/lib-sourcify/src/Compilation/AbstractCompilation.ts @@ -0,0 +1,129 @@ +import { AuxdataStyle } from '@ethereum-sourcify/bytecode-utils'; +import { + CompilationTarget, + CompiledContractCborAuxdata, + Metadata, +} from './CompilationTypes'; +import { + ImmutableReferences, + ISolidityCompiler, + SolidityJsonInput, + SolidityOutput, + SolidityOutputContract, +} from './SolidityTypes'; +import { + IVyperCompiler, + VyperJsonInput, + VyperOutput, + VyperOutputContract, +} from './VyperTypes'; +import { logInfo, logSilly, logWarn } from '../lib/logger'; + +export abstract class AbstractCompilation { + /** + * Constructor parameters + */ + abstract compiler: ISolidityCompiler | IVyperCompiler; + abstract compilerVersion: string; + abstract compilationTarget: CompilationTarget; + abstract jsonInput: SolidityJsonInput | VyperJsonInput; + + metadata?: Metadata; + compilerOutput?: SolidityOutput | VyperOutput; + + abstract auxdataStyle: AuxdataStyle; + + /** Marks the positions of the CborAuxdata parts in the bytecode */ + creationBytecodeCborAuxdata?: CompiledContractCborAuxdata; + runtimeBytecodeCborAuxdata?: CompiledContractCborAuxdata; + + /** + * Recompiles the contract with the specified compiler settings + * @param forceEmscripten Whether to force using the WebAssembly binary for compilation (only for Solidity) + */ + abstract compile(forceEmscripten?: boolean): Promise; + abstract generateCborAuxdataPositions( + forceEmscripten?: boolean, + ): Promise; + + public async compileAndReturnCompilationTarget( + forceEmscripten = false, + ): Promise { + const version = this.compilerVersion; + + const compilationStartTime = Date.now(); + logInfo('Compiling contract', { + version, + contract: this.compilationTarget.name, + path: this.compilationTarget.path, + forceEmscripten, + }); + logSilly('Compilation input', { solcJsonInput: this.jsonInput }); + this.compilerOutput = await this.compiler.compile( + version, + this.jsonInput as any, + forceEmscripten, + ); + if (this.compilerOutput === undefined) { + const error = new Error('Compiler error'); + logWarn('Compiler error', { + errorMessages: ['compilerOutput is undefined'], + }); + throw error; + } + + // We call getCompilationTarget() before logging because it can throw an error + const compilationTarget = this.getCompilationTarget(); + + const compilationEndTime = Date.now(); + const compilationDuration = compilationEndTime - compilationStartTime; + logSilly('Compilation output', { compilerOutput: this.compilerOutput }); + logInfo('Compiled contract', { + version, + contract: this.compilationTarget.name, + path: this.compilationTarget.path, + forceEmscripten, + compilationDuration: `${compilationDuration}ms`, + }); + + return compilationTarget; + } + + getCompilationTarget(): SolidityOutputContract | VyperOutputContract { + if (!this.compilerOutput) { + logWarn('Compiler output is undefined'); + throw new Error('Compiler output is undefined'); + } + if ( + !this.compilerOutput.contracts || + !this.compilerOutput.contracts[this.compilationTarget.path] || + !this.compilerOutput.contracts[this.compilationTarget.path][ + this.compilationTarget.name + ] + ) { + const error = new Error('Contract not found in compiler output'); + logWarn('Contract not found in compiler output'); + throw error; + } + return this.compilerOutput.contracts[this.compilationTarget.path][ + this.compilationTarget.name + ]; + } + + getCreationBytecode() { + return `0x${this.getCompilationTarget().evm.bytecode.object}`; + } + + getRuntimeBytecode() { + return `0x${this.getCompilationTarget().evm.deployedBytecode.object}`; + } + + getMetadata(): Metadata { + if (!this.metadata) { + throw new Error('Metadata is not set'); + } + return this.metadata; + } + + abstract getImmutableReferences(): ImmutableReferences; +} diff --git a/packages/lib-sourcify/src/Compilation/CompilationTypes.ts b/packages/lib-sourcify/src/Compilation/CompilationTypes.ts new file mode 100644 index 000000000..af80b90df --- /dev/null +++ b/packages/lib-sourcify/src/Compilation/CompilationTypes.ts @@ -0,0 +1,145 @@ +import { Abi } from 'abitype'; + +export interface LinkReferences { + [filePath: string]: { + [libraryName: string]: [ + { + length: number; + start: number; + }, + ]; + }; +} + +export interface MetadataSource { + keccak256: string; + content?: string; + urls?: string[]; + license?: string; +} + +export interface MetadataSourceMap { + [index: string]: MetadataSource; +} + +export interface Devdoc { + author?: string; + details?: string; + errors?: { + [index: string]: { + details?: string; + }; + }; + events?: { + [index: string]: { + details?: string; + params?: any; + }; + }; + kind: 'dev'; + methods: { + [index: string]: { + details?: string; + params?: any; + returns?: any; + }; + }; + stateVariables?: any; + title?: string; + version?: number; +} + +export interface Userdoc { + errors?: { + [index: string]: { + notice?: string; + }[]; + }; + events?: { + [index: string]: { + notice?: string; + }; + }; + kind: 'user'; + methods: { + [index: string]: { + notice: string; + }; + }; + version?: number; +} + +export interface MetadataOutput { + abi: Abi; + devdoc: Devdoc; + userdoc: Userdoc; +} + +// Metadata type that reflects the metadata object from +// https://docs.soliditylang.org/en/latest/metadata.html +export interface Metadata { + compiler: { + keccak256?: string; + version: string; + }; + language: string; + output: MetadataOutput; + settings: { + compilationTarget: { + [sourceName: string]: string; + }; + evmVersion?: string; + libraries?: { + [index: string]: string; + }; + metadata?: { + appendCBOR?: boolean; + bytecodeHash?: 'none' | 'ipfs' | 'bzzr0' | 'bzzr1'; + useLiteralContent?: boolean; + }; + optimizer?: { + details?: { + constantOptimizer?: boolean; + cse?: boolean; + deduplicate?: boolean; + inliner?: boolean; + jumpdestRemover?: boolean; + orderLiterals?: boolean; + peephole?: boolean; + yul?: boolean; + yulDetails?: { + optimizerSteps?: string; + stackAllocation?: boolean; + }; + }; + enabled: boolean; + runs: number; + }; + viaIR?: boolean; + outputSelection?: any; + }; + sources: MetadataSourceMap; + version: number; +} + +export interface CompiledContractCborAuxdata { + [index: string]: { + offset: number; + value: string; + }; +} + +export interface StringMap { + [key: string]: string; +} + +export interface AuxdataDiff { + real: string; + diffStart: number; + diff: string; +} + +export interface CompilationTarget { + name: string; + path: string; +} diff --git a/packages/lib-sourcify/src/Compilation/SolidityCompilation.ts b/packages/lib-sourcify/src/Compilation/SolidityCompilation.ts new file mode 100644 index 000000000..c99404f31 --- /dev/null +++ b/packages/lib-sourcify/src/Compilation/SolidityCompilation.ts @@ -0,0 +1,214 @@ +import { AuxdataStyle, splitAuxdata } from '@ethereum-sourcify/bytecode-utils'; +import { logWarn } from '../lib/logger'; +import { AbstractCompilation } from './AbstractCompilation'; +import { + ImmutableReferences, + ISolidityCompiler, + SolidityJsonInput, + SolidityOutput, + SolidityOutputContract, +} from './SolidityTypes'; +import { CompilationTarget } from './CompilationTypes'; +import { + findAuxdataPositions, + findAuxdatasInLegacyAssembly, +} from './auxdataUtils'; + +/** + * Abstraction of a solidity compilation + */ +export class SolidityCompilation extends AbstractCompilation { + // Use declare to override AbstractCompilation's types to target Solidity types + declare compilerOutput?: SolidityOutput; + declare compileAndReturnCompilationTarget: ( + forceEmscripten: boolean, + ) => Promise; + declare getCompilationTarget: () => SolidityOutputContract; + + // Specify the auxdata style, used for extracting the auxdata from the compiler output + readonly auxdataStyle: AuxdataStyle.SOLIDITY = AuxdataStyle.SOLIDITY; + + public constructor( + public compiler: ISolidityCompiler, + public compilerVersion: string, + public jsonInput: SolidityJsonInput, + public compilationTarget: CompilationTarget, + ) { + super(); + this.initSolidityJsonInput(); + } + + initSolidityJsonInput() { + this.jsonInput.settings.outputSelection = { + '*': { + '*': [ + 'abi', + 'devdoc', + 'userdoc', + 'storageLayout', + 'evm.legacyAssembly', + 'evm.bytecode.object', + 'evm.bytecode.sourceMap', + 'evm.bytecode.linkReferences', + 'evm.bytecode.generatedSources', + 'evm.deployedBytecode.object', + 'evm.deployedBytecode.sourceMap', + 'evm.deployedBytecode.linkReferences', + 'evm.deployedBytecode.immutableReferences', + 'metadata', + ], + }, + }; + } + + /** Generates an edited contract with a space at the end of each source file to create a different source file hash and consequently a different metadata hash. + * This differenence is then used to determine the positions of the auxdata in the raw bytecode. + */ + public async generateEditedContract(compilerSettings: { + version: string; + solcJsonInput: SolidityJsonInput; + forceEmscripten: boolean; + }) { + const newCompilerSettings: { + version: string; + solcJsonInput: SolidityJsonInput; + forceEmscripten: boolean; + } = JSON.parse(JSON.stringify(compilerSettings)); + Object.values(newCompilerSettings.solcJsonInput.sources).forEach( + (source) => (source.content += ' '), + ); + return await this.compiler.compile( + newCompilerSettings.version, + newCompilerSettings.solcJsonInput, + newCompilerSettings.forceEmscripten, + ); + } + + /** + * Finds the positions of the auxdata in the runtime and creation bytecodes. + * Saves the CborAuxdata position (offset) and value in the runtime- and creationBytecodeCborAuxdata fields. + * @returns false if the auxdata positions cannot be generated or if the auxdata in legacyAssembly differs from the auxdata in the bytecode, true otherwise. + */ + public async generateCborAuxdataPositions(forceEmscripten = false) { + // Auxdata array extracted from the compiler's `legacyAssembly` field + const auxdatasFromCompilerOutput = findAuxdatasInLegacyAssembly( + this.getCompilationTarget().evm.legacyAssembly, + ); + + // Case: there is not auxadata + if (auxdatasFromCompilerOutput.length === 0) { + this.creationBytecodeCborAuxdata = {}; + this.runtimeBytecodeCborAuxdata = {}; + return true; + } + + // Case: there is only one auxdata, no need to recompile + if (auxdatasFromCompilerOutput.length === 1) { + // Extract the auxdata from the end of the recompiled runtime bytecode + const [, runtimeAuxdataCbor, runtimeCborLengthHex] = splitAuxdata( + this.getRuntimeBytecode(), + this.auxdataStyle, + ); + + const auxdataFromRawRuntimeBytecode = `${runtimeAuxdataCbor}${runtimeCborLengthHex}`; + + // For some reason the auxdata from raw bytecode differs from the legacyAssembly's auxdata + if (auxdatasFromCompilerOutput[0] !== auxdataFromRawRuntimeBytecode) { + logWarn( + "The auxdata from raw bytecode differs from the legacyAssembly's auxdata", + { + name: this.compilationTarget.name, + }, + ); + return false; + } + + // we divide by 2 because we store the length in bytes (without 0x) + this.runtimeBytecodeCborAuxdata = { + '1': { + offset: + this.getRuntimeBytecode().substring(2).length / 2 - + parseInt(runtimeCborLengthHex, 16) - + 2, // bytecode has 2 bytes of cbor length prefix at the end + value: `0x${auxdataFromRawRuntimeBytecode}`, + }, + }; + + // Try to extract the auxdata from the end of the recompiled creation bytecode + const [, creationAuxdataCbor, creationCborLengthHex] = splitAuxdata( + this.getCreationBytecode(), + this.auxdataStyle, + ); + + // If we can find the auxdata at the end of the bytecode return; otherwise continue with `generateEditedContract` + if (creationAuxdataCbor) { + const auxdataFromRawCreationBytecode = `${creationAuxdataCbor}${creationCborLengthHex}`; + if (auxdatasFromCompilerOutput[0] === auxdataFromRawCreationBytecode) { + // we divide by 2 because we store the length in bytes (without 0x) + this.creationBytecodeCborAuxdata = { + '1': { + offset: + this.getCreationBytecode().substring(2).length / 2 - + parseInt(creationCborLengthHex, 16) - + 2, // bytecode has 2 bytes of cbor length prefix at the end + value: `0x${auxdataFromRawCreationBytecode}`, + }, + }; + return true; + } else { + logWarn( + "The creation auxdata from raw bytecode differs from the legacyAssembly's auxdata", + { name: this.compilationTarget.name }, + ); + } + } + } + + // Case: multiple auxdatas or failing creation auxdata, + // we need to recompile with a slightly edited file to check the differences + const editedContractCompilerOutput = await this.generateEditedContract({ + version: this.compilerVersion, + solcJsonInput: this.jsonInput, + forceEmscripten, + }); + const editedContract = + editedContractCompilerOutput?.contracts[this.compilationTarget.path][ + this.compilationTarget.name + ]; + + const editedContractAuxdatasFromCompilerOutput = + findAuxdatasInLegacyAssembly(editedContract.evm.legacyAssembly); + + // Potentially we already found runtimeBytecodeCborAuxdata in the case of failing creation auxdata + // so no need to call `findAuxdataPositions` + if (this.runtimeBytecodeCborAuxdata === undefined) { + this.runtimeBytecodeCborAuxdata = findAuxdataPositions( + this.getRuntimeBytecode(), + `0x${editedContract?.evm?.deployedBytecode?.object}`, + auxdatasFromCompilerOutput, + editedContractAuxdatasFromCompilerOutput, + ); + } + + this.creationBytecodeCborAuxdata = findAuxdataPositions( + this.getCreationBytecode(), + `0x${editedContract?.evm.bytecode.object}`, + auxdatasFromCompilerOutput, + editedContractAuxdatasFromCompilerOutput, + ); + + return true; + } + + public async compile(forceEmscripten = false) { + const contract = + await this.compileAndReturnCompilationTarget(forceEmscripten); + this.metadata = JSON.parse(contract.metadata.trim()); + } + + getImmutableReferences(): ImmutableReferences { + return ( + this.getCompilationTarget().evm.deployedBytecode.immutableReferences || {} + ); + } +} diff --git a/packages/lib-sourcify/src/Compilation/SolidityTypes.ts b/packages/lib-sourcify/src/Compilation/SolidityTypes.ts new file mode 100644 index 000000000..a02333dc4 --- /dev/null +++ b/packages/lib-sourcify/src/Compilation/SolidityTypes.ts @@ -0,0 +1,214 @@ +import { Abi } from 'abitype'; + +interface File { + keccak256?: string; + urls?: string[]; + content?: string; +} + +export interface Sources { + [key: string]: File; +} + +interface YulDetails { + stackAllocation: boolean; + optimizerSteps?: string; +} + +interface Details { + peephole?: boolean; + inliner?: boolean; + jumpdestRemover?: boolean; + orderLiterals?: boolean; + deduplicate?: boolean; + cse?: boolean; + constantOptimizer?: boolean; + yul?: boolean; + yulDetails?: YulDetails; +} + +interface Optimizer { + enabled?: boolean; + runs?: number; + details?: Details; +} + +enum DebugInfo { + default = 'default', + strip = 'strip', + debug = 'debug', + verboseDebug = 'verboseDebug', +} + +interface Debug { + revertStrings: DebugInfo; + debugInfo?: string[]; +} + +interface SettingsMetadata { + useLiteralContent?: boolean; + bytecodeHash?: string; + appendCBOR?: boolean; +} + +interface MapContractAddress { + [key: string]: string; +} + +export interface Libraries { + [key: string]: MapContractAddress; +} + +interface OutputSelection { + [key: string]: any; +} + +interface Contracts { + [key: string]: string[]; +} + +interface ModelChecker { + contracts?: Contracts; + divModNoSlacks?: boolean; + engine?: string; + invariants?: string[]; + showUnproved?: boolean; + solvers?: string[]; + targets?: string[]; + timeout?: number; +} + +interface Settings { + stopAfter?: string; + remappings?: string[]; + optimizer?: Optimizer; + evmVersion?: string; + viaIR?: boolean; + debug?: Debug; + metadata?: SettingsMetadata; + libraries?: Libraries; + outputSelection: OutputSelection; + modelChecker?: ModelChecker; + compilationTarget?: string; +} + +export interface SolidityJsonInput { + language: string; + sources: Sources; + settings: Settings; +} + +interface SolidityOutputError { + sourceLocation?: { + file: string; + start: number; + end: number; + }; + type: 'TypeError' | 'InternalCompilerError' | 'Exception'; + component: 'general' | 'ewasm'; + severity: 'error' | 'warning'; + message: string; + formattedMessage?: string; +} +interface SolidityOutputEvmBytecode { + object: string; + opcodes?: string; + sourceMap?: string; + linkReferences?: + | {} + | { + [globalName: string]: { + [name: string]: { start: number; length: number }[]; + }; + }; +} + +export interface ImmutableReferences { + [key: string]: Array<{ + length: number; + start: number; + }>; +} + +interface SolidityOutputEvmDeployedBytecode extends SolidityOutputEvmBytecode { + immutableReferences?: ImmutableReferences; +} +export interface SolidityOutputSources { + [globalName: string]: { + id: number; + ast: any; + legacyAST: any; + }; +} + +export interface StorageLayout { + storage: { + astId: number; + contract: string; + label: string; + offset: number; + slot: string; + type: string; + }; + types: { + [index: string]: { + encoding: string; + label: string; + numberOfBytes: string; + }; + }; +} +export interface SolidityOutputContract { + abi: Abi; + metadata: string; + userdoc?: any; + devdoc?: any; + ir?: string; + irAst?: any; + irOptimized?: string; + irOptimizedAst?: any; + storageLayout?: StorageLayout; + evm: { + assembly?: string; + legacyAssembly?: any; + bytecode: SolidityOutputEvmBytecode; + deployedBytecode: SolidityOutputEvmDeployedBytecode; + methodIdentifiers?: { + [methodName: string]: string; + }; + gasEstimates?: { + creation: { + codeDepositCost: string; + executionCost: string; + totalCost: string; + }; + external: { + [functionSignature: string]: string; + }; + internal: { + [functionSignature: string]: string; + }; + }; + }; + ewasm?: { + wast: string; + wasm: string; + }; +} +export interface SolidityOutput { + errors?: SolidityOutputError[]; + sources?: SolidityOutputSources; + contracts: { + [globalName: string]: { + [contractName: string]: SolidityOutputContract; + }; + }; +} + +export interface ISolidityCompiler { + compile( + version: string, + solcJsonInput: SolidityJsonInput, + forceEmscripten?: boolean, + ): Promise; +} diff --git a/packages/lib-sourcify/src/Compilation/VyperCompilation.ts b/packages/lib-sourcify/src/Compilation/VyperCompilation.ts new file mode 100644 index 000000000..f12330cef --- /dev/null +++ b/packages/lib-sourcify/src/Compilation/VyperCompilation.ts @@ -0,0 +1,231 @@ +import { logWarn } from '../lib/logger'; +import { AbstractCompilation } from './AbstractCompilation'; +import { id as keccak256str } from 'ethers'; +import { + AuxdataStyle, + decode, + splitAuxdata, +} from '@ethereum-sourcify/bytecode-utils'; +import semver, { gte } from 'semver'; +import { + IVyperCompiler, + VyperJsonInput, + VyperOutput, + VyperOutputContract, +} from './VyperTypes'; +import { + CompilationTarget, + CompiledContractCborAuxdata, +} from './CompilationTypes'; +import { ImmutableReferences } from './SolidityTypes'; + +/** + * Abstraction of a vyper compilation + */ +export class VyperCompilation extends AbstractCompilation { + // Use declare to override AbstractCompilation's types to target Solidity types + declare compilerOutput?: VyperOutput; + declare compileAndReturnCompilationTarget: ( + forceEmscripten: boolean, + ) => Promise; + + // Specify the auxdata style, used for extracting the auxdata from the compiler output + public auxdataStyle: + | AuxdataStyle.VYPER + | AuxdataStyle.VYPER_LT_0_3_10 + | AuxdataStyle.VYPER_LT_0_3_5; + + // Vyper version is not semver compliant, so we need to handle it differently + public compilerVersionCompatibleWithSemver: string; + + /** + * Vyper compiler does not produce a metadata but we generate it ourselves for backward + * compatibility reasons e.g. in the legacy Sourcify API that always assumes a metadata.json + */ + generateMetadata() { + const contract = this.getCompilationTarget(); + const outputMetadata = { + abi: contract.abi, + devdoc: contract.devdoc, + userdoc: contract.userdoc, + }; + + const sourcesWithHashes = Object.entries(this.jsonInput.sources).reduce( + (acc, [path, source]) => ({ + ...acc, + [path]: { + keccak256: keccak256str(source.content), + content: source.content, + }, + }), + {}, + ); + + this.metadata = { + compiler: { version: this.compilerVersion }, + language: 'Vyper', + output: outputMetadata, + settings: { + ...this.jsonInput.settings, + compilationTarget: { + [this.compilationTarget.path]: this.compilationTarget.name, + }, + }, + sources: sourcesWithHashes, + version: 1, + }; + } + + initVyperJsonInput() { + const outputSelection = { + [this.compilationTarget.path]: [ + 'abi', + 'ast', + 'interface', + 'ir', + 'userdoc', + 'devdoc', + 'evm.bytecode.object', + 'evm.bytecode.opcodes', + 'evm.deployedBytecode.object', + 'evm.deployedBytecode.opcodes', + 'evm.deployedBytecode.sourceMap', + 'evm.methodIdentifiers', + ], + }; + this.jsonInput.settings.outputSelection = outputSelection; + } + + public constructor( + public compiler: IVyperCompiler, + public compilerVersion: string, + public jsonInput: VyperJsonInput, + public compilationTarget: CompilationTarget, + ) { + super(); + + // Vyper beta and rc versions are not semver compliant, so we need to handle them differently + if (semver.valid(this.compilerVersion)) { + this.compilerVersionCompatibleWithSemver = this.compilerVersion; + } else { + // Check for beta or release candidate versions + if (this.compilerVersion.match(/\d+\.\d+\.\d+(b\d+|rc\d+)/)) { + this.compilerVersionCompatibleWithSemver = `${this.compilerVersion + .split('+')[0] + .replace(/(b\d+|rc\d+)$/, '')}+${this.compilerVersion.split('+')[1]}`; + } else { + throw new Error('Invalid Vyper compiler version'); + } + } + + // Vyper version support for auxdata is different for each version + if (semver.lt(this.compilerVersionCompatibleWithSemver, '0.3.5')) { + this.auxdataStyle = AuxdataStyle.VYPER_LT_0_3_5; + } else if (semver.lt(this.compilerVersionCompatibleWithSemver, '0.3.10')) { + this.auxdataStyle = AuxdataStyle.VYPER_LT_0_3_10; + } else { + this.auxdataStyle = AuxdataStyle.VYPER; + } + this.initVyperJsonInput(); + } + + getImmutableReferences(): ImmutableReferences { + if (!this.getCreationBytecode() || !this.getRuntimeBytecode()) { + throw new Error( + 'Cannot generate immutable references if bytecodes are not set', + ); + } + let immutableReferences = {}; + if (gte(this.compilerVersionCompatibleWithSemver, '0.3.10')) { + try { + const { immutableSize } = decode( + this.getCreationBytecode(), + this.auxdataStyle, + ); + if (immutableSize) { + immutableReferences = { + '0': [ + { + length: immutableSize, + start: this.getRuntimeBytecode().substring(2).length / 2, + }, + ], + }; + } + } catch (e) { + logWarn('Cannot decode vyper contract bytecode', { + creationBytecode: this.getCreationBytecode(), + }); + } + } + return immutableReferences; + } + + public async compile() { + await this.compileAndReturnCompilationTarget(false); + this.generateMetadata(); + } + /** + * Generate the cbor auxdata positions for the creation and runtime bytecodes. + * @returns false if the auxdata positions cannot be generated, true otherwise. + */ + public async generateCborAuxdataPositions() { + const [, runtimeAuxdataCbor, runtimeCborLengthHex] = splitAuxdata( + this.getRuntimeBytecode(), + this.auxdataStyle, + ); + + // Vyper 0.3.10 and higher does not have the auxdata in the runtime bytecode + if (this.auxdataStyle === AuxdataStyle.VYPER_LT_0_3_10) { + this.runtimeBytecodeCborAuxdata = this.tryGenerateCborAuxdataPosition( + this.getRuntimeBytecode(), + runtimeAuxdataCbor, + runtimeCborLengthHex, + ); + } + + const [, creationAuxdataCbor, creationCborLengthHex] = splitAuxdata( + this.getCreationBytecode(), + this.auxdataStyle, + ); + + this.creationBytecodeCborAuxdata = this.tryGenerateCborAuxdataPosition( + this.getCreationBytecode(), + creationAuxdataCbor, + creationCborLengthHex, + ); + + return true; + } + + private tryGenerateCborAuxdataPosition( + bytecode: string, + auxdataCbor: string, + cborLengthHex: string, + ): CompiledContractCborAuxdata { + if (!auxdataCbor) { + return {}; + } + + const auxdataFromRawBytecode = `${auxdataCbor}${cborLengthHex}`; + + // Handles vyper lower than 0.3.10 in which the auxdata length bytes count + const auxdataLengthOffset = + this.auxdataStyle === AuxdataStyle.VYPER_LT_0_3_10 ? 2 : 0; + + return { + '1': { + offset: + // we divide by 2 because we store the length in bytes (without 0x) + bytecode.substring(2).length / 2 - + parseInt( + cborLengthHex || + 'b' /** handles vyper lower than 0.3.5 in which cborLengthHex is '' */, + 16, + ) - + auxdataLengthOffset, + value: `0x${auxdataFromRawBytecode}`, + }, + }; + } +} diff --git a/packages/lib-sourcify/src/Compilation/VyperTypes.ts b/packages/lib-sourcify/src/Compilation/VyperTypes.ts new file mode 100644 index 000000000..d86fdc242 --- /dev/null +++ b/packages/lib-sourcify/src/Compilation/VyperTypes.ts @@ -0,0 +1,98 @@ +export interface VyperSettings { + /** EVM version to compile for */ + evmVersion?: 'london' | 'paris' | 'shanghai' | 'cancun' | 'istanbul'; + /** Optimization mode */ + optimize?: 'gas' | 'codesize' | 'none' | boolean; + /** Whether the bytecode should include Vyper's signature */ + bytecodeMetadata?: boolean; + /** Whether to use the experimental venom pipeline */ + experimentalCodegen?: boolean; + /** The search paths to use for resolving imports */ + search_paths?: string[]; + outputSelection: { + [key: string]: string[] | { [contractName: string]: string[] }; + }; +} + +export interface VyperJsonInput { + language: 'Vyper'; + sources: { + [sourcePath: string]: { + keccak256?: string; + content: string; + }; + }; + /** + * Optional: Sources made available for import by the compiled contracts. + * For .vy suffix, compiler expects Vyper syntax. + * For .json suffix, compiler expects an ABI object. + */ + interfaces?: { + [interfacePath: string]: { + content?: string; + abi?: any[]; + }; + }; + settings: VyperSettings; +} + +export interface VyperError { + sourceLocation?: { + file: string; + lineno: number; + col_offset: number; + }; + type: string; + component: string; + severity: 'error' | 'warning'; + message: string; + formattedMessage?: string; +} + +interface VyperOutputSource { + id: number; + ast: any; +} + +export interface VyperOutputContract { + abi: any[]; + devdoc: any; + ir: string; + userdoc: any; + evm: { + bytecode: { + object: string; + opcodes: string; + }; + deployedBytecode: { + object: string; + opcodes: string; + sourceMap: string; + }; + methodIdentifiers: { + [methodName: string]: string; + }; + }; +} + +interface VyperOutputContracts { + [sourcePath: string]: { + [contractName: string]: VyperOutputContract; + }; +} + +export interface VyperOutput { + compiler: string; + errors?: VyperError[]; + sources: { + [sourcePath: string]: VyperOutputSource; + }; + contracts: VyperOutputContracts; +} + +export interface IVyperCompiler { + compile( + version: string, + vyperJsonInput: VyperJsonInput, + ): Promise; +} diff --git a/packages/lib-sourcify/src/Compilation/auxdataUtils.ts b/packages/lib-sourcify/src/Compilation/auxdataUtils.ts new file mode 100644 index 000000000..e2fd54439 --- /dev/null +++ b/packages/lib-sourcify/src/Compilation/auxdataUtils.ts @@ -0,0 +1,142 @@ +import { AuxdataDiff, CompiledContractCborAuxdata } from './CompilationTypes'; + +function getAuxdataInLegacyAssemblyBranch( + legacyAssemblyBranch: any, + auxdatas: string[], +) { + if (typeof legacyAssemblyBranch === 'object') { + Object.keys(legacyAssemblyBranch).forEach((key) => { + switch (key) { + case '.auxdata': { + auxdatas.push(legacyAssemblyBranch[key]); + break; + } + case '.code': { + break; + } + default: { + if (key === '.data' || Number.isInteger(Number(key))) { + return getAuxdataInLegacyAssemblyBranch( + legacyAssemblyBranch[key], + auxdatas, + ); + } + } + } + }); + } +} + +export function findAuxdatasInLegacyAssembly(legacyAssembly: any) { + const auxdatas: string[] = []; + getAuxdataInLegacyAssemblyBranch(legacyAssembly, auxdatas); + return auxdatas; +} + +/** + * Given two bytecodes, this function returns an array of ALL differing indexes. + * @example getDiffPositions(['A', 'b', 'c', 'A', 'd'], ['A', 'x', 'y', 'A', 'z']) => [1, 2, 4] + * + */ +function getDiffPositions(original: string, modified: string): number[] { + const differences: number[] = []; + const minLength = Math.min(original.length, modified.length); + + for (let i = 0; i < minLength; i++) { + if (original[i] !== modified[i]) { + differences.push(i); + } + } + + return differences; +} + +/** + * Checks the raw bytecode indeed includes the auxdata diff at the given position + */ +function bytecodeIncludesAuxdataDiffAt( + bytecode: string, + auxdataDiff: AuxdataDiff, + position: number, +): boolean { + const { real, diffStart } = auxdataDiff; + // the difference (i.e metadata hash) starts from "position". To get the whole auxdata instead of metadata go back "diffStart" and until + "real.length" of the auxdata. + const extracted = bytecode.slice( + position - diffStart, + position - diffStart + real.length, + ); + return extracted === real; +} + +function getAuxdatasDiff(originalAuxdatas: string[], editedAuxdatas: string[]) { + const auxdataDiffs: AuxdataDiff[] = []; + for (let i = 0; i < originalAuxdatas.length; i++) { + const diffPositions = getDiffPositions( + originalAuxdatas[i], + editedAuxdatas[i], + ); + auxdataDiffs.push({ + real: originalAuxdatas[i], + diffStart: diffPositions[0], + diff: originalAuxdatas[i].substring( + diffPositions[0], + diffPositions[diffPositions.length - 1] + 1, + ), + }); + } + return auxdataDiffs; +} + +/** + * Finds the positions of the auxdata in the bytecode. + * The compiler outputs the auxdata values in the `legacyAssembly` field. However we can't use these values to do a simple string search on the compiled bytecode because an attacker can embed these values in the compiled contract code and cause the correspoding field in the onchain bytecode to be ignored falsely during verification. + * A way to find the *metadata hashes* in the bytecode is to recompile the contract with a slightly edited source code and compare the differences in the raw bytecodes. However, this will only give us the positions of the metadata hashes in the bytecode. We need to find the positions of the whole *auxdata* in the bytecode. + * So we go through each of the differences in the raw bytecode and check if an auxdata diff value from the legacyAssembly is included in that difference. If it is, we have found the position of the auxdata in the bytecode. + */ +export function findAuxdataPositions( + originalBytecode: string, + editedBytecode: string, + originalAuxdatas: string[], + editedAuxdatas: string[], +): CompiledContractCborAuxdata { + const auxdataDiffObjects = getAuxdatasDiff(originalAuxdatas, editedAuxdatas); + + const diffPositionsBytecodes = getDiffPositions( + originalBytecode, + editedBytecode, + ); + const auxdataPositions: CompiledContractCborAuxdata = {}; + + let prevDiffPosition = -99; + for (const diffPosition of diffPositionsBytecodes) { + // Don't check consecutive diffs like 55, 56, 57... , only if there's a gap like 55, 57, 58, then 78, 79, 80... + if (prevDiffPosition + 1 === diffPosition) { + prevDiffPosition = diffPosition; + continue; + } + // New diff position + for (const auxdataDiffIndex in auxdataDiffObjects) { + const auxdataPositionsIndex = parseInt(auxdataDiffIndex) + 1; + if ( + auxdataPositions[auxdataPositionsIndex] === undefined && + bytecodeIncludesAuxdataDiffAt( + originalBytecode, + auxdataDiffObjects[auxdataDiffIndex], + diffPosition, + ) + ) { + auxdataPositions[auxdataPositionsIndex] = { + offset: + (diffPosition - + auxdataDiffObjects[auxdataDiffIndex].diffStart - + 2) / + 2, + value: `0x${auxdataDiffObjects[auxdataDiffIndex].real}`, + }; + } + } + prevDiffPosition = diffPosition; + } + + return auxdataPositions; +} diff --git a/packages/lib-sourcify/test/Compilation/SolidityCompilation.spec.ts b/packages/lib-sourcify/test/Compilation/SolidityCompilation.spec.ts new file mode 100644 index 000000000..baa33c7dd --- /dev/null +++ b/packages/lib-sourcify/test/Compilation/SolidityCompilation.spec.ts @@ -0,0 +1,266 @@ +import { describe, it } from 'mocha'; +import { expect, use } from 'chai'; +import path from 'path'; +import fs from 'fs'; +import { SolidityCompilation } from '../../src/Compilation/SolidityCompilation'; +import { solc } from '../utils'; +import { + CompilationTarget, + Metadata, +} from '../../src/Compilation/CompilationTypes'; +import { SolidityJsonInput } from '../../src/Compilation/SolidityTypes'; +import chaiAsPromised from 'chai-as-promised'; + +use(chaiAsPromised); + +function getSolcSettingsFromMetadata(metadata: Metadata) { + const metadataSettings = JSON.parse(JSON.stringify(metadata.settings)); + delete metadataSettings.compilationTarget; + return metadataSettings; +} + +function getCompilationTargetFromMetadata( + metadata: Metadata, +): CompilationTarget { + const path = Object.keys(metadata.settings.compilationTarget)[0]; + const name = metadata.settings.compilationTarget[path]; + return { + name, + path, + }; +} + +describe('SolidityCompilation', () => { + it('should compile a simple contract', async () => { + const contractPath = path.join(__dirname, '..', 'sources', 'Storage'); + const metadata = JSON.parse( + fs.readFileSync(path.join(contractPath, 'metadata.json'), 'utf8'), + ); + const sources = { + 'project:/contracts/Storage.sol': { + content: fs.readFileSync( + path.join(contractPath, 'sources', 'Storage.sol'), + 'utf8', + ), + }, + }; + + const compilation = new SolidityCompilation( + solc, + metadata.compiler.version, + { + language: 'Solidity', + sources, + settings: getSolcSettingsFromMetadata(metadata), + }, + getCompilationTargetFromMetadata(metadata), + ); + + await compilation.compile(); + expect(compilation.getCreationBytecode()).to.not.be.undefined; + expect(compilation.getRuntimeBytecode()).to.not.be.undefined; + }); + + it('should generate correct CBOR auxdata positions', async () => { + const contractPath = path.join(__dirname, '..', 'sources', 'Storage'); + const metadata = JSON.parse( + fs.readFileSync(path.join(contractPath, 'metadata.json'), 'utf8'), + ); + const sources = { + 'project:/contracts/Storage.sol': { + content: fs.readFileSync( + path.join(contractPath, 'sources', 'Storage.sol'), + 'utf8', + ), + }, + }; + + const compilation = new SolidityCompilation( + solc, + metadata.compiler.version, + { + language: 'Solidity', + sources, + settings: getSolcSettingsFromMetadata(metadata), + }, + getCompilationTargetFromMetadata(metadata), + ); + + await compilation.compile(); + const success = await compilation.generateCborAuxdataPositions(); + expect(success).to.be.true; + expect(compilation.runtimeBytecodeCborAuxdata).to.not.be.empty; + }); + + it('should handle multiple auxdatas correctly', async () => { + const contractPath = path.join( + __dirname, + '..', + 'sources', + 'WithMultipleAuxdatas', + ); + const metadata = JSON.parse( + fs.readFileSync(path.join(contractPath, 'metadata.json'), 'utf8'), + ); + + // Need to read all source files referenced in metadata + const sources: { [key: string]: { content: string } } = {}; + for (const [sourcePath] of Object.entries(metadata.sources)) { + const fullPath = path.join( + contractPath, + 'sources', + path.basename(sourcePath), + ); + sources[sourcePath] = { + content: fs.readFileSync(fullPath, 'utf8'), + }; + } + + const compilation = new SolidityCompilation( + solc, + metadata.compiler.version, + { + language: 'Solidity', + sources, + settings: getSolcSettingsFromMetadata(metadata), + }, + getCompilationTargetFromMetadata(metadata), + ); + + await compilation.compile(); + const success = await compilation.generateCborAuxdataPositions(); + expect(success).to.be.true; + + console.log(compilation.runtimeBytecodeCborAuxdata); + console.log(compilation.creationBytecodeCborAuxdata); + expect(compilation.runtimeBytecodeCborAuxdata).to.not.be.undefined; + expect(compilation.creationBytecodeCborAuxdata).to.not.be.undefined; + expect( + Object.keys(compilation.runtimeBytecodeCborAuxdata!).length, + ).to.be.greaterThan(1); + expect( + Object.keys(compilation.creationBytecodeCborAuxdata!).length, + ).to.be.greaterThan(1); + }); + + it('should handle case with no auxdata when metadata is disabled', async () => { + const contractPath = path.join(__dirname, '..', 'sources', 'Storage'); + const metadata = JSON.parse( + fs.readFileSync(path.join(contractPath, 'metadata.json'), 'utf8'), + ); + const sources = { + 'project:/contracts/Storage.sol': { + content: fs.readFileSync( + path.join(contractPath, 'sources', 'Storage.sol'), + 'utf8', + ), + }, + }; + + const solcJsonInput: SolidityJsonInput = { + language: 'Solidity', + sources, + settings: { + // Disable metadata output + metadata: { + appendCBOR: false, + }, + outputSelection: { + '*': { + '*': ['*'], + }, + }, + }, + }; + + const compilation = new SolidityCompilation( + solc, + '0.8.19+commit.7dd6d404', // Force compiler version compatible with appendCBOR + solcJsonInput, + getCompilationTargetFromMetadata(metadata), + ); + + await compilation.compile(); + const result = await compilation.generateCborAuxdataPositions(); + + expect(result).to.be.true; + expect(compilation.creationBytecodeCborAuxdata).to.deep.equal({}); + expect(compilation.runtimeBytecodeCborAuxdata).to.deep.equal({}); + }); + + it('should handle case with single auxdata using Storage contract', async () => { + const contractPath = path.join(__dirname, '..', 'sources', 'Storage'); + const metadata = JSON.parse( + fs.readFileSync(path.join(contractPath, 'metadata.json'), 'utf8'), + ); + const sources = { + 'project:/contracts/Storage.sol': { + content: fs.readFileSync( + path.join(contractPath, 'sources', 'Storage.sol'), + 'utf8', + ), + }, + }; + + const compilation = new SolidityCompilation( + solc, + metadata.compiler.version, + { + language: 'Solidity', + sources, + settings: getSolcSettingsFromMetadata(metadata), + }, + getCompilationTargetFromMetadata(metadata), + ); + + await compilation.compile(); + + const result = await compilation.generateCborAuxdataPositions(); + + expect(result).to.be.true; + expect(compilation.runtimeBytecodeCborAuxdata).to.have.property('1'); + expect(compilation.runtimeBytecodeCborAuxdata!['1']).to.have.property( + 'offset', + ); + expect(compilation.runtimeBytecodeCborAuxdata!['1']).to.have.property( + 'value', + ); + expect(compilation.creationBytecodeCborAuxdata).to.have.property('1'); + expect(compilation.creationBytecodeCborAuxdata!['1']).to.have.property( + 'offset', + ); + expect(compilation.creationBytecodeCborAuxdata!['1']).to.have.property( + 'value', + ); + }); + + it('should throw when compilation target is invalid', async () => { + const contractPath = path.join(__dirname, '..', 'sources', 'Storage'); + const metadata = JSON.parse( + fs.readFileSync(path.join(contractPath, 'metadata.json'), 'utf8'), + ); + const sources = { + 'project:/contracts/Storage.sol': { + content: fs.readFileSync( + path.join(contractPath, 'sources', 'Storage.sol'), + 'utf8', + ), + }, + }; + + const compilation = new SolidityCompilation( + solc, + metadata.compiler.version, + { + language: 'Solidity', + sources, + settings: getSolcSettingsFromMetadata(metadata), + }, + { name: 'NonExistentContract', path: 'Storage.sol' }, + ); + + await expect(compilation.compile()).to.be.eventually.rejectedWith( + 'Contract not found in compiler output', + ); + }); +}); diff --git a/packages/lib-sourcify/test/Compilation/VyperCompilation.spec.ts b/packages/lib-sourcify/test/Compilation/VyperCompilation.spec.ts new file mode 100644 index 000000000..1ee80fc08 --- /dev/null +++ b/packages/lib-sourcify/test/Compilation/VyperCompilation.spec.ts @@ -0,0 +1,184 @@ +import { describe, it } from 'mocha'; +import { expect } from 'chai'; +import path from 'path'; +import fs from 'fs'; +import { VyperCompilation } from '../../src/Compilation/VyperCompilation'; +import { vyperCompiler } from '../utils'; + +describe('VyperCompilation', () => { + it('should compile a simple Vyper contract', async () => { + const contractPath = path.join( + __dirname, + '..', + 'sources', + 'Vyper', + 'testcontract', + ); + const contractFileName = 'test.vy'; + const contractContent = fs.readFileSync( + path.join(contractPath, contractFileName), + 'utf8', + ); + + const compilation = new VyperCompilation( + vyperCompiler, + '0.3.10+commit.91361694', + { + language: 'Vyper', + sources: { + [contractFileName]: { + content: contractContent, + }, + }, + settings: { + evmVersion: 'istanbul', + outputSelection: { + '*': ['evm.bytecode'], + }, + }, + }, + { + name: contractFileName.split('.')[0], + path: contractFileName, + }, + ); + + await compilation.compile(); + expect(compilation.getCreationBytecode()).to.not.be.undefined; + expect(compilation.getRuntimeBytecode()).to.not.be.undefined; + }); + + it('should handle immutable references correctly', async () => { + const contractPath = path.join( + __dirname, + '..', + 'sources', + 'Vyper', + 'withImmutables', + ); + const contractFileName = 'test.vy'; + const contractContent = fs.readFileSync( + path.join(contractPath, contractFileName), + 'utf8', + ); + + const compilation = new VyperCompilation( + vyperCompiler, + '0.4.0+commit.e9db8d9f', + { + language: 'Vyper', + sources: { + [contractFileName]: { + content: contractContent, + }, + }, + settings: { + evmVersion: 'london', + outputSelection: { + '*': ['evm.bytecode'], + }, + }, + }, + { + name: contractFileName.split('.')[0], + path: contractFileName, + }, + ); + + await compilation.compile(); + expect(compilation.getImmutableReferences()).to.not.be.empty; + }); + + it('should generate correct CBOR auxdata positions', async () => { + const contractPath = path.join( + __dirname, + '..', + 'sources', + 'Vyper', + 'testcontract', + ); + const contractFileName = 'test.vy'; + const contractContent = fs.readFileSync( + path.join(contractPath, contractFileName), + 'utf8', + ); + + const compilation = new VyperCompilation( + vyperCompiler, + '0.3.10+commit.91361694', + { + language: 'Vyper', + sources: { + [contractFileName]: { + content: contractContent, + }, + }, + settings: { + evmVersion: 'istanbul', + outputSelection: { + '*': ['evm.bytecode'], + }, + }, + }, + { + name: contractFileName.split('.')[0], + path: contractFileName, + }, + ); + + await compilation.compile(); + const success = await compilation.generateCborAuxdataPositions(); + expect(success).to.be.true; + expect(compilation.creationBytecodeCborAuxdata).to.not.be.empty; + }); + + it('should handle different Vyper versions correctly', async () => { + const versions = [ + '0.3.4+commit.f31f0ec4', + '0.3.7+commit.6020b8bb', + '0.3.10+commit.91361694', + ]; + + for (const version of versions) { + const contractPath = path.join( + __dirname, + '..', + 'sources', + 'Vyper', + 'testcontract', + ); + const contractFileName = 'test.vy'; + const contractContent = fs.readFileSync( + path.join(contractPath, contractFileName), + 'utf8', + ); + + const compilation = new VyperCompilation( + vyperCompiler, + version, + { + language: 'Vyper', + sources: { + [contractFileName]: { + content: contractContent, + }, + }, + settings: { + evmVersion: 'istanbul', + outputSelection: { + '*': ['evm.bytecode'], + }, + }, + }, + { + name: contractFileName.split('.')[0], + path: contractFileName, + }, + ); + + await compilation.compile(); + expect(compilation.getCreationBytecode()).to.not.be.undefined; + expect(compilation.getRuntimeBytecode()).to.not.be.undefined; + } + }); +}); diff --git a/packages/lib-sourcify/test/compiler/solidityCompiler.ts b/packages/lib-sourcify/test/compiler/solidityCompiler.ts index c68b3026a..438d41837 100644 --- a/packages/lib-sourcify/test/compiler/solidityCompiler.ts +++ b/packages/lib-sourcify/test/compiler/solidityCompiler.ts @@ -3,11 +3,14 @@ import path from 'path'; import fs from 'fs'; import { exec, spawnSync } from 'child_process'; import { StatusCodes } from 'http-status-codes'; -import { SolidityOutput, JsonInput } from '../../src'; import { logDebug, logError, logInfo, logWarn } from '../../src/lib/logger'; import semver from 'semver'; import { Worker, WorkerOptions } from 'worker_threads'; import { fetchWithBackoff } from './common'; +import { + SolidityJsonInput, + SolidityOutput, +} from '../../src/Compilation/SolidityTypes'; // eslint-disable-next-line @typescript-eslint/no-var-requires const solc = require('solc'); @@ -39,7 +42,7 @@ export function findSolcPlatform(): string | false { export async function useSolidityCompiler( version: string, - solcJsonInput: JsonInput, + solcJsonInput: SolidityJsonInput, forceEmscripten = false, ): Promise { // For nightly builds, Solidity version is saved as 0.8.17-ci.2022.8.9+commit.6b60524c instead of 0.8.17-nightly.2022.8.9+commit.6b60524c. diff --git a/packages/lib-sourcify/test/utils.ts b/packages/lib-sourcify/test/utils.ts index d926a1152..a6149049b 100644 --- a/packages/lib-sourcify/test/utils.ts +++ b/packages/lib-sourcify/test/utils.ts @@ -9,17 +9,10 @@ import path from 'path'; import fs from 'fs'; -import { - SolidityOutput, - JsonInput, - Match, - SourcifyChain, - verifyDeployed, -} from '../src'; +import { Match, SourcifyChain, verifyDeployed } from '../src'; import { checkFilesWithMetadata } from '../src'; import { expect } from 'chai'; import { ContractFactory, Signer } from 'ethers'; -import { ISolidityCompiler } from '../src/lib/ISolidityCompiler'; import { useSolidityCompiler } from './compiler/solidityCompiler'; import { useVyperCompiler } from './compiler/vyperCompiler'; import { @@ -27,6 +20,11 @@ import { VyperJsonInput, VyperOutput, } from '../src/lib/IVyperCompiler'; +import { + ISolidityCompiler, + SolidityJsonInput, + SolidityOutput, +} from '../src/Compilation/SolidityTypes'; /** * Function to deploy contracts from provider unlocked accounts * contractFolderPath must contain an artifact.json file with "abi" and "bytecode" fields @@ -62,7 +60,7 @@ export async function deployFromAbiAndBytecode( class Solc implements ISolidityCompiler { async compile( version: string, - solcJsonInput: JsonInput, + solcJsonInput: SolidityJsonInput, forceEmscripten: boolean = false, ): Promise { return await useSolidityCompiler(version, solcJsonInput, forceEmscripten); From 3a875d8e800b4d1a5c58c5e478d9a163ca745a1f Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Wed, 15 Jan 2025 16:23:44 +0100 Subject: [PATCH 4/7] remove lib-sourcify touch --- packages/lib-sourcify/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/lib-sourcify/README.md b/packages/lib-sourcify/README.md index 67c50356f..c02ca7fe4 100644 --- a/packages/lib-sourcify/README.md +++ b/packages/lib-sourcify/README.md @@ -1,5 +1,3 @@ -// TODO delete this - # lib-sourcify [![codecov](https://codecov.io/gh/ethereum/sourcify/branch/staging/graph/badge.svg?token=eN6XDAwWfV&flag=lib-sourcify)](https://codecov.io/gh/ethereum/sourcify) From d1491e1f2cfd0c94ffe004f91350310dfa59655c Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Thu, 23 Jan 2025 11:13:21 +0100 Subject: [PATCH 5/7] Implement Compilation tests with 100% coverage (#1856) * push vyper tests (still need refactoring) * Implement solidity test to get 100% coverage * Refactor Vyper tests * refactor solidity tests * Update packages/lib-sourcify/src/Compilation/VyperCompilation.ts Co-authored-by: Manuel Wedler <34456797+manuelwedler@users.noreply.github.com> * Update packages/lib-sourcify/test/Compilation/VyperCompilation.spec.ts Co-authored-by: Manuel Wedler <34456797+manuelwedler@users.noreply.github.com> * fix duplicate check --------- Co-authored-by: Manuel Wedler <34456797+manuelwedler@users.noreply.github.com> --- .../src/Compilation/SolidityCompilation.ts | 52 +- .../src/Compilation/VyperCompilation.ts | 69 +-- .../Compilation/SolidityCompilation.spec.ts | 142 ++++-- .../test/Compilation/VyperCompilation.spec.ts | 474 ++++++++++++++++-- .../sources/NoImmutableField/metadata.json | 46 ++ .../NoImmutableField/sources/NoImmutable.sol | 13 + 6 files changed, 648 insertions(+), 148 deletions(-) create mode 100644 packages/lib-sourcify/test/sources/NoImmutableField/metadata.json create mode 100644 packages/lib-sourcify/test/sources/NoImmutableField/sources/NoImmutable.sol diff --git a/packages/lib-sourcify/src/Compilation/SolidityCompilation.ts b/packages/lib-sourcify/src/Compilation/SolidityCompilation.ts index c99404f31..0cfbd3d54 100644 --- a/packages/lib-sourcify/src/Compilation/SolidityCompilation.ts +++ b/packages/lib-sourcify/src/Compilation/SolidityCompilation.ts @@ -1,5 +1,4 @@ import { AuxdataStyle, splitAuxdata } from '@ethereum-sourcify/bytecode-utils'; -import { logWarn } from '../lib/logger'; import { AbstractCompilation } from './AbstractCompilation'; import { ImmutableReferences, @@ -112,17 +111,6 @@ export class SolidityCompilation extends AbstractCompilation { const auxdataFromRawRuntimeBytecode = `${runtimeAuxdataCbor}${runtimeCborLengthHex}`; - // For some reason the auxdata from raw bytecode differs from the legacyAssembly's auxdata - if (auxdatasFromCompilerOutput[0] !== auxdataFromRawRuntimeBytecode) { - logWarn( - "The auxdata from raw bytecode differs from the legacyAssembly's auxdata", - { - name: this.compilationTarget.name, - }, - ); - return false; - } - // we divide by 2 because we store the length in bytes (without 0x) this.runtimeBytecodeCborAuxdata = { '1': { @@ -143,24 +131,17 @@ export class SolidityCompilation extends AbstractCompilation { // If we can find the auxdata at the end of the bytecode return; otherwise continue with `generateEditedContract` if (creationAuxdataCbor) { const auxdataFromRawCreationBytecode = `${creationAuxdataCbor}${creationCborLengthHex}`; - if (auxdatasFromCompilerOutput[0] === auxdataFromRawCreationBytecode) { - // we divide by 2 because we store the length in bytes (without 0x) - this.creationBytecodeCborAuxdata = { - '1': { - offset: - this.getCreationBytecode().substring(2).length / 2 - - parseInt(creationCborLengthHex, 16) - - 2, // bytecode has 2 bytes of cbor length prefix at the end - value: `0x${auxdataFromRawCreationBytecode}`, - }, - }; - return true; - } else { - logWarn( - "The creation auxdata from raw bytecode differs from the legacyAssembly's auxdata", - { name: this.compilationTarget.name }, - ); - } + // we divide by 2 because we store the length in bytes (without 0x) + this.creationBytecodeCborAuxdata = { + '1': { + offset: + this.getCreationBytecode().substring(2).length / 2 - + parseInt(creationCborLengthHex, 16) - + 2, // bytecode has 2 bytes of cbor length prefix at the end + value: `0x${auxdataFromRawCreationBytecode}`, + }, + }; + return true; } } @@ -172,7 +153,7 @@ export class SolidityCompilation extends AbstractCompilation { forceEmscripten, }); const editedContract = - editedContractCompilerOutput?.contracts[this.compilationTarget.path][ + editedContractCompilerOutput.contracts[this.compilationTarget.path][ this.compilationTarget.name ]; @@ -184,7 +165,7 @@ export class SolidityCompilation extends AbstractCompilation { if (this.runtimeBytecodeCborAuxdata === undefined) { this.runtimeBytecodeCborAuxdata = findAuxdataPositions( this.getRuntimeBytecode(), - `0x${editedContract?.evm?.deployedBytecode?.object}`, + `0x${editedContract.evm.deployedBytecode.object}`, auxdatasFromCompilerOutput, editedContractAuxdatasFromCompilerOutput, ); @@ -192,7 +173,7 @@ export class SolidityCompilation extends AbstractCompilation { this.creationBytecodeCborAuxdata = findAuxdataPositions( this.getCreationBytecode(), - `0x${editedContract?.evm.bytecode.object}`, + `0x${editedContract.evm.bytecode.object}`, auxdatasFromCompilerOutput, editedContractAuxdatasFromCompilerOutput, ); @@ -207,8 +188,7 @@ export class SolidityCompilation extends AbstractCompilation { } getImmutableReferences(): ImmutableReferences { - return ( - this.getCompilationTarget().evm.deployedBytecode.immutableReferences || {} - ); + const compilationTarget = this.getCompilationTarget(); + return compilationTarget.evm.deployedBytecode.immutableReferences || {}; } } diff --git a/packages/lib-sourcify/src/Compilation/VyperCompilation.ts b/packages/lib-sourcify/src/Compilation/VyperCompilation.ts index f12330cef..5033b48f1 100644 --- a/packages/lib-sourcify/src/Compilation/VyperCompilation.ts +++ b/packages/lib-sourcify/src/Compilation/VyperCompilation.ts @@ -130,11 +130,6 @@ export class VyperCompilation extends AbstractCompilation { } getImmutableReferences(): ImmutableReferences { - if (!this.getCreationBytecode() || !this.getRuntimeBytecode()) { - throw new Error( - 'Cannot generate immutable references if bytecodes are not set', - ); - } let immutableReferences = {}; if (gte(this.compilerVersionCompatibleWithSemver, '0.3.10')) { try { @@ -170,32 +165,42 @@ export class VyperCompilation extends AbstractCompilation { * @returns false if the auxdata positions cannot be generated, true otherwise. */ public async generateCborAuxdataPositions() { - const [, runtimeAuxdataCbor, runtimeCborLengthHex] = splitAuxdata( - this.getRuntimeBytecode(), - this.auxdataStyle, - ); - - // Vyper 0.3.10 and higher does not have the auxdata in the runtime bytecode - if (this.auxdataStyle === AuxdataStyle.VYPER_LT_0_3_10) { - this.runtimeBytecodeCborAuxdata = this.tryGenerateCborAuxdataPosition( + try { + const [, runtimeAuxdataCbor, runtimeCborLengthHex] = splitAuxdata( this.getRuntimeBytecode(), - runtimeAuxdataCbor, - runtimeCborLengthHex, + this.auxdataStyle, ); - } - const [, creationAuxdataCbor, creationCborLengthHex] = splitAuxdata( - this.getCreationBytecode(), - this.auxdataStyle, - ); + // Vyper 0.3.10 and higher does not have the auxdata in the runtime bytecode + if ( + this.auxdataStyle === AuxdataStyle.VYPER_LT_0_3_10 || + this.auxdataStyle === AuxdataStyle.VYPER_LT_0_3_5 + ) { + this.runtimeBytecodeCborAuxdata = this.tryGenerateCborAuxdataPosition( + this.getRuntimeBytecode(), + runtimeAuxdataCbor, + runtimeCborLengthHex, + ); + } - this.creationBytecodeCborAuxdata = this.tryGenerateCborAuxdataPosition( - this.getCreationBytecode(), - creationAuxdataCbor, - creationCborLengthHex, - ); + const [, creationAuxdataCbor, creationCborLengthHex] = splitAuxdata( + this.getCreationBytecode(), + this.auxdataStyle, + ); + + this.creationBytecodeCborAuxdata = this.tryGenerateCborAuxdataPosition( + this.getCreationBytecode(), + creationAuxdataCbor, + creationCborLengthHex, + ); - return true; + return true; + } catch (error) { + logWarn('Cannot generate cbor auxdata positions', { + error, + }); + return false; + } } private tryGenerateCborAuxdataPosition( @@ -203,10 +208,6 @@ export class VyperCompilation extends AbstractCompilation { auxdataCbor: string, cborLengthHex: string, ): CompiledContractCborAuxdata { - if (!auxdataCbor) { - return {}; - } - const auxdataFromRawBytecode = `${auxdataCbor}${cborLengthHex}`; // Handles vyper lower than 0.3.10 in which the auxdata length bytes count @@ -228,4 +229,12 @@ export class VyperCompilation extends AbstractCompilation { }, }; } + + // Override the bytecodes' getter methods to not duplicate the 0x prefix + getCreationBytecode() { + return this.getCompilationTarget().evm.bytecode.object; + } + getRuntimeBytecode() { + return this.getCompilationTarget().evm.deployedBytecode.object; + } } diff --git a/packages/lib-sourcify/test/Compilation/SolidityCompilation.spec.ts b/packages/lib-sourcify/test/Compilation/SolidityCompilation.spec.ts index baa33c7dd..11591cfda 100644 --- a/packages/lib-sourcify/test/Compilation/SolidityCompilation.spec.ts +++ b/packages/lib-sourcify/test/Compilation/SolidityCompilation.spec.ts @@ -57,8 +57,12 @@ describe('SolidityCompilation', () => { ); await compilation.compile(); - expect(compilation.getCreationBytecode()).to.not.be.undefined; - expect(compilation.getRuntimeBytecode()).to.not.be.undefined; + expect(compilation.getCreationBytecode()).to.equal( + '0x608060405234801561001057600080fd5b5061012f806100206000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80632e64cec11460375780636057361d146051575b600080fd5b603d6069565b6040516048919060c2565b60405180910390f35b6067600480360381019060639190608f565b6072565b005b60008054905090565b8060008190555050565b60008135905060898160e5565b92915050565b60006020828403121560a057600080fd5b600060ac84828501607c565b91505092915050565b60bc8160db565b82525050565b600060208201905060d5600083018460b5565b92915050565b6000819050919050565b60ec8160db565b811460f657600080fd5b5056fea264697066735822122005183dd5df276b396683ae62d0c96c3a406d6f9dad1ad0923daf492c531124b164736f6c63430008040033', + ); + expect(compilation.getRuntimeBytecode()).to.equal( + '0x6080604052348015600f57600080fd5b506004361060325760003560e01c80632e64cec11460375780636057361d146051575b600080fd5b603d6069565b6040516048919060c2565b60405180910390f35b6067600480360381019060639190608f565b6072565b005b60008054905090565b8060008190555050565b60008135905060898160e5565b92915050565b60006020828403121560a057600080fd5b600060ac84828501607c565b91505092915050565b60bc8160db565b82525050565b600060208201905060d5600083018460b5565b92915050565b6000819050919050565b60ec8160db565b811460f657600080fd5b5056fea264697066735822122005183dd5df276b396683ae62d0c96c3a406d6f9dad1ad0923daf492c531124b164736f6c63430008040033', + ); }); it('should generate correct CBOR auxdata positions', async () => { @@ -89,7 +93,20 @@ describe('SolidityCompilation', () => { await compilation.compile(); const success = await compilation.generateCborAuxdataPositions(); expect(success).to.be.true; - expect(compilation.runtimeBytecodeCborAuxdata).to.not.be.empty; + expect(compilation.runtimeBytecodeCborAuxdata).to.deep.equal({ + '1': { + offset: 250, + value: + '0xa264697066735822122005183dd5df276b396683ae62d0c96c3a406d6f9dad1ad0923daf492c531124b164736f6c63430008040033', + }, + }); + expect(compilation.creationBytecodeCborAuxdata).to.deep.equal({ + '1': { + offset: 282, + value: + '0xa264697066735822122005183dd5df276b396683ae62d0c96c3a406d6f9dad1ad0923daf492c531124b164736f6c63430008040033', + }, + }); }); it('should handle multiple auxdatas correctly', async () => { @@ -130,17 +147,40 @@ describe('SolidityCompilation', () => { await compilation.compile(); const success = await compilation.generateCborAuxdataPositions(); expect(success).to.be.true; - - console.log(compilation.runtimeBytecodeCborAuxdata); - console.log(compilation.creationBytecodeCborAuxdata); - expect(compilation.runtimeBytecodeCborAuxdata).to.not.be.undefined; - expect(compilation.creationBytecodeCborAuxdata).to.not.be.undefined; - expect( - Object.keys(compilation.runtimeBytecodeCborAuxdata!).length, - ).to.be.greaterThan(1); - expect( - Object.keys(compilation.creationBytecodeCborAuxdata!).length, - ).to.be.greaterThan(1); + expect(compilation.runtimeBytecodeCborAuxdata).to.deep.equal({ + '1': { + offset: 4116, + value: + '0xa26469706673582212203f80215daaa57bc563fda2c3949f4197328f188fccab68874ea57b874c93bd4f64736f6c63430008090033', + }, + '2': { + offset: 2743, + value: + '0xa264697066735822122062aefa28f677414dcac37f95b9b8ce7fcf373fd22d1bae136401de85504508e764736f6c63430008090033', + }, + '3': { + offset: 4063, + value: + '0xa2646970667358221220eb4312065a8c0fb940ef11ef5853554a447a5325095ee0f8fbbbbfc43dbb1b7464736f6c63430008090033', + }, + }); + expect(compilation.creationBytecodeCborAuxdata).to.deep.equal({ + '1': { + offset: 4148, + value: + '0xa26469706673582212203f80215daaa57bc563fda2c3949f4197328f188fccab68874ea57b874c93bd4f64736f6c63430008090033', + }, + '2': { + offset: 2775, + value: + '0xa264697066735822122062aefa28f677414dcac37f95b9b8ce7fcf373fd22d1bae136401de85504508e764736f6c63430008090033', + }, + '3': { + offset: 4095, + value: + '0xa2646970667358221220eb4312065a8c0fb940ef11ef5853554a447a5325095ee0f8fbbbbfc43dbb1b7464736f6c63430008090033', + }, + }); }); it('should handle case with no auxdata when metadata is disabled', async () => { @@ -188,7 +228,7 @@ describe('SolidityCompilation', () => { expect(compilation.runtimeBytecodeCborAuxdata).to.deep.equal({}); }); - it('should handle case with single auxdata using Storage contract', async () => { + it('should throw when compilation target is invalid', async () => { const contractPath = path.join(__dirname, '..', 'sources', 'Storage'); const metadata = JSON.parse( fs.readFileSync(path.join(contractPath, 'metadata.json'), 'utf8'), @@ -210,39 +250,63 @@ describe('SolidityCompilation', () => { sources, settings: getSolcSettingsFromMetadata(metadata), }, - getCompilationTargetFromMetadata(metadata), + { name: 'NonExistentContract', path: 'Storage.sol' }, ); - await compilation.compile(); - - const result = await compilation.generateCborAuxdataPositions(); - - expect(result).to.be.true; - expect(compilation.runtimeBytecodeCborAuxdata).to.have.property('1'); - expect(compilation.runtimeBytecodeCborAuxdata!['1']).to.have.property( - 'offset', + await expect(compilation.compile()).to.be.eventually.rejectedWith( + 'Contract not found in compiler output', ); - expect(compilation.runtimeBytecodeCborAuxdata!['1']).to.have.property( - 'value', + }); + + it('should return empty object when no immutable references exist', async () => { + const contractPath = path.join( + __dirname, + '..', + 'sources', + 'NoImmutableField', ); - expect(compilation.creationBytecodeCborAuxdata).to.have.property('1'); - expect(compilation.creationBytecodeCborAuxdata!['1']).to.have.property( - 'offset', + const metadata = JSON.parse( + fs.readFileSync(path.join(contractPath, 'metadata.json'), 'utf8'), ); - expect(compilation.creationBytecodeCborAuxdata!['1']).to.have.property( - 'value', + const sources = { + 'NoImmutable.sol': { + content: fs.readFileSync( + path.join(contractPath, 'sources', 'NoImmutable.sol'), + 'utf8', + ), + }, + }; + + const compilation = new SolidityCompilation( + solc, + metadata.compiler.version, + { + language: 'Solidity', + sources, + settings: getSolcSettingsFromMetadata(metadata), + }, + getCompilationTargetFromMetadata(metadata), ); + + await compilation.compile(); + const immutableRefs = compilation.getImmutableReferences(); + expect(immutableRefs).to.deep.equal({}); }); - it('should throw when compilation target is invalid', async () => { - const contractPath = path.join(__dirname, '..', 'sources', 'Storage'); + it('should return immutable references when they exist', async () => { + const contractPath = path.join( + __dirname, + '..', + 'sources', + 'WithImmutables', + ); const metadata = JSON.parse( fs.readFileSync(path.join(contractPath, 'metadata.json'), 'utf8'), ); const sources = { - 'project:/contracts/Storage.sol': { + 'contracts/WithImmutables.sol': { content: fs.readFileSync( - path.join(contractPath, 'sources', 'Storage.sol'), + path.join(contractPath, 'sources', 'WithImmutables.sol'), 'utf8', ), }, @@ -256,11 +320,11 @@ describe('SolidityCompilation', () => { sources, settings: getSolcSettingsFromMetadata(metadata), }, - { name: 'NonExistentContract', path: 'Storage.sol' }, + getCompilationTargetFromMetadata(metadata), ); - await expect(compilation.compile()).to.be.eventually.rejectedWith( - 'Contract not found in compiler output', - ); + await compilation.compile(); + const immutableRefs = compilation.getImmutableReferences(); + expect(immutableRefs).to.deep.equal({ '3': [{ length: 32, start: 608 }] }); }); }); diff --git a/packages/lib-sourcify/test/Compilation/VyperCompilation.spec.ts b/packages/lib-sourcify/test/Compilation/VyperCompilation.spec.ts index 1ee80fc08..9fb5c440a 100644 --- a/packages/lib-sourcify/test/Compilation/VyperCompilation.spec.ts +++ b/packages/lib-sourcify/test/Compilation/VyperCompilation.spec.ts @@ -44,8 +44,12 @@ describe('VyperCompilation', () => { ); await compilation.compile(); - expect(compilation.getCreationBytecode()).to.not.be.undefined; - expect(compilation.getRuntimeBytecode()).to.not.be.undefined; + expect(compilation.getCreationBytecode()).to.equal( + '0x61008f61000f60003961008f6000f360003560e01c63c605f76c8118610084573461008a57602080608052600c6040527f48656c6c6f20576f726c6421000000000000000000000000000000000000000060605260408160800181518152602082015160208201528051806020830101601f82600003163682375050601f19601f8251602001011690509050810190506080f35b60006000fd5b600080fd84188f8000a16576797065728300030a0012', + ); + expect(compilation.getRuntimeBytecode()).to.equal( + '0x60003560e01c63c605f76c8118610084573461008a57602080608052600c6040527f48656c6c6f20576f726c6421000000000000000000000000000000000000000060605260408160800181518152602082015160208201528051806020830101601f82600003163682375050601f19601f8251602001011690509050810190506080f35b60006000fd5b600080fd', + ); }); it('should handle immutable references correctly', async () => { @@ -86,7 +90,9 @@ describe('VyperCompilation', () => { ); await compilation.compile(); - expect(compilation.getImmutableReferences()).to.not.be.empty; + expect(compilation.getImmutableReferences()).to.deep.equal({ + '0': [{ length: 96, start: 167 }], + }); }); it('should generate correct CBOR auxdata positions', async () => { @@ -129,56 +135,438 @@ describe('VyperCompilation', () => { await compilation.compile(); const success = await compilation.generateCborAuxdataPositions(); expect(success).to.be.true; - expect(compilation.creationBytecodeCborAuxdata).to.not.be.empty; + expect(compilation.creationBytecodeCborAuxdata).to.deep.equal({ + '1': { offset: 158, value: '0x84188f8000a16576797065728300030a0012' }, + }); }); - it('should handle different Vyper versions correctly', async () => { - const versions = [ - '0.3.4+commit.f31f0ec4', - '0.3.7+commit.6020b8bb', + it('should throw compilation errors', async () => { + const invalidContent = 'invalid vyper code @123'; + const contractFileName = 'invalid.vy'; + + const compilation = new VyperCompilation( + vyperCompiler, '0.3.10+commit.91361694', - ]; - - for (const version of versions) { - const contractPath = path.join( - __dirname, - '..', - 'sources', - 'Vyper', - 'testcontract', - ); - const contractFileName = 'test.vy'; - const contractContent = fs.readFileSync( - path.join(contractPath, contractFileName), - 'utf8', - ); - - const compilation = new VyperCompilation( - vyperCompiler, - version, - { - language: 'Vyper', - sources: { - [contractFileName]: { - content: contractContent, - }, + { + language: 'Vyper', + sources: { + [contractFileName]: { + content: invalidContent, + }, + }, + settings: { + evmVersion: 'istanbul', + outputSelection: { + '*': ['evm.bytecode'], }, - settings: { - evmVersion: 'istanbul', - outputSelection: { - '*': ['evm.bytecode'], + }, + }, + { + name: contractFileName.split('.')[0], + path: contractFileName, + }, + ); + + try { + await compilation.compile(); + expect.fail('Should have thrown an error'); + } catch (error) { + expect(error).to.exist; + } + }); + + it('should handle missing bytecode in compilation output', async () => { + const contractPath = path.join( + __dirname, + '..', + 'sources', + 'Vyper', + 'testcontract', + ); + const contractName = 'test'; + const contractFileName = `${contractName}.vy`; + const contractContent = fs.readFileSync( + path.join(contractPath, contractFileName), + 'utf8', + ); + + // Mock vyperCompiler to return output without bytecode + const mockCompiler = { + compile: async () => ({ + contracts: { + ['different' + contractFileName]: { + ['different' + contractName]: { + evm: { + bytecode: { + object: '', + }, + }, }, }, }, - { - name: contractFileName.split('.')[0], - path: contractFileName, + }), + }; + + const compilation = new VyperCompilation( + mockCompiler as any, + '0.3.10+commit.91361694', + { + language: 'Vyper', + sources: { + [contractFileName]: { + content: contractContent, + }, + }, + settings: { + evmVersion: 'istanbul', + outputSelection: { + '*': ['evm.bytecode'], + }, }, - ); + }, + { + name: contractName, + path: contractFileName, + }, + ); + try { await compilation.compile(); - expect(compilation.getCreationBytecode()).to.not.be.undefined; - expect(compilation.getRuntimeBytecode()).to.not.be.undefined; + expect.fail('Should have thrown an error'); + } catch (error: any) { + expect(error.message).to.equal('Contract not found in compiler output'); } }); + + it('should handle errors in CBOR auxdata positions generation', async () => { + const contractPath = path.join( + __dirname, + '..', + 'sources', + 'Vyper', + 'testcontract', + ); + const contractName = 'test'; + const contractFileName = `${contractName}.vy`; + const contractContent = fs.readFileSync( + path.join(contractPath, contractFileName), + 'utf8', + ); + + // Mock vyperCompiler to return output without metadata + const mockCompiler = { + compile: async () => ({ + contracts: { + [contractFileName]: { + [contractName]: { + evm: { + bytecode: { + object: '0x123456', + }, + }, + }, + }, + }, + }), + }; + + const compilation = new VyperCompilation( + mockCompiler as any, + '0.3.10+commit.91361694', + { + language: 'Vyper', + sources: { + [contractFileName]: { + content: contractContent, + }, + }, + settings: { + evmVersion: 'istanbul', + outputSelection: { + '*': ['evm.bytecode'], + }, + }, + }, + { + name: contractName, + path: contractFileName, + }, + ); + + await compilation.compile(); + const success = await compilation.generateCborAuxdataPositions(); + expect(success).to.be.false; + }); + + it('should handle beta versions of Vyper, transforming the version to a valid semver', async () => { + const contractPath = path.join( + __dirname, + '..', + 'sources', + 'Vyper', + 'testcontract', + ); + const contractName = 'test'; + const contractFileName = `${contractName}.vy`; + const contractContent = fs.readFileSync( + path.join(contractPath, contractFileName), + 'utf8', + ); + + // We don't actually need to compile here, we just need to test the version transformation + const mockCompiler = { + compile: async () => ({ + contracts: { + [contractFileName]: { + [contractName]: { + evm: { + bytecode: { + object: '0x123456', + }, + }, + }, + }, + }, + }), + }; + + const compilation = new VyperCompilation( + mockCompiler as any, + '0.4.1b4+commit.4507d2a6', + { + language: 'Vyper', + sources: { + [contractFileName]: { + content: contractContent, + }, + }, + settings: { + evmVersion: 'london', + outputSelection: { + '*': ['evm.bytecode'], + }, + }, + }, + { + name: contractName, + path: contractFileName, + }, + ); + + await compilation.compile(); + expect(compilation.compilerVersionCompatibleWithSemver).to.equal( + '0.4.1+commit.4507d2a6', + ); + }); + + it('should throw error for invalid Vyper version format', () => { + const contractPath = path.join( + __dirname, + '..', + 'sources', + 'Vyper', + 'testcontract', + ); + const contractFileName = 'test.vy'; + const contractContent = fs.readFileSync( + path.join(contractPath, contractFileName), + 'utf8', + ); + + expect( + () => + new VyperCompilation( + vyperCompiler, + 'invalid.version.format', // Invalid version format + { + language: 'Vyper', + sources: { + [contractFileName]: { + content: contractContent, + }, + }, + settings: { + evmVersion: 'istanbul', + outputSelection: { + '*': ['evm.bytecode'], + }, + }, + }, + { + name: contractFileName.split('.')[0], + path: contractFileName, + }, + ), + ).to.throw('Invalid Vyper compiler version'); + }); + + it('should handle bytecode decoding errors in getImmutableReferences', async () => { + const contractPath = path.join( + __dirname, + '..', + 'sources', + 'Vyper', + 'testcontract', + ); + const contractName = 'test'; + const contractFileName = `${contractName}.vy`; + const contractContent = fs.readFileSync( + path.join(contractPath, contractFileName), + 'utf8', + ); + + // Mock compiler to return invalid bytecode that will cause decode to fail + const mockCompiler = { + compile: async () => ({ + contracts: { + [contractFileName]: { + [contractName]: { + evm: { + bytecode: { + object: '0x1234', // Invalid/malformed bytecode + }, + deployedBytecode: { + object: '0x5678', + }, + }, + }, + }, + }, + }), + }; + + const compilation = new VyperCompilation( + mockCompiler as any, + '0.3.10+commit.91361694', // Using version >= 0.3.10 to trigger immutable reference check + { + language: 'Vyper', + sources: { + [contractFileName]: { + content: contractContent, + }, + }, + settings: { + evmVersion: 'istanbul', + outputSelection: { + '*': ['evm.bytecode'], + }, + }, + }, + { + name: contractName, + path: contractFileName, + }, + ); + + await compilation.compile(); + const immutableRefs = compilation.getImmutableReferences(); + expect(immutableRefs).to.be.empty; + }); + + it('should handle vyper versions lower than 0.3.5', async () => { + const contractPath = path.join( + __dirname, + '..', + 'sources', + 'Vyper', + 'testcontract', + ); + const contractFileName = 'test.vy'; + const contractContent = fs.readFileSync( + path.join(contractPath, contractFileName), + 'utf8', + ); + + const compilation = new VyperCompilation( + vyperCompiler, + '0.3.4+commit.f31f0ec4', + { + language: 'Vyper', + sources: { + [contractFileName]: { + content: contractContent, + }, + }, + settings: { + evmVersion: 'istanbul', + outputSelection: { + '*': ['evm.bytecode'], + }, + }, + }, + { + name: contractFileName.split('.')[0], + path: contractFileName, + }, + ); + + await compilation.compile(); + await compilation.generateCborAuxdataPositions(); + expect(compilation.getCreationBytecode()).to.equal( + '0x6100b761000f6000396100b76000f36003361161000c576100a1565b60003560e01c346100a75763c605f76c811861009f57600436186100a757602080608052600c6040527f48656c6c6f20576f726c6421000000000000000000000000000000000000000060605260408160800181518082526020830160208301815181525050508051806020830101601f82600003163682375050601f19601f8251602001011690509050810190506080f35b505b60006000fd5b600080fda165767970657283000304', + ); + expect(compilation.getRuntimeBytecode()).to.equal( + '0x6003361161000c576100a1565b60003560e01c346100a75763c605f76c811861009f57600436186100a757602080608052600c6040527f48656c6c6f20576f726c6421000000000000000000000000000000000000000060605260408160800181518082526020830160208301815181525050508051806020830101601f82600003163682375050601f19601f8251602001011690509050810190506080f35b505b60006000fd5b600080fda165767970657283000304', + ); + expect(compilation.creationBytecodeCborAuxdata).to.deep.equal({ + '1': { offset: 187, value: '0xa165767970657283000304' }, + }); + expect(compilation.runtimeBytecodeCborAuxdata).to.deep.equal({ + '1': { offset: 172, value: '0xa165767970657283000304' }, + }); + }); + + it('should handle vyper versions lower than 0.3.10', async () => { + const contractPath = path.join( + __dirname, + '..', + 'sources', + 'Vyper', + 'testcontract', + ); + const contractFileName = 'test.vy'; + const contractContent = fs.readFileSync( + path.join(contractPath, contractFileName), + 'utf8', + ); + + // Test with version < 0.3.10 + const compilation = new VyperCompilation( + vyperCompiler, + '0.3.7+commit.6020b8bb', + { + language: 'Vyper', + sources: { + [contractFileName]: { + content: contractContent, + }, + }, + settings: { + evmVersion: 'istanbul', + outputSelection: { + '*': ['evm.bytecode'], + }, + }, + }, + { + name: contractFileName.split('.')[0], + path: contractFileName, + }, + ); + + await compilation.compile(); + await compilation.generateCborAuxdataPositions(); + expect(compilation.getCreationBytecode()).to.equal( + '0x6100b961000f6000396100b96000f36003361161000c576100a1565b60003560e01c346100a75763c605f76c811861009f57600436106100a757602080608052600c6040527f48656c6c6f20576f726c6421000000000000000000000000000000000000000060605260408160800181518082526020830160208301815181525050508051806020830101601f82600003163682375050601f19601f8251602001011690509050810190506080f35b505b60006000fd5b600080fda165767970657283000307000b', + ); + expect(compilation.getRuntimeBytecode()).to.equal( + '0x6003361161000c576100a1565b60003560e01c346100a75763c605f76c811861009f57600436106100a757602080608052600c6040527f48656c6c6f20576f726c6421000000000000000000000000000000000000000060605260408160800181518082526020830160208301815181525050508051806020830101601f82600003163682375050601f19601f8251602001011690509050810190506080f35b505b60006000fd5b600080fda165767970657283000307000b', + ); + expect(compilation.creationBytecodeCborAuxdata).to.deep.equal({ + '1': { offset: 187, value: '0xa165767970657283000307000b' }, + }); + expect(compilation.runtimeBytecodeCborAuxdata).to.deep.equal({ + '1': { offset: 172, value: '0xa165767970657283000307000b' }, + }); + }); }); diff --git a/packages/lib-sourcify/test/sources/NoImmutableField/metadata.json b/packages/lib-sourcify/test/sources/NoImmutableField/metadata.json new file mode 100644 index 000000000..f2375d7b8 --- /dev/null +++ b/packages/lib-sourcify/test/sources/NoImmutableField/metadata.json @@ -0,0 +1,46 @@ +{ + "compiler": { "version": "0.6.4+commit.1dca32f3" }, + "language": "Solidity", + "output": { + "abi": [ + { + "inputs": [], + "name": "retrieve", + "outputs": [ + { "internalType": "uint256", "name": "", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "num", "type": "uint256" } + ], + "name": "store", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "devdoc": { "methods": {} }, + "userdoc": { "methods": {} } + }, + "settings": { + "compilationTarget": { "NoImmutable.sol": "NoImmutable" }, + "evmVersion": "istanbul", + "libraries": {}, + "metadata": { "bytecodeHash": "ipfs" }, + "optimizer": { "enabled": false, "runs": 200 }, + "remappings": [] + }, + "sources": { + "NoImmutable.sol": { + "keccak256": "0x1b9113058fc0d35ecd13728d39b136f6eea73a603bf68ffaa23faf0397ba131a", + "urls": [ + "bzz-raw://f1a56b1830d323ae787bf93666711344f5f8f5015cc4236977e5837e74faf5c9", + "dweb:/ipfs/QmXurkTv7odZ14rpFtEG7QbTyu4Tu9Lq2jwxbGZVQ1cPV6" + ] + } + }, + "version": 1 +} diff --git a/packages/lib-sourcify/test/sources/NoImmutableField/sources/NoImmutable.sol b/packages/lib-sourcify/test/sources/NoImmutableField/sources/NoImmutable.sol new file mode 100644 index 000000000..b82f46065 --- /dev/null +++ b/packages/lib-sourcify/test/sources/NoImmutableField/sources/NoImmutable.sol @@ -0,0 +1,13 @@ +pragma solidity 0.6.4; + +contract NoImmutable { + uint256 number; + + function store(uint256 num) public { + number = num; + } + + function retrieve() public view returns (uint256) { + return number; + } +} From 02b208a5c39c9b7f93e9b223f3d99fdb2c3269e6 Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Mon, 27 Jan 2025 10:22:22 +0100 Subject: [PATCH 6/7] Add tests for SolidityMetadataContract (#1869) * Add tests for SolidityMetadataContract * use real SolidityCompilation class in SolidityMetadataContract * fixes from PR review --- .../Validation/SolidityMetadataContract.ts | 69 +-- .../SolidityMetadataContract.spec.ts | 511 ++++++++++++++++++ 2 files changed, 536 insertions(+), 44 deletions(-) create mode 100644 packages/lib-sourcify/test/Validation-draft/SolidityMetadataContract.spec.ts diff --git a/packages/lib-sourcify/src/Validation/SolidityMetadataContract.ts b/packages/lib-sourcify/src/Validation/SolidityMetadataContract.ts index 231eac7ae..f09f97bb9 100644 --- a/packages/lib-sourcify/src/Validation/SolidityMetadataContract.ts +++ b/packages/lib-sourcify/src/Validation/SolidityMetadataContract.ts @@ -1,19 +1,20 @@ import { InvalidSources, - ISolidityCompiler, - IVyperCompiler, Metadata, MissingSources, PathContent, StringMap, - VyperJsonInput, - JsonInput, Settings, Libraries, } from '..'; import { id as keccak256str } from 'ethers'; import semver from 'semver'; import { getIpfsGateway, performFetch } from './fetchUtils'; +import { SolidityCompilation } from '../Compilation/SolidityCompilation'; +import { + ISolidityCompiler, + SolidityJsonInput, +} from '../Compilation/SolidityTypes'; const CONTENT_VARIATORS = [ (content: string) => content.replace(/\r?\n/g, '\r\n'), @@ -28,25 +29,6 @@ const ENDING_VARIATORS = [ (content: string) => content + '\r\n', ]; -// Dummy Compilation class -class Compilation { - compiler: ISolidityCompiler | IVyperCompiler; - compilerVersion: string; - compilationTarget: string; - jsonInput: JsonInput | VyperJsonInput; - constructor( - compiler: ISolidityCompiler | IVyperCompiler, - compilerVersion: string, - compilationTarget: string, - jsonInput: JsonInput | VyperJsonInput, - ) { - this.compiler = compiler; - this.compilerVersion = compilerVersion; - this.compilationTarget = compilationTarget; - this.jsonInput = jsonInput; - } -} - export class SolidityMetadataContract { metadata: Metadata; name: string; @@ -58,8 +40,8 @@ export class SolidityMetadataContract { invalidSources: InvalidSources; unusedSourceFiles: string[]; metadataPathToProvidedFilePath: StringMap; // maps the file path as in metadata.sources to the path of the provided by the user. E.g. metadata can have "contracts/1_Storage.sol" but the user provided "/Users/user/project/contracts/1_Storage.sol" - compilation: Compilation | null; - solcJsonInput: JsonInput | null; + compilation: SolidityCompilation | null; + solcJsonInput: SolidityJsonInput | null; constructor(metadata: Metadata, providedSources: PathContent[]) { this.metadata = metadata; @@ -81,26 +63,25 @@ export class SolidityMetadataContract { this.providedSourcesByHash = this.storeByHash(providedSources); this.assembleContract(); if (this.isCompilable()) { - this.#createJsonInputFromMetadata(); + this.createJsonInputFromMetadata(); } } async createCompilation(compiler: ISolidityCompiler) { - if (!this.solcJsonInput) { - throw new Error( - `No JsonInput found, cannot create compilation for SolidityMetadataContract: ${this.path}:${this.name}`, - ); - } - if (Object.keys(this.missingSources).length > 0) { await this.fetchMissing(); } - this.compilation = new Compilation( + this.createJsonInputFromMetadata(); + + this.compilation = new SolidityCompilation( compiler, this.metadata.compiler.version, - this.path + ':' + this.name, - this.solcJsonInput, + this.solcJsonInput!, + { + path: this.path, + name: this.name, + }, ); return this.compilation; @@ -249,7 +230,7 @@ export class SolidityMetadataContract { throw error; } - this.#createJsonInputFromMetadata(); + this.createJsonInputFromMetadata(); } private generateVariations(pathContent: PathContent): PathContent[] { @@ -286,7 +267,7 @@ export class SolidityMetadataContract { }); } - #createJsonInputFromMetadata() { + createJsonInputFromMetadata() { if ( Object.keys(this.missingSources).length > 0 || Object.keys(this.invalidSources).length > 0 @@ -298,7 +279,7 @@ export class SolidityMetadataContract { ); } - this.solcJsonInput = {} as JsonInput; + this.solcJsonInput = {} as SolidityJsonInput; // Clone the settings object to avoid mutating the original metadata this.solcJsonInput.settings = JSON.parse( JSON.stringify(this.metadata.settings), @@ -310,15 +291,15 @@ export class SolidityMetadataContract { Object.keys(this.metadata.settings.compilationTarget).length != 1 ) { throw new Error( - `Can't create JsonInput from metadata: Invalid compilationTarget in metadata: ${ - this.metadata?.settings?.compilationTarget - }`, + `Can't create JsonInput from metadata: Invalid compilationTarget in metadata: ${Object.keys( + this.metadata.settings.compilationTarget, + ).join(',')}`, ); } this.handleInlinerBug(); // Standard JSON does not have compilationTarget, only in metadata.json - delete this.solcJsonInput?.settings?.compilationTarget; + delete this.solcJsonInput.settings.compilationTarget; this.solcJsonInput.sources = {}; for (const source in this.metadata.sources) { @@ -332,9 +313,9 @@ export class SolidityMetadataContract { // Convert the libraries from the metadata format to the compiler_settings format // metadata format: "contracts/1_Storage.sol:Journal": "0x7d53f102f4d4aa014db4e10d6deec2009b3cda6b" // settings format: "contracts/1_Storage.sol": { Journal: "0x7d53f102f4d4aa014db4e10d6deec2009b3cda6b" } - const metadataLibraries = this.metadata.settings?.libraries || {}; + const metadataLibraries = this.metadata.settings.libraries || {}; this.solcJsonInput.settings.libraries = Object.keys( - metadataLibraries || {}, + metadataLibraries, ).reduce((libraries, libraryKey) => { // Before Solidity v0.7.5: { "ERC20": "0x..."} if (!libraryKey.includes(':')) { diff --git a/packages/lib-sourcify/test/Validation-draft/SolidityMetadataContract.spec.ts b/packages/lib-sourcify/test/Validation-draft/SolidityMetadataContract.spec.ts new file mode 100644 index 000000000..8cae0e8e0 --- /dev/null +++ b/packages/lib-sourcify/test/Validation-draft/SolidityMetadataContract.spec.ts @@ -0,0 +1,511 @@ +import { expect } from 'chai'; +import { SolidityMetadataContract } from '../../src/Validation/SolidityMetadataContract'; +import { id as keccak256str } from 'ethers'; +import nock from 'nock'; +import { ISolidityCompiler } from '../../src/Compilation/SolidityTypes'; +import { Metadata } from '../../src/Compilation/CompilationTypes'; +import { PathContent } from '../../src/lib/types'; + +describe('SolidityMetadataContract', () => { + let validMetadata: Metadata; + let validSourceContent: string; + let validSourcePath: string; + let validSources: PathContent[]; + let validName: string; + + beforeEach(() => { + // Setup sample source content and calculate its hash + validSourcePath = 'contracts/Storage.sol'; + validName = 'Storage'; + validSourceContent = ` + // SPDX-License-Identifier: MIT + pragma solidity ^0.8.0; + + contract Storage { + uint256 number; + + function store(uint256 num) public { + number = num; + } + + function retrieve() public view returns (uint256){ + return number; + } + }`; + const sourceHash = keccak256str(validSourceContent); + + // Create sample metadata + validMetadata = { + compiler: { + version: '0.8.0', + }, + language: 'Solidity', + output: { + abi: [], + devdoc: { + kind: 'dev', + methods: {}, + version: 1, + }, + userdoc: { + kind: 'user', + methods: {}, + version: 1, + }, + }, + settings: { + compilationTarget: { + [validSourcePath]: validName, + }, + evmVersion: 'london', + libraries: {}, + metadata: { + bytecodeHash: 'ipfs', + }, + optimizer: { + enabled: false, + runs: 200, + }, + }, + sources: { + [validSourcePath]: { + keccak256: sourceHash, + urls: [], + }, + }, + version: 1, + }; + + validSources = [ + { + path: validSourcePath, + content: validSourceContent, + }, + ]; + }); + + describe('constructor', () => { + it('should correctly initialize all properties with valid metadata and sources', () => { + const contract = new SolidityMetadataContract( + validMetadata, + validSources, + ); + + expect(contract.metadata).to.deep.equal(validMetadata); + expect(contract.name).to.equal(validName); + expect(contract.path).to.equal(validSourcePath); + expect(contract.providedSources).to.deep.equal(validSources); + expect(contract.providedSourcesByHash).to.be.instanceOf(Map); + expect(contract.providedSourcesByHash.size).to.equal(1); + const sourceHash = keccak256str(validSourceContent); + expect(contract.providedSourcesByHash.get(sourceHash)).to.deep.equal( + validSources[0], + ); + expect(contract.foundSources).to.deep.equal({ + [validSourcePath]: validSourceContent, + }); + expect(Object.keys(contract.missingSources)).to.be.empty; + expect(Object.keys(contract.invalidSources)).to.be.empty; + expect(contract.unusedSourceFiles).to.deep.equal([]); + expect(contract.metadataPathToProvidedFilePath).to.deep.equal({ + [validSourcePath]: validSourcePath, + }); + expect(contract.compilation).to.be.null; + expect(contract.solcJsonInput).to.exist; + expect(contract.solcJsonInput?.language).to.equal('Solidity'); + expect(contract.solcJsonInput?.sources).to.deep.equal({ + [validSourcePath]: { + content: validSourceContent, + }, + }); + expect(contract.solcJsonInput?.settings).to.not.have.property( + 'compilationTarget', + ); + expect(contract.solcJsonInput?.settings.evmVersion).to.equal( + validMetadata.settings.evmVersion, + ); + expect(contract.solcJsonInput?.settings.optimizer).to.deep.equal( + validMetadata.settings.optimizer, + ); + }); + + it('should handle missing sources', () => { + const contract = new SolidityMetadataContract(validMetadata, []); + expect(Object.keys(contract.missingSources)).to.have.lengthOf(1); + expect(contract.missingSources[validSourcePath]).to.exist; + }); + + it('should mark sources with non-matching hashes as missing', () => { + const invalidContent = 'invalid content'; + const invalidSources = [ + { + path: validSourcePath, + content: invalidContent, + }, + ]; + const contract = new SolidityMetadataContract( + validMetadata, + invalidSources, + ); + expect(Object.keys(contract.missingSources)).to.have.lengthOf(1); + expect(contract.missingSources[validSourcePath]).to.exist; + expect(Object.keys(contract.invalidSources)).to.be.empty; + }); + + it('should handle invalid source content in metadata', () => { + const invalidMetadata = { ...validMetadata }; + const invalidContent = 'invalid content'; + const expectedHash = keccak256str(validSourceContent); + const calculatedHash = keccak256str(invalidContent); + invalidMetadata.sources[validSourcePath].content = invalidContent; + + const contract = new SolidityMetadataContract(invalidMetadata, []); + expect(Object.keys(contract.invalidSources)).to.have.lengthOf(1); + expect(contract.invalidSources[validSourcePath]).to.exist; + expect(contract.invalidSources[validSourcePath].msg).to.include( + "don't match", + ); + expect(contract.invalidSources[validSourcePath].expectedHash).to.equal( + expectedHash, + ); + expect(contract.invalidSources[validSourcePath].calculatedHash).to.equal( + calculatedHash, + ); + }); + }); + + describe('handleInlinerBug', () => { + it('should remove inliner setting for affected versions', () => { + const affectedVersions = ['0.8.2', '0.8.3', '0.8.4']; + + for (const version of affectedVersions) { + const metadataWithAffectedVersion = { ...validMetadata }; + metadataWithAffectedVersion.compiler.version = version; + metadataWithAffectedVersion.settings.optimizer = { + enabled: true, + runs: 200, + details: { + inliner: true, + }, + }; + + const contract = new SolidityMetadataContract( + metadataWithAffectedVersion, + validSources, + ); + contract.createJsonInputFromMetadata(); + expect(contract.solcJsonInput?.settings?.optimizer?.details?.inliner).to + .be.undefined; + } + }); + + it('should keep inliner setting for unaffected versions', () => { + const unaffectedVersions = ['0.8.1', '0.8.5', '0.8.0']; + + for (const version of unaffectedVersions) { + const metadataWithUnaffectedVersion = { ...validMetadata }; + metadataWithUnaffectedVersion.compiler.version = version; + metadataWithUnaffectedVersion.settings.optimizer = { + enabled: true, + runs: 200, + details: { + inliner: true, + }, + }; + + const contract = new SolidityMetadataContract( + metadataWithUnaffectedVersion, + validSources, + ); + contract.createJsonInputFromMetadata(); + expect(contract.solcJsonInput?.settings?.optimizer?.details?.inliner).to + .be.true; + } + }); + }); + + describe('compilationTarget', () => { + it('should handle invalid compilation target', () => { + const metadata = { + ...validMetadata, + settings: { + ...validMetadata.settings, + compilationTarget: { + 'contract1.sol': 'Contract1', + 'contract2.sol': 'Contract2', + }, + }, + }; + expect( + () => new SolidityMetadataContract(metadata, validSources), + ).to.throw( + "Can't create JsonInput from metadata: Invalid compilationTarget in metadata: contract1.sol,contract2.sol", + ); + }); + }); + + describe('generateSourceVariations', () => { + it('should generate variations with different line endings', () => { + const content = 'line1\nline2\r\nline3\rline4'; + const sources = [ + { + path: validSourcePath, + content, + }, + ]; + + const contract = new SolidityMetadataContract(validMetadata, sources); + contract.generateSourceVariations(); + + const variations = Array.from(contract.providedSourcesByHash.values()); + expect(variations.length).to.be.greaterThan(1); + variations.forEach((variation) => { + expect(variation.path).to.equal(validSourcePath); + }); + }); + }); + + describe('handleLibraries', () => { + it('should handle pre-0.7.5 library format', () => { + const metadata = { + ...validMetadata, + settings: { + ...validMetadata.settings, + libraries: { + ERC20: '0x1234567890123456789012345678901234567890', + }, + }, + }; + const contract = new SolidityMetadataContract(metadata, validSources); + contract.createJsonInputFromMetadata(); + expect( + contract.solcJsonInput?.settings?.libraries?.[''] as any, + ).to.deep.equal({ + ERC20: '0x1234567890123456789012345678901234567890', + }); + }); + + it('should handle post-0.7.5 library format', () => { + const metadata = { + ...validMetadata, + settings: { + ...validMetadata.settings, + libraries: { + 'contracts/Library.sol:ERC20': + '0x1234567890123456789012345678901234567890', + }, + }, + }; + const contract = new SolidityMetadataContract(metadata, validSources); + contract.createJsonInputFromMetadata(); + expect( + contract.solcJsonInput?.settings?.libraries?.[ + 'contracts/Library.sol' + ] as any, + ).to.deep.equal({ + ERC20: '0x1234567890123456789012345678901234567890', + }); + }); + }); + + describe('createJsonInputFromMetadata', () => { + it('should throw error when there are missing sources', () => { + const contract = new SolidityMetadataContract(validMetadata, []); + expect(() => contract.createJsonInputFromMetadata()).to.throw( + "Can't create JsonInput from metadata: Missing or invalid sources in metadata", + ); + }); + + it('should throw error when there are missing sources', () => { + const invalidSources = [ + { + path: validSourcePath, + content: 'invalid content', + }, + ]; + const contract = new SolidityMetadataContract( + validMetadata, + invalidSources, + ); + expect(() => contract.createJsonInputFromMetadata()).to.throw( + "Can't create JsonInput from metadata: Missing or invalid sources in metadata", + ); + }); + }); + + describe('createCompilation', () => { + afterEach(() => { + nock.cleanAll(); + }); + + it('should create compilation when sources are available', async () => { + const contract = new SolidityMetadataContract( + validMetadata, + validSources, + ); + const mockCompiler: ISolidityCompiler = { + compile: async () => ({ + contracts: {}, + sources: {}, + errors: [], + }), + }; + const compilation = await contract.createCompilation(mockCompiler); + expect(compilation).to.exist; + expect(compilation.compiler).to.equal(mockCompiler); + expect(compilation.compilerVersion).to.equal( + validMetadata.compiler.version, + ); + expect(compilation.compilationTarget).to.deep.equal({ + name: validName, + path: validSourcePath, + }); + }); + + it('should fetch missing sources before creating compilation', async () => { + // Create metadata with IPFS source URL + const ipfsMetadata = { ...validMetadata }; + const ipfsHash = 'QmTest'; + ipfsMetadata.sources[validSourcePath].urls = [`dweb:/ipfs/${ipfsHash}`]; + + // Setup mock IPFS response + nock('https://ipfs.io') + .get(`/ipfs/${ipfsHash}`) + .reply(200, validSourceContent); + + const contract = new SolidityMetadataContract(ipfsMetadata, []); + expect(Object.keys(contract.missingSources)).to.have.lengthOf(1); + + const mockCompiler: ISolidityCompiler = { + compile: async () => ({ + contracts: {}, + sources: {}, + errors: [], + }), + }; + + const compilation = await contract.createCompilation(mockCompiler); + + // Verify missing sources were fetched + expect(Object.keys(contract.missingSources)).to.be.empty; + expect(contract.foundSources[validSourcePath]).to.equal( + validSourceContent, + ); + + // Verify compilation was created successfully + expect(compilation).to.exist; + expect(compilation.compiler).to.equal(mockCompiler); + expect(compilation.compilerVersion).to.equal( + ipfsMetadata.compiler.version, + ); + }); + }); + + describe('fetchMissing', () => { + afterEach(() => { + nock.cleanAll(); + }); + + it('should fetch missing sources from GitHub', async () => { + const githubMetadata = { ...validMetadata }; + const githubPath = + 'https://github.com/test/repo/blob/main/contracts/Storage.sol'; + + // Use the GitHub URL as the source path in metadata + githubMetadata.sources = { + [githubPath]: { + keccak256: keccak256str(validSourceContent), + urls: [], // URLs array can be empty since the GitHub path is in the filename + }, + }; + + nock('https://raw.githubusercontent.com') + .get('/test/repo/main/contracts/Storage.sol') + .reply(200, validSourceContent); + + const contract = new SolidityMetadataContract(githubMetadata, []); + expect(Object.keys(contract.missingSources)).to.have.lengthOf(1); + + await contract.fetchMissing(); + expect(Object.keys(contract.missingSources)).to.be.empty; + expect(contract.foundSources[githubPath]).to.equal(validSourceContent); + }); + + it('should fetch missing sources from IPFS', async () => { + const ipfsMetadata = { ...validMetadata }; + const ipfsHash = 'QmTest'; + ipfsMetadata.sources[validSourcePath].urls = [`dweb:/ipfs/${ipfsHash}`]; + + nock('https://ipfs.io') + .get(`/ipfs/${ipfsHash}`) + .reply(200, validSourceContent); + + const contract = new SolidityMetadataContract(ipfsMetadata, []); + expect(Object.keys(contract.missingSources)).to.have.lengthOf(1); + + await contract.fetchMissing(); + expect(Object.keys(contract.missingSources)).to.be.empty; + expect(contract.foundSources[validSourcePath]).to.equal( + validSourceContent, + ); + }); + + it('should throw error when sources cannot be fetched', async () => { + const contract = new SolidityMetadataContract(validMetadata, []); + expect(Object.keys(contract.missingSources)).to.have.lengthOf(1); + + nock('https://ipfs.io').get(/.*/).reply(404); + + try { + await contract.fetchMissing(); + expect.fail('Should have thrown an error'); + } catch (error) { + expect(error).to.be.an('Error'); + expect((error as Error).message).to.include('Resource missing'); + } + }); + }); + + describe('assembleContract', () => { + it('should find sources after generating variations', () => { + // Create source with different line endings than expected + const contentWithDifferentEnding = validSourceContent.replace( + /\n/g, + '\r\n', + ); + const sourceHash = keccak256str(validSourceContent); // Hash of the expected content + + // Create metadata that expects the original content hash + const metadataWithHash = { + ...validMetadata, + sources: { + [validSourcePath]: { + keccak256: sourceHash, + urls: [], + }, + }, + }; + + // Provide source with different line endings + const sourcesWithDifferentEnding = [ + { + path: validSourcePath, + content: contentWithDifferentEnding, + }, + ]; + + const contract = new SolidityMetadataContract( + metadataWithHash, + sourcesWithDifferentEnding, + ); + + // Initially the source should be missing because the hash doesn't match + expect(Object.keys(contract.missingSources)).to.be.empty; + // But after variations are generated, it should be found + expect(contract.foundSources[validSourcePath]).to.exist; + expect(contract.metadataPathToProvidedFilePath[validSourcePath]).to.equal( + validSourcePath, + ); + }); + }); +}); From 337fd91614f6dd64e9996b37b3bc5337ad77a948 Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Wed, 26 Feb 2025 18:36:30 +0100 Subject: [PATCH 7/7] Verification flow verification classes (#1879) * implement Verifcation.ts * Refactor Verification.ts * Add tests * Add more tests * Add comment to tests * Add Vyper tests in Verification * refactor Verification class * handle ContructorTransformation * add comment * Refactor runtime bytecode matching and add call protection transformation * Refactor Verification class to use getter methods and improve encapsulation * Refactor AbstractCompilation to use protected properties and getter methods * Move check transformation functions into Verification class Refactor project structure and import paths in lib-sourcify * restore old `verification.ts` tests * fix constructor arguments trasformantion tests * restore old `types.ts` * add VerificationError class * Implement Vyper constructor argument transformation test * Pass forceEmscripten option to compilation call * Split library map into runtime and creation maps * replace expectMatch with expectVerification * fix "should verify a contract with viaIR:true, optimizer disabled, and compiler <0.8.21" test * Add test for library verification with call protection transformation * fix call protection test * increase coverage * add missing tests from verification.spec.ts increasing coverage * Refactor bytecode transformations and verification logic - Extracted transformation functions from the Verification class to a separate module - Added a new SolidityBugType enum to handle specific compilation scenarios - Improved error handling for RPC unavailability * fix linting * Refactor error handling and type definitions in Sourcify library * Update error handling for bytecode fetching in Sourcify verification * Remove unnecessary compilationTarget deletion in SolidityCompilation * Refactor bytecode matching method signature and remove unused context interface * Improve bytecode matching readability with descriptive variable names * Remove unused getter abiEncodedConstructorArguments in Verification class * Refactor Solidity settings type and improve bug handling in verification * Update SolidityBugType and improve error handling in verification tests * Renamed functions in Transformations.ts to use 'extract' prefix instead of 'checkAndCreate' * Simplify extra file input bug detection and remove redundant error handling * Refactor Solidity metadata and compiler settings types * fixes for PR comments * Validate the bytecode length for Solidity and Vyper compilations before proceeding with bytecode matching * do not use existing transformations/values in `matchBytecode`. * Fix extra-file-input-bug at bytecode mismatch error * fix `should return null match when there is no perfect match and no auxdata` * fix "maliciously verify with creation bytecode that startsWith the creatorTx input" test * fixes after PR review * Improve source file reading and verification error handling - Update source file reading to support nested directory structures - Clarify error message for bytecode matching with no auxdata - Improve test description for bytecode matching scenario --------- Co-authored-by: Manuel Wedler --- .../src/Compilation/AbstractCompilation.ts | 47 +- .../src/Compilation/CompilationTypes.ts | 48 +- .../src/Compilation/SolidityCompilation.ts | 57 +- .../src/Compilation/SolidityTypes.ts | 5 +- .../src/Compilation/VyperCompilation.ts | 47 +- .../src/{lib => }/SourcifyChain.ts | 2 +- packages/lib-sourcify/src/SourcifyLibError.ts | 7 + .../Validation/SolidityMetadataContract.ts | 82 +- .../src/Validation/ValidationTypes.ts | 29 + .../lib-sourcify/src/Validation/fetchUtils.ts | 4 +- .../src/Validation/processFiles.ts | 5 +- .../lib-sourcify/src/Validation/zipUtils.ts | 4 +- .../src/Verification/Transformations.ts | 347 ++++ .../src/Verification/Verification.ts | 501 ++++++ .../src/Verification/VerificationTypes.ts | 35 + packages/lib-sourcify/src/index.ts | 4 +- .../src/lib/SolidityCheckedContract.ts | 2 +- .../src/lib/VyperCheckedContract.ts | 2 +- packages/lib-sourcify/src/lib/types.ts | 2 +- packages/lib-sourcify/src/lib/utils.ts | 2 +- packages/lib-sourcify/src/lib/validation.ts | 2 +- packages/lib-sourcify/src/lib/verification.ts | 22 +- packages/lib-sourcify/src/{lib => }/logger.ts | 0 .../Compilation/SolidityCompilation.spec.ts | 8 +- .../test/Compilation/VyperCompilation.spec.ts | 16 +- .../test/Verification/Verification.spec.ts | 1235 +++++++++++++ packages/lib-sourcify/test/compiler/common.ts | 2 +- .../test/compiler/solidityCompiler.ts | 2 +- .../test/compiler/vyperCompiler.ts | 2 +- .../ExtraFilesBytecodeMismatch/artifact.json | 808 +++++++++ .../contracts/token/ERC20/IERC20.sol | 77 + .../contracts/adapters/BaseUniswapAdapter.sol | 566 ++++++ .../adapters/FlashLiquidationAdapter.sol | 184 ++ .../adapters/UniswapLiquiditySwapAdapter.sol | 283 +++ .../adapters/UniswapRepayAdapter.sol | 266 +++ .../interfaces/IBaseUniswapAdapter.sol | 90 + .../openzeppelin/contracts/Address.sol | 61 + .../openzeppelin/contracts/Context.sol | 23 + .../openzeppelin/contracts/ERC20.sol | 344 ++++ .../openzeppelin/contracts/IERC20.sol | 80 + .../openzeppelin/contracts/IERC20Detailed.sol | 12 + .../openzeppelin/contracts/Ownable.sol | 69 + .../openzeppelin/contracts/SafeERC20.sol | 64 + .../openzeppelin/contracts/SafeMath.sol | 163 ++ .../AdminUpgradeabilityProxy.sol | 36 + .../BaseAdminUpgradeabilityProxy.sol | 126 ++ .../BaseUpgradeabilityProxy.sol | 66 + .../InitializableAdminUpgradeabilityProxy.sol | 42 + .../InitializableUpgradeabilityProxy.sol | 29 + .../openzeppelin/upgradeability/Proxy.sol | 72 + .../upgradeability/UpgradeabilityProxy.sol | 28 + .../deployments/ATokensAndRatesHelper.sol | 83 + .../StableAndVariableTokensHelper.sol | 47 + .../contracts/deployments/StringLib.sol | 8 + .../flashloan/base/FlashLoanReceiverBase.sol | 22 + .../interfaces/IFlashLoanReceiver.sol | 25 + .../contracts/interfaces/IAToken.sol | 102 ++ .../interfaces/IAaveIncentivesController.sol | 11 + .../interfaces/IChainlinkAggregator.sol | 17 + .../interfaces/ICreditDelegationToken.sol | 28 + .../contracts/interfaces/IDelegationToken.sol | 11 + .../contracts/interfaces/IERC20WithPermit.sol | 16 + .../contracts/interfaces/IExchangeAdapter.sol | 23 + .../interfaces/IInitializableAToken.sol | 55 + .../interfaces/IInitializableDebtToken.sol | 51 + .../contracts/interfaces/ILendingPool.sol | 410 +++++ .../ILendingPoolAddressesProvider.sol | 60 + .../ILendingPoolAddressesProviderRegistry.sol | 26 + .../ILendingPoolCollateralManager.sol | 60 + .../interfaces/ILendingPoolConfigurator.sol | 179 ++ .../interfaces/ILendingRateOracle.sol | 19 + .../interfaces/IPriceOracleGetter.sol | 16 + .../IReserveInterestRateStrategy.sol | 47 + .../interfaces/IScaledBalanceToken.sol | 26 + .../contracts/interfaces/IStableDebtToken.sol | 133 ++ .../interfaces/IUniswapV2Router02.sol | 30 + .../interfaces/IVariableDebtToken.sol | 62 + .../contracts/misc/AaveOracle.sol | 122 ++ .../misc/AaveProtocolDataProvider.sol | 180 ++ .../contracts/misc/UiPoolDataProvider.sol | 160 ++ .../contracts/misc/WETHGateway.sol | 189 ++ .../contracts/misc/WalletBalanceProvider.sol | 111 ++ .../misc/interfaces/IUiPoolDataProvider.sol | 93 + .../contracts/misc/interfaces/IWETH.sol | 16 + .../misc/interfaces/IWETHGateway.sol | 30 + .../mocks/flashloan/MockFlashLoanReceiver.sol | 84 + .../mocks/oracle/LendingRateOracle.sol | 26 + .../mocks/swap/MockUniswapV2Router02.sol | 106 ++ .../mocks/tokens/MintableDelegationERC20.sol | 34 + .../contracts/mocks/tokens/MintableERC20.sol | 28 + .../mocks/upgradeability/MockAToken.sol | 12 + .../upgradeability/MockStableDebtToken.sol | 10 + .../upgradeability/MockVariableDebtToken.sol | 10 + .../LendingPoolAddressesProvider.sol | 215 +++ .../LendingPoolAddressesProviderRegistry.sol | 89 + .../DefaultReserveInterestRateStrategy.sol | 261 +++ .../protocol/lendingpool/LendingPool.sol | 946 ++++++++++ .../LendingPoolCollateralManager.sol | 317 ++++ .../lendingpool/LendingPoolConfigurator.sol | 487 ++++++ .../lendingpool/LendingPoolStorage.sol | 32 + .../BaseImmutableAdminUpgradeabilityProxy.sol | 80 + ...zableImmutableAdminUpgradeabilityProxy.sol | 23 + .../VersionedInitializable.sol | 77 + .../configuration/ReserveConfiguration.sol | 366 ++++ .../configuration/UserConfiguration.sol | 111 ++ .../protocol/libraries/helpers/Errors.sol | 119 ++ .../protocol/libraries/helpers/Helpers.sol | 39 + .../protocol/libraries/logic/GenericLogic.sol | 275 +++ .../protocol/libraries/logic/ReserveLogic.sol | 373 ++++ .../libraries/logic/ValidationLogic.sol | 469 +++++ .../protocol/libraries/math/MathUtils.sol | 84 + .../libraries/math/PercentageMath.sol | 54 + .../protocol/libraries/math/WadRayMath.sol | 135 ++ .../protocol/libraries/types/DataTypes.sol | 49 + .../protocol/tokenization/AToken.sol | 406 +++++ .../tokenization/DelegationAwareAToken.sol | 30 + .../tokenization/IncentivizedERC20.sol | 255 +++ .../protocol/tokenization/StableDebtToken.sol | 435 +++++ .../tokenization/VariableDebtToken.sol | 209 +++ .../tokenization/base/DebtTokenBase.sol | 137 ++ .../complete_sources/hardhat/console.sol | 1532 +++++++++++++++++ .../ExtraFilesBytecodeMismatch/metadata.json | 1317 ++++++++++++++ .../sources/Address.sol | 61 + .../sources/DataTypes.sol | 49 + .../sources/Errors.sol | 119 ++ .../sources/GenericLogic.sol | 275 +++ .../sources/Helpers.sol | 39 + .../sources/IAToken.sol | 102 ++ .../sources/IAaveIncentivesController.sol | 11 + .../sources/IERC20.sol | 80 + .../sources/IFlashLoanReceiver.sol | 25 + .../sources/IInitializableAToken.sol | 55 + .../sources/IInitializableDebtToken.sol | 51 + .../sources/ILendingPool.sol | 410 +++++ .../sources/ILendingPoolAddressesProvider.sol | 60 + .../sources/IPriceOracleGetter.sol | 16 + .../sources/IReserveInterestRateStrategy.sol | 47 + .../sources/IScaledBalanceToken.sol | 26 + .../sources/IStableDebtToken.sol | 133 ++ .../sources/IVariableDebtToken.sol | 62 + .../sources/LendingPool.sol | 946 ++++++++++ .../sources/LendingPoolStorage.sol | 32 + .../sources/MathUtils.sol | 84 + .../sources/PercentageMath.sol | 54 + .../sources/ReserveConfiguration.sol | 366 ++++ .../sources/ReserveLogic.sol | 373 ++++ .../sources/SafeERC20.sol | 64 + .../sources/SafeMath.sol | 163 ++ .../sources/UserConfiguration.sol | 111 ++ .../sources/ValidationLogic.sol | 469 +++++ .../sources/VersionedInitializable.sol | 77 + .../sources/WadRayMath.sol | 135 ++ .../test/sources/NoAuxdata/artifact.json | 36 + .../test/sources/NoAuxdata/metadata.json | 74 + .../sources/NoAuxdata/sources/NoAuxdata.sol | 14 + .../sources/{C.sol => A} | 0 packages/lib-sourcify/test/utils.ts | 116 ++ 157 files changed, 22277 insertions(+), 168 deletions(-) rename packages/lib-sourcify/src/{lib => }/SourcifyChain.ts (99%) create mode 100644 packages/lib-sourcify/src/SourcifyLibError.ts create mode 100644 packages/lib-sourcify/src/Validation/ValidationTypes.ts create mode 100644 packages/lib-sourcify/src/Verification/Transformations.ts create mode 100644 packages/lib-sourcify/src/Verification/Verification.ts create mode 100644 packages/lib-sourcify/src/Verification/VerificationTypes.ts rename packages/lib-sourcify/src/{lib => }/logger.ts (100%) create mode 100644 packages/lib-sourcify/test/Verification/Verification.spec.ts create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/artifact.json create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/@openzeppelin/contracts/token/ERC20/IERC20.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/adapters/BaseUniswapAdapter.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/adapters/FlashLiquidationAdapter.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/adapters/UniswapLiquiditySwapAdapter.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/adapters/UniswapRepayAdapter.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/adapters/interfaces/IBaseUniswapAdapter.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/Address.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/Context.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/ERC20.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/IERC20.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/Ownable.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/SafeERC20.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/SafeMath.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/AdminUpgradeabilityProxy.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/BaseAdminUpgradeabilityProxy.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/BaseUpgradeabilityProxy.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/InitializableAdminUpgradeabilityProxy.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/InitializableUpgradeabilityProxy.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/Proxy.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/UpgradeabilityProxy.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/deployments/ATokensAndRatesHelper.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/deployments/StableAndVariableTokensHelper.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/deployments/StringLib.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/flashloan/base/FlashLoanReceiverBase.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/flashloan/interfaces/IFlashLoanReceiver.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IAToken.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IAaveIncentivesController.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IChainlinkAggregator.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ICreditDelegationToken.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IDelegationToken.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IERC20WithPermit.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IExchangeAdapter.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IInitializableAToken.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IInitializableDebtToken.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingPool.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingPoolAddressesProvider.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingPoolAddressesProviderRegistry.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingPoolCollateralManager.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingPoolConfigurator.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingRateOracle.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IPriceOracleGetter.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IReserveInterestRateStrategy.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IScaledBalanceToken.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IStableDebtToken.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IUniswapV2Router02.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IVariableDebtToken.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/AaveOracle.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/AaveProtocolDataProvider.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/UiPoolDataProvider.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/WETHGateway.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/WalletBalanceProvider.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/interfaces/IUiPoolDataProvider.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/interfaces/IWETH.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/interfaces/IWETHGateway.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/flashloan/MockFlashLoanReceiver.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/oracle/LendingRateOracle.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/swap/MockUniswapV2Router02.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/tokens/MintableDelegationERC20.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/tokens/MintableERC20.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/upgradeability/MockAToken.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/upgradeability/MockStableDebtToken.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/upgradeability/MockVariableDebtToken.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/configuration/LendingPoolAddressesProvider.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/configuration/LendingPoolAddressesProviderRegistry.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/lendingpool/DefaultReserveInterestRateStrategy.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/lendingpool/LendingPool.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/lendingpool/LendingPoolConfigurator.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/lendingpool/LendingPoolStorage.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/aave-upgradeability/BaseImmutableAdminUpgradeabilityProxy.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/aave-upgradeability/InitializableImmutableAdminUpgradeabilityProxy.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/aave-upgradeability/VersionedInitializable.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/configuration/ReserveConfiguration.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/configuration/UserConfiguration.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/helpers/Errors.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/helpers/Helpers.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/logic/GenericLogic.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/logic/ReserveLogic.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/logic/ValidationLogic.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/math/MathUtils.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/math/PercentageMath.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/math/WadRayMath.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/types/DataTypes.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/AToken.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/DelegationAwareAToken.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/IncentivizedERC20.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/StableDebtToken.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/VariableDebtToken.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/base/DebtTokenBase.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/hardhat/console.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/metadata.json create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/Address.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/DataTypes.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/Errors.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/GenericLogic.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/Helpers.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IAToken.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IAaveIncentivesController.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IERC20.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IFlashLoanReceiver.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IInitializableAToken.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IInitializableDebtToken.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/ILendingPool.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/ILendingPoolAddressesProvider.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IPriceOracleGetter.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IReserveInterestRateStrategy.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IScaledBalanceToken.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IStableDebtToken.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IVariableDebtToken.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/LendingPool.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/LendingPoolStorage.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/MathUtils.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/PercentageMath.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/ReserveConfiguration.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/ReserveLogic.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/SafeERC20.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/SafeMath.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/UserConfiguration.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/ValidationLogic.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/VersionedInitializable.sol create mode 100644 packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/WadRayMath.sol create mode 100644 packages/lib-sourcify/test/sources/NoAuxdata/artifact.json create mode 100644 packages/lib-sourcify/test/sources/NoAuxdata/metadata.json create mode 100644 packages/lib-sourcify/test/sources/NoAuxdata/sources/NoAuxdata.sol rename packages/lib-sourcify/test/sources/ViaIRUnoptimizedMismatch/sources/{C.sol => A} (100%) diff --git a/packages/lib-sourcify/src/Compilation/AbstractCompilation.ts b/packages/lib-sourcify/src/Compilation/AbstractCompilation.ts index 6294c7f1b..f9d5a851c 100644 --- a/packages/lib-sourcify/src/Compilation/AbstractCompilation.ts +++ b/packages/lib-sourcify/src/Compilation/AbstractCompilation.ts @@ -3,6 +3,7 @@ import { CompilationTarget, CompiledContractCborAuxdata, Metadata, + LinkReferences, } from './CompilationTypes'; import { ImmutableReferences, @@ -17,7 +18,7 @@ import { VyperOutput, VyperOutputContract, } from './VyperTypes'; -import { logInfo, logSilly, logWarn } from '../lib/logger'; +import { logInfo, logSilly, logWarn } from '../logger'; export abstract class AbstractCompilation { /** @@ -28,14 +29,14 @@ export abstract class AbstractCompilation { abstract compilationTarget: CompilationTarget; abstract jsonInput: SolidityJsonInput | VyperJsonInput; - metadata?: Metadata; + protected _metadata?: Metadata; compilerOutput?: SolidityOutput | VyperOutput; abstract auxdataStyle: AuxdataStyle; /** Marks the positions of the CborAuxdata parts in the bytecode */ - creationBytecodeCborAuxdata?: CompiledContractCborAuxdata; - runtimeBytecodeCborAuxdata?: CompiledContractCborAuxdata; + protected _creationBytecodeCborAuxdata?: CompiledContractCborAuxdata; + protected _runtimeBytecodeCborAuxdata?: CompiledContractCborAuxdata; /** * Recompiles the contract with the specified compiler settings @@ -73,7 +74,7 @@ export abstract class AbstractCompilation { } // We call getCompilationTarget() before logging because it can throw an error - const compilationTarget = this.getCompilationTarget(); + const compilationTarget = this.compilationTargetContract; const compilationEndTime = Date.now(); const compilationDuration = compilationEndTime - compilationStartTime; @@ -89,7 +90,9 @@ export abstract class AbstractCompilation { return compilationTarget; } - getCompilationTarget(): SolidityOutputContract | VyperOutputContract { + get compilationTargetContract(): + | SolidityOutputContract + | VyperOutputContract { if (!this.compilerOutput) { logWarn('Compiler output is undefined'); throw new Error('Compiler output is undefined'); @@ -110,20 +113,36 @@ export abstract class AbstractCompilation { ]; } - getCreationBytecode() { - return `0x${this.getCompilationTarget().evm.bytecode.object}`; + get creationBytecode() { + return `0x${this.compilationTargetContract.evm.bytecode.object}`; } - getRuntimeBytecode() { - return `0x${this.getCompilationTarget().evm.deployedBytecode.object}`; + get runtimeBytecode() { + return `0x${this.compilationTargetContract.evm.deployedBytecode.object}`; } - getMetadata(): Metadata { - if (!this.metadata) { + get metadata() { + if (!this._metadata) { throw new Error('Metadata is not set'); } - return this.metadata; + return this._metadata; } - abstract getImmutableReferences(): ImmutableReferences; + abstract get immutableReferences(): ImmutableReferences; + abstract get runtimeLinkReferences(): LinkReferences; + abstract get creationLinkReferences(): LinkReferences; + + get creationBytecodeCborAuxdata(): CompiledContractCborAuxdata { + if (!this._creationBytecodeCborAuxdata) { + throw new Error('Creation bytecode cbor auxdata is not set'); + } + return this._creationBytecodeCborAuxdata; + } + + get runtimeBytecodeCborAuxdata(): CompiledContractCborAuxdata { + if (!this._runtimeBytecodeCborAuxdata) { + throw new Error('Runtime bytecode cbor auxdata is not set'); + } + return this._runtimeBytecodeCborAuxdata; + } } diff --git a/packages/lib-sourcify/src/Compilation/CompilationTypes.ts b/packages/lib-sourcify/src/Compilation/CompilationTypes.ts index af80b90df..7141957fa 100644 --- a/packages/lib-sourcify/src/Compilation/CompilationTypes.ts +++ b/packages/lib-sourcify/src/Compilation/CompilationTypes.ts @@ -1,4 +1,5 @@ import { Abi } from 'abitype'; +import { SoliditySettings } from './SolidityTypes'; export interface LinkReferences { [filePath: string]: { @@ -75,6 +76,18 @@ export interface MetadataOutput { userdoc: Userdoc; } +// Metadata JSON's "settings" does have extra "compilationTarget" and its "libraries" field is in a different format +// ( libraries["MyContract.sol:Mycontract"]:"0xab..cd" vs libraries["MyContract.sol"]["MyContract"]:"0xab..cd") +export interface MetadataCompilerSettings + extends Omit { + compilationTarget: { + [sourceName: string]: string; + }; + libraries?: { + [index: string]: string; + }; +} + // Metadata type that reflects the metadata object from // https://docs.soliditylang.org/en/latest/metadata.html export interface Metadata { @@ -84,40 +97,7 @@ export interface Metadata { }; language: string; output: MetadataOutput; - settings: { - compilationTarget: { - [sourceName: string]: string; - }; - evmVersion?: string; - libraries?: { - [index: string]: string; - }; - metadata?: { - appendCBOR?: boolean; - bytecodeHash?: 'none' | 'ipfs' | 'bzzr0' | 'bzzr1'; - useLiteralContent?: boolean; - }; - optimizer?: { - details?: { - constantOptimizer?: boolean; - cse?: boolean; - deduplicate?: boolean; - inliner?: boolean; - jumpdestRemover?: boolean; - orderLiterals?: boolean; - peephole?: boolean; - yul?: boolean; - yulDetails?: { - optimizerSteps?: string; - stackAllocation?: boolean; - }; - }; - enabled: boolean; - runs: number; - }; - viaIR?: boolean; - outputSelection?: any; - }; + settings: MetadataCompilerSettings; sources: MetadataSourceMap; version: number; } diff --git a/packages/lib-sourcify/src/Compilation/SolidityCompilation.ts b/packages/lib-sourcify/src/Compilation/SolidityCompilation.ts index 0cfbd3d54..0269eda55 100644 --- a/packages/lib-sourcify/src/Compilation/SolidityCompilation.ts +++ b/packages/lib-sourcify/src/Compilation/SolidityCompilation.ts @@ -7,7 +7,7 @@ import { SolidityOutput, SolidityOutputContract, } from './SolidityTypes'; -import { CompilationTarget } from './CompilationTypes'; +import { CompilationTarget, LinkReferences } from './CompilationTypes'; import { findAuxdataPositions, findAuxdatasInLegacyAssembly, @@ -22,7 +22,6 @@ export class SolidityCompilation extends AbstractCompilation { declare compileAndReturnCompilationTarget: ( forceEmscripten: boolean, ) => Promise; - declare getCompilationTarget: () => SolidityOutputContract; // Specify the auxdata style, used for extracting the auxdata from the compiler output readonly auxdataStyle: AuxdataStyle.SOLIDITY = AuxdataStyle.SOLIDITY; @@ -91,31 +90,32 @@ export class SolidityCompilation extends AbstractCompilation { public async generateCborAuxdataPositions(forceEmscripten = false) { // Auxdata array extracted from the compiler's `legacyAssembly` field const auxdatasFromCompilerOutput = findAuxdatasInLegacyAssembly( - this.getCompilationTarget().evm.legacyAssembly, + (this.compilationTargetContract as SolidityOutputContract).evm + .legacyAssembly, ); // Case: there is not auxadata if (auxdatasFromCompilerOutput.length === 0) { - this.creationBytecodeCborAuxdata = {}; - this.runtimeBytecodeCborAuxdata = {}; + this._creationBytecodeCborAuxdata = {}; + this._runtimeBytecodeCborAuxdata = {}; return true; } - // Case: there is only one auxdata, no need to recompile + // Case: there is only one auxdata, no need to recompile if we find both runtime and creation auxdata at the end of the bytecode (creation auxdata can be in a different place) if (auxdatasFromCompilerOutput.length === 1) { // Extract the auxdata from the end of the recompiled runtime bytecode const [, runtimeAuxdataCbor, runtimeCborLengthHex] = splitAuxdata( - this.getRuntimeBytecode(), + this.runtimeBytecode, this.auxdataStyle, ); const auxdataFromRawRuntimeBytecode = `${runtimeAuxdataCbor}${runtimeCborLengthHex}`; // we divide by 2 because we store the length in bytes (without 0x) - this.runtimeBytecodeCborAuxdata = { + this._runtimeBytecodeCborAuxdata = { '1': { offset: - this.getRuntimeBytecode().substring(2).length / 2 - + this.runtimeBytecode.substring(2).length / 2 - parseInt(runtimeCborLengthHex, 16) - 2, // bytecode has 2 bytes of cbor length prefix at the end value: `0x${auxdataFromRawRuntimeBytecode}`, @@ -124,7 +124,7 @@ export class SolidityCompilation extends AbstractCompilation { // Try to extract the auxdata from the end of the recompiled creation bytecode const [, creationAuxdataCbor, creationCborLengthHex] = splitAuxdata( - this.getCreationBytecode(), + this.creationBytecode, this.auxdataStyle, ); @@ -132,10 +132,10 @@ export class SolidityCompilation extends AbstractCompilation { if (creationAuxdataCbor) { const auxdataFromRawCreationBytecode = `${creationAuxdataCbor}${creationCborLengthHex}`; // we divide by 2 because we store the length in bytes (without 0x) - this.creationBytecodeCborAuxdata = { + this._creationBytecodeCborAuxdata = { '1': { offset: - this.getCreationBytecode().substring(2).length / 2 - + this.creationBytecode.substring(2).length / 2 - parseInt(creationCborLengthHex, 16) - 2, // bytecode has 2 bytes of cbor length prefix at the end value: `0x${auxdataFromRawCreationBytecode}`, @@ -145,7 +145,7 @@ export class SolidityCompilation extends AbstractCompilation { } } - // Case: multiple auxdatas or failing creation auxdata, + // Case: multiple auxdatas or creation auxdata not found at the end of the bytecode, // we need to recompile with a slightly edited file to check the differences const editedContractCompilerOutput = await this.generateEditedContract({ version: this.compilerVersion, @@ -160,19 +160,19 @@ export class SolidityCompilation extends AbstractCompilation { const editedContractAuxdatasFromCompilerOutput = findAuxdatasInLegacyAssembly(editedContract.evm.legacyAssembly); - // Potentially we already found runtimeBytecodeCborAuxdata in the case of failing creation auxdata + // Potentially we already found runtimeBytecodeCborAuxdata in the case of creation auxdata not found at the end of the bytecode // so no need to call `findAuxdataPositions` - if (this.runtimeBytecodeCborAuxdata === undefined) { - this.runtimeBytecodeCborAuxdata = findAuxdataPositions( - this.getRuntimeBytecode(), + if (this._runtimeBytecodeCborAuxdata === undefined) { + this._runtimeBytecodeCborAuxdata = findAuxdataPositions( + this.runtimeBytecode, `0x${editedContract.evm.deployedBytecode.object}`, auxdatasFromCompilerOutput, editedContractAuxdatasFromCompilerOutput, ); } - this.creationBytecodeCborAuxdata = findAuxdataPositions( - this.getCreationBytecode(), + this._creationBytecodeCborAuxdata = findAuxdataPositions( + this.creationBytecode, `0x${editedContract.evm.bytecode.object}`, auxdatasFromCompilerOutput, editedContractAuxdatasFromCompilerOutput, @@ -184,11 +184,24 @@ export class SolidityCompilation extends AbstractCompilation { public async compile(forceEmscripten = false) { const contract = await this.compileAndReturnCompilationTarget(forceEmscripten); - this.metadata = JSON.parse(contract.metadata.trim()); + this._metadata = JSON.parse(contract.metadata.trim()); } - getImmutableReferences(): ImmutableReferences { - const compilationTarget = this.getCompilationTarget(); + get immutableReferences(): ImmutableReferences { + const compilationTarget = this + .compilationTargetContract as SolidityOutputContract; return compilationTarget.evm.deployedBytecode.immutableReferences || {}; } + + get runtimeLinkReferences(): LinkReferences { + const compilationTarget = this + .compilationTargetContract as SolidityOutputContract; + return compilationTarget.evm.deployedBytecode.linkReferences || {}; + } + + get creationLinkReferences(): LinkReferences { + const compilationTarget = this + .compilationTargetContract as SolidityOutputContract; + return compilationTarget.evm.bytecode.linkReferences || {}; + } } diff --git a/packages/lib-sourcify/src/Compilation/SolidityTypes.ts b/packages/lib-sourcify/src/Compilation/SolidityTypes.ts index a02333dc4..e1d028bd4 100644 --- a/packages/lib-sourcify/src/Compilation/SolidityTypes.ts +++ b/packages/lib-sourcify/src/Compilation/SolidityTypes.ts @@ -78,7 +78,7 @@ interface ModelChecker { timeout?: number; } -interface Settings { +export interface SoliditySettings { stopAfter?: string; remappings?: string[]; optimizer?: Optimizer; @@ -89,13 +89,12 @@ interface Settings { libraries?: Libraries; outputSelection: OutputSelection; modelChecker?: ModelChecker; - compilationTarget?: string; } export interface SolidityJsonInput { language: string; sources: Sources; - settings: Settings; + settings: SoliditySettings; } interface SolidityOutputError { diff --git a/packages/lib-sourcify/src/Compilation/VyperCompilation.ts b/packages/lib-sourcify/src/Compilation/VyperCompilation.ts index 5033b48f1..e152ed4e5 100644 --- a/packages/lib-sourcify/src/Compilation/VyperCompilation.ts +++ b/packages/lib-sourcify/src/Compilation/VyperCompilation.ts @@ -1,4 +1,4 @@ -import { logWarn } from '../lib/logger'; +import { logWarn } from '../logger'; import { AbstractCompilation } from './AbstractCompilation'; import { id as keccak256str } from 'ethers'; import { @@ -16,6 +16,7 @@ import { import { CompilationTarget, CompiledContractCborAuxdata, + LinkReferences, } from './CompilationTypes'; import { ImmutableReferences } from './SolidityTypes'; @@ -43,7 +44,7 @@ export class VyperCompilation extends AbstractCompilation { * compatibility reasons e.g. in the legacy Sourcify API that always assumes a metadata.json */ generateMetadata() { - const contract = this.getCompilationTarget(); + const contract = this.compilationTargetContract; const outputMetadata = { abi: contract.abi, devdoc: contract.devdoc, @@ -61,7 +62,7 @@ export class VyperCompilation extends AbstractCompilation { {}, ); - this.metadata = { + this._metadata = { compiler: { version: this.compilerVersion }, language: 'Vyper', output: outputMetadata, @@ -129,12 +130,12 @@ export class VyperCompilation extends AbstractCompilation { this.initVyperJsonInput(); } - getImmutableReferences(): ImmutableReferences { + get immutableReferences(): ImmutableReferences { let immutableReferences = {}; if (gte(this.compilerVersionCompatibleWithSemver, '0.3.10')) { try { const { immutableSize } = decode( - this.getCreationBytecode(), + this.creationBytecode, this.auxdataStyle, ); if (immutableSize) { @@ -142,20 +143,30 @@ export class VyperCompilation extends AbstractCompilation { '0': [ { length: immutableSize, - start: this.getRuntimeBytecode().substring(2).length / 2, + start: this.runtimeBytecode.substring(2).length / 2, }, ], }; } } catch (e) { logWarn('Cannot decode vyper contract bytecode', { - creationBytecode: this.getCreationBytecode(), + creationBytecode: this.creationBytecode, }); } } return immutableReferences; } + get runtimeLinkReferences(): LinkReferences { + // Vyper doesn't support libraries + return {}; + } + + get creationLinkReferences(): LinkReferences { + // Vyper doesn't support libraries + return {}; + } + public async compile() { await this.compileAndReturnCompilationTarget(false); this.generateMetadata(); @@ -167,7 +178,7 @@ export class VyperCompilation extends AbstractCompilation { public async generateCborAuxdataPositions() { try { const [, runtimeAuxdataCbor, runtimeCborLengthHex] = splitAuxdata( - this.getRuntimeBytecode(), + this.runtimeBytecode, this.auxdataStyle, ); @@ -176,20 +187,22 @@ export class VyperCompilation extends AbstractCompilation { this.auxdataStyle === AuxdataStyle.VYPER_LT_0_3_10 || this.auxdataStyle === AuxdataStyle.VYPER_LT_0_3_5 ) { - this.runtimeBytecodeCborAuxdata = this.tryGenerateCborAuxdataPosition( - this.getRuntimeBytecode(), + this._runtimeBytecodeCborAuxdata = this.tryGenerateCborAuxdataPosition( + this.runtimeBytecode, runtimeAuxdataCbor, runtimeCborLengthHex, ); + } else { + this._runtimeBytecodeCborAuxdata = {}; } const [, creationAuxdataCbor, creationCborLengthHex] = splitAuxdata( - this.getCreationBytecode(), + this.creationBytecode, this.auxdataStyle, ); - this.creationBytecodeCborAuxdata = this.tryGenerateCborAuxdataPosition( - this.getCreationBytecode(), + this._creationBytecodeCborAuxdata = this.tryGenerateCborAuxdataPosition( + this.creationBytecode, creationAuxdataCbor, creationCborLengthHex, ); @@ -231,10 +244,10 @@ export class VyperCompilation extends AbstractCompilation { } // Override the bytecodes' getter methods to not duplicate the 0x prefix - getCreationBytecode() { - return this.getCompilationTarget().evm.bytecode.object; + get creationBytecode() { + return this.compilationTargetContract.evm.bytecode.object; } - getRuntimeBytecode() { - return this.getCompilationTarget().evm.deployedBytecode.object; + get runtimeBytecode() { + return this.compilationTargetContract.evm.deployedBytecode.object; } } diff --git a/packages/lib-sourcify/src/lib/SourcifyChain.ts b/packages/lib-sourcify/src/SourcifyChain.ts similarity index 99% rename from packages/lib-sourcify/src/lib/SourcifyChain.ts rename to packages/lib-sourcify/src/SourcifyChain.ts index 702fdf6cf..2cfaf2cc5 100644 --- a/packages/lib-sourcify/src/lib/SourcifyChain.ts +++ b/packages/lib-sourcify/src/SourcifyChain.ts @@ -12,7 +12,7 @@ import { FetchContractCreationTxMethods, SourcifyChainExtension, TraceSupportedRPC, -} from './types'; +} from './lib/types'; import { logDebug, logError, logInfo, logWarn } from './logger'; const RPC_TIMEOUT = process.env.RPC_TIMEOUT diff --git a/packages/lib-sourcify/src/SourcifyLibError.ts b/packages/lib-sourcify/src/SourcifyLibError.ts new file mode 100644 index 000000000..a6144d8f9 --- /dev/null +++ b/packages/lib-sourcify/src/SourcifyLibError.ts @@ -0,0 +1,7 @@ +export class SourcifyLibError extends Error { + public code: string; + constructor(message: string, code: string) { + super(message); + this.code = code; + } +} diff --git a/packages/lib-sourcify/src/Validation/SolidityMetadataContract.ts b/packages/lib-sourcify/src/Validation/SolidityMetadataContract.ts index f09f97bb9..9b06d5923 100644 --- a/packages/lib-sourcify/src/Validation/SolidityMetadataContract.ts +++ b/packages/lib-sourcify/src/Validation/SolidityMetadataContract.ts @@ -1,20 +1,18 @@ -import { - InvalidSources, - Metadata, - MissingSources, - PathContent, - StringMap, - Settings, - Libraries, -} from '..'; import { id as keccak256str } from 'ethers'; import semver from 'semver'; import { getIpfsGateway, performFetch } from './fetchUtils'; import { SolidityCompilation } from '../Compilation/SolidityCompilation'; import { ISolidityCompiler, + Libraries, SolidityJsonInput, } from '../Compilation/SolidityTypes'; +import { + Metadata, + MetadataCompilerSettings, + StringMap, +} from '../Compilation/CompilationTypes'; +import { InvalidSources, MissingSources, PathContent } from './ValidationTypes'; const CONTENT_VARIATORS = [ (content: string) => content.replace(/\r?\n/g, '\r\n'), @@ -281,15 +279,22 @@ export class SolidityMetadataContract { this.solcJsonInput = {} as SolidityJsonInput; // Clone the settings object to avoid mutating the original metadata - this.solcJsonInput.settings = JSON.parse( + const settings = JSON.parse( JSON.stringify(this.metadata.settings), - ) as Settings; - - if ( - !this.metadata.settings || - !this.metadata.settings.compilationTarget || - Object.keys(this.metadata.settings.compilationTarget).length != 1 - ) { + ) as MetadataCompilerSettings; + + // Remove libraries and compilationTarget before assigning + const { + libraries: metadataLibraries, + compilationTarget, + ...settingsWithoutLibraries + } = settings; + this.solcJsonInput.settings = { + ...settingsWithoutLibraries, + outputSelection: {}, // Output selection can be set to empty object because it's overwritten by SolidityCompilation + }; + + if (!compilationTarget || Object.keys(compilationTarget).length != 1) { throw new Error( `Can't create JsonInput from metadata: Invalid compilationTarget in metadata: ${Object.keys( this.metadata.settings.compilationTarget, @@ -298,8 +303,6 @@ export class SolidityMetadataContract { } this.handleInlinerBug(); - // Standard JSON does not have compilationTarget, only in metadata.json - delete this.solcJsonInput.settings.compilationTarget; this.solcJsonInput.sources = {}; for (const source in this.metadata.sources) { @@ -313,28 +316,29 @@ export class SolidityMetadataContract { // Convert the libraries from the metadata format to the compiler_settings format // metadata format: "contracts/1_Storage.sol:Journal": "0x7d53f102f4d4aa014db4e10d6deec2009b3cda6b" // settings format: "contracts/1_Storage.sol": { Journal: "0x7d53f102f4d4aa014db4e10d6deec2009b3cda6b" } - const metadataLibraries = this.metadata.settings.libraries || {}; - this.solcJsonInput.settings.libraries = Object.keys( - metadataLibraries, - ).reduce((libraries, libraryKey) => { - // Before Solidity v0.7.5: { "ERC20": "0x..."} - if (!libraryKey.includes(':')) { - if (!libraries['']) { - libraries[''] = {}; + if (metadataLibraries) { + this.solcJsonInput.settings.libraries = Object.keys( + metadataLibraries, + ).reduce((libraries, libraryKey) => { + // Before Solidity v0.7.5: { "ERC20": "0x..."} + if (!libraryKey.includes(':')) { + if (!libraries['']) { + libraries[''] = {}; + } + // try using the global method, available for pre 0.7.5 versions + libraries[''][libraryKey] = metadataLibraries[libraryKey]; + return libraries; } - // try using the global method, available for pre 0.7.5 versions - libraries[''][libraryKey] = metadataLibraries[libraryKey]; - return libraries; - } - // After Solidity v0.7.5: { "ERC20.sol:ERC20": "0x..."} - const [contractPath, contractName] = libraryKey.split(':'); - if (!libraries[contractPath]) { - libraries[contractPath] = {}; - } - libraries[contractPath][contractName] = metadataLibraries[libraryKey]; - return libraries; - }, {} as Libraries); + // After Solidity v0.7.5: { "ERC20.sol:ERC20": "0x..."} + const [contractPath, contractName] = libraryKey.split(':'); + if (!libraries[contractPath]) { + libraries[contractPath] = {}; + } + libraries[contractPath][contractName] = metadataLibraries[libraryKey]; + return libraries; + }, {} as Libraries); + } } handleInlinerBug() { diff --git a/packages/lib-sourcify/src/Validation/ValidationTypes.ts b/packages/lib-sourcify/src/Validation/ValidationTypes.ts new file mode 100644 index 000000000..8c174287e --- /dev/null +++ b/packages/lib-sourcify/src/Validation/ValidationTypes.ts @@ -0,0 +1,29 @@ +export interface PathBuffer { + path: string; + buffer: Buffer; +} + +export interface PathContent { + path: string; + content: string; +} + +export interface MissingSources { + [key: string]: { + keccak256: string; + urls: string[]; + }; +} + +export interface InvalidSources { + [key: string]: { + expectedHash: string; + calculatedHash: string; + msg?: string; // Keep msg for compatibilty with legacy UI + }; +} + +export interface IpfsGateway { + url: string; + headers?: HeadersInit; +} diff --git a/packages/lib-sourcify/src/Validation/fetchUtils.ts b/packages/lib-sourcify/src/Validation/fetchUtils.ts index e6e7bbe1b..65fe444d6 100644 --- a/packages/lib-sourcify/src/Validation/fetchUtils.ts +++ b/packages/lib-sourcify/src/Validation/fetchUtils.ts @@ -1,6 +1,6 @@ -import { logError, logInfo, logDebug } from '../lib/logger'; +import { logError, logInfo, logDebug } from '../logger'; import { id as keccak256str } from 'ethers'; -import { IpfsGateway } from '../lib/types'; +import { IpfsGateway } from './ValidationTypes'; export async function performFetch( url: string, diff --git a/packages/lib-sourcify/src/Validation/processFiles.ts b/packages/lib-sourcify/src/Validation/processFiles.ts index c6f491a41..02fbc18e0 100644 --- a/packages/lib-sourcify/src/Validation/processFiles.ts +++ b/packages/lib-sourcify/src/Validation/processFiles.ts @@ -1,8 +1,9 @@ // Tools to assemble SolidityMetadataContract(s) from files. -import { logDebug, logInfo } from '../lib/logger'; -import { Metadata, PathBuffer, PathContent } from '../lib/types'; +import { Metadata } from '../Compilation/CompilationTypes'; +import { logDebug, logInfo } from '../logger'; import { SolidityMetadataContract } from './SolidityMetadataContract'; +import { PathBuffer, PathContent } from './ValidationTypes'; import { unzipFiles } from './zipUtils'; import fs from 'fs'; import Path from 'path'; diff --git a/packages/lib-sourcify/src/Validation/zipUtils.ts b/packages/lib-sourcify/src/Validation/zipUtils.ts index 728640b89..7bbc4ea8a 100644 --- a/packages/lib-sourcify/src/Validation/zipUtils.ts +++ b/packages/lib-sourcify/src/Validation/zipUtils.ts @@ -1,6 +1,6 @@ import JSZip from 'jszip'; -import { logDebug } from '../lib/logger'; -import { PathBuffer } from '../lib/types'; +import { logDebug } from '../logger'; +import { PathBuffer } from './ValidationTypes'; export async function unzipFiles(files: PathBuffer[]) { const allUnzipped: PathBuffer[] = []; diff --git a/packages/lib-sourcify/src/Verification/Transformations.ts b/packages/lib-sourcify/src/Verification/Transformations.ts new file mode 100644 index 000000000..279958089 --- /dev/null +++ b/packages/lib-sourcify/src/Verification/Transformations.ts @@ -0,0 +1,347 @@ +import { AuxdataStyle } from '@ethereum-sourcify/bytecode-utils'; +import { ImmutableReferences } from '../Compilation/SolidityTypes'; +import { + CompiledContractCborAuxdata, + LinkReferences, + Metadata, + StringMap, +} from '../Compilation/CompilationTypes'; +import { AbiConstructor } from 'abitype'; +import { defaultAbiCoder as abiCoder, ParamType } from '@ethersproject/abi'; +import { id as keccak256Str } from 'ethers'; +import { logError } from '../logger'; + +export type Transformation = { + type: 'insert' | 'replace'; + reason: + | 'constructorArguments' + | 'library' + | 'immutable' + | 'cborAuxdata' + | 'callProtection'; + offset: number; + id?: string; +}; + +// Call protection is always at the start of the runtime bytecode +export const CallProtectionTransformation = (): Transformation => ({ + type: 'replace', + reason: 'callProtection', + offset: 1, // 1 byte is always the PUSH20 opcode 0x73 +}); + +// TransformationValues only has one ConstructorTransformatino so no id field is needed +export const ConstructorTransformation = (offset: number): Transformation => ({ + type: 'insert', + reason: 'constructorArguments', + offset, +}); + +export const AuxdataTransformation = ( + offset: number, + id: string, +): Transformation => ({ + type: 'replace', + reason: 'cborAuxdata', + offset, + id, +}); + +export const LibraryTransformation = ( + offset: number, + id: string, +): Transformation => ({ + type: 'replace', + reason: 'library', + offset, + id, +}); + +export const ImmutablesTransformation = ( + offset: number, + id: string, + type: 'replace' | 'insert', +): Transformation => ({ + type, + reason: 'immutable', + offset, + id, +}); + +export interface TransformationValues { + constructorArguments?: string; + callProtection?: string; + libraries?: { + [id: string]: string; + }; + immutables?: { + [id: string]: string; + }; + cborAuxdata?: { + [id: string]: string; + }; +} + +// returns the full bytecode with the call protection replaced with the real address +export function extractCallProtectionTransformation( + populatedRecompiledBytecode: string, + onchainRuntimeBytecode: string, +) { + const transformations: Transformation[] = []; + const transformationValues: TransformationValues = {}; + const template = populatedRecompiledBytecode; + const real = onchainRuntimeBytecode; + + const push20CodeOp = '73'; + const callProtection = `0x${push20CodeOp}${'00'.repeat(20)}`; + + if (template.startsWith(callProtection)) { + const replacedCallProtection = real.slice(0, 0 + callProtection.length); + const callProtectionAddress = replacedCallProtection.slice(4); // remove 0x73 + transformations.push(CallProtectionTransformation()); + transformationValues.callProtection = '0x' + callProtectionAddress; + + return { + populatedRecompiledBytecode: + replacedCallProtection + template.substring(callProtection.length), + transformations, + transformationValues, + }; + } + return { + populatedRecompiledBytecode: template, + transformations, + transformationValues, + }; +} + +/** + * Replaces the values of the immutable variables in the (onchain) deployed bytecode with zeros, so that the bytecode can be compared with the (offchain) recompiled bytecode. + * Easier this way because we can simply replace with zeros + * Example immutableReferences: {"97":[{"length":32,"start":137}],"99":[{"length":32,"start":421}]} where 97 and 99 are the AST ids + */ +export function extractImmutablesTransformation( + populatedRecompiledBytecodeWith0x: string, + onchainRuntimeBytecodeWith0x: string, + immutableReferences: ImmutableReferences, + auxdataStyle: AuxdataStyle, +) { + const transformations: Transformation[] = []; + const transformationValues: TransformationValues = {}; + // Remove "0x" from the beginning of both bytecodes. + const onchainRuntimeBytecode = onchainRuntimeBytecodeWith0x.slice(2); + let populatedRecompiledBytecode = populatedRecompiledBytecodeWith0x.slice(2); + + Object.keys(immutableReferences).forEach((astId) => { + immutableReferences[astId].forEach((reference) => { + const { start, length } = reference; + + // Save the transformation + transformations.push( + ImmutablesTransformation( + start, + astId, + auxdataStyle === AuxdataStyle.SOLIDITY ? 'replace' : 'insert', + ), + ); + + // Extract the immutable value from the onchain bytecode. + const immutableValue = onchainRuntimeBytecode.slice( + start * 2, + start * 2 + length * 2, + ); + + // Save the transformation value + if (transformationValues.immutables === undefined) { + transformationValues.immutables = {}; + } + transformationValues.immutables[astId] = `0x${immutableValue}`; + + if (auxdataStyle === AuxdataStyle.SOLIDITY) { + // Replace the placeholder in the recompiled bytecode with the onchain immutable value. + populatedRecompiledBytecode = + populatedRecompiledBytecode.slice(0, start * 2) + + immutableValue + + populatedRecompiledBytecode.slice(start * 2 + length * 2); + } else if (auxdataStyle === AuxdataStyle.VYPER) { + // For Vyper, insert the immutable value. + populatedRecompiledBytecode = + populatedRecompiledBytecode + immutableValue; + } + }); + }); + return { + populatedRecompiledBytecode: '0x' + populatedRecompiledBytecode, + transformations, + transformationValues, + }; +} + +export function extractAbiEncodedConstructorArguments( + populatedRecompiledBytecode: string, + onchainCreationBytecode: string, +) { + if (onchainCreationBytecode.length === populatedRecompiledBytecode.length) + return undefined; + + return ( + '0x' + onchainCreationBytecode.slice(populatedRecompiledBytecode.length) + ); +} + +export function extractConstructorArgumentsTransformation( + populatedRecompiledBytecode: string, + onchainCreationBytecode: string, + metadata: Metadata, +) { + const transformations: Transformation[] = []; + const transformationValues: TransformationValues = {}; + const abiEncodedConstructorArguments = extractAbiEncodedConstructorArguments( + populatedRecompiledBytecode, + onchainCreationBytecode, + ); + const constructorAbiParamInputs = ( + metadata?.output?.abi?.find( + (param) => param.type === 'constructor', + ) as AbiConstructor + )?.inputs as ParamType[]; + if (abiEncodedConstructorArguments) { + if (!constructorAbiParamInputs) { + throw new Error( + `Failed to match with creation bytecode: constructor ABI Inputs are missing`, + ); + } + // abiCoder doesn't break if called with a wrong `abiEncodedConstructorArguments` + // so in order to successfuly check if the constructor arguments actually match + // we need to re-encode it and compare them + const decodeResult = abiCoder.decode( + constructorAbiParamInputs, + abiEncodedConstructorArguments, + ); + const encodeResult = abiCoder.encode( + constructorAbiParamInputs, + decodeResult, + ); + if (encodeResult !== abiEncodedConstructorArguments) { + throw new Error( + `Failed to match with creation bytecode: constructor arguments ABI decoding failed ${encodeResult} vs ${abiEncodedConstructorArguments}`, + ); + } + + transformations.push( + ConstructorTransformation( + populatedRecompiledBytecode.substring(2).length / 2, + ), + ); + transformationValues.constructorArguments = abiEncodedConstructorArguments; + } + return { + populatedRecompiledBytecode: '0x' + populatedRecompiledBytecode, + transformations, + transformationValues, + }; +} + +export function extractLibrariesTransformation( + template: string, + real: string, + linkReferences: LinkReferences, +) { + const transformations: Transformation[] = []; + const transformationValues: TransformationValues = {}; + const libraryMap: StringMap = {}; + for (const file in linkReferences) { + for (const lib in linkReferences[file]) { + for (const linkRefObj of linkReferences[file][lib]) { + const fqn = `${file}:${lib}`; // Fully Qualified (FQ) name + + const { start, length } = linkRefObj; + const strStart = start * 2 + 2; // Each byte 2 chars and +2 for 0x + const strLength = length * 2; + const placeholder = template.slice(strStart, strStart + strLength); + + // slice(2) removes 0x + const calculatedPlaceholder = + '__$' + keccak256Str(fqn).slice(2).slice(0, 34) + '$__'; + // Placeholder format was different pre v0.5.0 https://docs.soliditylang.org/en/v0.4.26/contracts.html#libraries + const trimmedFQN = fqn.slice(0, 36); // in case the fqn is too long + const calculatedPreV050Placeholder = '__' + trimmedFQN.padEnd(38, '_'); + + if ( + !( + placeholder === calculatedPlaceholder || + placeholder === calculatedPreV050Placeholder + ) + ) + throw new Error( + `Library placeholder mismatch: ${placeholder} vs ${calculatedPlaceholder} or ${calculatedPreV050Placeholder}`, + ); + + const address = real.slice(strStart, strStart + strLength); + libraryMap[placeholder] = address; + + // Replace the specific occurrence of the placeholder + template = + template.slice(0, strStart) + + address + + template.slice(strStart + strLength); + + transformations.push(LibraryTransformation(start, fqn)); + + if (!transformationValues.libraries) { + transformationValues.libraries = {}; + } + // Prepend the library addresses with "0x", this is the format for the DB. FS library-map is without "0x" + transformationValues.libraries[fqn] = '0x' + address; + } + } + } + + return { + populatedRecompiledBytecode: template, + libraryMap, + transformations, + transformationValues, + }; +} + +export function extractAuxdataTransformation( + recompiledBytecode: string, + onchainBytecode: string, + cborAuxdataPositions: CompiledContractCborAuxdata, +) { + try { + let populatedRecompiledBytecode = recompiledBytecode; + const transformations: Transformation[] = []; + const transformationValues: TransformationValues = {}; + // Instead of normalizing the onchain bytecode, we use its auxdata values to replace the corresponding sections in the recompiled bytecode. + Object.values(cborAuxdataPositions).forEach((auxdataValues, index) => { + const offsetStart = auxdataValues.offset * 2 + 2; + const offsetEnd = + auxdataValues.offset * 2 + 2 + auxdataValues.value.length - 2; + // Instead of zeroing out this segment, get the value from the onchain bytecode. + const onchainAuxdata = onchainBytecode.slice(offsetStart, offsetEnd); + populatedRecompiledBytecode = + populatedRecompiledBytecode.slice(0, offsetStart) + + onchainAuxdata + + populatedRecompiledBytecode.slice(offsetEnd); + const transformationIndex = `${index + 1}`; + transformations.push( + AuxdataTransformation(auxdataValues.offset, transformationIndex), + ); + if (!transformationValues.cborAuxdata) { + transformationValues.cborAuxdata = {}; + } + transformationValues.cborAuxdata[transformationIndex] = + `0x${onchainAuxdata}`; + }); + return { + populatedRecompiledBytecode, + transformations, + transformationValues, + }; + } catch (error: any) { + logError('Cannot populate bytecodes with the auxdata', { error }); + throw new Error('Cannot populate bytecodes with the auxdata'); + } +} diff --git a/packages/lib-sourcify/src/Verification/Verification.ts b/packages/lib-sourcify/src/Verification/Verification.ts new file mode 100644 index 000000000..5d132e0f3 --- /dev/null +++ b/packages/lib-sourcify/src/Verification/Verification.ts @@ -0,0 +1,501 @@ +import { AbstractCompilation } from '../Compilation/AbstractCompilation'; +import { logDebug, logInfo, logWarn } from '../logger'; +import SourcifyChain from '../SourcifyChain'; +import { lt } from 'semver'; +import { + splitAuxdata, + AuxdataStyle, + decode as decodeBytecode, + SolidityDecodedObject, +} from '@ethereum-sourcify/bytecode-utils'; +import { SolidityCompilation } from '../Compilation/SolidityCompilation'; +import { VyperCompilation } from '../Compilation/VyperCompilation'; +import { StringMap } from '../Compilation/CompilationTypes'; + +import { + extractAuxdataTransformation, + extractCallProtectionTransformation, + extractConstructorArgumentsTransformation, + extractImmutablesTransformation, + extractLibrariesTransformation, + Transformation, + TransformationValues, +} from './Transformations'; +import { + BytecodeMatchingResult, + SolidityBugType, + VerificationError, +} from './VerificationTypes'; +import { SoliditySettings } from '../Compilation/SolidityTypes'; + +export class Verification { + // Bytecodes + private _onchainRuntimeBytecode?: string; + private _onchainCreationBytecode?: string; + + // Transformations + private runtimeTransformations: Transformation[] = []; + private creationTransformations: Transformation[] = []; + private runtimeTransformationValues: TransformationValues = {}; + private creationTransformationValues: TransformationValues = {}; + + // Match status + private runtimeMatch: 'perfect' | 'partial' | null = null; + private creationMatch: 'perfect' | 'partial' | null = null; + private runtimeLibraryMap?: StringMap; + private creationLibraryMap?: StringMap; + private blockNumber?: number; + private txIndex?: number; + private deployer?: string; + + constructor( + private compilation: AbstractCompilation, + private sourcifyChain: SourcifyChain, + private address: string, + private creatorTxHash?: string, + ) {} + + async verify({ + forceEmscripten = false, + }: { forceEmscripten?: boolean } = {}): Promise { + logInfo('Verifying contract', { + address: this.address, + chainId: this.sourcifyChain.chainId, + }); + + try { + this._onchainRuntimeBytecode = await this.sourcifyChain.getBytecode( + this.address, + ); + } catch (e: any) { + throw new VerificationError( + `Cannot fetch bytecode for chain #${this.sourcifyChain.chainId} and address ${this.address}`, + 'cant_fetch_bytecode', + ); + } + + if (this.onchainRuntimeBytecode === '0x') { + throw new VerificationError( + `Chain #${this.sourcifyChain.chainId} does not have a contract deployed at ${this.address}.`, + 'contract_not_deployed', + ); + } + + // Compile the contract + await this.compilation.compile(forceEmscripten); + + const compiledRuntimeBytecode = this.compilation.runtimeBytecode; + const compiledCreationBytecode = this.compilation.creationBytecode; + + if (compiledRuntimeBytecode === '0x' || compiledCreationBytecode === '0x') { + throw new VerificationError( + `The compiled contract bytecode is "0x". Are you trying to verify an abstract contract?`, + 'compiled_bytecode_is_zero', + ); + } + + // Early bytecode length check: + // - For Solidity: bytecode lengths must match exactly + // - For Vyper: recompiled bytecode must not be longer than onchain as Vyper appends immutables at deployment + // We cannot do an early check for creation bytecode length mismatch because + // creation bytecode length can differ due to constructor arguments being appended at the end + if ( + (this.compilation instanceof SolidityCompilation && + compiledRuntimeBytecode.length !== + this.onchainRuntimeBytecode.length) || + (this.compilation instanceof VyperCompilation && + compiledRuntimeBytecode.length > this.onchainRuntimeBytecode.length) + ) { + // Before throwing the bytecode length mismatch error, check for Solidity extra file input bug + if (this.compilation instanceof SolidityCompilation) { + const solidityBugType = this.handleSolidityExtraFileInputBug(); + if (solidityBugType === SolidityBugType.EXTRA_FILE_INPUT_BUG) { + throw new VerificationError( + "It seems your contract's metadata hashes match but not the bytecodes. If you are verifying via metadata.json, use the original full standard JSON input file that has all files including those not needed by this contract. See the issue for more information: https://github.com/ethereum/sourcify/issues/618", + 'extra_file_input_bug', + ); + } + } + throw new VerificationError( + `The recompiled bytecode length doesn't match the onchain bytecode length.`, + 'bytecode_length_mismatch', + ); + } + + // We need to manually generate the auxdata positions because they are not automatically produced during compilation + // Read more: https://docs.sourcify.dev/blog/finding-auxdatas-in-bytecode/ + await this.compilation.generateCborAuxdataPositions(); + + // Try to match onchain runtime bytecode with compiled runtime bytecode + try { + logDebug('Matching with runtime bytecode', { + chain: this.sourcifyChain.chainId, + address: this.address, + }); + await this.matchWithRuntimeBytecode(); + } catch (e: any) { + logWarn('Error matching with runtime bytecode', { + chain: this.sourcifyChain.chainId, + address: this.address, + error: e.message, + }); + } + + if ( + this.compilation instanceof SolidityCompilation && + this.runtimeMatch === null + ) { + // Handle Solidity extra file input bug + let solidityBugType = this.handleSolidityExtraFileInputBug(); + if (solidityBugType === SolidityBugType.EXTRA_FILE_INPUT_BUG) { + throw new VerificationError( + "It seems your contract's metadata hashes match but not the bytecodes. If you are verifying via metadata.json, use the original full standard JSON input file that has all files including those not needed by this contract. See the issue for more information: https://github.com/ethereum/sourcify/issues/618", + 'extra_file_input_bug', + ); + } + + // Handle Solidity IR output ordering bug + solidityBugType = + await this.handleSolidityIROutputOrderingBug(forceEmscripten); + if (solidityBugType === SolidityBugType.IR_OUTPUT_ORDERING_BUG) { + return await this.verify({ forceEmscripten: true }); + } + } + + // Try to match onchain creation bytecode with compiled creation bytecode + if (this.creatorTxHash) { + try { + logDebug('Matching with creation tx', { + chain: this.sourcifyChain.chainId, + address: this.address, + creatorTxHash: this.creatorTxHash, + }); + + // Get creation transaction data + const creatorTx = await this.sourcifyChain.getTx(this.creatorTxHash); + this.blockNumber = creatorTx.blockNumber || undefined; + this.deployer = creatorTx.from; + + const { creationBytecode, txReceipt } = + await this.sourcifyChain.getContractCreationBytecodeAndReceipt( + this.address, + this.creatorTxHash, + creatorTx, + ); + this._onchainCreationBytecode = creationBytecode; + this.txIndex = txReceipt.index; + + await this.matchWithCreationTx(); + } catch (e: any) { + logWarn('Error matching with creation tx', { + chain: this.sourcifyChain.chainId, + address: this.address, + creatorTxHash: this.creatorTxHash, + error: e.message, + }); + } + } + + if (this.creationMatch !== null || this.runtimeMatch !== null) { + logInfo('Verified contract', { + address: this.address, + chainId: this.sourcifyChain.chainId, + runtimeMatch: this.runtimeMatch, + creationMatch: this.creationMatch, + }); + return; + } + + throw new VerificationError( + "The deployed and recompiled bytecode don't match.", + 'no_match', + ); + } + + handleSolidityExtraFileInputBug(): SolidityBugType { + // Case when extra unused files in compiler input cause different bytecode + // See issues: + // https://github.com/ethereum/sourcify/issues/618 + // https://github.com/ethereum/solidity/issues/14250 + // https://github.com/ethereum/solidity/issues/14494 + const [, deployedAuxdata] = splitAuxdata( + this.onchainRuntimeBytecode, + AuxdataStyle.SOLIDITY, + ); + const [, recompiledAuxdata] = splitAuxdata( + this.compilation.runtimeBytecode, + AuxdataStyle.SOLIDITY, + ); + // Metadata hashes match but bytecodes don't match. + if ( + deployedAuxdata === recompiledAuxdata && + (this.compilation.jsonInput.settings as SoliditySettings).optimizer + ?.enabled + ) { + return SolidityBugType.EXTRA_FILE_INPUT_BUG; + } + return SolidityBugType.NONE; + } + + async handleSolidityIROutputOrderingBug( + forceEmscripten: boolean, + ): Promise { + // Handle Solidity specific verification bug cases + const settings = this.compilation.jsonInput.settings as SoliditySettings; + + // Handle when <0.8.21 and with viaIR and with optimizer disabled + // See issues: + // https://github.com/ethereum/sourcify/issues/1088 + if ( + !forceEmscripten && // Enter this case only if we are not already forcing Emscripten + lt(this.compilation.compilerVersion, '0.8.21') && + !settings.optimizer?.enabled && + settings.viaIR + ) { + logInfo('Force Emscripten compiler', { + address: this.address, + chainId: this.sourcifyChain.chainId, + }); + + // Try to verify again with Emscripten + return SolidityBugType.IR_OUTPUT_ORDERING_BUG; + } + + return SolidityBugType.NONE; + } + + private async matchBytecodes( + isCreation: boolean, + populatedRecompiledBytecode: string, + ): Promise { + // Here we use bytecodes from the context because they are already processed + const onchainBytecode = isCreation + ? this.onchainCreationBytecode + : this.onchainRuntimeBytecode; + + const cborAuxdata = isCreation + ? this.compilation.creationBytecodeCborAuxdata + : this.compilation.runtimeBytecodeCborAuxdata; + + const linkReferences = isCreation + ? this.compilation.creationLinkReferences + : this.compilation.runtimeLinkReferences; + + const result: BytecodeMatchingResult = { + match: null, + transformations: [], + transformationValues: {}, + populatedRecompiledBytecode, + }; + + // Library transformations can be in both the runtime bytecode and creation bytecode, hence done here in `matchBytecodes` method. + const librariesTransformationResult = extractLibrariesTransformation( + populatedRecompiledBytecode, + onchainBytecode, + linkReferences, + ); + const libraryMap = librariesTransformationResult.libraryMap; + populatedRecompiledBytecode = + librariesTransformationResult.populatedRecompiledBytecode; + + // Direct bytecode match + const doBytecodesMatch = isCreation + ? onchainBytecode.startsWith(populatedRecompiledBytecode) + : populatedRecompiledBytecode === onchainBytecode; + + if (doBytecodesMatch) { + // If there is perfect match but auxdata doesn't contain any metadata hash, return partial match + if ( + !cborAuxdata || + Object.keys(cborAuxdata).length === 0 || + Object.values(cborAuxdata).some((cborAuxdata) => { + try { + const { ipfs, bzzr0, bzzr1 } = decodeBytecode( + cborAuxdata.value, + this.compilation.auxdataStyle, + ) as SolidityDecodedObject; + return ( + ipfs === undefined && bzzr0 === undefined && bzzr1 === undefined + ); + } catch { + return true; + } + }) + ) { + result.match = 'partial'; + } else { + result.match = 'perfect'; + } + + result.libraryMap = libraryMap; + result.transformations = librariesTransformationResult.transformations; + result.transformationValues = + librariesTransformationResult.transformationValues; + return result; + } + + // If bytecodes don't match and no auxdata to ignore, return null + if (!cborAuxdata || Object.keys(cborAuxdata).length === 0) { + return result; + } + + const auxdataTransformationResult = extractAuxdataTransformation( + populatedRecompiledBytecode, + onchainBytecode, + cborAuxdata, + ); + + result.populatedRecompiledBytecode = + auxdataTransformationResult.populatedRecompiledBytecode; + + /* eslint-disable indent */ + const doPopulatedBytecodesMatch = isCreation + ? onchainBytecode.startsWith( + auxdataTransformationResult.populatedRecompiledBytecode, + ) + : auxdataTransformationResult.populatedRecompiledBytecode === + onchainBytecode; + /* eslint-enable indent */ + + if (doPopulatedBytecodesMatch) { + result.match = 'partial'; + result.libraryMap = libraryMap; + result.transformations = [ + ...result.transformations, + ...librariesTransformationResult.transformations, + ...auxdataTransformationResult.transformations, + ]; + result.transformationValues = { + ...result.transformationValues, + ...librariesTransformationResult.transformationValues, + ...auxdataTransformationResult.transformationValues, + }; + } + + return result; + } + + private async matchWithRuntimeBytecode() { + // Check if is a library with call protection + const callProtectionTransformationResult = + extractCallProtectionTransformation( + this.compilation.runtimeBytecode, + this.onchainRuntimeBytecode, + ); + + // Handle immutable references + const immutablesTransformationResult = extractImmutablesTransformation( + callProtectionTransformationResult.populatedRecompiledBytecode, + this.onchainRuntimeBytecode, + this.compilation.immutableReferences, + this.compilation.auxdataStyle, + ); + + const matchBytecodesResult = await this.matchBytecodes( + false, + immutablesTransformationResult.populatedRecompiledBytecode, + ); + + this.runtimeTransformations = [ + ...callProtectionTransformationResult.transformations, + ...immutablesTransformationResult.transformations, + ...matchBytecodesResult.transformations, + ]; + this.runtimeTransformationValues = { + ...callProtectionTransformationResult.transformationValues, + ...immutablesTransformationResult.transformationValues, + ...matchBytecodesResult.transformationValues, + }; + + this.runtimeMatch = matchBytecodesResult.match; + this.runtimeLibraryMap = matchBytecodesResult.libraryMap; + } + + private async matchWithCreationTx() { + const matchBytecodesResult = await this.matchBytecodes( + true, + this.compilation.creationBytecode, + ); + + this.creationMatch = matchBytecodesResult.match; + this.creationLibraryMap = matchBytecodesResult.libraryMap; + this.creationTransformations = matchBytecodesResult.transformations; + this.creationTransformationValues = + matchBytecodesResult.transformationValues; + + if ( + matchBytecodesResult.match === 'partial' || + matchBytecodesResult.match === 'perfect' + ) { + const constructorTransformationResult = + extractConstructorArgumentsTransformation( + matchBytecodesResult.populatedRecompiledBytecode, + this.onchainCreationBytecode, + this.compilation.metadata, + ); + this.creationTransformations = [ + ...this.creationTransformations, + ...constructorTransformationResult.transformations, + ]; + this.creationTransformationValues = { + ...this.creationTransformationValues, + ...constructorTransformationResult.transformationValues, + }; + } + } + + get status() { + return { + runtimeMatch: this.runtimeMatch, + creationMatch: this.creationMatch, + }; + } + + get onchainRuntimeBytecode() { + if (!this._onchainRuntimeBytecode) { + throw new VerificationError( + 'Onchain runtime bytecode not available', + 'onchain_runtime_bytecode_not_available', + ); + } + return this._onchainRuntimeBytecode; + } + + get onchainCreationBytecode() { + if (!this._onchainCreationBytecode) { + throw new VerificationError( + 'Onchain creation bytecode not available', + 'onchain_creation_bytecode_not_available', + ); + } + return this._onchainCreationBytecode; + } + + get transformations() { + return { + runtime: { + list: this.runtimeTransformations, + values: this.runtimeTransformationValues, + }, + creation: { + list: this.creationTransformations, + values: this.creationTransformationValues, + }, + }; + } + + get deploymentInfo() { + return { + blockNumber: this.blockNumber, + txIndex: this.txIndex, + deployer: this.deployer, + }; + } + + get libraryMap() { + return { + runtime: this.runtimeLibraryMap, + creation: this.creationLibraryMap, + }; + } +} diff --git a/packages/lib-sourcify/src/Verification/VerificationTypes.ts b/packages/lib-sourcify/src/Verification/VerificationTypes.ts new file mode 100644 index 000000000..ee3e6269c --- /dev/null +++ b/packages/lib-sourcify/src/Verification/VerificationTypes.ts @@ -0,0 +1,35 @@ +import { StringMap } from '../Compilation/CompilationTypes'; +import { SourcifyLibError } from '../SourcifyLibError'; +import { Transformation, TransformationValues } from './Transformations'; + +export interface BytecodeMatchingResult { + match: 'perfect' | 'partial' | null; + libraryMap?: StringMap; + populatedRecompiledBytecode: string; + transformations: Transformation[]; + transformationValues: TransformationValues; + message?: string; +} + +export type VerificationErrorCode = + | 'cant_fetch_bytecode' + | 'contract_not_deployed' + | 'compiled_bytecode_is_zero' + | 'extra_file_input_bug' + | 'creation_bytecode_match_error' + | 'no_match' + | 'onchain_runtime_bytecode_not_available' + | 'onchain_creation_bytecode_not_available' + | 'bytecode_length_mismatch'; + +export class VerificationError extends SourcifyLibError { + constructor(message: string, code: VerificationErrorCode) { + super(message, code); + } +} + +export enum SolidityBugType { + NONE, + IR_OUTPUT_ORDERING_BUG, + EXTRA_FILE_INPUT_BUG, +} diff --git a/packages/lib-sourcify/src/index.ts b/packages/lib-sourcify/src/index.ts index 053c3a09e..b258aa674 100644 --- a/packages/lib-sourcify/src/index.ts +++ b/packages/lib-sourcify/src/index.ts @@ -1,11 +1,11 @@ -import { setLogger, setLevel, ILogger } from './lib/logger'; +import { setLogger, setLevel, ILogger } from './logger'; export * from './lib/validation'; export * from './lib/verification'; export * from './lib/SolidityCheckedContract'; export * from './lib/VyperCheckedContract'; export * from './lib/AbstractCheckedContract'; -export { default as SourcifyChain } from './lib/SourcifyChain'; +export { default as SourcifyChain } from './SourcifyChain'; export * from './lib/types'; export const setLibSourcifyLogger = setLogger; export const setLibSourcifyLoggerLevel = setLevel; diff --git a/packages/lib-sourcify/src/lib/SolidityCheckedContract.ts b/packages/lib-sourcify/src/lib/SolidityCheckedContract.ts index 833ac7d0e..6054c2642 100644 --- a/packages/lib-sourcify/src/lib/SolidityCheckedContract.ts +++ b/packages/lib-sourcify/src/lib/SolidityCheckedContract.ts @@ -26,7 +26,7 @@ import { } from '@ethereum-sourcify/bytecode-utils'; import { ipfsHash } from './hashFunctions/ipfsHash'; import { swarmBzzr0Hash, swarmBzzr1Hash } from './hashFunctions/swarmHash'; -import { logError, logInfo, logSilly, logWarn } from './logger'; +import { logError, logInfo, logSilly, logWarn } from '../logger'; import { ISolidityCompiler } from './ISolidityCompiler'; import { AbstractCheckedContract } from './AbstractCheckedContract'; diff --git a/packages/lib-sourcify/src/lib/VyperCheckedContract.ts b/packages/lib-sourcify/src/lib/VyperCheckedContract.ts index 379dfee7e..773924f48 100644 --- a/packages/lib-sourcify/src/lib/VyperCheckedContract.ts +++ b/packages/lib-sourcify/src/lib/VyperCheckedContract.ts @@ -6,7 +6,7 @@ import { RecompilationResult, StringMap, } from './types'; -import { logInfo, logSilly, logWarn } from './logger'; +import { logInfo, logSilly, logWarn } from '../logger'; import { IVyperCompiler, VyperJsonInput, diff --git a/packages/lib-sourcify/src/lib/types.ts b/packages/lib-sourcify/src/lib/types.ts index 4a8bb9e06..3b13cf5cc 100644 --- a/packages/lib-sourcify/src/lib/types.ts +++ b/packages/lib-sourcify/src/lib/types.ts @@ -1,5 +1,5 @@ import { Abi } from 'abitype'; -import SourcifyChain from './SourcifyChain'; +import SourcifyChain from '../SourcifyChain'; import { FetchRequest } from 'ethers'; export interface PathBuffer { path: string; diff --git a/packages/lib-sourcify/src/lib/utils.ts b/packages/lib-sourcify/src/lib/utils.ts index 55000a912..ab8d6fb68 100644 --- a/packages/lib-sourcify/src/lib/utils.ts +++ b/packages/lib-sourcify/src/lib/utils.ts @@ -1,4 +1,4 @@ -import { logDebug, logError } from './logger'; +import { logDebug, logError } from '../logger'; /** * Fetches a resource with an exponential timeout. diff --git a/packages/lib-sourcify/src/lib/validation.ts b/packages/lib-sourcify/src/lib/validation.ts index f32f82c70..a4f1ec702 100644 --- a/packages/lib-sourcify/src/lib/validation.ts +++ b/packages/lib-sourcify/src/lib/validation.ts @@ -12,7 +12,7 @@ import JSZip from 'jszip'; import fs from 'fs'; import Path from 'path'; import { ISolidityCompiler } from './ISolidityCompiler'; -import { logDebug, logInfo } from './logger'; +import { logDebug, logInfo } from '../logger'; import { IVyperCompiler } from './IVyperCompiler'; import { AbstractCheckedContract } from './AbstractCheckedContract'; import { VyperCheckedContract } from './VyperCheckedContract'; diff --git a/packages/lib-sourcify/src/lib/verification.ts b/packages/lib-sourcify/src/lib/verification.ts index c0252dabe..718777c03 100644 --- a/packages/lib-sourcify/src/lib/verification.ts +++ b/packages/lib-sourcify/src/lib/verification.ts @@ -2,17 +2,10 @@ import { SolidityCheckedContract } from './SolidityCheckedContract'; import { Create2Args, ImmutableReferences, - ImmutablesTransformation, Match, Metadata, - AuxdataTransformation, RecompilationResult, StringMap, - Transformation, - LibraryTransformation, - ConstructorTransformation, - CallProtectionTransformation, - TransformationValues, CompiledContractCborAuxdata, LinkReferences, } from './types'; @@ -26,11 +19,20 @@ import { hexZeroPad, isHexString } from '@ethersproject/bytes'; import { BigNumber } from '@ethersproject/bignumber'; import { defaultAbiCoder as abiCoder, ParamType } from '@ethersproject/abi'; import { AbiConstructor } from 'abitype'; -import { logDebug, logError, logInfo, logWarn } from './logger'; -import SourcifyChain from './SourcifyChain'; +import { logDebug, logError, logInfo, logWarn } from '../logger'; +import SourcifyChain from '../SourcifyChain'; import { lt } from 'semver'; -import { replaceBytecodeAuxdatasWithZeros } from './utils'; import { AbstractCheckedContract } from './AbstractCheckedContract'; +import { + AuxdataTransformation, + CallProtectionTransformation, + ConstructorTransformation, + ImmutablesTransformation, + LibraryTransformation, + Transformation, + TransformationValues, +} from '../Verification/Transformations'; +import { replaceBytecodeAuxdatasWithZeros } from './utils'; export async function verifyDeployed( checkedContract: AbstractCheckedContract, diff --git a/packages/lib-sourcify/src/lib/logger.ts b/packages/lib-sourcify/src/logger.ts similarity index 100% rename from packages/lib-sourcify/src/lib/logger.ts rename to packages/lib-sourcify/src/logger.ts diff --git a/packages/lib-sourcify/test/Compilation/SolidityCompilation.spec.ts b/packages/lib-sourcify/test/Compilation/SolidityCompilation.spec.ts index 11591cfda..d48d73e3a 100644 --- a/packages/lib-sourcify/test/Compilation/SolidityCompilation.spec.ts +++ b/packages/lib-sourcify/test/Compilation/SolidityCompilation.spec.ts @@ -57,10 +57,10 @@ describe('SolidityCompilation', () => { ); await compilation.compile(); - expect(compilation.getCreationBytecode()).to.equal( + expect(compilation.creationBytecode).to.equal( '0x608060405234801561001057600080fd5b5061012f806100206000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80632e64cec11460375780636057361d146051575b600080fd5b603d6069565b6040516048919060c2565b60405180910390f35b6067600480360381019060639190608f565b6072565b005b60008054905090565b8060008190555050565b60008135905060898160e5565b92915050565b60006020828403121560a057600080fd5b600060ac84828501607c565b91505092915050565b60bc8160db565b82525050565b600060208201905060d5600083018460b5565b92915050565b6000819050919050565b60ec8160db565b811460f657600080fd5b5056fea264697066735822122005183dd5df276b396683ae62d0c96c3a406d6f9dad1ad0923daf492c531124b164736f6c63430008040033', ); - expect(compilation.getRuntimeBytecode()).to.equal( + expect(compilation.runtimeBytecode).to.equal( '0x6080604052348015600f57600080fd5b506004361060325760003560e01c80632e64cec11460375780636057361d146051575b600080fd5b603d6069565b6040516048919060c2565b60405180910390f35b6067600480360381019060639190608f565b6072565b005b60008054905090565b8060008190555050565b60008135905060898160e5565b92915050565b60006020828403121560a057600080fd5b600060ac84828501607c565b91505092915050565b60bc8160db565b82525050565b600060208201905060d5600083018460b5565b92915050565b6000819050919050565b60ec8160db565b811460f657600080fd5b5056fea264697066735822122005183dd5df276b396683ae62d0c96c3a406d6f9dad1ad0923daf492c531124b164736f6c63430008040033', ); }); @@ -289,7 +289,7 @@ describe('SolidityCompilation', () => { ); await compilation.compile(); - const immutableRefs = compilation.getImmutableReferences(); + const immutableRefs = compilation.immutableReferences; expect(immutableRefs).to.deep.equal({}); }); @@ -324,7 +324,7 @@ describe('SolidityCompilation', () => { ); await compilation.compile(); - const immutableRefs = compilation.getImmutableReferences(); + const immutableRefs = compilation.immutableReferences; expect(immutableRefs).to.deep.equal({ '3': [{ length: 32, start: 608 }] }); }); }); diff --git a/packages/lib-sourcify/test/Compilation/VyperCompilation.spec.ts b/packages/lib-sourcify/test/Compilation/VyperCompilation.spec.ts index 9fb5c440a..b7a7e5388 100644 --- a/packages/lib-sourcify/test/Compilation/VyperCompilation.spec.ts +++ b/packages/lib-sourcify/test/Compilation/VyperCompilation.spec.ts @@ -44,10 +44,10 @@ describe('VyperCompilation', () => { ); await compilation.compile(); - expect(compilation.getCreationBytecode()).to.equal( + expect(compilation.creationBytecode).to.equal( '0x61008f61000f60003961008f6000f360003560e01c63c605f76c8118610084573461008a57602080608052600c6040527f48656c6c6f20576f726c6421000000000000000000000000000000000000000060605260408160800181518152602082015160208201528051806020830101601f82600003163682375050601f19601f8251602001011690509050810190506080f35b60006000fd5b600080fd84188f8000a16576797065728300030a0012', ); - expect(compilation.getRuntimeBytecode()).to.equal( + expect(compilation.runtimeBytecode).to.equal( '0x60003560e01c63c605f76c8118610084573461008a57602080608052600c6040527f48656c6c6f20576f726c6421000000000000000000000000000000000000000060605260408160800181518152602082015160208201528051806020830101601f82600003163682375050601f19601f8251602001011690509050810190506080f35b60006000fd5b600080fd', ); }); @@ -90,7 +90,7 @@ describe('VyperCompilation', () => { ); await compilation.compile(); - expect(compilation.getImmutableReferences()).to.deep.equal({ + expect(compilation.immutableReferences).to.deep.equal({ '0': [{ length: 96, start: 167 }], }); }); @@ -459,7 +459,7 @@ describe('VyperCompilation', () => { ); await compilation.compile(); - const immutableRefs = compilation.getImmutableReferences(); + const immutableRefs = compilation.immutableReferences; expect(immutableRefs).to.be.empty; }); @@ -502,10 +502,10 @@ describe('VyperCompilation', () => { await compilation.compile(); await compilation.generateCborAuxdataPositions(); - expect(compilation.getCreationBytecode()).to.equal( + expect(compilation.creationBytecode).to.equal( '0x6100b761000f6000396100b76000f36003361161000c576100a1565b60003560e01c346100a75763c605f76c811861009f57600436186100a757602080608052600c6040527f48656c6c6f20576f726c6421000000000000000000000000000000000000000060605260408160800181518082526020830160208301815181525050508051806020830101601f82600003163682375050601f19601f8251602001011690509050810190506080f35b505b60006000fd5b600080fda165767970657283000304', ); - expect(compilation.getRuntimeBytecode()).to.equal( + expect(compilation.runtimeBytecode).to.equal( '0x6003361161000c576100a1565b60003560e01c346100a75763c605f76c811861009f57600436186100a757602080608052600c6040527f48656c6c6f20576f726c6421000000000000000000000000000000000000000060605260408160800181518082526020830160208301815181525050508051806020830101601f82600003163682375050601f19601f8251602001011690509050810190506080f35b505b60006000fd5b600080fda165767970657283000304', ); expect(compilation.creationBytecodeCborAuxdata).to.deep.equal({ @@ -556,10 +556,10 @@ describe('VyperCompilation', () => { await compilation.compile(); await compilation.generateCborAuxdataPositions(); - expect(compilation.getCreationBytecode()).to.equal( + expect(compilation.creationBytecode).to.equal( '0x6100b961000f6000396100b96000f36003361161000c576100a1565b60003560e01c346100a75763c605f76c811861009f57600436106100a757602080608052600c6040527f48656c6c6f20576f726c6421000000000000000000000000000000000000000060605260408160800181518082526020830160208301815181525050508051806020830101601f82600003163682375050601f19601f8251602001011690509050810190506080f35b505b60006000fd5b600080fda165767970657283000307000b', ); - expect(compilation.getRuntimeBytecode()).to.equal( + expect(compilation.runtimeBytecode).to.equal( '0x6003361161000c576100a1565b60003560e01c346100a75763c605f76c811861009f57600436106100a757602080608052600c6040527f48656c6c6f20576f726c6421000000000000000000000000000000000000000060605260408160800181518082526020830160208301815181525050508051806020830101601f82600003163682375050601f19601f8251602001011690509050810190506080f35b505b60006000fd5b600080fda165767970657283000307000b', ); expect(compilation.creationBytecodeCborAuxdata).to.deep.equal({ diff --git a/packages/lib-sourcify/test/Verification/Verification.spec.ts b/packages/lib-sourcify/test/Verification/Verification.spec.ts new file mode 100644 index 000000000..8a95068e5 --- /dev/null +++ b/packages/lib-sourcify/test/Verification/Verification.spec.ts @@ -0,0 +1,1235 @@ +import { describe, it, before, after } from 'mocha'; +import { expect, use } from 'chai'; +import { Verification } from '../../src/Verification/Verification'; +import SourcifyChain from '../../src/SourcifyChain'; +import { ChildProcess } from 'child_process'; +import { JsonRpcSigner } from 'ethers'; +import path from 'path'; +import { + deployFromAbiAndBytecode, + expectVerification, + vyperCompiler, +} from '../utils'; +import { + startHardhatNetwork, + stopHardhatNetwork, +} from '../hardhat-network-helper'; +import { SolidityMetadataContract } from '../../src/Validation/SolidityMetadataContract'; +import { + ISolidityCompiler, + SolidityOutput, +} from '../../src/Compilation/SolidityTypes'; +import { useSolidityCompiler } from '../compiler/solidityCompiler'; +import { findSolcPlatform } from '../compiler/solidityCompiler'; +import fs from 'fs'; +import { VyperCompilation } from '../../src/Compilation/VyperCompilation'; +import { PathContent } from '../../src/Validation/ValidationTypes'; +import chaiAsPromised from 'chai-as-promised'; + +use(chaiAsPromised); + +class TestSolidityCompiler implements ISolidityCompiler { + async compile( + version: string, + solcJsonInput: any, + forceEmscripten = false, + ): Promise { + return useSolidityCompiler(version, solcJsonInput, forceEmscripten); + } +} + +// Helper function to get compilation from metadata +async function getCompilationFromMetadata(contractFolderPath: string) { + // Read metadata.json directly + const metadataPath = path.join(contractFolderPath, 'metadata.json'); + const metadataRaw = fs.readFileSync(metadataPath, 'utf8'); + const metadata = JSON.parse(metadataRaw); + + // Read source files from the sources directory + const sourcesPath = path.join(contractFolderPath, 'sources'); + const sources: PathContent[] = []; + + // Recursively read all files from the sources directory + const readDirRecursively = (dir: string, baseDir: string = '') => { + const files = fs.readdirSync(dir); + for (const file of files) { + const fullPath = path.join(dir, file); + const relativePath = path.join(baseDir, file); + if (fs.statSync(fullPath).isDirectory()) { + readDirRecursively(fullPath, relativePath); + } else { + const content = fs.readFileSync(fullPath, 'utf8'); + sources.push({ + path: relativePath, + content, + }); + } + } + }; + + readDirRecursively(sourcesPath); + + // Create metadata contract + const metadataContract = new SolidityMetadataContract(metadata, sources); + + // Create compilation + return await metadataContract.createCompilation(new TestSolidityCompiler()); +} + +// Helper function to create Vyper compilation +async function createVyperCompilation( + contractFolderPath: string, + version: string, + settings: { + evmVersion?: 'london' | 'paris' | 'shanghai' | 'cancun' | 'istanbul'; + optimize?: 'gas' | 'codesize' | 'none' | boolean; + } = { evmVersion: 'istanbul' }, +) { + const contractFileName = 'test.vy'; + const contractFileContent = await fs.promises.readFile( + path.join(contractFolderPath, contractFileName), + ); + + return new VyperCompilation( + vyperCompiler, + version, + { + language: 'Vyper', + sources: { + [contractFileName]: { + content: contractFileContent.toString(), + }, + }, + settings: { + ...settings, + outputSelection: { + '*': ['evm.bytecode'], + }, + }, + }, + { + path: contractFileName, + name: contractFileName.split('.')[0], + }, + ); +} + +const HARDHAT_PORT = 8544; + +const UNUSED_ADDRESS = '0x1F98431c8aD98523631AE4a59f267346ea31F984'; + +const hardhatChain = { + name: 'Hardhat Network Localhost', + shortName: 'Hardhat Network', + chainId: 31337, + faucets: [], + infoURL: 'localhost', + nativeCurrency: { name: 'localETH', symbol: 'localETH', decimals: 18 }, + network: 'testnet', + networkId: 31337, + rpc: [`http://localhost:${HARDHAT_PORT}`], + supported: true, +}; + +const sourcifyChainHardhat: SourcifyChain = new SourcifyChain(hardhatChain); + +let hardhatNodeProcess: ChildProcess; +let signer: JsonRpcSigner; + +describe('Verification Class Tests', () => { + before(async () => { + hardhatNodeProcess = await startHardhatNetwork(HARDHAT_PORT); + signer = await sourcifyChainHardhat.providers[0].getSigner(); + }); + + after(async () => { + await stopHardhatNetwork(hardhatNodeProcess); + }); + + describe('Basic Contract Verification', () => { + it('should verify a simple contract', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'Storage', + ); + const { contractAddress } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + ); + + const compilation = await getCompilationFromMetadata(contractFolderPath); + const verification = new Verification( + compilation, + sourcifyChainHardhat, + contractAddress, + ); + await verification.verify(); + + expectVerification(verification, { + status: { + runtimeMatch: 'perfect', + creationMatch: null, + }, + }); + }); + + it('should partially verify a simple contract', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'Storage', + ); + const modifiedContractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'StorageModified', + ); + const { contractAddress } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + ); + + const compilation = await getCompilationFromMetadata( + modifiedContractFolderPath, + ); + const verification = new Verification( + compilation, + sourcifyChainHardhat, + contractAddress, + ); + await verification.verify(); + + expectVerification(verification, { + status: { + runtimeMatch: 'partial', + creationMatch: null, + }, + }); + }); + + it('should fail to verify a non-existing address', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'Storage', + ); + const compilation = await getCompilationFromMetadata(contractFolderPath); + const verification = new Verification( + compilation, + sourcifyChainHardhat, + UNUSED_ADDRESS, + ); + + await expect(verification.verify()) + .to.eventually.be.rejectedWith() + .and.have.property('code', 'contract_not_deployed'); + }); + + it('should verify a contract with multiple auxdata with wrong auxdata leading to a partial match', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'WithMultipleAuxdatas', + ); + const { contractAddress, txHash } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + [], + ); + + const compilation = await getCompilationFromMetadata(contractFolderPath); + const verification = new Verification( + compilation, + sourcifyChainHardhat, + contractAddress, + txHash, + ); + await verification.verify(); + + expectVerification(verification, { + status: { + runtimeMatch: 'partial', + creationMatch: 'partial', + }, + transformations: { + creation: { + list: [ + { + type: 'replace', + reason: 'cborAuxdata', + offset: 4148, + id: '1', + }, + { + type: 'replace', + reason: 'cborAuxdata', + offset: 2775, + id: '2', + }, + { + type: 'replace', + reason: 'cborAuxdata', + offset: 4095, + id: '3', + }, + ], + values: { + cborAuxdata: { + '1': '0xa2646970667358221220fe338e4778c1623b5865cd4121849802c8ed68e688def4d95b606f2f02ec563e64736f6c63430008090033', + '2': '0xa2646970667358221220bc654cadfb13b9ef229b6a2db4424f95dc4c52e3ae9b60648aa276f8eb0b3f8464736f6c63430008090033', + '3': '0xa2646970667358221220eb4312065a8c0fb940ef11ef5853554a447a5325095ee0f8fbbbbfc43dbb1b7464736f6c63430008090033', + }, + }, + }, + }, + }); + }); + + it('should verify a library with call protection and add the transformation', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'CallProtectionForLibraries', + ); + const { contractAddress } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + ); + + const compilation = await getCompilationFromMetadata(contractFolderPath); + const verification = new Verification( + compilation, + sourcifyChainHardhat, + contractAddress, + ); + await verification.verify(); + + expectVerification(verification, { + status: { + runtimeMatch: 'perfect', + creationMatch: null, + }, + transformations: { + runtime: { + list: [ + { + type: 'replace', + reason: 'callProtection', + offset: 1, + }, + ], + values: { + callProtection: contractAddress.toLowerCase(), + }, + }, + }, + }); + }); + + it('should return partial match when there is perfect bytecode match but no auxdata', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'NoAuxdata', + ); + const { contractAddress } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + ); + + const compilation = await getCompilationFromMetadata(contractFolderPath); + + const verification = new Verification( + compilation, + sourcifyChainHardhat, + contractAddress, + ); + await verification.verify(); + + expectVerification(verification, { + status: { + runtimeMatch: 'partial', + creationMatch: null, + }, + }); + }); + + it('should throw an error when bytecodes dont match and no auxdata to ignore for a partial match', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'NoAuxdata', + ); + const { contractAddress } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + ); + + const compilation = await getCompilationFromMetadata(contractFolderPath); + + Object.defineProperty(compilation, 'runtimeBytecode', { + get: () => + '0x6080604052348015600f57600080fd5b506004361060325760003560e01c80633fa4f24514603757806355241077146051575b600080fd5b603d6069565b604051604891906090565b60405180910390f35b606760048036038101906063919060d5565b606f565b005b60005481565b8060008190555050565b6000819050919050565b608a816079565b82525050565b600060208201905060a360008301846083565b92915050565b600080fd5b60b5816079565b811460bf57600080fd5b50565b60008135905060cf8160ae565b92915050565b60006020828403121560e85760e760a9565b5b600060f48482850160c2565b9150509291505055', + }); + + const verification = new Verification( + compilation, + sourcifyChainHardhat, + contractAddress, + ); + + await expect(verification.verify()) + .to.eventually.be.rejectedWith() + .and.have.property('code', 'no_match'); + }); + + it('should add constructor transformation with correct offset and arguments', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'WithImmutables', + ); + const { contractAddress, txHash } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + ['12345'], + ); + + const compilation = await getCompilationFromMetadata(contractFolderPath); + const verification = new Verification( + compilation, + sourcifyChainHardhat, + contractAddress, + txHash, + ); + await verification.verify(); + + expectVerification(verification, { + status: { + runtimeMatch: 'perfect', + creationMatch: 'perfect', + }, + transformations: { + creation: { + list: [ + { + type: 'insert', + reason: 'constructorArguments', + offset: 970, + }, + ], + values: { + constructorArguments: + '0x0000000000000000000000000000000000000000000000000000000000003039', + }, + }, + }, + }); + }); + + it('should detect extra file input bug when optimizer is enabled', async () => { + // Deploy the original contract + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'ExtraFilesBytecodeMismatch', + ); + const { contractAddress } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + ); + + // Try to verify with a compilation that has extra files + const compilation = await getCompilationFromMetadata(contractFolderPath); + + const failingVerification = new Verification( + compilation, + sourcifyChainHardhat, + contractAddress, + ); + + await expect(failingVerification.verify()) + .to.eventually.be.rejectedWith() + .and.have.property('code', 'extra_file_input_bug'); + + // Read all files from the sources directory + const sourcesPath = path.join(contractFolderPath, 'complete_sources'); + const sourceFiles: string[] = []; + const readDirRecursively = (dir: string) => { + const files = fs.readdirSync(dir); + for (const file of files) { + const fullPath = path.join(dir, file); + if (fs.statSync(fullPath).isDirectory()) { + readDirRecursively(fullPath); + } else { + sourceFiles.push(fullPath); + } + } + }; + readDirRecursively(sourcesPath); + + const additionalSources: Record = {}; + for (const fullPath of sourceFiles) { + const content = fs.readFileSync(fullPath, 'utf8'); + const relativePath = path.relative(sourcesPath, fullPath); + additionalSources[relativePath] = { content }; + } + + // Use the full sources as the input + compilation.jsonInput.sources = additionalSources; + + // Try to verify again with the full sources + const verification = new Verification( + compilation, + sourcifyChainHardhat, + contractAddress, + ); + await verification.verify(); + + expectVerification(verification, { + status: { + runtimeMatch: 'perfect', + creationMatch: null, + }, + }); + }); + + it('should fail when bytecode could not be fetched', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'Storage', + ); + // Create a chain with invalid RPC to simulate unavailability + const unavailableChain = new SourcifyChain({ + ...hardhatChain, + rpc: ['http://localhost:1234'], + }); + + const compilation = await getCompilationFromMetadata(contractFolderPath); + const verification = new Verification( + compilation, + unavailableChain, + UNUSED_ADDRESS, + ); + + await expect(verification.verify()) + .to.eventually.be.rejectedWith() + .and.have.property('code', 'cant_fetch_bytecode'); + }); + }); + + describe('Library Contract Verification', () => { + it('should verify a contract with library placeholders', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'UsingLibrary', + ); + const { contractAddress } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + ); + + const compilation = await getCompilationFromMetadata(contractFolderPath); + const verification = new Verification( + compilation, + sourcifyChainHardhat, + contractAddress, + ); + await verification.verify(); + + expectVerification(verification, { + status: { + runtimeMatch: 'perfect', + creationMatch: null, + }, + libraryMap: { + runtime: { + __$da572ae5e60c838574a0f88b27a0543803$__: + '11fea6722e00ba9f43861a6e4da05fecdf9806b7', + }, + }, + }); + }); + }); + + describe('Immutable Contract Verification', () => { + it('should verify a contract with immutables', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'WithImmutables', + ); + const { contractAddress, txHash } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + ['12345'], + ); + + const compilation = await getCompilationFromMetadata(contractFolderPath); + const verification = new Verification( + compilation, + sourcifyChainHardhat, + contractAddress, + txHash, + ); + await verification.verify(); + + expectVerification(verification, { + status: { + runtimeMatch: 'perfect', + creationMatch: 'perfect', + }, + transformations: { + runtime: { + list: [ + { + type: 'replace', + reason: 'immutable', + offset: 608, + id: '3', + }, + ], + values: { + immutables: { + '3': '0x0000000000000000000000000000000000000000000000000000000000003039', + }, + }, + }, + creation: { + list: [ + { + type: 'insert', + reason: 'constructorArguments', + offset: 970, + }, + ], + values: { + constructorArguments: + '0x0000000000000000000000000000000000000000000000000000000000003039', + }, + }, + }, + }); + }); + }); + + describe('Verification Results', () => { + it('should return correct verification status and bytecodes', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'Storage', + ); + + // Deploy the contract + const { contractAddress, txHash } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + ); + + // Get compilation using the helper + const compilation = await getCompilationFromMetadata(contractFolderPath); + + // Create and verify using the Verification class directly + const verification = new Verification( + compilation, + sourcifyChainHardhat, + contractAddress, + txHash, + ); + await verification.verify(); + + // This test just checks that Verification's getter properties return values formatted correctly + expectVerification(verification, { + status: { + runtimeMatch: 'perfect', + creationMatch: 'perfect', + }, + }); + + // Test onchainRuntimeBytecode + expect(verification.onchainRuntimeBytecode).to.be.a('string'); + + // Test onchainCreationBytecode + expect(verification.onchainCreationBytecode).to.be.a('string'); + + // Test getTransformations + expectVerification(verification, { + transformations: { + runtime: { + list: [], + values: {}, + }, + creation: { + list: [], + values: {}, + }, + }, + }); + + // Test getDeploymentInfo + const deploymentInfo = verification.deploymentInfo; + expectVerification(verification, { + deploymentInfo: { + blockNumber: deploymentInfo.blockNumber, + txIndex: deploymentInfo.txIndex, + deployer: deploymentInfo.deployer, + }, + }); + + // Test getLibraryMap + expectVerification(verification, { + libraryMap: { + runtime: {}, + creation: {}, + }, + }); + }); + }); + + describe('ViaIR Compilation', () => { + it('should verify a contract with viaIR:true', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'StorageViaIR', + ); + const { contractAddress } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + ); + + const compilation = await getCompilationFromMetadata(contractFolderPath); + const verification = new Verification( + compilation, + sourcifyChainHardhat, + contractAddress, + ); + await verification.verify(); + + expectVerification(verification, { + status: { + runtimeMatch: 'perfect', + creationMatch: null, + }, + }); + }); + + it('should verify a contract with viaIR:true, optimizer disabled, and compiler <0.8.21', async function () { + const solcPlatform = findSolcPlatform(); + // can't really run this if we can't run a platform-native binary but only Emscripten (solc-js) + if (!solcPlatform) { + console.log( + `skipping test as the running machine can't run a platform-native binary. The platform and architechture is ${process.platform} ${process.arch}`, + ); + this.skip(); + } + + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'ViaIRUnoptimizedMismatch', + ); + const { contractAddress } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + ); + + const compilation = await getCompilationFromMetadata(contractFolderPath); + const verification = new Verification( + compilation, + sourcifyChainHardhat, + contractAddress, + ); + await verification.verify(); + + expectVerification(verification, { + status: { + runtimeMatch: 'perfect', + creationMatch: null, + }, + }); + }); + }); + + describe('Creation Transaction Verification', () => { + it('should verify with creation transaction hash', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'WithImmutables', + ); + const { contractAddress, txHash } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + ['12345'], + ); + + const compilation = await getCompilationFromMetadata(contractFolderPath); + const verification = new Verification( + compilation, + sourcifyChainHardhat, + contractAddress, + txHash, + ); + await verification.verify(); + + expectVerification(verification, { + status: { + runtimeMatch: 'perfect', + creationMatch: 'perfect', + }, + }); + }); + }); + + describe('Vyper Compilation Tests', () => { + it('should verify a simple Vyper contract', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'Vyper', + 'testcontract', + ); + const { contractAddress, txHash } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + ); + + const vyperCompilation = await createVyperCompilation( + contractFolderPath, + '0.3.10+commit.91361694', + ); + + const verification = new Verification( + vyperCompilation, + sourcifyChainHardhat, + contractAddress, + txHash, + ); + await verification.verify(); + + expectVerification(verification, { + status: { + runtimeMatch: 'partial', + creationMatch: 'partial', + }, + }); + }); + + it('should verify a Vyper contract with immutables', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'Vyper', + 'withImmutables', + ); + const { contractAddress } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + [5], // Constructor argument for immutable value + ); + + const vyperCompilation = await createVyperCompilation( + contractFolderPath, + '0.4.0+commit.e9db8d9f', + { + evmVersion: 'london', + optimize: 'codesize', + }, + ); + + const verification = new Verification( + vyperCompilation, + sourcifyChainHardhat, + contractAddress, + ); + await verification.verify(); + + // Check if immutable values are correctly set + expectVerification(verification, { + status: { + runtimeMatch: 'partial', + creationMatch: null, + }, + transformations: { + runtime: { + list: [ + { + type: 'insert', + reason: 'immutable', + offset: 167, + id: '0', + }, + ], + values: { + immutables: { + '0': '0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000eca7a2f8618d6f', + }, + }, + }, + creation: { + list: [], + values: {}, + }, + }, + }); + }); + + it('should add constructor transformation with correct offset and arguments for Vyper contract', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'Vyper', + 'withImmutables', + ); + const contractFileName = 'test.vy'; + const vyperContent = await fs.promises.readFile( + path.join(contractFolderPath, contractFileName), + ); + + const constructorArg = 5; + const { contractAddress, txHash } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + [constructorArg], + ); + + const vyperCompilation = new VyperCompilation( + vyperCompiler, + '0.4.0+commit.e9db8d9f', + { + language: 'Vyper', + sources: { + [contractFileName]: { + content: vyperContent.toString(), + }, + }, + settings: { + evmVersion: 'london', + optimize: 'codesize', + outputSelection: { + '*': ['evm.bytecode'], + }, + }, + }, + { + path: contractFileName, + name: contractFileName.split('.')[0], + }, + ); + + const verification = new Verification( + vyperCompilation, + sourcifyChainHardhat, + contractAddress, + txHash, + ); + await verification.verify(); + + expectVerification(verification, { + status: { + runtimeMatch: 'partial', + creationMatch: 'partial', + }, + transformations: { + creation: { + list: [ + { + type: 'insert', + reason: 'constructorArguments', + offset: 245, + }, + ], + values: { + constructorArguments: + '0x0000000000000000000000000000000000000000000000000000000000000005', + }, + }, + }, + }); + }); + + it('should fail to verify when using wrong Vyper contract', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'Vyper', + 'testcontract', + ); + const wrongContractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'Vyper', + 'testcontract2', + ); + const { contractAddress } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + ); + + const vyperCompilation = await createVyperCompilation( + wrongContractFolderPath, + '0.3.10+commit.91361694', + ); + + const verification = new Verification( + vyperCompilation, + sourcifyChainHardhat, + contractAddress, + ); + + await expect(verification.verify()) + .to.eventually.be.rejectedWith() + .and.have.property('code', 'no_match'); + }); + + it('should handle Vyper contracts with different auxdata versions', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'Vyper', + 'testcontract', + ); + const { contractAddress, txHash } = await deployFromAbiAndBytecode( + signer, + path.join(contractFolderPath, 'wrongAuxdata'), + ); + + const vyperCompilation = await createVyperCompilation( + contractFolderPath, + '0.3.10+commit.91361694', + ); + + const verification = new Verification( + vyperCompilation, + sourcifyChainHardhat, + contractAddress, + txHash, + ); + await verification.verify(); + + expectVerification(verification, { + status: { + runtimeMatch: 'partial', + creationMatch: 'partial', + }, + transformations: { + creation: { + list: [ + { + type: 'replace', + reason: 'cborAuxdata', + offset: 158, + id: '1', + }, + ], + values: { + cborAuxdata: { + '1': '0x84188f8000a1657679706572830003090012', + }, + }, + }, + }, + }); + }); + }); + + describe('Creation transaction matching tests', () => { + // https://github.com/ethereum/sourcify/pull/1623 + it('should verify a contract partially with the creation bytecode after transformation fields are normalized', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'ConstructorModified', + ); + const { contractAddress, txHash } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + ['12345'], + ); + + const compilation = await getCompilationFromMetadata(contractFolderPath); + const verification = new Verification( + compilation, + sourcifyChainHardhat, + contractAddress, + txHash, + ); + await verification.verify(); + + expectVerification(verification, { + status: { + runtimeMatch: 'partial', + creationMatch: 'partial', + }, + transformations: { + creation: { + list: [ + { + type: 'replace', + reason: 'cborAuxdata', + offset: 279, + id: '1', + }, + { + type: 'insert', + reason: 'constructorArguments', + offset: 332, + }, + ], + values: { + cborAuxdata: { + '1': '0xa26469706673582212208a693a7ed29129e25fc67a65f83955fb3d86f5fbc378940d697827714b955df564736f6c634300081a0033', + }, + constructorArguments: + '0x0000000000000000000000000000000000000000000000000000000000003039', + }, + }, + }, + }); + }); + + it('should return null creationMatch when the wrong creation tx hash is passed', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'WithImmutables', + ); + const { contractAddress } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + ['12345'], + ); + + // Deploy another contract to get a different transaction hash + const { txHash: wrongTxHash } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + ['12345'], + ); + + const compilation = await getCompilationFromMetadata(contractFolderPath); + const verification = new Verification( + compilation, + sourcifyChainHardhat, + contractAddress, + wrongTxHash, + ); + + await verification.verify(); + + expectVerification(verification, { + status: { + runtimeMatch: 'perfect', + creationMatch: null, + }, + }); + }); + + it('should fail verification when trying to maliciously verify with creation bytecode that startsWith the creatorTx input', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'WithImmutables', + ); + const maliciousContractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'WithImmutablesCreationBytecodeAttack', + ); + + const { contractAddress, txHash } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + ['12345'], + ); + + const compilation = await getCompilationFromMetadata( + maliciousContractFolderPath, + ); + const verification = new Verification( + compilation, + sourcifyChainHardhat, + contractAddress, + txHash, + ); + + await expect(verification.verify()) + .to.eventually.be.rejectedWith() + .and.have.property('code', 'bytecode_length_mismatch'); + + expectVerification(verification, { + status: { + runtimeMatch: null, + creationMatch: null, + }, + }); + }); + + it('should fail verification when using an abstract contract', async () => { + const contractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'WithImmutables', + ); + const abstractContractFolderPath = path.join( + __dirname, + '..', + 'sources', + 'AbstractCreationBytecodeAttack', + ); + + const { contractAddress, txHash } = await deployFromAbiAndBytecode( + signer, + contractFolderPath, + ['12345'], + ); + + const compilation = await getCompilationFromMetadata( + abstractContractFolderPath, + ); + const verification = new Verification( + compilation, + sourcifyChainHardhat, + contractAddress, + txHash, + ); + + await expect(verification.verify()) + .to.eventually.be.rejectedWith() + .and.have.property('code', 'compiled_bytecode_is_zero'); + }); + }); +}); diff --git a/packages/lib-sourcify/test/compiler/common.ts b/packages/lib-sourcify/test/compiler/common.ts index 3e1a95127..b7d2886b1 100644 --- a/packages/lib-sourcify/test/compiler/common.ts +++ b/packages/lib-sourcify/test/compiler/common.ts @@ -1,4 +1,4 @@ -import { logDebug, logError } from '../../src/lib/logger'; +import { logDebug, logError } from '../../src/logger'; /** * Fetches a resource with an exponential timeout. diff --git a/packages/lib-sourcify/test/compiler/solidityCompiler.ts b/packages/lib-sourcify/test/compiler/solidityCompiler.ts index 438d41837..af6a3fafd 100644 --- a/packages/lib-sourcify/test/compiler/solidityCompiler.ts +++ b/packages/lib-sourcify/test/compiler/solidityCompiler.ts @@ -3,7 +3,7 @@ import path from 'path'; import fs from 'fs'; import { exec, spawnSync } from 'child_process'; import { StatusCodes } from 'http-status-codes'; -import { logDebug, logError, logInfo, logWarn } from '../../src/lib/logger'; +import { logDebug, logError, logInfo, logWarn } from '../../src/logger'; import semver from 'semver'; import { Worker, WorkerOptions } from 'worker_threads'; import { fetchWithBackoff } from './common'; diff --git a/packages/lib-sourcify/test/compiler/vyperCompiler.ts b/packages/lib-sourcify/test/compiler/vyperCompiler.ts index aa739665a..af0214226 100644 --- a/packages/lib-sourcify/test/compiler/vyperCompiler.ts +++ b/packages/lib-sourcify/test/compiler/vyperCompiler.ts @@ -5,7 +5,7 @@ import { exec, spawnSync } from 'child_process'; import { StatusCodes } from 'http-status-codes'; import { VyperOutput, VyperJsonInput } from '@ethereum-sourcify/lib-sourcify'; import { fetchWithBackoff } from './common'; -import { logDebug, logError, logWarn } from '../../src/lib/logger'; +import { logDebug, logError, logWarn } from '../../src/logger'; const HOST_VYPER_REPO = 'https://github.com/vyperlang/vyper/releases/download/'; diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/artifact.json b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/artifact.json new file mode 100644 index 000000000..3e1cb5aa6 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/artifact.json @@ -0,0 +1,808 @@ +{ + "bytecode": "0x60806040526000805534801561001457600080fd5b506155c180620000256000396000f3fe608060405234801561001057600080fd5b50600436106101c35760003560e01c8063ab9c4b5d116100f9578063d15e005311610097578063e82fec2f11610071578063e82fec2f146103c2578063e8eda9df146103ca578063f8119d51146103dd578063fe65acfe146103e5576101c3565b8063d15e005314610387578063d1946dbc1461039a578063d5ed3933146103af576101c3565b8063bf92857c116100d3578063bf92857c14610329578063c44b11f71461034e578063c4d66de814610361578063cd11238214610374576101c3565b8063ab9c4b5d146102f0578063b8d2927614610303578063bedb86fb14610316576101c3565b80635a3b74b9116101665780637a708e92116101405780637a708e92146102af5780638afaff02146102c257806394ba89a2146102ca578063a415bcad146102dd576101c3565b80635a3b74b9146102745780635c975abb1461028757806369328dec1461029c576101c3565b806335ea6a75116101a257806335ea6a751461020e578063386497fd1461022e5780634417a58314610241578063573ade8114610261576101c3565b8062a718a9146101c8578063074b2e43146101dd5780631d2118f9146101fb575b600080fd5b6101db6101d6366004614875565b6103fa565b005b6101e56105d0565b6040516101f291906154c2565b60405180910390f35b6101db6102093660046147cd565b6105d6565b61022161021c366004614795565b61060f565b6040516101f291906152d5565b6101e561023c366004614795565b6106f1565b61025461024f366004614795565b610718565b6040516101f291906152cb565b6101e561026f366004614b14565b61074b565b6101db610282366004614a2a565b610a77565b61028f610c3c565b6040516101f29190615111565b6101e56102aa366004614a82565b610c45565b6101db6102bd366004614805565b610f6f565b6101e5611051565b6101db6102d8366004614a57565b611056565b6101db6102eb366004614b5d565b6113c3565b6101db6102fe366004614932565b611443565b6101db610311366004614a57565b611b17565b6101db610324366004614b9c565b611b3b565b61033c610337366004614795565b611bb6565b6040516101f296959493929190615514565b61025461035c366004614795565b611cb2565b6101db61036f366004614795565b611ce5565b6101db6103823660046147cd565b611d8d565b6101e5610395366004614795565b612003565b6103a2612024565b6040516101f291906150c4565b6101db6103bd3660046148ce565b6120c9565b6101e5612312565b6101db6103d8366004614ac3565b612318565b6101e5612545565b6103ed61254b565b6040516101f29190614de3565b61040261255a565b6034546040805163712d917160e01b815290516000926001600160a01b03169163712d9171916004808301926020929190829003018186803b15801561044757600080fd5b505afa15801561045b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061047f91906147b1565b905060006060826001600160a01b031688888888886040516024016104a8959493929190614e6b565b60408051601f198184030181529181526020820180516001600160e01b031662a718a960e01b179052516104dc9190614dc7565b600060405180830381855af49150503d8060008114610517576040519150601f19603f3d011682016040523d82523d6000602084013e61051c565b606091505b50915091508160405180604001604052806002815260200161323360f01b815250906105645760405162461bcd60e51b815260040161055b919061511c565b60405180910390fd5b50600060608280602001905181019061057d9190614bec565b9150915081600014816040516020016105969190614dc7565b604051602081830303815290604052906105c35760405162461bcd60e51b815260040161055b919061511c565b5050505050505050505050565b603b5490565b6105de612598565b6001600160a01b03918216600090815260356020526040902060070180546001600160a01b03191691909216179055565b6106176144e3565b506001600160a01b0381811660009081526035602090815260409182902082516101a08101845281546101808201908152815260018201546001600160801b0380821694830194909452600160801b908190048416948201949094526002820154808416606083015284900483166080820152600382015492831660a08201529290910464ffffffffff1660c08301526004810154831660e0830152600581015483166101008301526006810154831661012083015260070154918216610140820152600160a01b90910460ff166101608201525b919050565b6001600160a01b038116600090815260356020526040812061071290612657565b92915050565b61072061454e565b506001600160a01b031660009081526036602090815260409182902082519182019092529054815290565b600061075561255a565b6001600160a01b0385166000908152603560205260408120908061077985846126d4565b91509150600086600281111561078b57fe5b60405163fa0c214960e01b8152909150732b9d164fd084a022a5fdfa19403944434e13151a9063fa0c2149906107cf9087908c9086908c908a908a9060040161547d565b60006040518083038186803b1580156107e757600080fd5b505af41580156107fb573d6000803e3d6000fd5b50600092506001915061080b9050565b82600281111561081757fe5b146108225782610824565b835b9050808910156108315750875b61083a856127e9565b600182600281111561084857fe5b14156108b9576005850154604051632770a7eb60e21b81526001600160a01b0390911690639dc29fac90610882908a908590600401614e28565b600060405180830381600087803b15801561089c57600080fd5b505af11580156108b0573d6000803e3d6000fd5b50505050610937565b60068501546001860154604051637a94c56560e11b81526001600160a01b039092169163f5298aca91610904918b918691600160801b9091046001600160801b031690600401614e41565b600060405180830381600087803b15801561091e57600080fd5b505af1158015610932573d6000803e3d6000fd5b505050505b60048501546001600160a01b0316610953868c838560006128b6565b610967826109618787612c1e565b90612c43565b61099f5760078601546001600160a01b038916600090815260366020526040812061099f929091600160a01b90910460ff1690612c85565b6109b46001600160a01b038c16338385612cf5565b6040516388dd91a160e01b81526001600160a01b038216906388dd91a1906109e29033908690600401614e28565b600060405180830381600087803b1580156109fc57600080fd5b505af1158015610a10573d6000803e3d6000fd5b50505050336001600160a01b0316886001600160a01b03168c6001600160a01b03167f4cdde6e09bb755c9a5589ebaec640bbfedff1362d4b255ebf8339782b9942faa85604051610a6191906154c2565b60405180910390a4509998505050505050505050565b610a7f61255a565b6001600160a01b03808316600090815260356020818152604080842033855260368352938190206038546034548351631f94a27560e31b815293519697732b9d164fd084a022a5fdfa19403944434e13151a97635fa297e5978a978d978d9792969295603795939493169263fca513a892600480840193919291829003018186803b158015610b0d57600080fd5b505afa158015610b21573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b4591906147b1565b6040518963ffffffff1660e01b8152600401610b689897969594939291906153f6565b60006040518083038186803b158015610b8057600080fd5b505af4158015610b94573d6000803e3d6000fd5b505050506007810154336000908152603660205260409020610bc091600160a01b900460ff1684612d53565b8115610c005760405133906001600160a01b038516907e058a56ea94653cdf4f152d227ace22d4c00ad99e2a43f58cb7d9e3feb295f290600090a3610c37565b60405133906001600160a01b038516907f44c58d81365b66dd4b1a7f36c25aa97b8c71c361ee4937adc1a00000227db5dd90600090a35b505050565b60395460ff1690565b6000610c4f61255a565b6001600160a01b0380851660009081526035602052604080822060048082015492516370a0823160e01b8152919492909216929183916370a0823191610c9791339101614de3565b60206040518083038186803b158015610caf57600080fd5b505afa158015610cc3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ce79190614bd4565b905085600019811415610cf75750805b732b9d164fd084a022a5fdfa19403944434e13151a63d09db04a898385603560366000336001600160a01b03166001600160a01b031681526020019081526020016000206037603854603460009054906101000a90046001600160a01b03166001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b158015610d8e57600080fd5b505afa158015610da2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dc691906147b1565b6040518963ffffffff1660e01b8152600401610de9989796959493929190614f94565b60006040518083038186803b158015610e0157600080fd5b505af4158015610e15573d6000803e3d6000fd5b50505050610e22846127e9565b610e308489856000856128b6565b81811415610e9a576007840154336000908152603660205260408120610e63929091600160a01b90910460ff1690612d53565b60405133906001600160a01b038a16907f44c58d81365b66dd4b1a7f36c25aa97b8c71c361ee4937adc1a00000227db5dd90600090a35b6001840154604051636b81068560e11b81526001600160a01b0385169163d7020d0a91610edb9133918b9187916001600160801b0390911690600401614df7565b600060405180830381600087803b158015610ef557600080fd5b505af1158015610f09573d6000803e3d6000fd5b50505050856001600160a01b0316336001600160a01b0316896001600160a01b03167f3115d1449a7b732c986cba18244e897a450f61e1bb8d589cd2e69e6c8924f9f784604051610f5a91906154c2565b60405180910390a493505050505b9392505050565b610f77612598565b610f8085612dc9565b6040518060400160405280600281526020016106e760f31b81525090610fb95760405162461bcd60e51b815260040161055b919061511c565b506001600160a01b038516600090815260356020526040908190209051630acce25f60e21b8152738e6779f2ac23f196ef9a6b446e557a6f81fd4f5191632b33897c91611011919088908890889088906004016153c8565b60006040518083038186803b15801561102957600080fd5b505af415801561103d573d6000803e3d6000fd5b5050505061104a85612e02565b5050505050565b600281565b61105e61255a565b6001600160a01b0382166000908152603560205260408120908061108233846126d4565b91509150600084600281111561109457fe5b3360009081526036602052604090819020905163a8695b1d60e01b8152919250732b9d164fd084a022a5fdfa19403944434e13151a9163a8695b1d916110e591889190889088908890600401615438565b60006040518083038186803b1580156110fd57600080fd5b505af4158015611111573d6000803e3d6000fd5b5050505061111e846127e9565b600181600281111561112c57fe5b141561123c576005840154604051632770a7eb60e21b81526001600160a01b0390911690639dc29fac906111669033908790600401614e28565b600060405180830381600087803b15801561118057600080fd5b505af1158015611194573d6000803e3d6000fd5b505050506006840154600185015460405163b3f1c93d60e01b81526001600160a01b039092169163b3f1c93d916111e491339182918991600160801b90046001600160801b031690600401614df7565b602060405180830381600087803b1580156111fe57600080fd5b505af1158015611212573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112369190614bb8565b50611352565b60068401546001850154604051637a94c56560e11b81526001600160a01b039092169163f5298aca916112879133918791600160801b9091046001600160801b031690600401614e41565b600060405180830381600087803b1580156112a157600080fd5b505af11580156112b5573d6000803e3d6000fd5b505050506005840154600385015460405163b3f1c93d60e01b81526001600160a01b039092169163b3f1c93d916112fe913391829188916001600160801b031690600401614df7565b602060405180830381600087803b15801561131857600080fd5b505af115801561132c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113509190614bb8565b505b600484015461137090859088906001600160a01b03166000806128b6565b336001600160a01b0316866001600160a01b03167fea368a40e9570069bb8e6511d668293ad2e1f03b0d982431fd223de9f3b70ca6876040516113b391906154c2565b60405180910390a3505050505050565b6113cb61255a565b6001600160a01b038086166000818152603560209081526040918290208251610100810184529384523391840191909152848416918301919091526060820187905260808201869052600481015490921660a082015261ffff841660c0820152600160e082015261143b90612f0b565b505050505050565b61144b61255a565b611453614561565b6114c08b8b8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808f0282810182019093528e82529093508e92508d91829185019084908082843760009201919091525061340492505050565b60608a67ffffffffffffffff811180156114d957600080fd5b50604051908082528060200260200182016040528015611503578160200160208202803683370190505b50905060608b67ffffffffffffffff8111801561151f57600080fd5b50604051908082528060200260200182016040528015611549578160200160208202803683370190505b506001600160a01b038f1684526000604085015290505b60408301518c111561170d57603560008e8e866040015181811061158057fe5b90506020020160208101906115959190614795565b6001600160a01b03166001600160a01b0316815260200190815260200160002060040160009054906101000a90046001600160a01b0316828460400151815181106115dc57fe5b60200260200101906001600160a01b031690816001600160a01b03168152505061163361271061162d603b548e8e886040015181811061161857fe5b9050602002013561344290919063ffffffff16565b9061347c565b8184604001518151811061164357fe5b6020026020010181815250508183604001518151811061165f57fe5b60200260200101516001600160a01b0316634efecaa58f8d8d876040015181811061168657fe5b905060200201356040518363ffffffff1660e01b81526004016116aa929190614e28565b602060405180830381600087803b1580156116c457600080fd5b505af11580156116d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116fc9190614bd4565b506040830180516001019052611560565b82600001516001600160a01b031663920f5c848e8e8e8e86338d8d6040518963ffffffff1660e01b815260040161174b989796959493929190615000565b602060405180830381600087803b15801561176557600080fd5b505af1158015611779573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061179d9190614bb8565b604051806040016040528060028152602001611b1b60f11b815250906117d65760405162461bcd60e51b815260040161055b919061511c565b50600060408401525b60408301518c1115611b07578c8c84604001518181106117fb57fe5b90506020020160208101906118109190614795565b6001600160a01b0316606084015260408301518b908b9081811061183057fe5b905060200201358360a00181815250508083604001518151811061185057fe5b60200260200101518360c00181815250508183604001518151811061187157fe5b60209081029190910101516001600160a01b0316608084015260c083015160a084015161189d91612c1e565b60e08401526000898985604001518181106118b457fe5b9050602002013560028111156118c657fe5b60028111156118d157fe5b1415611a035760608301516001600160a01b031660009081526035602052604090206118fc906127e9565b61199c83608001516001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b15801561193c57600080fd5b505afa158015611950573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119749190614bd4565b60c085015160608601516001600160a01b0316600090815260356020526040902091906134be565b6060830151608084015160e08501516001600160a01b03831660009081526035602052604081206119d2949093909290916128b6565b6119fe8e84608001518560e0015186606001516001600160a01b0316612cf5909392919063ffffffff16565b611a92565b611a9260405180610100016040528085606001516001600160a01b03168152602001336001600160a01b03168152602001896001600160a01b031681526020018560a0015181526020018b8b8760400151818110611a5d57fe5b90506020020135815260200185608001516001600160a01b031681526020018661ffff16815260200160001515815250612f0b565b82606001516001600160a01b0316336001600160a01b03168f6001600160a01b03167f631042c832b07452973831137f2d73e395028b44b250dedc5abb0ee766e168ac8660a001518760c0015189604051611aef939291906154cb565b60405180910390a460408301805160010190526117df565b5050505050505050505050505050565b611b1f612598565b6001600160a01b03909116600090815260356020526040902055565b611b43612598565b6039805460ff1916821515179081905560ff1615611b89576040517f9e87fac88ff661f02d44f95383c817fece4bce600a3dab7a54406878b965e75290600090a1611bb3565b6040517fa45f47fdea8a1efdd9029a5691c7f759c32b7c698632b563573e155625d1693390600090a15b50565b600080600080600080611c8f876035603660008b6001600160a01b03166001600160a01b031681526020019081526020016000206040518060200160405290816000820154815250506037603854603460009054906101000a90046001600160a01b03166001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b158015611c5257600080fd5b505afa158015611c66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c8a91906147b1565b61357b565b93995091975090945092509050611ca7868684613a3c565b935091939550919395565b611cba61454e565b506001600160a01b031660009081526035602090815260409182902082519182019092529054815290565b6000611cef613a70565b60015490915060ff1680611d065750611d06613a75565b80611d12575060005481115b611d2e5760405162461bcd60e51b815260040161055b906151fc565b60015460ff16158015611d4d576001805460ff19168117905560008290555b603480546001600160a01b0319166001600160a01b0385161790556109c4603a556009603b556080603c558015610c37576001805460ff19169055505050565b611d9561255a565b6001600160a01b038083166000908152603560205260408082206005810154600682015460048084015494516370a0823160e01b81529396928316959183169490921692909185916370a0823191611def918a9101614de3565b60206040518083038186803b158015611e0757600080fd5b505afa158015611e1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e3f9190614bd4565b60405163548cad0960e01b8152909150732b9d164fd084a022a5fdfa19403944434e13151a9063548cad0990611e819088908b908990899089906004016153c8565b60006040518083038186803b158015611e9957600080fd5b505af4158015611ead573d6000803e3d6000fd5b50505050611eba856127e9565b604051632770a7eb60e21b81526001600160a01b03851690639dc29fac90611ee89089908590600401614e28565b600060405180830381600087803b158015611f0257600080fd5b505af1158015611f16573d6000803e3d6000fd5b505050600386015460405163b3f1c93d60e01b81526001600160a01b038716925063b3f1c93d91611f59918a91829187916001600160801b031690600401614df7565b602060405180830381600087803b158015611f7357600080fd5b505af1158015611f87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fab9190614bb8565b50611fba8588846000806128b6565b856001600160a01b0316876001600160a01b03167f9f439ae0c81e41a04d3fdfe07aed54e6a179fb0db15be7702eb66fa8ef6f530060405160405180910390a350505050505050565b6001600160a01b038116600090815260356020526040812061071290613a7b565b60608060385467ffffffffffffffff8111801561204057600080fd5b5060405190808252806020026020018201604052801561206a578160200160208202803683370190505b50905060005b6038548110156120c35760008181526037602052604090205482516001600160a01b03909116908390839081106120a357fe5b6001600160a01b0390921660209283029190910190910152600101612070565b50905090565b6120d161255a565b6001600160a01b038681166000908152603560209081526040918290206004015482518084019093526002835261363360f01b91830191909152909116331461212d5760405162461bcd60e51b815260040161055b919061511c565b506121e985603560366000896001600160a01b03166001600160a01b031681526020019081526020016000206037603854603460009054906101000a90046001600160a01b03166001600160a01b031663fca513a86040518163ffffffff1660e01b815260040160206040518083038186803b1580156121ac57600080fd5b505afa1580156121c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121e491906147b1565b613ada565b6001600160a01b03868116600090815260356020526040902060070154600160a01b900460ff169085811690871614612309576122268385612c43565b612292576001600160a01b0386166000908152603660205260408120906122509082908490612d53565b866001600160a01b0316886001600160a01b03167f44c58d81365b66dd4b1a7f36c25aa97b8c71c361ee4937adc1a00000227db5dd60405160405180910390a3505b8115801561229f57508315155b15612309576001600160a01b03851660009081526036602052604090206122c881836001612d53565b856001600160a01b0316886001600160a01b03167e058a56ea94653cdf4f152d227ace22d4c00ad99e2a43f58cb7d9e3feb295f260405160405180910390a3505b50505050505050565b603a5490565b61232061255a565b6001600160a01b038416600090815260356020526040908190209051630eca322b60e01b8152732b9d164fd084a022a5fdfa19403944434e13151a90630eca322b90612372908490889060040161546f565b60006040518083038186803b15801561238a57600080fd5b505af415801561239e573d6000803e3d6000fd5b5050505060048101546001600160a01b03166123b9826127e9565b6123c78287838860006128b6565b6123dc6001600160a01b038716338388612cf5565b6001820154604051630ab714fb60e11b81526000916001600160a01b0384169163156e29f69161241e9189918b916001600160801b0390911690600401614e41565b602060405180830381600087803b15801561243857600080fd5b505af115801561244c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124709190614bb8565b905080156124ea5760078301546001600160a01b03861660009081526036602052604090206124aa91600160a01b900460ff166001612d53565b846001600160a01b0316876001600160a01b03167e058a56ea94653cdf4f152d227ace22d4c00ad99e2a43f58cb7d9e3feb295f260405160405180910390a35b8361ffff16856001600160a01b0316886001600160a01b03167fde6857219544bb5b7746f48ed30be6386fefc61b2f864cacf559893bf50fd951338a604051612534929190614e28565b60405180910390a450505050505050565b603c5490565b6034546001600160a01b031690565b6039546040805180820190915260028152610d8d60f21b60208201529060ff1615611bb35760405162461bcd60e51b815260040161055b919061511c565b603454604080516385c858b160e01b8152905133926001600160a01b0316916385c858b1916004808301926020929190829003018186803b1580156125dc57600080fd5b505afa1580156125f0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061261491906147b1565b6001600160a01b03161460405180604001604052806002815260200161323760f01b81525090611bb35760405162461bcd60e51b815260040161055b919061511c565b600381015460009064ffffffffff600160801b90910481169042168114156126955750506001810154600160801b90046001600160801b03166106ec565b600183015460028401546000916126cc916001600160801b03600160801b928390048116926126c692041685613b50565b90613b5d565b949350505050565b60058101546040516370a0823160e01b815260009182916001600160a01b03909116906370a082319061270b908790600401614de3565b60206040518083038186803b15801561272357600080fd5b505afa158015612737573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061275b9190614bd4565b60068401546040516370a0823160e01b81526001600160a01b03909116906370a082319061278d908890600401614de3565b60206040518083038186803b1580156127a557600080fd5b505afa1580156127b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127dd9190614bd4565b915091505b9250929050565b60068101546040805163b1bf962d60e01b815290516000926001600160a01b03169163b1bf962d916004808301926020929190829003018186803b15801561283057600080fd5b505afa158015612844573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128689190614bd4565b60018301546003840154919250600160801b8082046001600160801b03908116939216910464ffffffffff166000806128a48787868887613bf0565b91509150612309878787858588613d4d565b6128be6145ad565b60058601546001600160a01b031680825260408051637b98f4df60e11b8152815163f731e9be92600480840193919291829003018186803b15801561290257600080fd5b505afa158015612916573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061293a9190614c93565b60c083015260408083019190915260018701546006880154825163b1bf962d60e01b815292516129df93600160801b9093046001600160801b0316926001600160a01b039092169163b1bf962d916004808301926020929190829003018186803b1580156129a757600080fd5b505afa1580156129bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126c69190614bd4565b60e082018190526007870154604083015160c08401516001600160a01b03909216926329db497d9289928992899289929190612a1a8f613f10565b6040518963ffffffff1660e01b8152600401612a3d989796959493929190614eec565b60606040518083038186803b158015612a5557600080fd5b505afa158015612a69573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a8d9190614cb6565b60a0840152608083015260608201819052604080518082019091526002815261353360f01b6020820152906001600160801b031015612adf5760405162461bcd60e51b815260040161055b919061511c565b506080810151604080518082019091526002815261353560f01b6020820152906001600160801b031015612b265760405162461bcd60e51b815260040161055b919061511c565b5060a08101516040805180820190915260028152610d4d60f21b6020820152906001600160801b031015612b6d5760405162461bcd60e51b815260040161055b919061511c565b506060810151600287018054608084015160038a0180546001600160801b03199081166001600160801b038085169190911790925560a08701519316818616178116600160801b84831681029190911790945560018b01546040516001600160a01b038c16967f804c9b842b2748a22bb64b345453a3de7ca54a6ca45ce00d415894979e22897a96612c0e96919594919380831693919004909116906154e5565b60405180910390a2505050505050565b600082820183811015610f685760405162461bcd60e51b815260040161055b9061514f565b6000610f6883836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250613f1b565b604080518082019091526002815261373760f01b602082015260808310612cbf5760405162461bcd60e51b815260040161055b919061511c565b508160020281612cd0576000612cd3565b60015b60ff16901b826002026001901b19846000015416178360000181905550505050565b612d4d846323b872dd60e01b858585604051602401612d1693929190614e9f565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613f47565b50505050565b604080518082019091526002815261373760f01b602082015260808310612d8d5760405162461bcd60e51b815260040161055b919061511c565b508160020260010181612da1576000612da4565b60015b60ff16901b826002026001016001901b19846000015416178360000181905550505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4708181148015906126cc575050151592915050565b603854603c54604080518082019091526002815261363560f01b6020820152908210612e415760405162461bcd60e51b815260040161055b919061511c565b506001600160a01b038216600090815260356020526040812060070154600160a01b900460ff16151580612eaa57506000805260376020527fa0a618d80eda9243166be83cb7421d97e9dab6ddddd3c70ac7a6b4440256e8e7546001600160a01b038481169116145b905080610c3757506001600160a01b03919091166000818152603560209081526040808320600701805460ff60a01b1916600160a01b60ff8816021790558483526037909152902080546001600160a01b0319169091179055600101603855565b80516001600160a01b0390811660009081526035602090815260408083208186015185168452603683528184206034548351631f94a27560e31b81529351929691959491169263fca513a89260048083019392829003018186803b158015612f7257600080fd5b505afa158015612f86573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612faa91906147b1565b9050600061304b612fba8561402c565b600a0a61162d8760600151856001600160a01b031663b3596f078a600001516040518263ffffffff1660e01b8152600401612ff59190614de3565b60206040518083038186803b15801561300d57600080fd5b505afa158015613021573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130459190614bd4565b90613442565b9050732b9d164fd084a022a5fdfa19403944434e13151a63721a92f986600001518688604001518960600151868b60800151603a5460358c60376038548e6040518d63ffffffff1660e01b81526004016130b09c9b9a99989796959493929190614f30565b60006040518083038186803b1580156130c857600080fd5b505af41580156130dc573d6000803e3d6000fd5b505050506130e9846127e9565b6000806001876080015160028111156130fe57fe5b600281111561310957fe5b14156131be576003860154600587015460208901516040808b015160608c0151915163b3f1c93d60e01b81526001600160801b0390951696506001600160a01b039093169363b3f1c93d93613165939290918890600401614ec3565b602060405180830381600087803b15801561317f57600080fd5b505af1158015613193573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131b79190614bb8565b905061326d565b600686015460208801516040808a015160608b015160018b0154925163b3f1c93d60e01b81526001600160a01b039095169463b3f1c93d946132189490939291600160801b9091046001600160801b031690600401614df7565b602060405180830381600087803b15801561323257600080fd5b505af1158015613246573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061326a9190614bb8565b90505b801561328f57600786015461328f908690600160a01b900460ff166001612c85565b6132be87600001518860a0015160008a60e001516132ae5760006132b4565b8a606001515b8a939291906128b6565b8660e0015115613356578660a001516001600160a01b0316634efecaa5886020015189606001516040518363ffffffff1660e01b8152600401613302929190614e28565b602060405180830381600087803b15801561331c57600080fd5b505af1158015613330573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133549190614bd4565b505b8660c0015161ffff1687604001516001600160a01b031688600001516001600160a01b03167fc6a898309e823ee50bac64e45ca8adba6690e99e7841c45d754e2a38e9019d9b8a602001518b606001518c60800151600160028111156133b857fe5b8e6080015160028111156133c857fe5b60028111156133d357fe5b146133f25760028d0154600160801b90046001600160801b03166133f4565b885b6040516125349493929190614fda565b805182511460405180604001604052806002815260200161373360f01b81525090610c375760405162461bcd60e51b815260040161055b919061511c565b60008261345157506000610712565b8282028284828161345e57fe5b0414610f685760405162461bcd60e51b815260040161055b906151bb565b6000610f6883836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614036565b60006134db6134cc8461406d565b6134d58461406d565b906140bd565b905060006134f16134ea614168565b8390612c1e565b600186015490915061350d9082906001600160801b0316613b5d565b604080518082019091526002815261353160f01b60208201529091506001600160801b038211156135515760405162461bcd60e51b815260040161055b919061511c565b5060019490940180546001600160801b0319166001600160801b0390951694909417909355505050565b600080600080600061358b6145fb565b6135948a614178565b156135b2576000806000806000199550955095509550955050613a2e565b600060e08201525b878160e00151101561398d5760e08101516135d6908b9061417d565b6135df5761397d565b60e0810151600090815260208a81526040808320546001600160a01b03166101e085018190528352908d90529020613616816141ce565b506080860181905260c08601929092525060a0840191909152600a0a60208301526101e082015160405163b3596f0760e01b81526001600160a01b038a169163b3596f07916136689190600401614de3565b60206040518083038186803b15801561368057600080fd5b505afa158015613694573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136b89190614bd4565b825260c0820151158015906136d8575060e08201516136d8908c906141f9565b156137f6578060040160009054906101000a90046001600160a01b03166001600160a01b03166370a082318e6040518263ffffffff1660e01b81526004016137209190614de3565b60206040518083038186803b15801561373857600080fd5b505afa15801561374c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137709190614bd4565b604083018190526020830151835160009261378f929161162d91613442565b6101208401519091506137a29082612c1e565b61012084015260a08301516137c8906137bc908390613442565b61016085015190612c1e565b61016084015260c08301516137ee906137e2908390613442565b61018085015190612c1e565b610180840152505b60e0820151613806908c90614251565b1561397b578060050160009054906101000a90046001600160a01b03166001600160a01b03166370a082318e6040518263ffffffff1660e01b815260040161384e9190614de3565b60206040518083038186803b15801561386657600080fd5b505afa15801561387a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061389e9190614bd4565b8260600181815250506139488160060160009054906101000a90046001600160a01b03166001600160a01b03166370a082318f6040518263ffffffff1660e01b81526004016138ed9190614de3565b60206040518083038186803b15801561390557600080fd5b505afa158015613919573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061393d9190614bd4565b606084015190612c1e565b606083018190526020830151835161397492613968929161162d91613442565b61014084015190612c1e565b6101408301525b505b60e08101805160010190526135ba565b6000816101200151116139a15760006139b6565b6101208101516101608201516139b69161347c565b6101608201526101208101516139cd5760006139e2565b6101208101516101808201516139e29161347c565b61018082018190526101208201516101408301516139ff926142a2565b610100820181905261012082015161014083015161016084015161018090940151919850965091945090925090505b965096509650965096915050565b600080613a4985846142c6565b905083811015613a5d576000915050610f68565b613a678185612c43565b95945050505050565b600290565b303b1590565b600381015460009064ffffffffff600160801b9091048116904216811415613ab257505060018101546001600160801b03166106ec565b600183015460028401546000916126cc916001600160801b03918216916126c6911685614335565b604080516020810190915284548152600090613afc908890889087878761357b565b945050505050670de0b6b3a7640000811015604051806040016040528060018152602001601b60f91b81525090613b465760405162461bcd60e51b815260040161055b919061511c565b5050505050505050565b6000610f68838342614373565b6000821580613b6a575081155b15613b7757506000610712565b816b019d971e4fe8401e740000001981613b8d57fe5b0483111560405180604001604052806002815260200161068760f31b81525090613bca5760405162461bcd60e51b815260040161055b919061511c565b506b033b2e3c9fd0803ce80000006002815b048385020181613be857fe5b049392505050565b600285015460009081906001600160801b031685858215613d1e576000613c178488614335565b9050613c23818a613b5d565b604080518082019091526002815261353160f01b60208201529093506001600160801b03841115613c675760405162461bcd60e51b815260040161055b919061511c565b5060018b0180546001600160801b0319166001600160801b0385161790558915613d1c5760028b0154600090613cad90600160801b90046001600160801b031689613b50565b9050613cb9818a613b5d565b6040805180820190915260028152611a9960f11b60208201529093506001600160801b03841115613cfd5760405162461bcd60e51b815260040161055b919061511c565b505060018b0180546001600160801b03808516600160801b0291161790555b505b600399909901805464ffffffffff60801b1916600160801b4264ffffffffff1602179055989650505050505050565b613d55614695565b613d5e87613f10565b6101208201819052613d70575061143b565b8660050160009054906101000a90046001600160a01b03166001600160a01b031663797743386040518163ffffffff1660e01b815260040160806040518083038186803b158015613dc057600080fd5b505afa158015613dd4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613df89190614ce3565b64ffffffffff1661014085015260a084015282526020820152613e1b8686613b5d565b6080820152613e2a8684613b5d565b606082015260a0810151610140820151613e4c919064ffffffffff8516614373565b60c082018190526020820151613e6191613b5d565b60408201819052608082015182516060840151613e8693926109619290918391612c1e565b60e08201819052610120820151613e9d91906142c6565b61010082018190521561230957600480880154610100830151604051637df5bd3b60e01b81526001600160a01b0390921692637df5bd3b92613ee2929189910161546f565b600060405180830381600087803b158015613efc57600080fd5b505af11580156105c3573d6000803e3d6000fd5b5460401c61ffff1690565b60008184841115613f3f5760405162461bcd60e51b815260040161055b919061511c565b505050900390565b613f59826001600160a01b0316612dc9565b613f755760405162461bcd60e51b815260040161055b90615294565b60006060836001600160a01b031683604051613f919190614dc7565b6000604051808303816000865af19150503d8060008114613fce576040519150601f19603f3d011682016040523d82523d6000602084013e613fd3565b606091505b509150915081613ff55760405162461bcd60e51b815260040161055b90615186565b805115612d4d57808060200190518101906140109190614bb8565b612d4d5760405162461bcd60e51b815260040161055b9061524a565b5460301c60ff1690565b600081836140575760405162461bcd60e51b815260040161055b919061511c565b50600083858161406357fe5b0495945050505050565b6000633b9aca0082810290839082041460405180604001604052806002815260200161068760f31b815250906140b65760405162461bcd60e51b815260040161055b919061511c565b5092915050565b604080518082019091526002815261035360f41b6020820152600090826140f75760405162461bcd60e51b815260040161055b919061511c565b5060408051808201909152600280825261068760f31b60208301528304906b033b2e3c9fd0803ce80000008219048511156141455760405162461bcd60e51b815260040161055b919061511c565b5082816b033b2e3c9fd0803ce80000008602018161415f57fe5b04949350505050565b6b033b2e3c9fd0803ce800000090565b511590565b60006080821060405180604001604052806002815260200161373760f01b815250906141bc5760405162461bcd60e51b815260040161055b919061511c565b50509051600360029092021c16151590565b5461ffff80821692601083901c821692602081901c831692603082901c60ff169260409290921c1690565b60006080821060405180604001604052806002815260200161373760f01b815250906142385760405162461bcd60e51b815260040161055b919061511c565b5050815160016002830281019190911c16151592915050565b60006080821060405180604001604052806002815260200161373760f01b815250906142905760405162461bcd60e51b815260040161055b919061511c565b50509051600160029092021c16151590565b6000826142b25750600019610f68565b6126cc836142c086856142c6565b90614449565b60008215806142d3575081155b156142e057506000610712565b8161138819816142ec57fe5b0483111560405180604001604052806002815260200161068760f31b815250906143295760405162461bcd60e51b815260040161055b919061511c565b50612710600281613bdc565b6000806143494264ffffffffff8516612c43565b90506126cc614356614168565b6301e133806143658785613442565b8161436c57fe5b0490612c1e565b6000806143878364ffffffffff8616612c43565b90508061439e57614396614168565b915050610f68565b60001981016000600283116143b45760006143b9565b600283035b90506301e13380870460006143ce8280613b5d565b905060006143dc8284613b5d565b9050600060026143f0846130458a8a613442565b816143f757fe5b0490506000600661440e8461304589818d8d613442565b8161441557fe5b04905061443981614433848161442b8a8e613442565b614433614168565b90612c1e565b9c9b505050505050505050505050565b604080518082019091526002815261035360f41b6020820152600090826144835760405162461bcd60e51b815260040161055b919061511c565b5060408051808201909152600280825261068760f31b6020830152830490670de0b6b3a76400008219048511156144cd5760405162461bcd60e51b815260040161055b919061511c565b508281670de0b6b3a76400008602018161415f57fe5b6040518061018001604052806144f761454e565b815260006020820181905260408201819052606082018190526080820181905260a0820181905260c0820181905260e082018190526101008201819052610120820181905261014082018190526101609091015290565b6040518060200160405280600081525090565b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915290565b60405180610100016040528060006001600160a01b03168152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b604051806102400160405280600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160001515815260200160006001600160a01b031681526020016000151581526020016000151581525090565b60405180610160016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600064ffffffffff1681525090565b803561071281615568565b60008083601f840112614712578182fd5b50813567ffffffffffffffff811115614729578182fd5b60208301915083602080830285010111156127e257600080fd5b60008083601f840112614754578182fd5b50813567ffffffffffffffff81111561476b578182fd5b6020830191508360208285010111156127e257600080fd5b803561ffff8116811461071257600080fd5b6000602082840312156147a6578081fd5b8135610f6881615568565b6000602082840312156147c2578081fd5b8151610f6881615568565b600080604083850312156147df578081fd5b82356147ea81615568565b915060208301356147fa81615568565b809150509250929050565b600080600080600060a0868803121561481c578081fd5b853561482781615568565b9450602086013561483781615568565b9350604086013561484781615568565b9250606086013561485781615568565b9150608086013561486781615568565b809150509295509295909350565b600080600080600060a0868803121561488c578081fd5b853561489781615568565b945060208601356148a781615568565b935060408601356148b781615568565b92506060860135915060808601356148678161557d565b60008060008060008060c087890312156148e6578081fd5b86356148f181615568565b9550602087013561490181615568565b9450604087013561491181615568565b959894975094956060810135955060808101359460a0909101359350915050565b600080600080600080600080600080600060e08c8e031215614952578485fd5b61495c8d8d6146f6565b9a5067ffffffffffffffff8060208e01351115614977578586fd5b6149878e60208f01358f01614701565b909b50995060408d013581101561499c578586fd5b6149ac8e60408f01358f01614701565b909950975060608d01358110156149c1578586fd5b6149d18e60608f01358f01614701565b90975095506149e38e60808f016146f6565b94508060a08e013511156149f5578384fd5b50614a068d60a08e01358e01614743565b9093509150614a188d60c08e01614783565b90509295989b509295989b9093969950565b60008060408385031215614a3c578081fd5b8235614a4781615568565b915060208301356147fa8161557d565b60008060408385031215614a69578182fd5b8235614a7481615568565b946020939093013593505050565b600080600060608486031215614a96578081fd5b8335614aa181615568565b9250602084013591506040840135614ab881615568565b809150509250925092565b60008060008060808587031215614ad8578182fd5b8435614ae381615568565b9350602085013592506040850135614afa81615568565b9150614b098660608701614783565b905092959194509250565b60008060008060808587031215614b29578182fd5b8435614b3481615568565b935060208501359250604085013591506060850135614b5281615568565b939692955090935050565b600080600080600060a08688031215614b74578283fd5b8535614b7f81615568565b945060208601359350604086013592506148578760608801614783565b600060208284031215614bad578081fd5b8135610f688161557d565b600060208284031215614bc9578081fd5b8151610f688161557d565b600060208284031215614be5578081fd5b5051919050565b60008060408385031215614bfe578182fd5b82519150602083015167ffffffffffffffff80821115614c1c578283fd5b818501915085601f830112614c2f578283fd5b815181811115614c3d578384fd5b604051601f8201601f191681016020018381118282101715614c5d578586fd5b604052818152838201602001881015614c74578485fd5b614c8582602083016020870161553c565b809450505050509250929050565b60008060408385031215614ca5578182fd5b505080516020909101519092909150565b600080600060608486031215614cca578081fd5b8351925060208401519150604084015190509250925092565b60008060008060808587031215614cf8578182fd5b845193506020850151925060408501519150606085015164ffffffffff81168114614b52578182fd5b6001600160a01b0316815260200190565b6001600160a01b03169052565b6000815180845260208085019450808401835b83811015614d6e57815187529582019590820190600101614d52565b509495945050505050565b60008284528282602086013780602084860101526020601f19601f85011685010190509392505050565b519052565b6001600160801b03169052565b64ffffffffff169052565b60ff169052565b60008251614dd981846020870161553c565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b03948516815292909316602083015260408201526001600160801b03909116606082015260800190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b0393909316835260208301919091526001600160801b0316604082015260600190565b6001600160a01b03958616815293851660208501529190931660408301526060820192909252901515608082015260a00190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b6001600160a01b03988916815296909716602087015260408601949094526060850192909252608084015260a083015260c082015260e08101919091526101000190565b6001600160a01b039c8d168152602081019b909b52988b1660408b015260608a0197909752608089019590955260a088019390935260c087019190915260e08601526101008501526101208401526101408301529091166101608201526101800190565b6001600160a01b039889168152602081019790975260408701959095526060860193909352608085019190915260a084015260c083015290911660e08201526101000190565b6001600160a01b0394909416845260208401929092526040830152606082015260800190565b600060a0820160a08352806150158b836154c2565b90508b9150825b8b811015615048576020830161503b8361503683876146f6565b614d21565b909350915060010161501c565b5083810360208501528881526001600160fb1b03891115615067578283fd5b602089029150818a602083013701602081810183815284830390910160408501526150928189614d3f565b9150506150a26060840187614d32565b82810360808401526150b5818587614d79565b9b9a5050505050505050505050565b6020808252825182820181905260009190848201906040850190845b818110156151055783516001600160a01b0316835292840192918401916001016150e0565b50909695505050505050565b901515815260200190565b600060208252825180602084015261513b81604085016020870161553c565b601f01601f19169190910160400192915050565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b6020808252818101527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604082015260600190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b6020808252602e908201527f436f6e747261637420696e7374616e63652068617320616c726561647920626560408201526d195b881a5b9a5d1a585b1a5e995960921b606082015260800190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6040820152691bdd081cdd58d8d9595960b21b606082015260800190565b6020808252601f908201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604082015260600190565b9051815260200190565b6000610180820190506152e9828451614da3565b60208301516152fb6020840182614da8565b50604083015161530e6040840182614da8565b5060608301516153216060840182614da8565b5060808301516153346080840182614da8565b5060a083015161534760a0840182614da8565b5060c083015161535a60c0840182614db5565b5060e083015161536d60e0840182614d32565b506101008084015161538182850182614d32565b50506101208084015161539682850182614d32565b5050610140808401516153ab82850182614d32565b5050610160808401516153c082850182614dc0565b505092915050565b9485526001600160a01b03938416602086015291831660408501528216606084015216608082015260a00190565b9788526001600160a01b03968716602089015294151560408801526060870193909352608086019190915260a085015260c08401521660e08201526101000190565b600060a0820190508682528560208301528460408301528360608301526003831061545f57fe5b8260808301529695505050505050565b918252602082015260400190565b8681526020810186905260c081016003861061549557fe5b60408201959095526001600160a01b03939093166060840152608083019190915260a09091015292915050565b90815260200190565b928352602083019190915261ffff16604082015260600190565b948552602085019390935260408401919091526001600160801b03908116606084015216608082015260a00190565b958652602086019490945260408501929092526060840152608083015260a082015260c00190565b60005b8381101561555757818101518382015260200161553f565b83811115612d4d5750506000910152565b6001600160a01b0381168114611bb357600080fd5b8015158114611bb357600080fdfea26469706673582212201165479451744cb0000cd91e47c068e4c30543f3b0316a8d676cdeb7e02ace9564736f6c634300060c0033", + "abi": [ + { + "type": "event", + "name": "Borrow", + "inputs": [ + { + "type": "address", + "name": "reserve", + "internalType": "address", + "indexed": true + }, + { + "type": "address", + "name": "user", + "internalType": "address", + "indexed": false + }, + { + "type": "address", + "name": "onBehalfOf", + "internalType": "address", + "indexed": true + }, + { + "type": "uint256", + "name": "amount", + "internalType": "uint256", + "indexed": false + }, + { + "type": "uint256", + "name": "borrowRateMode", + "internalType": "uint256", + "indexed": false + }, + { + "type": "uint256", + "name": "borrowRate", + "internalType": "uint256", + "indexed": false + }, + { + "type": "uint16", + "name": "referral", + "internalType": "uint16", + "indexed": true + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Deposit", + "inputs": [ + { + "type": "address", + "name": "reserve", + "internalType": "address", + "indexed": true + }, + { + "type": "address", + "name": "user", + "internalType": "address", + "indexed": false + }, + { + "type": "address", + "name": "onBehalfOf", + "internalType": "address", + "indexed": true + }, + { + "type": "uint256", + "name": "amount", + "internalType": "uint256", + "indexed": false + }, + { + "type": "uint16", + "name": "referral", + "internalType": "uint16", + "indexed": true + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "FlashLoan", + "inputs": [ + { + "type": "address", + "name": "target", + "internalType": "address", + "indexed": true + }, + { + "type": "address", + "name": "initiator", + "internalType": "address", + "indexed": true + }, + { + "type": "address", + "name": "asset", + "internalType": "address", + "indexed": true + }, + { + "type": "uint256", + "name": "amount", + "internalType": "uint256", + "indexed": false + }, + { + "type": "uint256", + "name": "premium", + "internalType": "uint256", + "indexed": false + }, + { + "type": "uint16", + "name": "referralCode", + "internalType": "uint16", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "LiquidationCall", + "inputs": [ + { + "type": "address", + "name": "collateralAsset", + "internalType": "address", + "indexed": true + }, + { + "type": "address", + "name": "debtAsset", + "internalType": "address", + "indexed": true + }, + { + "type": "address", + "name": "user", + "internalType": "address", + "indexed": true + }, + { + "type": "uint256", + "name": "debtToCover", + "internalType": "uint256", + "indexed": false + }, + { + "type": "uint256", + "name": "liquidatedCollateralAmount", + "internalType": "uint256", + "indexed": false + }, + { + "type": "address", + "name": "liquidator", + "internalType": "address", + "indexed": false + }, + { + "type": "bool", + "name": "receiveAToken", + "internalType": "bool", + "indexed": false + } + ], + "anonymous": false + }, + { "type": "event", "name": "Paused", "inputs": [], "anonymous": false }, + { + "type": "event", + "name": "RebalanceStableBorrowRate", + "inputs": [ + { + "type": "address", + "name": "reserve", + "internalType": "address", + "indexed": true + }, + { + "type": "address", + "name": "user", + "internalType": "address", + "indexed": true + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Repay", + "inputs": [ + { + "type": "address", + "name": "reserve", + "internalType": "address", + "indexed": true + }, + { + "type": "address", + "name": "user", + "internalType": "address", + "indexed": true + }, + { + "type": "address", + "name": "repayer", + "internalType": "address", + "indexed": true + }, + { + "type": "uint256", + "name": "amount", + "internalType": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ReserveDataUpdated", + "inputs": [ + { + "type": "address", + "name": "reserve", + "internalType": "address", + "indexed": true + }, + { + "type": "uint256", + "name": "liquidityRate", + "internalType": "uint256", + "indexed": false + }, + { + "type": "uint256", + "name": "stableBorrowRate", + "internalType": "uint256", + "indexed": false + }, + { + "type": "uint256", + "name": "variableBorrowRate", + "internalType": "uint256", + "indexed": false + }, + { + "type": "uint256", + "name": "liquidityIndex", + "internalType": "uint256", + "indexed": false + }, + { + "type": "uint256", + "name": "variableBorrowIndex", + "internalType": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ReserveUsedAsCollateralDisabled", + "inputs": [ + { + "type": "address", + "name": "reserve", + "internalType": "address", + "indexed": true + }, + { + "type": "address", + "name": "user", + "internalType": "address", + "indexed": true + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ReserveUsedAsCollateralEnabled", + "inputs": [ + { + "type": "address", + "name": "reserve", + "internalType": "address", + "indexed": true + }, + { + "type": "address", + "name": "user", + "internalType": "address", + "indexed": true + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Swap", + "inputs": [ + { + "type": "address", + "name": "reserve", + "internalType": "address", + "indexed": true + }, + { + "type": "address", + "name": "user", + "internalType": "address", + "indexed": true + }, + { + "type": "uint256", + "name": "rateMode", + "internalType": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { "type": "event", "name": "Unpaused", "inputs": [], "anonymous": false }, + { + "type": "event", + "name": "Withdraw", + "inputs": [ + { + "type": "address", + "name": "reserve", + "internalType": "address", + "indexed": true + }, + { + "type": "address", + "name": "user", + "internalType": "address", + "indexed": true + }, + { + "type": "address", + "name": "to", + "internalType": "address", + "indexed": true + }, + { + "type": "uint256", + "name": "amount", + "internalType": "uint256", + "indexed": false + } + ], + "anonymous": false + }, + { + "type": "function", + "stateMutability": "view", + "outputs": [{ "type": "uint256", "name": "", "internalType": "uint256" }], + "name": "FLASHLOAN_PREMIUM_TOTAL", + "inputs": [] + }, + { + "type": "function", + "stateMutability": "view", + "outputs": [{ "type": "uint256", "name": "", "internalType": "uint256" }], + "name": "LENDINGPOOL_REVISION", + "inputs": [] + }, + { + "type": "function", + "stateMutability": "view", + "outputs": [{ "type": "uint256", "name": "", "internalType": "uint256" }], + "name": "MAX_NUMBER_RESERVES", + "inputs": [] + }, + { + "type": "function", + "stateMutability": "view", + "outputs": [{ "type": "uint256", "name": "", "internalType": "uint256" }], + "name": "MAX_STABLE_RATE_BORROW_SIZE_PERCENT", + "inputs": [] + }, + { + "type": "function", + "stateMutability": "nonpayable", + "outputs": [], + "name": "borrow", + "inputs": [ + { "type": "address", "name": "asset", "internalType": "address" }, + { "type": "uint256", "name": "amount", "internalType": "uint256" }, + { + "type": "uint256", + "name": "interestRateMode", + "internalType": "uint256" + }, + { "type": "uint16", "name": "referralCode", "internalType": "uint16" }, + { "type": "address", "name": "onBehalfOf", "internalType": "address" } + ] + }, + { + "type": "function", + "stateMutability": "nonpayable", + "outputs": [], + "name": "deposit", + "inputs": [ + { "type": "address", "name": "asset", "internalType": "address" }, + { "type": "uint256", "name": "amount", "internalType": "uint256" }, + { "type": "address", "name": "onBehalfOf", "internalType": "address" }, + { "type": "uint16", "name": "referralCode", "internalType": "uint16" } + ] + }, + { + "type": "function", + "stateMutability": "nonpayable", + "outputs": [], + "name": "finalizeTransfer", + "inputs": [ + { "type": "address", "name": "asset", "internalType": "address" }, + { "type": "address", "name": "from", "internalType": "address" }, + { "type": "address", "name": "to", "internalType": "address" }, + { "type": "uint256", "name": "amount", "internalType": "uint256" }, + { + "type": "uint256", + "name": "balanceFromBefore", + "internalType": "uint256" + }, + { + "type": "uint256", + "name": "balanceToBefore", + "internalType": "uint256" + } + ] + }, + { + "type": "function", + "stateMutability": "nonpayable", + "outputs": [], + "name": "flashLoan", + "inputs": [ + { + "type": "address", + "name": "receiverAddress", + "internalType": "address" + }, + { "type": "address[]", "name": "assets", "internalType": "address[]" }, + { "type": "uint256[]", "name": "amounts", "internalType": "uint256[]" }, + { "type": "uint256[]", "name": "modes", "internalType": "uint256[]" }, + { "type": "address", "name": "onBehalfOf", "internalType": "address" }, + { "type": "bytes", "name": "params", "internalType": "bytes" }, + { "type": "uint16", "name": "referralCode", "internalType": "uint16" } + ] + }, + { + "type": "function", + "stateMutability": "view", + "outputs": [ + { + "type": "address", + "name": "", + "internalType": "contract ILendingPoolAddressesProvider" + } + ], + "name": "getAddressesProvider", + "inputs": [] + }, + { + "type": "function", + "stateMutability": "view", + "outputs": [ + { + "type": "tuple", + "name": "", + "internalType": "struct DataTypes.ReserveConfigurationMap", + "components": [ + { "type": "uint256", "name": "data", "internalType": "uint256" } + ] + } + ], + "name": "getConfiguration", + "inputs": [ + { "type": "address", "name": "asset", "internalType": "address" } + ] + }, + { + "type": "function", + "stateMutability": "view", + "outputs": [ + { + "type": "tuple", + "name": "", + "internalType": "struct DataTypes.ReserveData", + "components": [ + { + "type": "tuple", + "name": "configuration", + "internalType": "struct DataTypes.ReserveConfigurationMap", + "components": [ + { "type": "uint256", "name": "data", "internalType": "uint256" } + ] + }, + { + "type": "uint128", + "name": "liquidityIndex", + "internalType": "uint128" + }, + { + "type": "uint128", + "name": "variableBorrowIndex", + "internalType": "uint128" + }, + { + "type": "uint128", + "name": "currentLiquidityRate", + "internalType": "uint128" + }, + { + "type": "uint128", + "name": "currentVariableBorrowRate", + "internalType": "uint128" + }, + { + "type": "uint128", + "name": "currentStableBorrowRate", + "internalType": "uint128" + }, + { + "type": "uint40", + "name": "lastUpdateTimestamp", + "internalType": "uint40" + }, + { + "type": "address", + "name": "aTokenAddress", + "internalType": "address" + }, + { + "type": "address", + "name": "stableDebtTokenAddress", + "internalType": "address" + }, + { + "type": "address", + "name": "variableDebtTokenAddress", + "internalType": "address" + }, + { + "type": "address", + "name": "interestRateStrategyAddress", + "internalType": "address" + }, + { "type": "uint8", "name": "id", "internalType": "uint8" } + ] + } + ], + "name": "getReserveData", + "inputs": [ + { "type": "address", "name": "asset", "internalType": "address" } + ] + }, + { + "type": "function", + "stateMutability": "view", + "outputs": [{ "type": "uint256", "name": "", "internalType": "uint256" }], + "name": "getReserveNormalizedIncome", + "inputs": [ + { "type": "address", "name": "asset", "internalType": "address" } + ] + }, + { + "type": "function", + "stateMutability": "view", + "outputs": [{ "type": "uint256", "name": "", "internalType": "uint256" }], + "name": "getReserveNormalizedVariableDebt", + "inputs": [ + { "type": "address", "name": "asset", "internalType": "address" } + ] + }, + { + "type": "function", + "stateMutability": "view", + "outputs": [ + { "type": "address[]", "name": "", "internalType": "address[]" } + ], + "name": "getReservesList", + "inputs": [] + }, + { + "type": "function", + "stateMutability": "view", + "outputs": [ + { + "type": "uint256", + "name": "totalCollateralETH", + "internalType": "uint256" + }, + { + "type": "uint256", + "name": "totalDebtETH", + "internalType": "uint256" + }, + { + "type": "uint256", + "name": "availableBorrowsETH", + "internalType": "uint256" + }, + { + "type": "uint256", + "name": "currentLiquidationThreshold", + "internalType": "uint256" + }, + { "type": "uint256", "name": "ltv", "internalType": "uint256" }, + { "type": "uint256", "name": "healthFactor", "internalType": "uint256" } + ], + "name": "getUserAccountData", + "inputs": [ + { "type": "address", "name": "user", "internalType": "address" } + ] + }, + { + "type": "function", + "stateMutability": "view", + "outputs": [ + { + "type": "tuple", + "name": "", + "internalType": "struct DataTypes.UserConfigurationMap", + "components": [ + { "type": "uint256", "name": "data", "internalType": "uint256" } + ] + } + ], + "name": "getUserConfiguration", + "inputs": [ + { "type": "address", "name": "user", "internalType": "address" } + ] + }, + { + "type": "function", + "stateMutability": "nonpayable", + "outputs": [], + "name": "initReserve", + "inputs": [ + { "type": "address", "name": "asset", "internalType": "address" }, + { + "type": "address", + "name": "aTokenAddress", + "internalType": "address" + }, + { + "type": "address", + "name": "stableDebtAddress", + "internalType": "address" + }, + { + "type": "address", + "name": "variableDebtAddress", + "internalType": "address" + }, + { + "type": "address", + "name": "interestRateStrategyAddress", + "internalType": "address" + } + ] + }, + { + "type": "function", + "stateMutability": "nonpayable", + "outputs": [], + "name": "initialize", + "inputs": [ + { + "type": "address", + "name": "provider", + "internalType": "contract ILendingPoolAddressesProvider" + } + ] + }, + { + "type": "function", + "stateMutability": "nonpayable", + "outputs": [], + "name": "liquidationCall", + "inputs": [ + { + "type": "address", + "name": "collateralAsset", + "internalType": "address" + }, + { "type": "address", "name": "debtAsset", "internalType": "address" }, + { "type": "address", "name": "user", "internalType": "address" }, + { "type": "uint256", "name": "debtToCover", "internalType": "uint256" }, + { "type": "bool", "name": "receiveAToken", "internalType": "bool" } + ] + }, + { + "type": "function", + "stateMutability": "view", + "outputs": [{ "type": "bool", "name": "", "internalType": "bool" }], + "name": "paused", + "inputs": [] + }, + { + "type": "function", + "stateMutability": "nonpayable", + "outputs": [], + "name": "rebalanceStableBorrowRate", + "inputs": [ + { "type": "address", "name": "asset", "internalType": "address" }, + { "type": "address", "name": "user", "internalType": "address" } + ] + }, + { + "type": "function", + "stateMutability": "nonpayable", + "outputs": [{ "type": "uint256", "name": "", "internalType": "uint256" }], + "name": "repay", + "inputs": [ + { "type": "address", "name": "asset", "internalType": "address" }, + { "type": "uint256", "name": "amount", "internalType": "uint256" }, + { "type": "uint256", "name": "rateMode", "internalType": "uint256" }, + { "type": "address", "name": "onBehalfOf", "internalType": "address" } + ] + }, + { + "type": "function", + "stateMutability": "nonpayable", + "outputs": [], + "name": "setConfiguration", + "inputs": [ + { "type": "address", "name": "asset", "internalType": "address" }, + { + "type": "uint256", + "name": "configuration", + "internalType": "uint256" + } + ] + }, + { + "type": "function", + "stateMutability": "nonpayable", + "outputs": [], + "name": "setPause", + "inputs": [{ "type": "bool", "name": "val", "internalType": "bool" }] + }, + { + "type": "function", + "stateMutability": "nonpayable", + "outputs": [], + "name": "setReserveInterestRateStrategyAddress", + "inputs": [ + { "type": "address", "name": "asset", "internalType": "address" }, + { + "type": "address", + "name": "rateStrategyAddress", + "internalType": "address" + } + ] + }, + { + "type": "function", + "stateMutability": "nonpayable", + "outputs": [], + "name": "setUserUseReserveAsCollateral", + "inputs": [ + { "type": "address", "name": "asset", "internalType": "address" }, + { "type": "bool", "name": "useAsCollateral", "internalType": "bool" } + ] + }, + { + "type": "function", + "stateMutability": "nonpayable", + "outputs": [], + "name": "swapBorrowRateMode", + "inputs": [ + { "type": "address", "name": "asset", "internalType": "address" }, + { "type": "uint256", "name": "rateMode", "internalType": "uint256" } + ] + }, + { + "type": "function", + "stateMutability": "nonpayable", + "outputs": [{ "type": "uint256", "name": "", "internalType": "uint256" }], + "name": "withdraw", + "inputs": [ + { "type": "address", "name": "asset", "internalType": "address" }, + { "type": "uint256", "name": "amount", "internalType": "uint256" }, + { "type": "address", "name": "to", "internalType": "address" } + ] + } + ] +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/@openzeppelin/contracts/token/ERC20/IERC20.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/@openzeppelin/contracts/token/ERC20/IERC20.sol new file mode 100644 index 000000000..a1f9ca7f6 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/@openzeppelin/contracts/token/ERC20/IERC20.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.0; + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `recipient`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address recipient, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `sender` to `recipient` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); + + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/adapters/BaseUniswapAdapter.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/adapters/BaseUniswapAdapter.sol new file mode 100644 index 000000000..d83791ad3 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/adapters/BaseUniswapAdapter.sol @@ -0,0 +1,566 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {PercentageMath} from '../protocol/libraries/math/PercentageMath.sol'; +import {SafeMath} from '../dependencies/openzeppelin/contracts/SafeMath.sol'; +import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; +import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol'; +import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol'; +import {Ownable} from '../dependencies/openzeppelin/contracts/Ownable.sol'; +import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; +import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; +import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; +import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol'; +import {IERC20WithPermit} from '../interfaces/IERC20WithPermit.sol'; +import {FlashLoanReceiverBase} from '../flashloan/base/FlashLoanReceiverBase.sol'; +import {IBaseUniswapAdapter} from './interfaces/IBaseUniswapAdapter.sol'; + +/** + * @title BaseUniswapAdapter + * @notice Implements the logic for performing assets swaps in Uniswap V2 + * @author Aave + **/ +abstract contract BaseUniswapAdapter is FlashLoanReceiverBase, IBaseUniswapAdapter, Ownable { + using SafeMath for uint256; + using PercentageMath for uint256; + using SafeERC20 for IERC20; + + // Max slippage percent allowed + uint256 public constant override MAX_SLIPPAGE_PERCENT = 3000; // 30% + // FLash Loan fee set in lending pool + uint256 public constant override FLASHLOAN_PREMIUM_TOTAL = 9; + // USD oracle asset address + address public constant override USD_ADDRESS = 0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96; + + address public immutable override WETH_ADDRESS; + IPriceOracleGetter public immutable override ORACLE; + IUniswapV2Router02 public immutable override UNISWAP_ROUTER; + + constructor( + ILendingPoolAddressesProvider addressesProvider, + IUniswapV2Router02 uniswapRouter, + address wethAddress + ) public FlashLoanReceiverBase(addressesProvider) { + ORACLE = IPriceOracleGetter(addressesProvider.getPriceOracle()); + UNISWAP_ROUTER = uniswapRouter; + WETH_ADDRESS = wethAddress; + } + + /** + * @dev Given an input asset amount, returns the maximum output amount of the other asset and the prices + * @param amountIn Amount of reserveIn + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @return uint256 Amount out of the reserveOut + * @return uint256 The price of out amount denominated in the reserveIn currency (18 decimals) + * @return uint256 In amount of reserveIn value denominated in USD (8 decimals) + * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals) + */ + function getAmountsOut( + uint256 amountIn, + address reserveIn, + address reserveOut + ) + external + view + override + returns ( + uint256, + uint256, + uint256, + uint256, + address[] memory + ) + { + AmountCalc memory results = _getAmountsOutData(reserveIn, reserveOut, amountIn); + + return ( + results.calculatedAmount, + results.relativePrice, + results.amountInUsd, + results.amountOutUsd, + results.path + ); + } + + /** + * @dev Returns the minimum input asset amount required to buy the given output asset amount and the prices + * @param amountOut Amount of reserveOut + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @return uint256 Amount in of the reserveIn + * @return uint256 The price of in amount denominated in the reserveOut currency (18 decimals) + * @return uint256 In amount of reserveIn value denominated in USD (8 decimals) + * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals) + */ + function getAmountsIn( + uint256 amountOut, + address reserveIn, + address reserveOut + ) + external + view + override + returns ( + uint256, + uint256, + uint256, + uint256, + address[] memory + ) + { + AmountCalc memory results = _getAmountsInData(reserveIn, reserveOut, amountOut); + + return ( + results.calculatedAmount, + results.relativePrice, + results.amountInUsd, + results.amountOutUsd, + results.path + ); + } + + /** + * @dev Swaps an exact `amountToSwap` of an asset to another + * @param assetToSwapFrom Origin asset + * @param assetToSwapTo Destination asset + * @param amountToSwap Exact amount of `assetToSwapFrom` to be swapped + * @param minAmountOut the min amount of `assetToSwapTo` to be received from the swap + * @return the amount received from the swap + */ + function _swapExactTokensForTokens( + address assetToSwapFrom, + address assetToSwapTo, + uint256 amountToSwap, + uint256 minAmountOut, + bool useEthPath + ) internal returns (uint256) { + uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom); + uint256 toAssetDecimals = _getDecimals(assetToSwapTo); + + uint256 fromAssetPrice = _getPrice(assetToSwapFrom); + uint256 toAssetPrice = _getPrice(assetToSwapTo); + + uint256 expectedMinAmountOut = + amountToSwap + .mul(fromAssetPrice.mul(10**toAssetDecimals)) + .div(toAssetPrice.mul(10**fromAssetDecimals)) + .percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(MAX_SLIPPAGE_PERCENT)); + + require(expectedMinAmountOut < minAmountOut, 'minAmountOut exceed max slippage'); + + // Approves the transfer for the swap. Approves for 0 first to comply with tokens that implement the anti frontrunning approval fix. + IERC20(assetToSwapFrom).safeApprove(address(UNISWAP_ROUTER), 0); + IERC20(assetToSwapFrom).safeApprove(address(UNISWAP_ROUTER), amountToSwap); + + address[] memory path; + if (useEthPath) { + path = new address[](3); + path[0] = assetToSwapFrom; + path[1] = WETH_ADDRESS; + path[2] = assetToSwapTo; + } else { + path = new address[](2); + path[0] = assetToSwapFrom; + path[1] = assetToSwapTo; + } + uint256[] memory amounts = + UNISWAP_ROUTER.swapExactTokensForTokens( + amountToSwap, + minAmountOut, + path, + address(this), + block.timestamp + ); + + emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[amounts.length - 1]); + + return amounts[amounts.length - 1]; + } + + /** + * @dev Receive an exact amount `amountToReceive` of `assetToSwapTo` tokens for as few `assetToSwapFrom` tokens as + * possible. + * @param assetToSwapFrom Origin asset + * @param assetToSwapTo Destination asset + * @param maxAmountToSwap Max amount of `assetToSwapFrom` allowed to be swapped + * @param amountToReceive Exact amount of `assetToSwapTo` to receive + * @return the amount swapped + */ + function _swapTokensForExactTokens( + address assetToSwapFrom, + address assetToSwapTo, + uint256 maxAmountToSwap, + uint256 amountToReceive, + bool useEthPath + ) internal returns (uint256) { + uint256 fromAssetDecimals = _getDecimals(assetToSwapFrom); + uint256 toAssetDecimals = _getDecimals(assetToSwapTo); + + uint256 fromAssetPrice = _getPrice(assetToSwapFrom); + uint256 toAssetPrice = _getPrice(assetToSwapTo); + + uint256 expectedMaxAmountToSwap = + amountToReceive + .mul(toAssetPrice.mul(10**fromAssetDecimals)) + .div(fromAssetPrice.mul(10**toAssetDecimals)) + .percentMul(PercentageMath.PERCENTAGE_FACTOR.add(MAX_SLIPPAGE_PERCENT)); + + require(maxAmountToSwap < expectedMaxAmountToSwap, 'maxAmountToSwap exceed max slippage'); + + // Approves the transfer for the swap. Approves for 0 first to comply with tokens that implement the anti frontrunning approval fix. + IERC20(assetToSwapFrom).safeApprove(address(UNISWAP_ROUTER), 0); + IERC20(assetToSwapFrom).safeApprove(address(UNISWAP_ROUTER), maxAmountToSwap); + + address[] memory path; + if (useEthPath) { + path = new address[](3); + path[0] = assetToSwapFrom; + path[1] = WETH_ADDRESS; + path[2] = assetToSwapTo; + } else { + path = new address[](2); + path[0] = assetToSwapFrom; + path[1] = assetToSwapTo; + } + + uint256[] memory amounts = + UNISWAP_ROUTER.swapTokensForExactTokens( + amountToReceive, + maxAmountToSwap, + path, + address(this), + block.timestamp + ); + + emit Swapped(assetToSwapFrom, assetToSwapTo, amounts[0], amounts[amounts.length - 1]); + + return amounts[0]; + } + + /** + * @dev Get the price of the asset from the oracle denominated in eth + * @param asset address + * @return eth price for the asset + */ + function _getPrice(address asset) internal view returns (uint256) { + return ORACLE.getAssetPrice(asset); + } + + /** + * @dev Get the decimals of an asset + * @return number of decimals of the asset + */ + function _getDecimals(address asset) internal view returns (uint256) { + return IERC20Detailed(asset).decimals(); + } + + /** + * @dev Get the aToken associated to the asset + * @return address of the aToken + */ + function _getReserveData(address asset) internal view returns (DataTypes.ReserveData memory) { + return LENDING_POOL.getReserveData(asset); + } + + /** + * @dev Pull the ATokens from the user + * @param reserve address of the asset + * @param reserveAToken address of the aToken of the reserve + * @param user address + * @param amount of tokens to be transferred to the contract + * @param permitSignature struct containing the permit signature + */ + function _pullAToken( + address reserve, + address reserveAToken, + address user, + uint256 amount, + PermitSignature memory permitSignature + ) internal { + if (_usePermit(permitSignature)) { + IERC20WithPermit(reserveAToken).permit( + user, + address(this), + permitSignature.amount, + permitSignature.deadline, + permitSignature.v, + permitSignature.r, + permitSignature.s + ); + } + + // transfer from user to adapter + IERC20(reserveAToken).safeTransferFrom(user, address(this), amount); + + // withdraw reserve + LENDING_POOL.withdraw(reserve, amount, address(this)); + } + + /** + * @dev Tells if the permit method should be called by inspecting if there is a valid signature. + * If signature params are set to 0, then permit won't be called. + * @param signature struct containing the permit signature + * @return whether or not permit should be called + */ + function _usePermit(PermitSignature memory signature) internal pure returns (bool) { + return + !(uint256(signature.deadline) == uint256(signature.v) && uint256(signature.deadline) == 0); + } + + /** + * @dev Calculates the value denominated in USD + * @param reserve Address of the reserve + * @param amount Amount of the reserve + * @param decimals Decimals of the reserve + * @return whether or not permit should be called + */ + function _calcUsdValue( + address reserve, + uint256 amount, + uint256 decimals + ) internal view returns (uint256) { + uint256 ethUsdPrice = _getPrice(USD_ADDRESS); + uint256 reservePrice = _getPrice(reserve); + + return amount.mul(reservePrice).div(10**decimals).mul(ethUsdPrice).div(10**18); + } + + /** + * @dev Given an input asset amount, returns the maximum output amount of the other asset + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @param amountIn Amount of reserveIn + * @return Struct containing the following information: + * uint256 Amount out of the reserveOut + * uint256 The price of out amount denominated in the reserveIn currency (18 decimals) + * uint256 In amount of reserveIn value denominated in USD (8 decimals) + * uint256 Out amount of reserveOut value denominated in USD (8 decimals) + */ + function _getAmountsOutData( + address reserveIn, + address reserveOut, + uint256 amountIn + ) internal view returns (AmountCalc memory) { + // Subtract flash loan fee + uint256 finalAmountIn = amountIn.sub(amountIn.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); + + if (reserveIn == reserveOut) { + uint256 reserveDecimals = _getDecimals(reserveIn); + address[] memory path = new address[](1); + path[0] = reserveIn; + + return + AmountCalc( + finalAmountIn, + finalAmountIn.mul(10**18).div(amountIn), + _calcUsdValue(reserveIn, amountIn, reserveDecimals), + _calcUsdValue(reserveIn, finalAmountIn, reserveDecimals), + path + ); + } + + address[] memory simplePath = new address[](2); + simplePath[0] = reserveIn; + simplePath[1] = reserveOut; + + uint256[] memory amountsWithoutWeth; + uint256[] memory amountsWithWeth; + + address[] memory pathWithWeth = new address[](3); + if (reserveIn != WETH_ADDRESS && reserveOut != WETH_ADDRESS) { + pathWithWeth[0] = reserveIn; + pathWithWeth[1] = WETH_ADDRESS; + pathWithWeth[2] = reserveOut; + + try UNISWAP_ROUTER.getAmountsOut(finalAmountIn, pathWithWeth) returns ( + uint256[] memory resultsWithWeth + ) { + amountsWithWeth = resultsWithWeth; + } catch { + amountsWithWeth = new uint256[](3); + } + } else { + amountsWithWeth = new uint256[](3); + } + + uint256 bestAmountOut; + try UNISWAP_ROUTER.getAmountsOut(finalAmountIn, simplePath) returns ( + uint256[] memory resultAmounts + ) { + amountsWithoutWeth = resultAmounts; + + bestAmountOut = (amountsWithWeth[2] > amountsWithoutWeth[1]) + ? amountsWithWeth[2] + : amountsWithoutWeth[1]; + } catch { + amountsWithoutWeth = new uint256[](2); + bestAmountOut = amountsWithWeth[2]; + } + + uint256 reserveInDecimals = _getDecimals(reserveIn); + uint256 reserveOutDecimals = _getDecimals(reserveOut); + + uint256 outPerInPrice = + finalAmountIn.mul(10**18).mul(10**reserveOutDecimals).div( + bestAmountOut.mul(10**reserveInDecimals) + ); + + return + AmountCalc( + bestAmountOut, + outPerInPrice, + _calcUsdValue(reserveIn, amountIn, reserveInDecimals), + _calcUsdValue(reserveOut, bestAmountOut, reserveOutDecimals), + (bestAmountOut == 0) ? new address[](2) : (bestAmountOut == amountsWithoutWeth[1]) + ? simplePath + : pathWithWeth + ); + } + + /** + * @dev Returns the minimum input asset amount required to buy the given output asset amount + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @param amountOut Amount of reserveOut + * @return Struct containing the following information: + * uint256 Amount in of the reserveIn + * uint256 The price of in amount denominated in the reserveOut currency (18 decimals) + * uint256 In amount of reserveIn value denominated in USD (8 decimals) + * uint256 Out amount of reserveOut value denominated in USD (8 decimals) + */ + function _getAmountsInData( + address reserveIn, + address reserveOut, + uint256 amountOut + ) internal view returns (AmountCalc memory) { + if (reserveIn == reserveOut) { + // Add flash loan fee + uint256 amountIn = amountOut.add(amountOut.mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); + uint256 reserveDecimals = _getDecimals(reserveIn); + address[] memory path = new address[](1); + path[0] = reserveIn; + + return + AmountCalc( + amountIn, + amountOut.mul(10**18).div(amountIn), + _calcUsdValue(reserveIn, amountIn, reserveDecimals), + _calcUsdValue(reserveIn, amountOut, reserveDecimals), + path + ); + } + + (uint256[] memory amounts, address[] memory path) = + _getAmountsInAndPath(reserveIn, reserveOut, amountOut); + + // Add flash loan fee + uint256 finalAmountIn = amounts[0].add(amounts[0].mul(FLASHLOAN_PREMIUM_TOTAL).div(10000)); + + uint256 reserveInDecimals = _getDecimals(reserveIn); + uint256 reserveOutDecimals = _getDecimals(reserveOut); + + uint256 inPerOutPrice = + amountOut.mul(10**18).mul(10**reserveInDecimals).div( + finalAmountIn.mul(10**reserveOutDecimals) + ); + + return + AmountCalc( + finalAmountIn, + inPerOutPrice, + _calcUsdValue(reserveIn, finalAmountIn, reserveInDecimals), + _calcUsdValue(reserveOut, amountOut, reserveOutDecimals), + path + ); + } + + /** + * @dev Calculates the input asset amount required to buy the given output asset amount + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @param amountOut Amount of reserveOut + * @return uint256[] amounts Array containing the amountIn and amountOut for a swap + */ + function _getAmountsInAndPath( + address reserveIn, + address reserveOut, + uint256 amountOut + ) internal view returns (uint256[] memory, address[] memory) { + address[] memory simplePath = new address[](2); + simplePath[0] = reserveIn; + simplePath[1] = reserveOut; + + uint256[] memory amountsWithoutWeth; + uint256[] memory amountsWithWeth; + address[] memory pathWithWeth = new address[](3); + + if (reserveIn != WETH_ADDRESS && reserveOut != WETH_ADDRESS) { + pathWithWeth[0] = reserveIn; + pathWithWeth[1] = WETH_ADDRESS; + pathWithWeth[2] = reserveOut; + + try UNISWAP_ROUTER.getAmountsIn(amountOut, pathWithWeth) returns ( + uint256[] memory resultsWithWeth + ) { + amountsWithWeth = resultsWithWeth; + } catch { + amountsWithWeth = new uint256[](3); + } + } else { + amountsWithWeth = new uint256[](3); + } + + try UNISWAP_ROUTER.getAmountsIn(amountOut, simplePath) returns ( + uint256[] memory resultAmounts + ) { + amountsWithoutWeth = resultAmounts; + + return + (amountsWithWeth[0] < amountsWithoutWeth[0] && amountsWithWeth[0] != 0) + ? (amountsWithWeth, pathWithWeth) + : (amountsWithoutWeth, simplePath); + } catch { + return (amountsWithWeth, pathWithWeth); + } + } + + /** + * @dev Calculates the input asset amount required to buy the given output asset amount + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @param amountOut Amount of reserveOut + * @return uint256[] amounts Array containing the amountIn and amountOut for a swap + */ + function _getAmountsIn( + address reserveIn, + address reserveOut, + uint256 amountOut, + bool useEthPath + ) internal view returns (uint256[] memory) { + address[] memory path; + + if (useEthPath) { + path = new address[](3); + path[0] = reserveIn; + path[1] = WETH_ADDRESS; + path[2] = reserveOut; + } else { + path = new address[](2); + path[0] = reserveIn; + path[1] = reserveOut; + } + + return UNISWAP_ROUTER.getAmountsIn(amountOut, path); + } + + /** + * @dev Emergency rescue for token stucked on this contract, as failsafe mechanism + * - Funds should never remain in this contract more time than during transactions + * - Only callable by the owner + **/ + function rescueTokens(IERC20 token) external onlyOwner { + token.transfer(owner(), token.balanceOf(address(this))); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/adapters/FlashLiquidationAdapter.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/adapters/FlashLiquidationAdapter.sol new file mode 100644 index 000000000..8f74eae57 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/adapters/FlashLiquidationAdapter.sol @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {BaseUniswapAdapter} from './BaseUniswapAdapter.sol'; +import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; +import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; +import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; +import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; +import {Helpers} from '../protocol/libraries/helpers/Helpers.sol'; +import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol'; +import {IAToken} from '../interfaces/IAToken.sol'; +import {ReserveConfiguration} from '../protocol/libraries/configuration/ReserveConfiguration.sol'; + +/** + * @title UniswapLiquiditySwapAdapter + * @notice Uniswap V2 Adapter to swap liquidity. + * @author Aave + **/ +contract FlashLiquidationAdapter is BaseUniswapAdapter { + using ReserveConfiguration for DataTypes.ReserveConfigurationMap; + uint256 internal constant LIQUIDATION_CLOSE_FACTOR_PERCENT = 5000; + + struct LiquidationParams { + address collateralAsset; + address borrowedAsset; + address user; + uint256 debtToCover; + bool useEthPath; + } + + struct LiquidationCallLocalVars { + uint256 initFlashBorrowedBalance; + uint256 diffFlashBorrowedBalance; + uint256 initCollateralBalance; + uint256 diffCollateralBalance; + uint256 flashLoanDebt; + uint256 soldAmount; + uint256 remainingTokens; + uint256 borrowedAssetLeftovers; + } + + constructor( + ILendingPoolAddressesProvider addressesProvider, + IUniswapV2Router02 uniswapRouter, + address wethAddress + ) public BaseUniswapAdapter(addressesProvider, uniswapRouter, wethAddress) {} + + /** + * @dev Liquidate a non-healthy position collateral-wise, with a Health Factor below 1, using Flash Loan and Uniswap to repay flash loan premium. + * - The caller (liquidator) with a flash loan covers `debtToCover` amount of debt of the user getting liquidated, and receives + * a proportionally amount of the `collateralAsset` plus a bonus to cover market risk minus the flash loan premium. + * @param assets Address of asset to be swapped + * @param amounts Amount of the asset to be swapped + * @param premiums Fee of the flash loan + * @param initiator Address of the caller + * @param params Additional variadic field to include extra params. Expected parameters: + * address collateralAsset The collateral asset to release and will be exchanged to pay the flash loan premium + * address borrowedAsset The asset that must be covered + * address user The user address with a Health Factor below 1 + * uint256 debtToCover The amount of debt to cover + * bool useEthPath Use WETH as connector path between the collateralAsset and borrowedAsset at Uniswap + */ + function executeOperation( + address[] calldata assets, + uint256[] calldata amounts, + uint256[] calldata premiums, + address initiator, + bytes calldata params + ) external override returns (bool) { + require(msg.sender == address(LENDING_POOL), 'CALLER_MUST_BE_LENDING_POOL'); + + LiquidationParams memory decodedParams = _decodeParams(params); + + require(assets.length == 1 && assets[0] == decodedParams.borrowedAsset, 'INCONSISTENT_PARAMS'); + + _liquidateAndSwap( + decodedParams.collateralAsset, + decodedParams.borrowedAsset, + decodedParams.user, + decodedParams.debtToCover, + decodedParams.useEthPath, + amounts[0], + premiums[0], + initiator + ); + + return true; + } + + /** + * @dev + * @param collateralAsset The collateral asset to release and will be exchanged to pay the flash loan premium + * @param borrowedAsset The asset that must be covered + * @param user The user address with a Health Factor below 1 + * @param debtToCover The amount of debt to coverage, can be max(-1) to liquidate all possible debt + * @param useEthPath true if the swap needs to occur using ETH in the routing, false otherwise + * @param flashBorrowedAmount Amount of asset requested at the flash loan to liquidate the user position + * @param premium Fee of the requested flash loan + * @param initiator Address of the caller + */ + function _liquidateAndSwap( + address collateralAsset, + address borrowedAsset, + address user, + uint256 debtToCover, + bool useEthPath, + uint256 flashBorrowedAmount, + uint256 premium, + address initiator + ) internal { + LiquidationCallLocalVars memory vars; + vars.initCollateralBalance = IERC20(collateralAsset).balanceOf(address(this)); + if (collateralAsset != borrowedAsset) { + vars.initFlashBorrowedBalance = IERC20(borrowedAsset).balanceOf(address(this)); + + // Track leftover balance to rescue funds in case of external transfers into this contract + vars.borrowedAssetLeftovers = vars.initFlashBorrowedBalance.sub(flashBorrowedAmount); + } + vars.flashLoanDebt = flashBorrowedAmount.add(premium); + + // Approve LendingPool to use debt token for liquidation + IERC20(borrowedAsset).approve(address(LENDING_POOL), debtToCover); + + // Liquidate the user position and release the underlying collateral + LENDING_POOL.liquidationCall(collateralAsset, borrowedAsset, user, debtToCover, false); + + // Discover the liquidated tokens + uint256 collateralBalanceAfter = IERC20(collateralAsset).balanceOf(address(this)); + + // Track only collateral released, not current asset balance of the contract + vars.diffCollateralBalance = collateralBalanceAfter.sub(vars.initCollateralBalance); + + if (collateralAsset != borrowedAsset) { + // Discover flash loan balance after the liquidation + uint256 flashBorrowedAssetAfter = IERC20(borrowedAsset).balanceOf(address(this)); + + // Use only flash loan borrowed assets, not current asset balance of the contract + vars.diffFlashBorrowedBalance = flashBorrowedAssetAfter.sub(vars.borrowedAssetLeftovers); + + // Swap released collateral into the debt asset, to repay the flash loan + vars.soldAmount = _swapTokensForExactTokens( + collateralAsset, + borrowedAsset, + vars.diffCollateralBalance, + vars.flashLoanDebt.sub(vars.diffFlashBorrowedBalance), + useEthPath + ); + vars.remainingTokens = vars.diffCollateralBalance.sub(vars.soldAmount); + } else { + vars.remainingTokens = vars.diffCollateralBalance.sub(premium); + } + + // Allow repay of flash loan + IERC20(borrowedAsset).approve(address(LENDING_POOL), vars.flashLoanDebt); + + // Transfer remaining tokens to initiator + if (vars.remainingTokens > 0) { + IERC20(collateralAsset).transfer(initiator, vars.remainingTokens); + } + } + + /** + * @dev Decodes the information encoded in the flash loan params + * @param params Additional variadic field to include extra params. Expected parameters: + * address collateralAsset The collateral asset to claim + * address borrowedAsset The asset that must be covered and will be exchanged to pay the flash loan premium + * address user The user address with a Health Factor below 1 + * uint256 debtToCover The amount of debt to cover + * bool useEthPath Use WETH as connector path between the collateralAsset and borrowedAsset at Uniswap + * @return LiquidationParams struct containing decoded params + */ + function _decodeParams(bytes memory params) internal pure returns (LiquidationParams memory) { + ( + address collateralAsset, + address borrowedAsset, + address user, + uint256 debtToCover, + bool useEthPath + ) = abi.decode(params, (address, address, address, uint256, bool)); + + return LiquidationParams(collateralAsset, borrowedAsset, user, debtToCover, useEthPath); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/adapters/UniswapLiquiditySwapAdapter.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/adapters/UniswapLiquiditySwapAdapter.sol new file mode 100644 index 000000000..29115d7f8 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/adapters/UniswapLiquiditySwapAdapter.sol @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {BaseUniswapAdapter} from './BaseUniswapAdapter.sol'; +import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; +import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; +import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; + +/** + * @title UniswapLiquiditySwapAdapter + * @notice Uniswap V2 Adapter to swap liquidity. + * @author Aave + **/ +contract UniswapLiquiditySwapAdapter is BaseUniswapAdapter { + struct PermitParams { + uint256[] amount; + uint256[] deadline; + uint8[] v; + bytes32[] r; + bytes32[] s; + } + + struct SwapParams { + address[] assetToSwapToList; + uint256[] minAmountsToReceive; + bool[] swapAllBalance; + PermitParams permitParams; + bool[] useEthPath; + } + + constructor( + ILendingPoolAddressesProvider addressesProvider, + IUniswapV2Router02 uniswapRouter, + address wethAddress + ) public BaseUniswapAdapter(addressesProvider, uniswapRouter, wethAddress) {} + + /** + * @dev Swaps the received reserve amount from the flash loan into the asset specified in the params. + * The received funds from the swap are then deposited into the protocol on behalf of the user. + * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset and + * repay the flash loan. + * @param assets Address of asset to be swapped + * @param amounts Amount of the asset to be swapped + * @param premiums Fee of the flash loan + * @param initiator Address of the user + * @param params Additional variadic field to include extra params. Expected parameters: + * address[] assetToSwapToList List of the addresses of the reserve to be swapped to and deposited + * uint256[] minAmountsToReceive List of min amounts to be received from the swap + * bool[] swapAllBalance Flag indicating if all the user balance should be swapped + * uint256[] permitAmount List of amounts for the permit signature + * uint256[] deadline List of deadlines for the permit signature + * uint8[] v List of v param for the permit signature + * bytes32[] r List of r param for the permit signature + * bytes32[] s List of s param for the permit signature + */ + function executeOperation( + address[] calldata assets, + uint256[] calldata amounts, + uint256[] calldata premiums, + address initiator, + bytes calldata params + ) external override returns (bool) { + require(msg.sender == address(LENDING_POOL), 'CALLER_MUST_BE_LENDING_POOL'); + + SwapParams memory decodedParams = _decodeParams(params); + + require( + assets.length == decodedParams.assetToSwapToList.length && + assets.length == decodedParams.minAmountsToReceive.length && + assets.length == decodedParams.swapAllBalance.length && + assets.length == decodedParams.permitParams.amount.length && + assets.length == decodedParams.permitParams.deadline.length && + assets.length == decodedParams.permitParams.v.length && + assets.length == decodedParams.permitParams.r.length && + assets.length == decodedParams.permitParams.s.length && + assets.length == decodedParams.useEthPath.length, + 'INCONSISTENT_PARAMS' + ); + + for (uint256 i = 0; i < assets.length; i++) { + _swapLiquidity( + assets[i], + decodedParams.assetToSwapToList[i], + amounts[i], + premiums[i], + initiator, + decodedParams.minAmountsToReceive[i], + decodedParams.swapAllBalance[i], + PermitSignature( + decodedParams.permitParams.amount[i], + decodedParams.permitParams.deadline[i], + decodedParams.permitParams.v[i], + decodedParams.permitParams.r[i], + decodedParams.permitParams.s[i] + ), + decodedParams.useEthPath[i] + ); + } + + return true; + } + + struct SwapAndDepositLocalVars { + uint256 i; + uint256 aTokenInitiatorBalance; + uint256 amountToSwap; + uint256 receivedAmount; + address aToken; + } + + /** + * @dev Swaps an amount of an asset to another and deposits the new asset amount on behalf of the user without using + * a flash loan. This method can be used when the temporary transfer of the collateral asset to this contract + * does not affect the user position. + * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset and + * perform the swap. + * @param assetToSwapFromList List of addresses of the underlying asset to be swap from + * @param assetToSwapToList List of addresses of the underlying asset to be swap to and deposited + * @param amountToSwapList List of amounts to be swapped. If the amount exceeds the balance, the total balance is used for the swap + * @param minAmountsToReceive List of min amounts to be received from the swap + * @param permitParams List of struct containing the permit signatures + * uint256 permitAmount Amount for the permit signature + * uint256 deadline Deadline for the permit signature + * uint8 v param for the permit signature + * bytes32 r param for the permit signature + * bytes32 s param for the permit signature + * @param useEthPath true if the swap needs to occur using ETH in the routing, false otherwise + */ + function swapAndDeposit( + address[] calldata assetToSwapFromList, + address[] calldata assetToSwapToList, + uint256[] calldata amountToSwapList, + uint256[] calldata minAmountsToReceive, + PermitSignature[] calldata permitParams, + bool[] calldata useEthPath + ) external { + require( + assetToSwapFromList.length == assetToSwapToList.length && + assetToSwapFromList.length == amountToSwapList.length && + assetToSwapFromList.length == minAmountsToReceive.length && + assetToSwapFromList.length == permitParams.length, + 'INCONSISTENT_PARAMS' + ); + + SwapAndDepositLocalVars memory vars; + + for (vars.i = 0; vars.i < assetToSwapFromList.length; vars.i++) { + vars.aToken = _getReserveData(assetToSwapFromList[vars.i]).aTokenAddress; + + vars.aTokenInitiatorBalance = IERC20(vars.aToken).balanceOf(msg.sender); + vars.amountToSwap = amountToSwapList[vars.i] > vars.aTokenInitiatorBalance + ? vars.aTokenInitiatorBalance + : amountToSwapList[vars.i]; + + _pullAToken( + assetToSwapFromList[vars.i], + vars.aToken, + msg.sender, + vars.amountToSwap, + permitParams[vars.i] + ); + + vars.receivedAmount = _swapExactTokensForTokens( + assetToSwapFromList[vars.i], + assetToSwapToList[vars.i], + vars.amountToSwap, + minAmountsToReceive[vars.i], + useEthPath[vars.i] + ); + + // Deposit new reserve + IERC20(assetToSwapToList[vars.i]).safeApprove(address(LENDING_POOL), 0); + IERC20(assetToSwapToList[vars.i]).safeApprove(address(LENDING_POOL), vars.receivedAmount); + LENDING_POOL.deposit(assetToSwapToList[vars.i], vars.receivedAmount, msg.sender, 0); + } + } + + /** + * @dev Swaps an `amountToSwap` of an asset to another and deposits the funds on behalf of the initiator. + * @param assetFrom Address of the underlying asset to be swap from + * @param assetTo Address of the underlying asset to be swap to and deposited + * @param amount Amount from flash loan + * @param premium Premium of the flash loan + * @param minAmountToReceive Min amount to be received from the swap + * @param swapAllBalance Flag indicating if all the user balance should be swapped + * @param permitSignature List of struct containing the permit signature + * @param useEthPath true if the swap needs to occur using ETH in the routing, false otherwise + */ + + struct SwapLiquidityLocalVars { + address aToken; + uint256 aTokenInitiatorBalance; + uint256 amountToSwap; + uint256 receivedAmount; + uint256 flashLoanDebt; + uint256 amountToPull; + } + + function _swapLiquidity( + address assetFrom, + address assetTo, + uint256 amount, + uint256 premium, + address initiator, + uint256 minAmountToReceive, + bool swapAllBalance, + PermitSignature memory permitSignature, + bool useEthPath + ) internal { + SwapLiquidityLocalVars memory vars; + + vars.aToken = _getReserveData(assetFrom).aTokenAddress; + + vars.aTokenInitiatorBalance = IERC20(vars.aToken).balanceOf(initiator); + vars.amountToSwap = swapAllBalance && vars.aTokenInitiatorBalance.sub(premium) <= amount + ? vars.aTokenInitiatorBalance.sub(premium) + : amount; + + vars.receivedAmount = _swapExactTokensForTokens( + assetFrom, + assetTo, + vars.amountToSwap, + minAmountToReceive, + useEthPath + ); + + // Deposit new reserve + IERC20(assetTo).safeApprove(address(LENDING_POOL), 0); + IERC20(assetTo).safeApprove(address(LENDING_POOL), vars.receivedAmount); + LENDING_POOL.deposit(assetTo, vars.receivedAmount, initiator, 0); + + vars.flashLoanDebt = amount.add(premium); + vars.amountToPull = vars.amountToSwap.add(premium); + + _pullAToken(assetFrom, vars.aToken, initiator, vars.amountToPull, permitSignature); + + // Repay flash loan + IERC20(assetFrom).safeApprove(address(LENDING_POOL), 0); + IERC20(assetFrom).safeApprove(address(LENDING_POOL), vars.flashLoanDebt); + } + + /** + * @dev Decodes the information encoded in the flash loan params + * @param params Additional variadic field to include extra params. Expected parameters: + * address[] assetToSwapToList List of the addresses of the reserve to be swapped to and deposited + * uint256[] minAmountsToReceive List of min amounts to be received from the swap + * bool[] swapAllBalance Flag indicating if all the user balance should be swapped + * uint256[] permitAmount List of amounts for the permit signature + * uint256[] deadline List of deadlines for the permit signature + * uint8[] v List of v param for the permit signature + * bytes32[] r List of r param for the permit signature + * bytes32[] s List of s param for the permit signature + * bool[] useEthPath true if the swap needs to occur using ETH in the routing, false otherwise + * @return SwapParams struct containing decoded params + */ + function _decodeParams(bytes memory params) internal pure returns (SwapParams memory) { + ( + address[] memory assetToSwapToList, + uint256[] memory minAmountsToReceive, + bool[] memory swapAllBalance, + uint256[] memory permitAmount, + uint256[] memory deadline, + uint8[] memory v, + bytes32[] memory r, + bytes32[] memory s, + bool[] memory useEthPath + ) = + abi.decode( + params, + (address[], uint256[], bool[], uint256[], uint256[], uint8[], bytes32[], bytes32[], bool[]) + ); + + return + SwapParams( + assetToSwapToList, + minAmountsToReceive, + swapAllBalance, + PermitParams(permitAmount, deadline, v, r, s), + useEthPath + ); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/adapters/UniswapRepayAdapter.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/adapters/UniswapRepayAdapter.sol new file mode 100644 index 000000000..490f7a382 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/adapters/UniswapRepayAdapter.sol @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {BaseUniswapAdapter} from './BaseUniswapAdapter.sol'; +import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; +import {IUniswapV2Router02} from '../interfaces/IUniswapV2Router02.sol'; +import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; +import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; + +/** + * @title UniswapRepayAdapter + * @notice Uniswap V2 Adapter to perform a repay of a debt with collateral. + * @author Aave + **/ +contract UniswapRepayAdapter is BaseUniswapAdapter { + struct RepayParams { + address collateralAsset; + uint256 collateralAmount; + uint256 rateMode; + PermitSignature permitSignature; + bool useEthPath; + } + + constructor( + ILendingPoolAddressesProvider addressesProvider, + IUniswapV2Router02 uniswapRouter, + address wethAddress + ) public BaseUniswapAdapter(addressesProvider, uniswapRouter, wethAddress) {} + + /** + * @dev Uses the received funds from the flash loan to repay a debt on the protocol on behalf of the user. Then pulls + * the collateral from the user and swaps it to the debt asset to repay the flash loan. + * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset, swap it + * and repay the flash loan. + * Supports only one asset on the flash loan. + * @param assets Address of debt asset + * @param amounts Amount of the debt to be repaid + * @param premiums Fee of the flash loan + * @param initiator Address of the user + * @param params Additional variadic field to include extra params. Expected parameters: + * address collateralAsset Address of the reserve to be swapped + * uint256 collateralAmount Amount of reserve to be swapped + * uint256 rateMode Rate modes of the debt to be repaid + * uint256 permitAmount Amount for the permit signature + * uint256 deadline Deadline for the permit signature + * uint8 v V param for the permit signature + * bytes32 r R param for the permit signature + * bytes32 s S param for the permit signature + */ + function executeOperation( + address[] calldata assets, + uint256[] calldata amounts, + uint256[] calldata premiums, + address initiator, + bytes calldata params + ) external override returns (bool) { + require(msg.sender == address(LENDING_POOL), 'CALLER_MUST_BE_LENDING_POOL'); + + RepayParams memory decodedParams = _decodeParams(params); + + _swapAndRepay( + decodedParams.collateralAsset, + assets[0], + amounts[0], + decodedParams.collateralAmount, + decodedParams.rateMode, + initiator, + premiums[0], + decodedParams.permitSignature, + decodedParams.useEthPath + ); + + return true; + } + + /** + * @dev Swaps the user collateral for the debt asset and then repay the debt on the protocol on behalf of the user + * without using flash loans. This method can be used when the temporary transfer of the collateral asset to this + * contract does not affect the user position. + * The user should give this contract allowance to pull the ATokens in order to withdraw the underlying asset + * @param collateralAsset Address of asset to be swapped + * @param debtAsset Address of debt asset + * @param collateralAmount Amount of the collateral to be swapped + * @param debtRepayAmount Amount of the debt to be repaid + * @param debtRateMode Rate mode of the debt to be repaid + * @param permitSignature struct containing the permit signature + * @param useEthPath struct containing the permit signature + + */ + function swapAndRepay( + address collateralAsset, + address debtAsset, + uint256 collateralAmount, + uint256 debtRepayAmount, + uint256 debtRateMode, + PermitSignature calldata permitSignature, + bool useEthPath + ) external { + DataTypes.ReserveData memory collateralReserveData = _getReserveData(collateralAsset); + DataTypes.ReserveData memory debtReserveData = _getReserveData(debtAsset); + + address debtToken = + DataTypes.InterestRateMode(debtRateMode) == DataTypes.InterestRateMode.STABLE + ? debtReserveData.stableDebtTokenAddress + : debtReserveData.variableDebtTokenAddress; + + uint256 currentDebt = IERC20(debtToken).balanceOf(msg.sender); + uint256 amountToRepay = debtRepayAmount <= currentDebt ? debtRepayAmount : currentDebt; + + if (collateralAsset != debtAsset) { + uint256 maxCollateralToSwap = collateralAmount; + if (amountToRepay < debtRepayAmount) { + maxCollateralToSwap = maxCollateralToSwap.mul(amountToRepay).div(debtRepayAmount); + } + + // Get exact collateral needed for the swap to avoid leftovers + uint256[] memory amounts = + _getAmountsIn(collateralAsset, debtAsset, amountToRepay, useEthPath); + require(amounts[0] <= maxCollateralToSwap, 'slippage too high'); + + // Pull aTokens from user + _pullAToken( + collateralAsset, + collateralReserveData.aTokenAddress, + msg.sender, + amounts[0], + permitSignature + ); + + // Swap collateral for debt asset + _swapTokensForExactTokens(collateralAsset, debtAsset, amounts[0], amountToRepay, useEthPath); + } else { + // Pull aTokens from user + _pullAToken( + collateralAsset, + collateralReserveData.aTokenAddress, + msg.sender, + amountToRepay, + permitSignature + ); + } + + // Repay debt. Approves 0 first to comply with tokens that implement the anti frontrunning approval fix + IERC20(debtAsset).safeApprove(address(LENDING_POOL), 0); + IERC20(debtAsset).safeApprove(address(LENDING_POOL), amountToRepay); + LENDING_POOL.repay(debtAsset, amountToRepay, debtRateMode, msg.sender); + } + + /** + * @dev Perform the repay of the debt, pulls the initiator collateral and swaps to repay the flash loan + * + * @param collateralAsset Address of token to be swapped + * @param debtAsset Address of debt token to be received from the swap + * @param amount Amount of the debt to be repaid + * @param collateralAmount Amount of the reserve to be swapped + * @param rateMode Rate mode of the debt to be repaid + * @param initiator Address of the user + * @param premium Fee of the flash loan + * @param permitSignature struct containing the permit signature + */ + function _swapAndRepay( + address collateralAsset, + address debtAsset, + uint256 amount, + uint256 collateralAmount, + uint256 rateMode, + address initiator, + uint256 premium, + PermitSignature memory permitSignature, + bool useEthPath + ) internal { + DataTypes.ReserveData memory collateralReserveData = _getReserveData(collateralAsset); + + // Repay debt. Approves for 0 first to comply with tokens that implement the anti frontrunning approval fix. + IERC20(debtAsset).safeApprove(address(LENDING_POOL), 0); + IERC20(debtAsset).safeApprove(address(LENDING_POOL), amount); + uint256 repaidAmount = IERC20(debtAsset).balanceOf(address(this)); + LENDING_POOL.repay(debtAsset, amount, rateMode, initiator); + repaidAmount = repaidAmount.sub(IERC20(debtAsset).balanceOf(address(this))); + + if (collateralAsset != debtAsset) { + uint256 maxCollateralToSwap = collateralAmount; + if (repaidAmount < amount) { + maxCollateralToSwap = maxCollateralToSwap.mul(repaidAmount).div(amount); + } + + uint256 neededForFlashLoanDebt = repaidAmount.add(premium); + uint256[] memory amounts = + _getAmountsIn(collateralAsset, debtAsset, neededForFlashLoanDebt, useEthPath); + require(amounts[0] <= maxCollateralToSwap, 'slippage too high'); + + // Pull aTokens from user + _pullAToken( + collateralAsset, + collateralReserveData.aTokenAddress, + initiator, + amounts[0], + permitSignature + ); + + // Swap collateral asset to the debt asset + _swapTokensForExactTokens( + collateralAsset, + debtAsset, + amounts[0], + neededForFlashLoanDebt, + useEthPath + ); + } else { + // Pull aTokens from user + _pullAToken( + collateralAsset, + collateralReserveData.aTokenAddress, + initiator, + repaidAmount.add(premium), + permitSignature + ); + } + + // Repay flashloan. Approves for 0 first to comply with tokens that implement the anti frontrunning approval fix. + IERC20(debtAsset).safeApprove(address(LENDING_POOL), 0); + IERC20(debtAsset).safeApprove(address(LENDING_POOL), amount.add(premium)); + } + + /** + * @dev Decodes debt information encoded in the flash loan params + * @param params Additional variadic field to include extra params. Expected parameters: + * address collateralAsset Address of the reserve to be swapped + * uint256 collateralAmount Amount of reserve to be swapped + * uint256 rateMode Rate modes of the debt to be repaid + * uint256 permitAmount Amount for the permit signature + * uint256 deadline Deadline for the permit signature + * uint8 v V param for the permit signature + * bytes32 r R param for the permit signature + * bytes32 s S param for the permit signature + * bool useEthPath use WETH path route + * @return RepayParams struct containing decoded params + */ + function _decodeParams(bytes memory params) internal pure returns (RepayParams memory) { + ( + address collateralAsset, + uint256 collateralAmount, + uint256 rateMode, + uint256 permitAmount, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s, + bool useEthPath + ) = + abi.decode( + params, + (address, uint256, uint256, uint256, uint256, uint8, bytes32, bytes32, bool) + ); + + return + RepayParams( + collateralAsset, + collateralAmount, + rateMode, + PermitSignature(permitAmount, deadline, v, r, s), + useEthPath + ); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/adapters/interfaces/IBaseUniswapAdapter.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/adapters/interfaces/IBaseUniswapAdapter.sol new file mode 100644 index 000000000..184aee1eb --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/adapters/interfaces/IBaseUniswapAdapter.sol @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol'; +import {IUniswapV2Router02} from '../../interfaces/IUniswapV2Router02.sol'; + +interface IBaseUniswapAdapter { + event Swapped(address fromAsset, address toAsset, uint256 fromAmount, uint256 receivedAmount); + + struct PermitSignature { + uint256 amount; + uint256 deadline; + uint8 v; + bytes32 r; + bytes32 s; + } + + struct AmountCalc { + uint256 calculatedAmount; + uint256 relativePrice; + uint256 amountInUsd; + uint256 amountOutUsd; + address[] path; + } + + function WETH_ADDRESS() external returns (address); + + function MAX_SLIPPAGE_PERCENT() external returns (uint256); + + function FLASHLOAN_PREMIUM_TOTAL() external returns (uint256); + + function USD_ADDRESS() external returns (address); + + function ORACLE() external returns (IPriceOracleGetter); + + function UNISWAP_ROUTER() external returns (IUniswapV2Router02); + + /** + * @dev Given an input asset amount, returns the maximum output amount of the other asset and the prices + * @param amountIn Amount of reserveIn + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @return uint256 Amount out of the reserveOut + * @return uint256 The price of out amount denominated in the reserveIn currency (18 decimals) + * @return uint256 In amount of reserveIn value denominated in USD (8 decimals) + * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals) + * @return address[] The exchange path + */ + function getAmountsOut( + uint256 amountIn, + address reserveIn, + address reserveOut + ) + external + view + returns ( + uint256, + uint256, + uint256, + uint256, + address[] memory + ); + + /** + * @dev Returns the minimum input asset amount required to buy the given output asset amount and the prices + * @param amountOut Amount of reserveOut + * @param reserveIn Address of the asset to be swap from + * @param reserveOut Address of the asset to be swap to + * @return uint256 Amount in of the reserveIn + * @return uint256 The price of in amount denominated in the reserveOut currency (18 decimals) + * @return uint256 In amount of reserveIn value denominated in USD (8 decimals) + * @return uint256 Out amount of reserveOut value denominated in USD (8 decimals) + * @return address[] The exchange path + */ + function getAmountsIn( + uint256 amountOut, + address reserveIn, + address reserveOut + ) + external + view + returns ( + uint256, + uint256, + uint256, + uint256, + address[] memory + ); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/Address.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/Address.sol new file mode 100644 index 000000000..bec641a8a --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/Address.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + */ + function isContract(address account) internal view returns (bool) { + // According to EIP-1052, 0x0 is the value returned for not-yet created accounts + // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned + // for accounts without code, i.e. `keccak256('')` + bytes32 codehash; + bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; + // solhint-disable-next-line no-inline-assembly + assembly { + codehash := extcodehash(account) + } + return (codehash != accountHash && codehash != 0x0); + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, 'Address: insufficient balance'); + + // solhint-disable-next-line avoid-low-level-calls, avoid-call-value + (bool success, ) = recipient.call{value: amount}(''); + require(success, 'Address: unable to send value, recipient may have reverted'); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/Context.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/Context.sol new file mode 100644 index 000000000..c7cfeebeb --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/Context.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.6.12; + +/* + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with GSN meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal view virtual returns (address payable) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes memory) { + this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 + return msg.data; + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/ERC20.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/ERC20.sol new file mode 100644 index 000000000..ff91c4bfe --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/ERC20.sol @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.0; + +import './Context.sol'; +import './IERC20.sol'; +import './SafeMath.sol'; +import './Address.sol'; + +/** + * @dev Implementation of the {IERC20} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * For a generic mechanism see {ERC20PresetMinterPauser}. + * + * TIP: For a detailed writeup see our guide + * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How + * to implement supply mechanisms]. + * + * We have followed general OpenZeppelin guidelines: functions revert instead + * of returning `false` on failure. This behavior is nonetheless conventional + * and does not conflict with the expectations of ERC20 applications. + * + * Additionally, an {Approval} event is emitted on calls to {transferFrom}. + * This allows applications to reconstruct the allowance for all accounts just + * by listening to said events. Other implementations of the EIP may not emit + * these events, as it isn't required by the specification. + * + * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} + * functions have been added to mitigate the well-known issues around setting + * allowances. See {IERC20-approve}. + */ +contract ERC20 is Context, IERC20 { + using SafeMath for uint256; + using Address for address; + + mapping(address => uint256) private _balances; + + mapping(address => mapping(address => uint256)) private _allowances; + + uint256 private _totalSupply; + + string private _name; + string private _symbol; + uint8 private _decimals; + + /** + * @dev Sets the values for {name} and {symbol}, initializes {decimals} with + * a default value of 18. + * + * To select a different value for {decimals}, use {_setupDecimals}. + * + * All three of these values are immutable: they can only be set once during + * construction. + */ + constructor(string memory name, string memory symbol) public { + _name = name; + _symbol = symbol; + _decimals = 18; + } + + /** + * @dev Returns the name of the token. + */ + function name() public view returns (string memory) { + return _name; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view returns (string memory) { + return _symbol; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5,05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is + * called. + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC20-balanceOf} and {IERC20-transfer}. + */ + function decimals() public view returns (uint8) { + return _decimals; + } + + /** + * @dev See {IERC20-totalSupply}. + */ + function totalSupply() public view override returns (uint256) { + return _totalSupply; + } + + /** + * @dev See {IERC20-balanceOf}. + */ + function balanceOf(address account) public view override returns (uint256) { + return _balances[account]; + } + + /** + * @dev See {IERC20-transfer}. + * + * Requirements: + * + * - `recipient` cannot be the zero address. + * - the caller must have a balance of at least `amount`. + */ + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { + _transfer(_msgSender(), recipient, amount); + return true; + } + + /** + * @dev See {IERC20-allowance}. + */ + function allowance(address owner, address spender) + public + view + virtual + override + returns (uint256) + { + return _allowances[owner][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function approve(address spender, uint256 amount) public virtual override returns (bool) { + _approve(_msgSender(), spender, amount); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * Emits an {Approval} event indicating the updated allowance. This is not + * required by the EIP. See the note at the beginning of {ERC20}; + * + * Requirements: + * - `sender` and `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `amount`. + * - the caller must have allowance for ``sender``'s tokens of at least + * `amount`. + */ + function transferFrom( + address sender, + address recipient, + uint256 amount + ) public virtual override returns (bool) { + _transfer(sender, recipient, amount); + _approve( + sender, + _msgSender(), + _allowances[sender][_msgSender()].sub(amount, 'ERC20: transfer amount exceeds allowance') + ); + return true; + } + + /** + * @dev Atomically increases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); + return true; + } + + /** + * @dev Atomically decreases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `spender` must have allowance for the caller of at least + * `subtractedValue`. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) + public + virtual + returns (bool) + { + _approve( + _msgSender(), + spender, + _allowances[_msgSender()][spender].sub( + subtractedValue, + 'ERC20: decreased allowance below zero' + ) + ); + return true; + } + + /** + * @dev Moves tokens `amount` from `sender` to `recipient`. + * + * This is internal function is equivalent to {transfer}, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a {Transfer} event. + * + * Requirements: + * + * - `sender` cannot be the zero address. + * - `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `amount`. + */ + function _transfer( + address sender, + address recipient, + uint256 amount + ) internal virtual { + require(sender != address(0), 'ERC20: transfer from the zero address'); + require(recipient != address(0), 'ERC20: transfer to the zero address'); + + _beforeTokenTransfer(sender, recipient, amount); + + _balances[sender] = _balances[sender].sub(amount, 'ERC20: transfer amount exceeds balance'); + _balances[recipient] = _balances[recipient].add(amount); + emit Transfer(sender, recipient, amount); + } + + /** @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * Emits a {Transfer} event with `from` set to the zero address. + * + * Requirements + * + * - `to` cannot be the zero address. + */ + function _mint(address account, uint256 amount) internal virtual { + require(account != address(0), 'ERC20: mint to the zero address'); + + _beforeTokenTransfer(address(0), account, amount); + + _totalSupply = _totalSupply.add(amount); + _balances[account] = _balances[account].add(amount); + emit Transfer(address(0), account, amount); + } + + /** + * @dev Destroys `amount` tokens from `account`, reducing the + * total supply. + * + * Emits a {Transfer} event with `to` set to the zero address. + * + * Requirements + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + */ + function _burn(address account, uint256 amount) internal virtual { + require(account != address(0), 'ERC20: burn from the zero address'); + + _beforeTokenTransfer(account, address(0), amount); + + _balances[account] = _balances[account].sub(amount, 'ERC20: burn amount exceeds balance'); + _totalSupply = _totalSupply.sub(amount); + emit Transfer(account, address(0), amount); + } + + /** + * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. + * + * This is internal function is equivalent to `approve`, and can be used to + * e.g. set automatic allowances for certain subsystems, etc. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `owner` cannot be the zero address. + * - `spender` cannot be the zero address. + */ + function _approve( + address owner, + address spender, + uint256 amount + ) internal virtual { + require(owner != address(0), 'ERC20: approve from the zero address'); + require(spender != address(0), 'ERC20: approve to the zero address'); + + _allowances[owner][spender] = amount; + emit Approval(owner, spender, amount); + } + + /** + * @dev Sets {decimals} to a value other than the default one of 18. + * + * WARNING: This function should only be called from the constructor. Most + * applications that interact with token contracts will not expect + * {decimals} to ever change, and may work incorrectly if it does. + */ + function _setupDecimals(uint8 decimals_) internal { + _decimals = decimals_; + } + + /** + * @dev Hook that is called before any transfer of tokens. This includes + * minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * will be to transferred to `to`. + * - when `from` is zero, `amount` tokens will be minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens will be burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual {} +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/IERC20.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/IERC20.sol new file mode 100644 index 000000000..557e03e3f --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/IERC20.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `recipient`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address recipient, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `sender` to `recipient` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address sender, + address recipient, + uint256 amount + ) external returns (bool); + + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol new file mode 100644 index 000000000..80d319527 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/IERC20Detailed.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {IERC20} from './IERC20.sol'; + +interface IERC20Detailed is IERC20 { + function name() external view returns (string memory); + + function symbol() external view returns (string memory); + + function decimals() external view returns (uint8); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/Ownable.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/Ownable.sol new file mode 100644 index 000000000..aa8ddd8e8 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/Ownable.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.0; + +import './Context.sol'; + +/** + * @dev Contract module which provides a basic access control mechanism, where + * there is an account (an owner) that can be granted exclusive access to + * specific functions. + * + * By default, the owner account will be the one that deploys the contract. This + * can later be changed with {transferOwnership}. + * + * This module is used through inheritance. It will make available the modifier + * `onlyOwner`, which can be applied to your functions to restrict their use to + * the owner. + */ +contract Ownable is Context { + address private _owner; + + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + /** + * @dev Initializes the contract setting the deployer as the initial owner. + */ + constructor() internal { + address msgSender = _msgSender(); + _owner = msgSender; + emit OwnershipTransferred(address(0), msgSender); + } + + /** + * @dev Returns the address of the current owner. + */ + function owner() public view returns (address) { + return _owner; + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + require(_owner == _msgSender(), 'Ownable: caller is not the owner'); + _; + } + + /** + * @dev Leaves the contract without owner. It will not be possible to call + * `onlyOwner` functions anymore. Can only be called by the current owner. + * + * NOTE: Renouncing ownership will leave the contract without an owner, + * thereby removing any functionality that is only available to the owner. + */ + function renounceOwnership() public virtual onlyOwner { + emit OwnershipTransferred(_owner, address(0)); + _owner = address(0); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Can only be called by the current owner. + */ + function transferOwnership(address newOwner) public virtual onlyOwner { + require(newOwner != address(0), 'Ownable: new owner is the zero address'); + emit OwnershipTransferred(_owner, newOwner); + _owner = newOwner; + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/SafeERC20.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/SafeERC20.sol new file mode 100644 index 000000000..86d009d7e --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/SafeERC20.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.6.12; + +import {IERC20} from './IERC20.sol'; +import {SafeMath} from './SafeMath.sol'; +import {Address} from './Address.sol'; + +/** + * @title SafeERC20 + * @dev Wrappers around ERC20 operations that throw on failure (when the token + * contract returns false). Tokens that return no value (and instead revert or + * throw on failure) are also supported, non-reverting calls are assumed to be + * successful. + * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, + * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. + */ +library SafeERC20 { + using SafeMath for uint256; + using Address for address; + + function safeTransfer( + IERC20 token, + address to, + uint256 value + ) internal { + callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); + } + + function safeTransferFrom( + IERC20 token, + address from, + address to, + uint256 value + ) internal { + callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); + } + + function safeApprove( + IERC20 token, + address spender, + uint256 value + ) internal { + require( + (value == 0) || (token.allowance(address(this), spender) == 0), + 'SafeERC20: approve from non-zero to non-zero allowance' + ); + callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); + } + + function callOptionalReturn(IERC20 token, bytes memory data) private { + require(address(token).isContract(), 'SafeERC20: call to non-contract'); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = address(token).call(data); + require(success, 'SafeERC20: low-level call failed'); + + if (returndata.length > 0) { + // Return data is optional + // solhint-disable-next-line max-line-length + require(abi.decode(returndata, (bool)), 'SafeERC20: ERC20 operation did not succeed'); + } + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/SafeMath.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/SafeMath.sol new file mode 100644 index 000000000..6f814a002 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/contracts/SafeMath.sol @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +/** + * @dev Wrappers over Solidity's arithmetic operations with added overflow + * checks. + * + * Arithmetic operations in Solidity wrap on overflow. This can easily result + * in bugs, because programmers usually assume that an overflow raises an + * error, which is the standard behavior in high level programming languages. + * `SafeMath` restores this intuition by reverting the transaction when an + * operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + */ +library SafeMath { + /** + * @dev Returns the addition of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * - Addition cannot overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a + b; + require(c >= a, 'SafeMath: addition overflow'); + + return c; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * - Subtraction cannot overflow. + */ + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + return sub(a, b, 'SafeMath: subtraction overflow'); + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting with custom message on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * - Subtraction cannot overflow. + */ + function sub( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + require(b <= a, errorMessage); + uint256 c = a - b; + + return c; + } + + /** + * @dev Returns the multiplication of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * - Multiplication cannot overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 + if (a == 0) { + return 0; + } + + uint256 c = a * b; + require(c / a == b, 'SafeMath: multiplication overflow'); + + return c; + } + + /** + * @dev Returns the integer division of two unsigned integers. Reverts on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function div(uint256 a, uint256 b) internal pure returns (uint256) { + return div(a, b, 'SafeMath: division by zero'); + } + + /** + * @dev Returns the integer division of two unsigned integers. Reverts with custom message on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function div( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + // Solidity only automatically asserts when dividing by 0 + require(b > 0, errorMessage); + uint256 c = a / b; + // assert(a == b * c + a % b); // There is no case in which this doesn't hold + + return c; + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * Reverts when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function mod(uint256 a, uint256 b) internal pure returns (uint256) { + return mod(a, b, 'SafeMath: modulo by zero'); + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * Reverts with custom message when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function mod( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + require(b != 0, errorMessage); + return a % b; + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/AdminUpgradeabilityProxy.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/AdminUpgradeabilityProxy.sol new file mode 100644 index 000000000..106e1fc1c --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/AdminUpgradeabilityProxy.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import './BaseAdminUpgradeabilityProxy.sol'; + +/** + * @title AdminUpgradeabilityProxy + * @dev Extends from BaseAdminUpgradeabilityProxy with a constructor for + * initializing the implementation, admin, and init data. + */ +contract AdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, UpgradeabilityProxy { + /** + * Contract constructor. + * @param _logic address of the initial implementation. + * @param _admin Address of the proxy administrator. + * @param _data Data to send as msg.data to the implementation to initialize the proxied contract. + * It should include the signature and the parameters of the function to be called, as described in + * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. + * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped. + */ + constructor( + address _logic, + address _admin, + bytes memory _data + ) public payable UpgradeabilityProxy(_logic, _data) { + assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)); + _setAdmin(_admin); + } + + /** + * @dev Only fall back when the sender is not the admin. + */ + function _willFallback() internal override(BaseAdminUpgradeabilityProxy, Proxy) { + BaseAdminUpgradeabilityProxy._willFallback(); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/BaseAdminUpgradeabilityProxy.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/BaseAdminUpgradeabilityProxy.sol new file mode 100644 index 000000000..352a2e0f6 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/BaseAdminUpgradeabilityProxy.sol @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import './UpgradeabilityProxy.sol'; + +/** + * @title BaseAdminUpgradeabilityProxy + * @dev This contract combines an upgradeability proxy with an authorization + * mechanism for administrative tasks. + * All external functions in this contract must be guarded by the + * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity + * feature proposal that would enable this to be done automatically. + */ +contract BaseAdminUpgradeabilityProxy is BaseUpgradeabilityProxy { + /** + * @dev Emitted when the administration has been transferred. + * @param previousAdmin Address of the previous admin. + * @param newAdmin Address of the new admin. + */ + event AdminChanged(address previousAdmin, address newAdmin); + + /** + * @dev Storage slot with the admin of the contract. + * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is + * validated in the constructor. + */ + bytes32 internal constant ADMIN_SLOT = + 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + + /** + * @dev Modifier to check whether the `msg.sender` is the admin. + * If it is, it will run the function. Otherwise, it will delegate the call + * to the implementation. + */ + modifier ifAdmin() { + if (msg.sender == _admin()) { + _; + } else { + _fallback(); + } + } + + /** + * @return The address of the proxy admin. + */ + function admin() external ifAdmin returns (address) { + return _admin(); + } + + /** + * @return The address of the implementation. + */ + function implementation() external ifAdmin returns (address) { + return _implementation(); + } + + /** + * @dev Changes the admin of the proxy. + * Only the current admin can call this function. + * @param newAdmin Address to transfer proxy administration to. + */ + function changeAdmin(address newAdmin) external ifAdmin { + require(newAdmin != address(0), 'Cannot change the admin of a proxy to the zero address'); + emit AdminChanged(_admin(), newAdmin); + _setAdmin(newAdmin); + } + + /** + * @dev Upgrade the backing implementation of the proxy. + * Only the admin can call this function. + * @param newImplementation Address of the new implementation. + */ + function upgradeTo(address newImplementation) external ifAdmin { + _upgradeTo(newImplementation); + } + + /** + * @dev Upgrade the backing implementation of the proxy and call a function + * on the new implementation. + * This is useful to initialize the proxied contract. + * @param newImplementation Address of the new implementation. + * @param data Data to send as msg.data in the low level call. + * It should include the signature and the parameters of the function to be called, as described in + * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. + */ + function upgradeToAndCall(address newImplementation, bytes calldata data) + external + payable + ifAdmin + { + _upgradeTo(newImplementation); + (bool success, ) = newImplementation.delegatecall(data); + require(success); + } + + /** + * @return adm The admin slot. + */ + function _admin() internal view returns (address adm) { + bytes32 slot = ADMIN_SLOT; + //solium-disable-next-line + assembly { + adm := sload(slot) + } + } + + /** + * @dev Sets the address of the proxy admin. + * @param newAdmin Address of the new proxy admin. + */ + function _setAdmin(address newAdmin) internal { + bytes32 slot = ADMIN_SLOT; + //solium-disable-next-line + assembly { + sstore(slot, newAdmin) + } + } + + /** + * @dev Only fall back when the sender is not the admin. + */ + function _willFallback() internal virtual override { + require(msg.sender != _admin(), 'Cannot call fallback function from the proxy admin'); + super._willFallback(); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/BaseUpgradeabilityProxy.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/BaseUpgradeabilityProxy.sol new file mode 100644 index 000000000..c80a2a52b --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/BaseUpgradeabilityProxy.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import './Proxy.sol'; +import '../contracts/Address.sol'; + +/** + * @title BaseUpgradeabilityProxy + * @dev This contract implements a proxy that allows to change the + * implementation address to which it will delegate. + * Such a change is called an implementation upgrade. + */ +contract BaseUpgradeabilityProxy is Proxy { + /** + * @dev Emitted when the implementation is upgraded. + * @param implementation Address of the new implementation. + */ + event Upgraded(address indexed implementation); + + /** + * @dev Storage slot with the address of the current implementation. + * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is + * validated in the constructor. + */ + bytes32 internal constant IMPLEMENTATION_SLOT = + 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + + /** + * @dev Returns the current implementation. + * @return impl Address of the current implementation + */ + function _implementation() internal view override returns (address impl) { + bytes32 slot = IMPLEMENTATION_SLOT; + //solium-disable-next-line + assembly { + impl := sload(slot) + } + } + + /** + * @dev Upgrades the proxy to a new implementation. + * @param newImplementation Address of the new implementation. + */ + function _upgradeTo(address newImplementation) internal { + _setImplementation(newImplementation); + emit Upgraded(newImplementation); + } + + /** + * @dev Sets the implementation address of the proxy. + * @param newImplementation Address of the new implementation. + */ + function _setImplementation(address newImplementation) internal { + require( + Address.isContract(newImplementation), + 'Cannot set a proxy implementation to a non-contract address' + ); + + bytes32 slot = IMPLEMENTATION_SLOT; + + //solium-disable-next-line + assembly { + sstore(slot, newImplementation) + } + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/InitializableAdminUpgradeabilityProxy.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/InitializableAdminUpgradeabilityProxy.sol new file mode 100644 index 000000000..83c60e506 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/InitializableAdminUpgradeabilityProxy.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import './BaseAdminUpgradeabilityProxy.sol'; +import './InitializableUpgradeabilityProxy.sol'; + +/** + * @title InitializableAdminUpgradeabilityProxy + * @dev Extends from BaseAdminUpgradeabilityProxy with an initializer for + * initializing the implementation, admin, and init data. + */ +contract InitializableAdminUpgradeabilityProxy is + BaseAdminUpgradeabilityProxy, + InitializableUpgradeabilityProxy +{ + /** + * Contract initializer. + * @param logic address of the initial implementation. + * @param admin Address of the proxy administrator. + * @param data Data to send as msg.data to the implementation to initialize the proxied contract. + * It should include the signature and the parameters of the function to be called, as described in + * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. + * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped. + */ + function initialize( + address logic, + address admin, + bytes memory data + ) public payable { + require(_implementation() == address(0)); + InitializableUpgradeabilityProxy.initialize(logic, data); + assert(ADMIN_SLOT == bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)); + _setAdmin(admin); + } + + /** + * @dev Only fall back when the sender is not the admin. + */ + function _willFallback() internal override(BaseAdminUpgradeabilityProxy, Proxy) { + BaseAdminUpgradeabilityProxy._willFallback(); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/InitializableUpgradeabilityProxy.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/InitializableUpgradeabilityProxy.sol new file mode 100644 index 000000000..183d400f0 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/InitializableUpgradeabilityProxy.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import './BaseUpgradeabilityProxy.sol'; + +/** + * @title InitializableUpgradeabilityProxy + * @dev Extends BaseUpgradeabilityProxy with an initializer for initializing + * implementation and init data. + */ +contract InitializableUpgradeabilityProxy is BaseUpgradeabilityProxy { + /** + * @dev Contract initializer. + * @param _logic Address of the initial implementation. + * @param _data Data to send as msg.data to the implementation to initialize the proxied contract. + * It should include the signature and the parameters of the function to be called, as described in + * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. + * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped. + */ + function initialize(address _logic, bytes memory _data) public payable { + require(_implementation() == address(0)); + assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)); + _setImplementation(_logic); + if (_data.length > 0) { + (bool success, ) = _logic.delegatecall(_data); + require(success); + } + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/Proxy.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/Proxy.sol new file mode 100644 index 000000000..276073529 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/Proxy.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.6.0; + +/** + * @title Proxy + * @dev Implements delegation of calls to other contracts, with proper + * forwarding of return values and bubbling of failures. + * It defines a fallback function that delegates all calls to the address + * returned by the abstract _implementation() internal function. + */ +abstract contract Proxy { + /** + * @dev Fallback function. + * Implemented entirely in `_fallback`. + */ + fallback() external payable { + _fallback(); + } + + /** + * @return The Address of the implementation. + */ + function _implementation() internal view virtual returns (address); + + /** + * @dev Delegates execution to an implementation contract. + * This is a low level function that doesn't return to its internal call site. + * It will return to the external caller whatever the implementation returns. + * @param implementation Address to delegate. + */ + function _delegate(address implementation) internal { + //solium-disable-next-line + assembly { + // Copy msg.data. We take full control of memory in this inline assembly + // block because it will not return to Solidity code. We overwrite the + // Solidity scratch pad at memory position 0. + calldatacopy(0, 0, calldatasize()) + + // Call the implementation. + // out and outsize are 0 because we don't know the size yet. + let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) + + // Copy the returned data. + returndatacopy(0, 0, returndatasize()) + + switch result + // delegatecall returns 0 on error. + case 0 { + revert(0, returndatasize()) + } + default { + return(0, returndatasize()) + } + } + } + + /** + * @dev Function that is run as the first thing in the fallback function. + * Can be redefined in derived contracts to add functionality. + * Redefinitions must call super._willFallback(). + */ + function _willFallback() internal virtual {} + + /** + * @dev fallback implementation. + * Extracted to enable manual triggering. + */ + function _fallback() internal { + _willFallback(); + _delegate(_implementation()); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/UpgradeabilityProxy.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/UpgradeabilityProxy.sol new file mode 100644 index 000000000..3427ea423 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/dependencies/openzeppelin/upgradeability/UpgradeabilityProxy.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import './BaseUpgradeabilityProxy.sol'; + +/** + * @title UpgradeabilityProxy + * @dev Extends BaseUpgradeabilityProxy with a constructor for initializing + * implementation and init data. + */ +contract UpgradeabilityProxy is BaseUpgradeabilityProxy { + /** + * @dev Contract constructor. + * @param _logic Address of the initial implementation. + * @param _data Data to send as msg.data to the implementation to initialize the proxied contract. + * It should include the signature and the parameters of the function to be called, as described in + * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. + * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped. + */ + constructor(address _logic, bytes memory _data) public payable { + assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)); + _setImplementation(_logic); + if (_data.length > 0) { + (bool success, ) = _logic.delegatecall(_data); + require(success); + } + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/deployments/ATokensAndRatesHelper.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/deployments/ATokensAndRatesHelper.sol new file mode 100644 index 000000000..cda8c51e1 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/deployments/ATokensAndRatesHelper.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {LendingPool} from '../protocol/lendingpool/LendingPool.sol'; +import { + LendingPoolAddressesProvider +} from '../protocol/configuration/LendingPoolAddressesProvider.sol'; +import {LendingPoolConfigurator} from '../protocol/lendingpool/LendingPoolConfigurator.sol'; +import {AToken} from '../protocol/tokenization/AToken.sol'; +import { + DefaultReserveInterestRateStrategy +} from '../protocol/lendingpool/DefaultReserveInterestRateStrategy.sol'; +import {Ownable} from '../dependencies/openzeppelin/contracts/Ownable.sol'; +import {StringLib} from './StringLib.sol'; + +contract ATokensAndRatesHelper is Ownable { + address payable private pool; + address private addressesProvider; + address private poolConfigurator; + event deployedContracts(address aToken, address strategy); + + struct InitDeploymentInput { + address asset; + uint256[6] rates; + } + + struct ConfigureReserveInput { + address asset; + uint256 baseLTV; + uint256 liquidationThreshold; + uint256 liquidationBonus; + uint256 reserveFactor; + bool stableBorrowingEnabled; + } + + constructor( + address payable _pool, + address _addressesProvider, + address _poolConfigurator + ) public { + pool = _pool; + addressesProvider = _addressesProvider; + poolConfigurator = _poolConfigurator; + } + + function initDeployment(InitDeploymentInput[] calldata inputParams) external onlyOwner { + for (uint256 i = 0; i < inputParams.length; i++) { + emit deployedContracts( + address(new AToken()), + address( + new DefaultReserveInterestRateStrategy( + LendingPoolAddressesProvider(addressesProvider), + inputParams[i].rates[0], + inputParams[i].rates[1], + inputParams[i].rates[2], + inputParams[i].rates[3], + inputParams[i].rates[4], + inputParams[i].rates[5] + ) + ) + ); + } + } + + function configureReserves(ConfigureReserveInput[] calldata inputParams) external onlyOwner { + LendingPoolConfigurator configurator = LendingPoolConfigurator(poolConfigurator); + for (uint256 i = 0; i < inputParams.length; i++) { + configurator.configureReserveAsCollateral( + inputParams[i].asset, + inputParams[i].baseLTV, + inputParams[i].liquidationThreshold, + inputParams[i].liquidationBonus + ); + + configurator.enableBorrowingOnReserve( + inputParams[i].asset, + inputParams[i].stableBorrowingEnabled + ); + configurator.setReserveFactor(inputParams[i].asset, inputParams[i].reserveFactor); + } + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/deployments/StableAndVariableTokensHelper.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/deployments/StableAndVariableTokensHelper.sol new file mode 100644 index 000000000..cefbb45c7 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/deployments/StableAndVariableTokensHelper.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {StableDebtToken} from '../protocol/tokenization/StableDebtToken.sol'; +import {VariableDebtToken} from '../protocol/tokenization/VariableDebtToken.sol'; +import {LendingRateOracle} from '../mocks/oracle/LendingRateOracle.sol'; +import {Ownable} from '../dependencies/openzeppelin/contracts/Ownable.sol'; +import {StringLib} from './StringLib.sol'; + +contract StableAndVariableTokensHelper is Ownable { + address payable private pool; + address private addressesProvider; + event deployedContracts(address stableToken, address variableToken); + + constructor(address payable _pool, address _addressesProvider) public { + pool = _pool; + addressesProvider = _addressesProvider; + } + + function initDeployment(address[] calldata tokens, string[] calldata symbols) external onlyOwner { + require(tokens.length == symbols.length, 'Arrays not same length'); + require(pool != address(0), 'Pool can not be zero address'); + for (uint256 i = 0; i < tokens.length; i++) { + emit deployedContracts(address(new StableDebtToken()), address(new VariableDebtToken())); + } + } + + function setOracleBorrowRates( + address[] calldata assets, + uint256[] calldata rates, + address oracle + ) external onlyOwner { + require(assets.length == rates.length, 'Arrays not same length'); + + for (uint256 i = 0; i < assets.length; i++) { + // LendingRateOracle owner must be this contract + LendingRateOracle(oracle).setMarketBorrowRate(assets[i], rates[i]); + } + } + + function setOracleOwnership(address oracle, address admin) external onlyOwner { + require(admin != address(0), 'owner can not be zero'); + require(LendingRateOracle(oracle).owner() == address(this), 'helper is not owner'); + LendingRateOracle(oracle).transferOwnership(admin); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/deployments/StringLib.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/deployments/StringLib.sol new file mode 100644 index 000000000..0567849aa --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/deployments/StringLib.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +library StringLib { + function concat(string memory a, string memory b) internal pure returns (string memory) { + return string(abi.encodePacked(a, b)); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/flashloan/base/FlashLoanReceiverBase.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/flashloan/base/FlashLoanReceiverBase.sol new file mode 100644 index 000000000..e4a20591c --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/flashloan/base/FlashLoanReceiverBase.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol'; +import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol'; +import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol'; +import {IFlashLoanReceiver} from '../interfaces/IFlashLoanReceiver.sol'; +import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol'; +import {ILendingPool} from '../../interfaces/ILendingPool.sol'; + +abstract contract FlashLoanReceiverBase is IFlashLoanReceiver { + using SafeERC20 for IERC20; + using SafeMath for uint256; + + ILendingPoolAddressesProvider public immutable override ADDRESSES_PROVIDER; + ILendingPool public immutable override LENDING_POOL; + + constructor(ILendingPoolAddressesProvider provider) public { + ADDRESSES_PROVIDER = provider; + LENDING_POOL = ILendingPool(provider.getLendingPool()); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/flashloan/interfaces/IFlashLoanReceiver.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/flashloan/interfaces/IFlashLoanReceiver.sol new file mode 100644 index 000000000..ec2d267ff --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/flashloan/interfaces/IFlashLoanReceiver.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol'; +import {ILendingPool} from '../../interfaces/ILendingPool.sol'; + +/** + * @title IFlashLoanReceiver interface + * @notice Interface for the Aave fee IFlashLoanReceiver. + * @author Aave + * @dev implement this interface to develop a flashloan-compatible flashLoanReceiver contract + **/ +interface IFlashLoanReceiver { + function executeOperation( + address[] calldata assets, + uint256[] calldata amounts, + uint256[] calldata premiums, + address initiator, + bytes calldata params + ) external returns (bool); + + function ADDRESSES_PROVIDER() external view returns (ILendingPoolAddressesProvider); + + function LENDING_POOL() external view returns (ILendingPool); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IAToken.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IAToken.sol new file mode 100644 index 000000000..0bcc9f874 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IAToken.sol @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; +import {IScaledBalanceToken} from './IScaledBalanceToken.sol'; +import {IInitializableAToken} from './IInitializableAToken.sol'; +import {IAaveIncentivesController} from './IAaveIncentivesController.sol'; + +interface IAToken is IERC20, IScaledBalanceToken, IInitializableAToken { + /** + * @dev Emitted after the mint action + * @param from The address performing the mint + * @param value The amount being + * @param index The new liquidity index of the reserve + **/ + event Mint(address indexed from, uint256 value, uint256 index); + + /** + * @dev Mints `amount` aTokens to `user` + * @param user The address receiving the minted tokens + * @param amount The amount of tokens getting minted + * @param index The new liquidity index of the reserve + * @return `true` if the the previous balance of the user was 0 + */ + function mint( + address user, + uint256 amount, + uint256 index + ) external returns (bool); + + /** + * @dev Emitted after aTokens are burned + * @param from The owner of the aTokens, getting them burned + * @param target The address that will receive the underlying + * @param value The amount being burned + * @param index The new liquidity index of the reserve + **/ + event Burn(address indexed from, address indexed target, uint256 value, uint256 index); + + /** + * @dev Emitted during the transfer action + * @param from The user whose tokens are being transferred + * @param to The recipient + * @param value The amount being transferred + * @param index The new liquidity index of the reserve + **/ + event BalanceTransfer(address indexed from, address indexed to, uint256 value, uint256 index); + + /** + * @dev Burns aTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying` + * @param user The owner of the aTokens, getting them burned + * @param receiverOfUnderlying The address that will receive the underlying + * @param amount The amount being burned + * @param index The new liquidity index of the reserve + **/ + function burn( + address user, + address receiverOfUnderlying, + uint256 amount, + uint256 index + ) external; + + /** + * @dev Mints aTokens to the reserve treasury + * @param amount The amount of tokens getting minted + * @param index The new liquidity index of the reserve + */ + function mintToTreasury(uint256 amount, uint256 index) external; + + /** + * @dev Transfers aTokens in the event of a borrow being liquidated, in case the liquidators reclaims the aToken + * @param from The address getting liquidated, current owner of the aTokens + * @param to The recipient + * @param value The amount of tokens getting transferred + **/ + function transferOnLiquidation( + address from, + address to, + uint256 value + ) external; + + /** + * @dev Transfers the underlying asset to `target`. Used by the LendingPool to transfer + * assets in borrow(), withdraw() and flashLoan() + * @param user The recipient of the underlying + * @param amount The amount getting transferred + * @return The amount transferred + **/ + function transferUnderlyingTo(address user, uint256 amount) external returns (uint256); + + /** + * @dev Invoked to execute actions on the aToken side after a repayment. + * @param user The user executing the repayment + * @param amount The amount getting repaid + **/ + function handleRepayment(address user, uint256 amount) external; + + /** + * @dev Returns the address of the incentives controller contract + **/ + function getIncentivesController() external view returns (IAaveIncentivesController); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IAaveIncentivesController.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IAaveIncentivesController.sol new file mode 100644 index 000000000..9e5c09c4a --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IAaveIncentivesController.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +interface IAaveIncentivesController { + function handleAction( + address user, + uint256 userBalance, + uint256 totalSupply + ) external; +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IChainlinkAggregator.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IChainlinkAggregator.sol new file mode 100644 index 000000000..30aeddc8e --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IChainlinkAggregator.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +interface IChainlinkAggregator { + function latestAnswer() external view returns (int256); + + function latestTimestamp() external view returns (uint256); + + function latestRound() external view returns (uint256); + + function getAnswer(uint256 roundId) external view returns (int256); + + function getTimestamp(uint256 roundId) external view returns (uint256); + + event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp); + event NewRound(uint256 indexed roundId, address indexed startedBy); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ICreditDelegationToken.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ICreditDelegationToken.sol new file mode 100644 index 000000000..fa08e2a1e --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ICreditDelegationToken.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +interface ICreditDelegationToken { + event BorrowAllowanceDelegated( + address indexed fromUser, + address indexed toUser, + address asset, + uint256 amount + ); + + /** + * @dev delegates borrowing power to a user on the specific debt token + * @param delegatee the address receiving the delegated borrowing power + * @param amount the maximum amount being delegated. Delegation will still + * respect the liquidation constraints (even if delegated, a delegatee cannot + * force a delegator HF to go below 1) + **/ + function approveDelegation(address delegatee, uint256 amount) external; + + /** + * @dev returns the borrow allowance of the user + * @param fromUser The user to giving allowance + * @param toUser The user to give allowance to + * @return the current allowance of toUser + **/ + function borrowAllowance(address fromUser, address toUser) external view returns (uint256); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IDelegationToken.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IDelegationToken.sol new file mode 100644 index 000000000..f76f302aa --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IDelegationToken.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +/** + * @title IDelegationToken + * @dev Implements an interface for tokens with delegation COMP/UNI compatible + * @author Aave + **/ +interface IDelegationToken { + function delegate(address delegatee) external; +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IERC20WithPermit.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IERC20WithPermit.sol new file mode 100644 index 000000000..90f4552be --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IERC20WithPermit.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; + +interface IERC20WithPermit is IERC20 { + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IExchangeAdapter.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IExchangeAdapter.sol new file mode 100644 index 000000000..a583f34b5 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IExchangeAdapter.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; + +interface IExchangeAdapter { + event Exchange( + address indexed from, + address indexed to, + address indexed platform, + uint256 fromAmount, + uint256 toAmount + ); + + function approveExchange(IERC20[] calldata tokens) external; + + function exchange( + address from, + address to, + uint256 amount, + uint256 maxSlippage + ) external returns (uint256); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IInitializableAToken.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IInitializableAToken.sol new file mode 100644 index 000000000..73fe3f093 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IInitializableAToken.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {ILendingPool} from './ILendingPool.sol'; +import {IAaveIncentivesController} from './IAaveIncentivesController.sol'; + +/** + * @title IInitializableAToken + * @notice Interface for the initialize function on AToken + * @author Aave + **/ +interface IInitializableAToken { + /** + * @dev Emitted when an aToken is initialized + * @param underlyingAsset The address of the underlying asset + * @param pool The address of the associated lending pool + * @param treasury The address of the treasury + * @param incentivesController The address of the incentives controller for this aToken + * @param aTokenDecimals the decimals of the underlying + * @param aTokenName the name of the aToken + * @param aTokenSymbol the symbol of the aToken + * @param params A set of encoded parameters for additional initialization + **/ + event Initialized( + address indexed underlyingAsset, + address indexed pool, + address treasury, + address incentivesController, + uint8 aTokenDecimals, + string aTokenName, + string aTokenSymbol, + bytes params + ); + + /** + * @dev Initializes the aToken + * @param pool The address of the lending pool where this aToken will be used + * @param treasury The address of the Aave treasury, receiving the fees on this aToken + * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH) + * @param incentivesController The smart contract managing potential incentives distribution + * @param aTokenDecimals The decimals of the aToken, same as the underlying asset's + * @param aTokenName The name of the aToken + * @param aTokenSymbol The symbol of the aToken + */ + function initialize( + ILendingPool pool, + address treasury, + address underlyingAsset, + IAaveIncentivesController incentivesController, + uint8 aTokenDecimals, + string calldata aTokenName, + string calldata aTokenSymbol, + bytes calldata params + ) external; +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IInitializableDebtToken.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IInitializableDebtToken.sol new file mode 100644 index 000000000..725a7a6e6 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IInitializableDebtToken.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {ILendingPool} from './ILendingPool.sol'; +import {IAaveIncentivesController} from './IAaveIncentivesController.sol'; + +/** + * @title IInitializableDebtToken + * @notice Interface for the initialize function common between debt tokens + * @author Aave + **/ +interface IInitializableDebtToken { + /** + * @dev Emitted when a debt token is initialized + * @param underlyingAsset The address of the underlying asset + * @param pool The address of the associated lending pool + * @param incentivesController The address of the incentives controller for this aToken + * @param debtTokenDecimals the decimals of the debt token + * @param debtTokenName the name of the debt token + * @param debtTokenSymbol the symbol of the debt token + * @param params A set of encoded parameters for additional initialization + **/ + event Initialized( + address indexed underlyingAsset, + address indexed pool, + address incentivesController, + uint8 debtTokenDecimals, + string debtTokenName, + string debtTokenSymbol, + bytes params + ); + + /** + * @dev Initializes the debt token. + * @param pool The address of the lending pool where this aToken will be used + * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH) + * @param incentivesController The smart contract managing potential incentives distribution + * @param debtTokenDecimals The decimals of the debtToken, same as the underlying asset's + * @param debtTokenName The name of the token + * @param debtTokenSymbol The symbol of the token + */ + function initialize( + ILendingPool pool, + address underlyingAsset, + IAaveIncentivesController incentivesController, + uint8 debtTokenDecimals, + string memory debtTokenName, + string memory debtTokenSymbol, + bytes calldata params + ) external; +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingPool.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingPool.sol new file mode 100644 index 000000000..4b49fffc8 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingPool.sol @@ -0,0 +1,410 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {ILendingPoolAddressesProvider} from './ILendingPoolAddressesProvider.sol'; +import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; + +interface ILendingPool { + /** + * @dev Emitted on deposit() + * @param reserve The address of the underlying asset of the reserve + * @param user The address initiating the deposit + * @param onBehalfOf The beneficiary of the deposit, receiving the aTokens + * @param amount The amount deposited + * @param referral The referral code used + **/ + event Deposit( + address indexed reserve, + address user, + address indexed onBehalfOf, + uint256 amount, + uint16 indexed referral + ); + + /** + * @dev Emitted on withdraw() + * @param reserve The address of the underlyng asset being withdrawn + * @param user The address initiating the withdrawal, owner of aTokens + * @param to Address that will receive the underlying + * @param amount The amount to be withdrawn + **/ + event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount); + + /** + * @dev Emitted on borrow() and flashLoan() when debt needs to be opened + * @param reserve The address of the underlying asset being borrowed + * @param user The address of the user initiating the borrow(), receiving the funds on borrow() or just + * initiator of the transaction on flashLoan() + * @param onBehalfOf The address that will be getting the debt + * @param amount The amount borrowed out + * @param borrowRateMode The rate mode: 1 for Stable, 2 for Variable + * @param borrowRate The numeric rate at which the user has borrowed + * @param referral The referral code used + **/ + event Borrow( + address indexed reserve, + address user, + address indexed onBehalfOf, + uint256 amount, + uint256 borrowRateMode, + uint256 borrowRate, + uint16 indexed referral + ); + + /** + * @dev Emitted on repay() + * @param reserve The address of the underlying asset of the reserve + * @param user The beneficiary of the repayment, getting his debt reduced + * @param repayer The address of the user initiating the repay(), providing the funds + * @param amount The amount repaid + **/ + event Repay( + address indexed reserve, + address indexed user, + address indexed repayer, + uint256 amount + ); + + /** + * @dev Emitted on swapBorrowRateMode() + * @param reserve The address of the underlying asset of the reserve + * @param user The address of the user swapping his rate mode + * @param rateMode The rate mode that the user wants to swap to + **/ + event Swap(address indexed reserve, address indexed user, uint256 rateMode); + + /** + * @dev Emitted on setUserUseReserveAsCollateral() + * @param reserve The address of the underlying asset of the reserve + * @param user The address of the user enabling the usage as collateral + **/ + event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user); + + /** + * @dev Emitted on setUserUseReserveAsCollateral() + * @param reserve The address of the underlying asset of the reserve + * @param user The address of the user enabling the usage as collateral + **/ + event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user); + + /** + * @dev Emitted on rebalanceStableBorrowRate() + * @param reserve The address of the underlying asset of the reserve + * @param user The address of the user for which the rebalance has been executed + **/ + event RebalanceStableBorrowRate(address indexed reserve, address indexed user); + + /** + * @dev Emitted on flashLoan() + * @param target The address of the flash loan receiver contract + * @param initiator The address initiating the flash loan + * @param asset The address of the asset being flash borrowed + * @param amount The amount flash borrowed + * @param premium The fee flash borrowed + * @param referralCode The referral code used + **/ + event FlashLoan( + address indexed target, + address indexed initiator, + address indexed asset, + uint256 amount, + uint256 premium, + uint16 referralCode + ); + + /** + * @dev Emitted when the pause is triggered. + */ + event Paused(); + + /** + * @dev Emitted when the pause is lifted. + */ + event Unpaused(); + + /** + * @dev Emitted when a borrower is liquidated. This event is emitted by the LendingPool via + * LendingPoolCollateral manager using a DELEGATECALL + * This allows to have the events in the generated ABI for LendingPool. + * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation + * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation + * @param user The address of the borrower getting liquidated + * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover + * @param liquidatedCollateralAmount The amount of collateral received by the liiquidator + * @param liquidator The address of the liquidator + * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants + * to receive the underlying collateral asset directly + **/ + event LiquidationCall( + address indexed collateralAsset, + address indexed debtAsset, + address indexed user, + uint256 debtToCover, + uint256 liquidatedCollateralAmount, + address liquidator, + bool receiveAToken + ); + + /** + * @dev Emitted when the state of a reserve is updated. NOTE: This event is actually declared + * in the ReserveLogic library and emitted in the updateInterestRates() function. Since the function is internal, + * the event will actually be fired by the LendingPool contract. The event is therefore replicated here so it + * gets added to the LendingPool ABI + * @param reserve The address of the underlying asset of the reserve + * @param liquidityRate The new liquidity rate + * @param stableBorrowRate The new stable borrow rate + * @param variableBorrowRate The new variable borrow rate + * @param liquidityIndex The new liquidity index + * @param variableBorrowIndex The new variable borrow index + **/ + event ReserveDataUpdated( + address indexed reserve, + uint256 liquidityRate, + uint256 stableBorrowRate, + uint256 variableBorrowRate, + uint256 liquidityIndex, + uint256 variableBorrowIndex + ); + + /** + * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens. + * - E.g. User deposits 100 USDC and gets in return 100 aUSDC + * @param asset The address of the underlying asset to deposit + * @param amount The amount to be deposited + * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user + * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens + * is a different wallet + * @param referralCode Code used to register the integrator originating the operation, for potential rewards. + * 0 if the action is executed directly by the user, without any middle-man + **/ + function deposit( + address asset, + uint256 amount, + address onBehalfOf, + uint16 referralCode + ) external; + + /** + * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned + * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC + * @param asset The address of the underlying asset to withdraw + * @param amount The underlying amount to be withdrawn + * - Send the value type(uint256).max in order to withdraw the whole aToken balance + * @param to Address that will receive the underlying, same as msg.sender if the user + * wants to receive it on his own wallet, or a different address if the beneficiary is a + * different wallet + * @return The final amount withdrawn + **/ + function withdraw( + address asset, + uint256 amount, + address to + ) external returns (uint256); + + /** + * @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower + * already deposited enough collateral, or he was given enough allowance by a credit delegator on the + * corresponding debt token (StableDebtToken or VariableDebtToken) + * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet + * and 100 stable/variable debt tokens, depending on the `interestRateMode` + * @param asset The address of the underlying asset to borrow + * @param amount The amount to be borrowed + * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable + * @param referralCode Code used to register the integrator originating the operation, for potential rewards. + * 0 if the action is executed directly by the user, without any middle-man + * @param onBehalfOf Address of the user who will receive the debt. Should be the address of the borrower itself + * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator + * if he has been given credit delegation allowance + **/ + function borrow( + address asset, + uint256 amount, + uint256 interestRateMode, + uint16 referralCode, + address onBehalfOf + ) external; + + /** + * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned + * - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address + * @param asset The address of the borrowed underlying asset previously borrowed + * @param amount The amount to repay + * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode` + * @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable + * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the + * user calling the function if he wants to reduce/remove his own debt, or the address of any other + * other borrower whose debt should be removed + * @return The final amount repaid + **/ + function repay( + address asset, + uint256 amount, + uint256 rateMode, + address onBehalfOf + ) external returns (uint256); + + /** + * @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa + * @param asset The address of the underlying asset borrowed + * @param rateMode The rate mode that the user wants to swap to + **/ + function swapBorrowRateMode(address asset, uint256 rateMode) external; + + /** + * @dev Rebalances the stable interest rate of a user to the current stable rate defined on the reserve. + * - Users can be rebalanced if the following conditions are satisfied: + * 1. Usage ratio is above 95% + * 2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been + * borrowed at a stable rate and depositors are not earning enough + * @param asset The address of the underlying asset borrowed + * @param user The address of the user to be rebalanced + **/ + function rebalanceStableBorrowRate(address asset, address user) external; + + /** + * @dev Allows depositors to enable/disable a specific deposited asset as collateral + * @param asset The address of the underlying asset deposited + * @param useAsCollateral `true` if the user wants to use the deposit as collateral, `false` otherwise + **/ + function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external; + + /** + * @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1 + * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives + * a proportionally amount of the `collateralAsset` plus a bonus to cover market risk + * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation + * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation + * @param user The address of the borrower getting liquidated + * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover + * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants + * to receive the underlying collateral asset directly + **/ + function liquidationCall( + address collateralAsset, + address debtAsset, + address user, + uint256 debtToCover, + bool receiveAToken + ) external; + + /** + * @dev Allows smartcontracts to access the liquidity of the pool within one transaction, + * as long as the amount taken plus a fee is returned. + * IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration. + * For further details please visit https://developers.aave.com + * @param receiverAddress The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface + * @param assets The addresses of the assets being flash-borrowed + * @param amounts The amounts amounts being flash-borrowed + * @param modes Types of the debt to open if the flash loan is not returned: + * 0 -> Don't open any debt, just revert if funds can't be transferred from the receiver + * 1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address + * 2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address + * @param onBehalfOf The address that will receive the debt in the case of using on `modes` 1 or 2 + * @param params Variadic packed params to pass to the receiver as extra information + * @param referralCode Code used to register the integrator originating the operation, for potential rewards. + * 0 if the action is executed directly by the user, without any middle-man + **/ + function flashLoan( + address receiverAddress, + address[] calldata assets, + uint256[] calldata amounts, + uint256[] calldata modes, + address onBehalfOf, + bytes calldata params, + uint16 referralCode + ) external; + + /** + * @dev Returns the user account data across all the reserves + * @param user The address of the user + * @return totalCollateralETH the total collateral in ETH of the user + * @return totalDebtETH the total debt in ETH of the user + * @return availableBorrowsETH the borrowing power left of the user + * @return currentLiquidationThreshold the liquidation threshold of the user + * @return ltv the loan to value of the user + * @return healthFactor the current health factor of the user + **/ + function getUserAccountData(address user) + external + view + returns ( + uint256 totalCollateralETH, + uint256 totalDebtETH, + uint256 availableBorrowsETH, + uint256 currentLiquidationThreshold, + uint256 ltv, + uint256 healthFactor + ); + + function initReserve( + address reserve, + address aTokenAddress, + address stableDebtAddress, + address variableDebtAddress, + address interestRateStrategyAddress + ) external; + + function setReserveInterestRateStrategyAddress(address reserve, address rateStrategyAddress) + external; + + function setConfiguration(address reserve, uint256 configuration) external; + + /** + * @dev Returns the configuration of the reserve + * @param asset The address of the underlying asset of the reserve + * @return The configuration of the reserve + **/ + function getConfiguration(address asset) + external + view + returns (DataTypes.ReserveConfigurationMap memory); + + /** + * @dev Returns the configuration of the user across all the reserves + * @param user The user address + * @return The configuration of the user + **/ + function getUserConfiguration(address user) + external + view + returns (DataTypes.UserConfigurationMap memory); + + /** + * @dev Returns the normalized income normalized income of the reserve + * @param asset The address of the underlying asset of the reserve + * @return The reserve's normalized income + */ + function getReserveNormalizedIncome(address asset) external view returns (uint256); + + /** + * @dev Returns the normalized variable debt per unit of asset + * @param asset The address of the underlying asset of the reserve + * @return The reserve normalized variable debt + */ + function getReserveNormalizedVariableDebt(address asset) external view returns (uint256); + + /** + * @dev Returns the state and configuration of the reserve + * @param asset The address of the underlying asset of the reserve + * @return The state of the reserve + **/ + function getReserveData(address asset) external view returns (DataTypes.ReserveData memory); + + function finalizeTransfer( + address asset, + address from, + address to, + uint256 amount, + uint256 balanceFromAfter, + uint256 balanceToBefore + ) external; + + function getReservesList() external view returns (address[] memory); + + function getAddressesProvider() external view returns (ILendingPoolAddressesProvider); + + function setPause(bool val) external; + + function paused() external view returns (bool); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingPoolAddressesProvider.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingPoolAddressesProvider.sol new file mode 100644 index 000000000..89b217869 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingPoolAddressesProvider.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +/** + * @title LendingPoolAddressesProvider contract + * @dev Main registry of addresses part of or connected to the protocol, including permissioned roles + * - Acting also as factory of proxies and admin of those, so with right to change its implementations + * - Owned by the Aave Governance + * @author Aave + **/ +interface ILendingPoolAddressesProvider { + event MarketIdSet(string newMarketId); + event LendingPoolUpdated(address indexed newAddress); + event ConfigurationAdminUpdated(address indexed newAddress); + event EmergencyAdminUpdated(address indexed newAddress); + event LendingPoolConfiguratorUpdated(address indexed newAddress); + event LendingPoolCollateralManagerUpdated(address indexed newAddress); + event PriceOracleUpdated(address indexed newAddress); + event LendingRateOracleUpdated(address indexed newAddress); + event ProxyCreated(bytes32 id, address indexed newAddress); + event AddressSet(bytes32 id, address indexed newAddress, bool hasProxy); + + function getMarketId() external view returns (string memory); + + function setMarketId(string calldata marketId) external; + + function setAddress(bytes32 id, address newAddress) external; + + function setAddressAsProxy(bytes32 id, address impl) external; + + function getAddress(bytes32 id) external view returns (address); + + function getLendingPool() external view returns (address); + + function setLendingPoolImpl(address pool) external; + + function getLendingPoolConfigurator() external view returns (address); + + function setLendingPoolConfiguratorImpl(address configurator) external; + + function getLendingPoolCollateralManager() external view returns (address); + + function setLendingPoolCollateralManager(address manager) external; + + function getPoolAdmin() external view returns (address); + + function setPoolAdmin(address admin) external; + + function getEmergencyAdmin() external view returns (address); + + function setEmergencyAdmin(address admin) external; + + function getPriceOracle() external view returns (address); + + function setPriceOracle(address priceOracle) external; + + function getLendingRateOracle() external view returns (address); + + function setLendingRateOracle(address lendingRateOracle) external; +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingPoolAddressesProviderRegistry.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingPoolAddressesProviderRegistry.sol new file mode 100644 index 000000000..7d2304973 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingPoolAddressesProviderRegistry.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +/** + * @title LendingPoolAddressesProviderRegistry contract + * @dev Main registry of LendingPoolAddressesProvider of multiple Aave protocol's markets + * - Used for indexing purposes of Aave protocol's markets + * - The id assigned to a LendingPoolAddressesProvider refers to the market it is connected with, + * for example with `0` for the Aave main market and `1` for the next created + * @author Aave + **/ +interface ILendingPoolAddressesProviderRegistry { + event AddressesProviderRegistered(address indexed newAddress); + event AddressesProviderUnregistered(address indexed newAddress); + + function getAddressesProvidersList() external view returns (address[] memory); + + function getAddressesProviderIdByAddress(address addressesProvider) + external + view + returns (uint256); + + function registerAddressesProvider(address provider, uint256 id) external; + + function unregisterAddressesProvider(address provider) external; +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingPoolCollateralManager.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingPoolCollateralManager.sol new file mode 100644 index 000000000..f6352f4e1 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingPoolCollateralManager.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +/** + * @title ILendingPoolCollateralManager + * @author Aave + * @notice Defines the actions involving management of collateral in the protocol. + **/ +interface ILendingPoolCollateralManager { + /** + * @dev Emitted when a borrower is liquidated + * @param collateral The address of the collateral being liquidated + * @param principal The address of the reserve + * @param user The address of the user being liquidated + * @param debtToCover The total amount liquidated + * @param liquidatedCollateralAmount The amount of collateral being liquidated + * @param liquidator The address of the liquidator + * @param receiveAToken true if the liquidator wants to receive aTokens, false otherwise + **/ + event LiquidationCall( + address indexed collateral, + address indexed principal, + address indexed user, + uint256 debtToCover, + uint256 liquidatedCollateralAmount, + address liquidator, + bool receiveAToken + ); + + /** + * @dev Emitted when a reserve is disabled as collateral for an user + * @param reserve The address of the reserve + * @param user The address of the user + **/ + event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user); + + /** + * @dev Emitted when a reserve is enabled as collateral for an user + * @param reserve The address of the reserve + * @param user The address of the user + **/ + event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user); + + /** + * @dev Users can invoke this function to liquidate an undercollateralized position. + * @param collateral The address of the collateral to liquidated + * @param principal The address of the principal reserve + * @param user The address of the borrower + * @param debtToCover The amount of principal that the liquidator wants to repay + * @param receiveAToken true if the liquidators wants to receive the aTokens, false if + * he wants to receive the underlying asset directly + **/ + function liquidationCall( + address collateral, + address principal, + address user, + uint256 debtToCover, + bool receiveAToken + ) external returns (uint256, string memory); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingPoolConfigurator.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingPoolConfigurator.sol new file mode 100644 index 000000000..f06fba98c --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingPoolConfigurator.sol @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +interface ILendingPoolConfigurator { + struct InitReserveInput { + address aTokenImpl; + address stableDebtTokenImpl; + address variableDebtTokenImpl; + uint8 underlyingAssetDecimals; + address interestRateStrategyAddress; + address underlyingAsset; + address treasury; + address incentivesController; + string underlyingAssetName; + string aTokenName; + string aTokenSymbol; + string variableDebtTokenName; + string variableDebtTokenSymbol; + string stableDebtTokenName; + string stableDebtTokenSymbol; + bytes params; + } + + struct UpdateATokenInput { + address asset; + address treasury; + address incentivesController; + string name; + string symbol; + address implementation; + bytes params; + } + + struct UpdateDebtTokenInput { + address asset; + address incentivesController; + string name; + string symbol; + address implementation; + bytes params; + } + + /** + * @dev Emitted when a reserve is initialized. + * @param asset The address of the underlying asset of the reserve + * @param aToken The address of the associated aToken contract + * @param stableDebtToken The address of the associated stable rate debt token + * @param variableDebtToken The address of the associated variable rate debt token + * @param interestRateStrategyAddress The address of the interest rate strategy for the reserve + **/ + event ReserveInitialized( + address indexed asset, + address indexed aToken, + address stableDebtToken, + address variableDebtToken, + address interestRateStrategyAddress + ); + + /** + * @dev Emitted when borrowing is enabled on a reserve + * @param asset The address of the underlying asset of the reserve + * @param stableRateEnabled True if stable rate borrowing is enabled, false otherwise + **/ + event BorrowingEnabledOnReserve(address indexed asset, bool stableRateEnabled); + + /** + * @dev Emitted when borrowing is disabled on a reserve + * @param asset The address of the underlying asset of the reserve + **/ + event BorrowingDisabledOnReserve(address indexed asset); + + /** + * @dev Emitted when the collateralization risk parameters for the specified asset are updated. + * @param asset The address of the underlying asset of the reserve + * @param ltv The loan to value of the asset when used as collateral + * @param liquidationThreshold The threshold at which loans using this asset as collateral will be considered undercollateralized + * @param liquidationBonus The bonus liquidators receive to liquidate this asset + **/ + event CollateralConfigurationChanged( + address indexed asset, + uint256 ltv, + uint256 liquidationThreshold, + uint256 liquidationBonus + ); + + /** + * @dev Emitted when stable rate borrowing is enabled on a reserve + * @param asset The address of the underlying asset of the reserve + **/ + event StableRateEnabledOnReserve(address indexed asset); + + /** + * @dev Emitted when stable rate borrowing is disabled on a reserve + * @param asset The address of the underlying asset of the reserve + **/ + event StableRateDisabledOnReserve(address indexed asset); + + /** + * @dev Emitted when a reserve is activated + * @param asset The address of the underlying asset of the reserve + **/ + event ReserveActivated(address indexed asset); + + /** + * @dev Emitted when a reserve is deactivated + * @param asset The address of the underlying asset of the reserve + **/ + event ReserveDeactivated(address indexed asset); + + /** + * @dev Emitted when a reserve is frozen + * @param asset The address of the underlying asset of the reserve + **/ + event ReserveFrozen(address indexed asset); + + /** + * @dev Emitted when a reserve is unfrozen + * @param asset The address of the underlying asset of the reserve + **/ + event ReserveUnfrozen(address indexed asset); + + /** + * @dev Emitted when a reserve factor is updated + * @param asset The address of the underlying asset of the reserve + * @param factor The new reserve factor + **/ + event ReserveFactorChanged(address indexed asset, uint256 factor); + + /** + * @dev Emitted when the reserve decimals are updated + * @param asset The address of the underlying asset of the reserve + * @param decimals The new decimals + **/ + event ReserveDecimalsChanged(address indexed asset, uint256 decimals); + + /** + * @dev Emitted when a reserve interest strategy contract is updated + * @param asset The address of the underlying asset of the reserve + * @param strategy The new address of the interest strategy contract + **/ + event ReserveInterestRateStrategyChanged(address indexed asset, address strategy); + + /** + * @dev Emitted when an aToken implementation is upgraded + * @param asset The address of the underlying asset of the reserve + * @param proxy The aToken proxy address + * @param implementation The new aToken implementation + **/ + event ATokenUpgraded( + address indexed asset, + address indexed proxy, + address indexed implementation + ); + + /** + * @dev Emitted when the implementation of a stable debt token is upgraded + * @param asset The address of the underlying asset of the reserve + * @param proxy The stable debt token proxy address + * @param implementation The new aToken implementation + **/ + event StableDebtTokenUpgraded( + address indexed asset, + address indexed proxy, + address indexed implementation + ); + + /** + * @dev Emitted when the implementation of a variable debt token is upgraded + * @param asset The address of the underlying asset of the reserve + * @param proxy The variable debt token proxy address + * @param implementation The new aToken implementation + **/ + event VariableDebtTokenUpgraded( + address indexed asset, + address indexed proxy, + address indexed implementation + ); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingRateOracle.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingRateOracle.sol new file mode 100644 index 000000000..46bce9908 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/ILendingRateOracle.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +/** + * @title ILendingRateOracle interface + * @notice Interface for the Aave borrow rate oracle. Provides the average market borrow rate to be used as a base for the stable borrow rate calculations + **/ + +interface ILendingRateOracle { + /** + @dev returns the market borrow rate in ray + **/ + function getMarketBorrowRate(address asset) external view returns (uint256); + + /** + @dev sets the market borrow rate. Rate value must be in ray + **/ + function setMarketBorrowRate(address asset, uint256 rate) external; +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IPriceOracleGetter.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IPriceOracleGetter.sol new file mode 100644 index 000000000..c8b220707 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IPriceOracleGetter.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +/** + * @title IPriceOracleGetter interface + * @notice Interface for the Aave price oracle. + **/ + +interface IPriceOracleGetter { + /** + * @dev returns the asset price in ETH + * @param asset the address of the asset + * @return the ETH price of the asset + **/ + function getAssetPrice(address asset) external view returns (uint256); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IReserveInterestRateStrategy.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IReserveInterestRateStrategy.sol new file mode 100644 index 000000000..d902050b1 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IReserveInterestRateStrategy.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +/** + * @title IReserveInterestRateStrategyInterface interface + * @dev Interface for the calculation of the interest rates + * @author Aave + */ +interface IReserveInterestRateStrategy { + function baseVariableBorrowRate() external view returns (uint256); + + function getMaxVariableBorrowRate() external view returns (uint256); + + function calculateInterestRates( + address reserve, + uint256 availableLiquidity, + uint256 totalStableDebt, + uint256 totalVariableDebt, + uint256 averageStableBorrowRate, + uint256 reserveFactor + ) + external + view + returns ( + uint256, + uint256, + uint256 + ); + + function calculateInterestRates( + address reserve, + address aToken, + uint256 liquidityAdded, + uint256 liquidityTaken, + uint256 totalStableDebt, + uint256 totalVariableDebt, + uint256 averageStableBorrowRate, + uint256 reserveFactor + ) + external + view + returns ( + uint256 liquidityRate, + uint256 stableBorrowRate, + uint256 variableBorrowRate + ); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IScaledBalanceToken.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IScaledBalanceToken.sol new file mode 100644 index 000000000..cbf02ce08 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IScaledBalanceToken.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +interface IScaledBalanceToken { + /** + * @dev Returns the scaled balance of the user. The scaled balance is the sum of all the + * updated stored balance divided by the reserve's liquidity index at the moment of the update + * @param user The user whose balance is calculated + * @return The scaled balance of the user + **/ + function scaledBalanceOf(address user) external view returns (uint256); + + /** + * @dev Returns the scaled balance of the user and the scaled total supply. + * @param user The address of the user + * @return The scaled balance of the user + * @return The scaled balance and the scaled total supply + **/ + function getScaledUserBalanceAndSupply(address user) external view returns (uint256, uint256); + + /** + * @dev Returns the scaled total supply of the variable debt token. Represents sum(debt/index) + * @return The scaled total supply + **/ + function scaledTotalSupply() external view returns (uint256); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IStableDebtToken.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IStableDebtToken.sol new file mode 100644 index 000000000..1efef4011 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IStableDebtToken.sol @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {IInitializableDebtToken} from './IInitializableDebtToken.sol'; +import {IAaveIncentivesController} from './IAaveIncentivesController.sol'; + +/** + * @title IStableDebtToken + * @notice Defines the interface for the stable debt token + * @dev It does not inherit from IERC20 to save in code size + * @author Aave + **/ + +interface IStableDebtToken is IInitializableDebtToken { + /** + * @dev Emitted when new stable debt is minted + * @param user The address of the user who triggered the minting + * @param onBehalfOf The recipient of stable debt tokens + * @param amount The amount minted + * @param currentBalance The current balance of the user + * @param balanceIncrease The increase in balance since the last action of the user + * @param newRate The rate of the debt after the minting + * @param avgStableRate The new average stable rate after the minting + * @param newTotalSupply The new total supply of the stable debt token after the action + **/ + event Mint( + address indexed user, + address indexed onBehalfOf, + uint256 amount, + uint256 currentBalance, + uint256 balanceIncrease, + uint256 newRate, + uint256 avgStableRate, + uint256 newTotalSupply + ); + + /** + * @dev Emitted when new stable debt is burned + * @param user The address of the user + * @param amount The amount being burned + * @param currentBalance The current balance of the user + * @param balanceIncrease The the increase in balance since the last action of the user + * @param avgStableRate The new average stable rate after the burning + * @param newTotalSupply The new total supply of the stable debt token after the action + **/ + event Burn( + address indexed user, + uint256 amount, + uint256 currentBalance, + uint256 balanceIncrease, + uint256 avgStableRate, + uint256 newTotalSupply + ); + + /** + * @dev Mints debt token to the `onBehalfOf` address. + * - The resulting rate is the weighted average between the rate of the new debt + * and the rate of the previous debt + * @param user The address receiving the borrowed underlying, being the delegatee in case + * of credit delegate, or same as `onBehalfOf` otherwise + * @param onBehalfOf The address receiving the debt tokens + * @param amount The amount of debt tokens to mint + * @param rate The rate of the debt being minted + **/ + function mint( + address user, + address onBehalfOf, + uint256 amount, + uint256 rate + ) external returns (bool); + + /** + * @dev Burns debt of `user` + * - The resulting rate is the weighted average between the rate of the new debt + * and the rate of the previous debt + * @param user The address of the user getting his debt burned + * @param amount The amount of debt tokens getting burned + **/ + function burn(address user, uint256 amount) external; + + /** + * @dev Returns the average rate of all the stable rate loans. + * @return The average stable rate + **/ + function getAverageStableRate() external view returns (uint256); + + /** + * @dev Returns the stable rate of the user debt + * @return The stable rate of the user + **/ + function getUserStableRate(address user) external view returns (uint256); + + /** + * @dev Returns the timestamp of the last update of the user + * @return The timestamp + **/ + function getUserLastUpdated(address user) external view returns (uint40); + + /** + * @dev Returns the principal, the total supply and the average stable rate + **/ + function getSupplyData() + external + view + returns ( + uint256, + uint256, + uint256, + uint40 + ); + + /** + * @dev Returns the timestamp of the last update of the total supply + * @return The timestamp + **/ + function getTotalSupplyLastUpdated() external view returns (uint40); + + /** + * @dev Returns the total supply and the average stable rate + **/ + function getTotalSupplyAndAvgRate() external view returns (uint256, uint256); + + /** + * @dev Returns the principal debt balance of the user + * @return The debt balance of the user since the last burn/mint action + **/ + function principalBalanceOf(address user) external view returns (uint256); + + /** + * @dev Returns the address of the incentives controller contract + **/ + function getIncentivesController() external view returns (IAaveIncentivesController); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IUniswapV2Router02.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IUniswapV2Router02.sol new file mode 100644 index 000000000..7fec80094 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IUniswapV2Router02.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +interface IUniswapV2Router02 { + function swapExactTokensForTokens( + uint256 amountIn, + uint256 amountOutMin, + address[] calldata path, + address to, + uint256 deadline + ) external returns (uint256[] memory amounts); + + function swapTokensForExactTokens( + uint256 amountOut, + uint256 amountInMax, + address[] calldata path, + address to, + uint256 deadline + ) external returns (uint256[] memory amounts); + + function getAmountsOut(uint256 amountIn, address[] calldata path) + external + view + returns (uint256[] memory amounts); + + function getAmountsIn(uint256 amountOut, address[] calldata path) + external + view + returns (uint256[] memory amounts); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IVariableDebtToken.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IVariableDebtToken.sol new file mode 100644 index 000000000..2411ffbca --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/interfaces/IVariableDebtToken.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {IScaledBalanceToken} from './IScaledBalanceToken.sol'; +import {IInitializableDebtToken} from './IInitializableDebtToken.sol'; +import {IAaveIncentivesController} from './IAaveIncentivesController.sol'; + +/** + * @title IVariableDebtToken + * @author Aave + * @notice Defines the basic interface for a variable debt token. + **/ +interface IVariableDebtToken is IScaledBalanceToken, IInitializableDebtToken { + /** + * @dev Emitted after the mint action + * @param from The address performing the mint + * @param onBehalfOf The address of the user on which behalf minting has been performed + * @param value The amount to be minted + * @param index The last index of the reserve + **/ + event Mint(address indexed from, address indexed onBehalfOf, uint256 value, uint256 index); + + /** + * @dev Mints debt token to the `onBehalfOf` address + * @param user The address receiving the borrowed underlying, being the delegatee in case + * of credit delegate, or same as `onBehalfOf` otherwise + * @param onBehalfOf The address receiving the debt tokens + * @param amount The amount of debt being minted + * @param index The variable debt index of the reserve + * @return `true` if the the previous balance of the user is 0 + **/ + function mint( + address user, + address onBehalfOf, + uint256 amount, + uint256 index + ) external returns (bool); + + /** + * @dev Emitted when variable debt is burnt + * @param user The user which debt has been burned + * @param amount The amount of debt being burned + * @param index The index of the user + **/ + event Burn(address indexed user, uint256 amount, uint256 index); + + /** + * @dev Burns user variable debt + * @param user The user which debt is burnt + * @param index The variable debt index of the reserve + **/ + function burn( + address user, + uint256 amount, + uint256 index + ) external; + + /** + * @dev Returns the address of the incentives controller contract + **/ + function getIncentivesController() external view returns (IAaveIncentivesController); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/AaveOracle.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/AaveOracle.sol new file mode 100644 index 000000000..2fe2457ca --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/AaveOracle.sol @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {Ownable} from '../dependencies/openzeppelin/contracts/Ownable.sol'; +import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; + +import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol'; +import {IChainlinkAggregator} from '../interfaces/IChainlinkAggregator.sol'; +import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol'; + +/// @title AaveOracle +/// @author Aave +/// @notice Proxy smart contract to get the price of an asset from a price source, with Chainlink Aggregator +/// smart contracts as primary option +/// - If the returned price by a Chainlink aggregator is <= 0, the call is forwarded to a fallbackOracle +/// - Owned by the Aave governance system, allowed to add sources for assets, replace them +/// and change the fallbackOracle +contract AaveOracle is IPriceOracleGetter, Ownable { + using SafeERC20 for IERC20; + + event WethSet(address indexed weth); + event AssetSourceUpdated(address indexed asset, address indexed source); + event FallbackOracleUpdated(address indexed fallbackOracle); + + mapping(address => IChainlinkAggregator) private assetsSources; + IPriceOracleGetter private _fallbackOracle; + address public immutable WETH; + + /// @notice Constructor + /// @param assets The addresses of the assets + /// @param sources The address of the source of each asset + /// @param fallbackOracle The address of the fallback oracle to use if the data of an + /// aggregator is not consistent + constructor( + address[] memory assets, + address[] memory sources, + address fallbackOracle, + address weth + ) public { + _setFallbackOracle(fallbackOracle); + _setAssetsSources(assets, sources); + WETH = weth; + emit WethSet(weth); + } + + /// @notice External function called by the Aave governance to set or replace sources of assets + /// @param assets The addresses of the assets + /// @param sources The address of the source of each asset + function setAssetSources(address[] calldata assets, address[] calldata sources) + external + onlyOwner + { + _setAssetsSources(assets, sources); + } + + /// @notice Sets the fallbackOracle + /// - Callable only by the Aave governance + /// @param fallbackOracle The address of the fallbackOracle + function setFallbackOracle(address fallbackOracle) external onlyOwner { + _setFallbackOracle(fallbackOracle); + } + + /// @notice Internal function to set the sources for each asset + /// @param assets The addresses of the assets + /// @param sources The address of the source of each asset + function _setAssetsSources(address[] memory assets, address[] memory sources) internal { + require(assets.length == sources.length, 'INCONSISTENT_PARAMS_LENGTH'); + for (uint256 i = 0; i < assets.length; i++) { + assetsSources[assets[i]] = IChainlinkAggregator(sources[i]); + emit AssetSourceUpdated(assets[i], sources[i]); + } + } + + /// @notice Internal function to set the fallbackOracle + /// @param fallbackOracle The address of the fallbackOracle + function _setFallbackOracle(address fallbackOracle) internal { + _fallbackOracle = IPriceOracleGetter(fallbackOracle); + emit FallbackOracleUpdated(fallbackOracle); + } + + /// @notice Gets an asset price by address + /// @param asset The asset address + function getAssetPrice(address asset) public view override returns (uint256) { + IChainlinkAggregator source = assetsSources[asset]; + + if (asset == WETH) { + return 1 ether; + } else if (address(source) == address(0)) { + return _fallbackOracle.getAssetPrice(asset); + } else { + int256 price = IChainlinkAggregator(source).latestAnswer(); + if (price > 0) { + return uint256(price); + } else { + return _fallbackOracle.getAssetPrice(asset); + } + } + } + + /// @notice Gets a list of prices from a list of assets addresses + /// @param assets The list of assets addresses + function getAssetsPrices(address[] calldata assets) external view returns (uint256[] memory) { + uint256[] memory prices = new uint256[](assets.length); + for (uint256 i = 0; i < assets.length; i++) { + prices[i] = getAssetPrice(assets[i]); + } + return prices; + } + + /// @notice Gets the address of the source for an asset address + /// @param asset The address of the asset + /// @return address The address of the source + function getSourceOfAsset(address asset) external view returns (address) { + return address(assetsSources[asset]); + } + + /// @notice Gets the address of the fallback oracle + /// @return address The addres of the fallback oracle + function getFallbackOracle() external view returns (address) { + return address(_fallbackOracle); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/AaveProtocolDataProvider.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/AaveProtocolDataProvider.sol new file mode 100644 index 000000000..428df106e --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/AaveProtocolDataProvider.sol @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol'; +import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; +import {ILendingPool} from '../interfaces/ILendingPool.sol'; +import {IStableDebtToken} from '../interfaces/IStableDebtToken.sol'; +import {IVariableDebtToken} from '../interfaces/IVariableDebtToken.sol'; +import {ReserveConfiguration} from '../protocol/libraries/configuration/ReserveConfiguration.sol'; +import {UserConfiguration} from '../protocol/libraries/configuration/UserConfiguration.sol'; +import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; + +contract AaveProtocolDataProvider { + using ReserveConfiguration for DataTypes.ReserveConfigurationMap; + using UserConfiguration for DataTypes.UserConfigurationMap; + + address constant MKR = 0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2; + address constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + + struct TokenData { + string symbol; + address tokenAddress; + } + + ILendingPoolAddressesProvider public immutable ADDRESSES_PROVIDER; + + constructor(ILendingPoolAddressesProvider addressesProvider) public { + ADDRESSES_PROVIDER = addressesProvider; + } + + function getAllReservesTokens() external view returns (TokenData[] memory) { + ILendingPool pool = ILendingPool(ADDRESSES_PROVIDER.getLendingPool()); + address[] memory reserves = pool.getReservesList(); + TokenData[] memory reservesTokens = new TokenData[](reserves.length); + for (uint256 i = 0; i < reserves.length; i++) { + if (reserves[i] == MKR) { + reservesTokens[i] = TokenData({symbol: 'MKR', tokenAddress: reserves[i]}); + continue; + } + if (reserves[i] == ETH) { + reservesTokens[i] = TokenData({symbol: 'ETH', tokenAddress: reserves[i]}); + continue; + } + reservesTokens[i] = TokenData({ + symbol: IERC20Detailed(reserves[i]).symbol(), + tokenAddress: reserves[i] + }); + } + return reservesTokens; + } + + function getAllATokens() external view returns (TokenData[] memory) { + ILendingPool pool = ILendingPool(ADDRESSES_PROVIDER.getLendingPool()); + address[] memory reserves = pool.getReservesList(); + TokenData[] memory aTokens = new TokenData[](reserves.length); + for (uint256 i = 0; i < reserves.length; i++) { + DataTypes.ReserveData memory reserveData = pool.getReserveData(reserves[i]); + aTokens[i] = TokenData({ + symbol: IERC20Detailed(reserveData.aTokenAddress).symbol(), + tokenAddress: reserveData.aTokenAddress + }); + } + return aTokens; + } + + function getReserveConfigurationData(address asset) + external + view + returns ( + uint256 decimals, + uint256 ltv, + uint256 liquidationThreshold, + uint256 liquidationBonus, + uint256 reserveFactor, + bool usageAsCollateralEnabled, + bool borrowingEnabled, + bool stableBorrowRateEnabled, + bool isActive, + bool isFrozen + ) + { + DataTypes.ReserveConfigurationMap memory configuration = + ILendingPool(ADDRESSES_PROVIDER.getLendingPool()).getConfiguration(asset); + + (ltv, liquidationThreshold, liquidationBonus, decimals, reserveFactor) = configuration + .getParamsMemory(); + + (isActive, isFrozen, borrowingEnabled, stableBorrowRateEnabled) = configuration + .getFlagsMemory(); + + usageAsCollateralEnabled = liquidationThreshold > 0; + } + + function getReserveData(address asset) + external + view + returns ( + uint256 availableLiquidity, + uint256 totalStableDebt, + uint256 totalVariableDebt, + uint256 liquidityRate, + uint256 variableBorrowRate, + uint256 stableBorrowRate, + uint256 averageStableBorrowRate, + uint256 liquidityIndex, + uint256 variableBorrowIndex, + uint40 lastUpdateTimestamp + ) + { + DataTypes.ReserveData memory reserve = + ILendingPool(ADDRESSES_PROVIDER.getLendingPool()).getReserveData(asset); + + return ( + IERC20Detailed(asset).balanceOf(reserve.aTokenAddress), + IERC20Detailed(reserve.stableDebtTokenAddress).totalSupply(), + IERC20Detailed(reserve.variableDebtTokenAddress).totalSupply(), + reserve.currentLiquidityRate, + reserve.currentVariableBorrowRate, + reserve.currentStableBorrowRate, + IStableDebtToken(reserve.stableDebtTokenAddress).getAverageStableRate(), + reserve.liquidityIndex, + reserve.variableBorrowIndex, + reserve.lastUpdateTimestamp + ); + } + + function getUserReserveData(address asset, address user) + external + view + returns ( + uint256 currentATokenBalance, + uint256 currentStableDebt, + uint256 currentVariableDebt, + uint256 principalStableDebt, + uint256 scaledVariableDebt, + uint256 stableBorrowRate, + uint256 liquidityRate, + uint40 stableRateLastUpdated, + bool usageAsCollateralEnabled + ) + { + DataTypes.ReserveData memory reserve = + ILendingPool(ADDRESSES_PROVIDER.getLendingPool()).getReserveData(asset); + + DataTypes.UserConfigurationMap memory userConfig = + ILendingPool(ADDRESSES_PROVIDER.getLendingPool()).getUserConfiguration(user); + + currentATokenBalance = IERC20Detailed(reserve.aTokenAddress).balanceOf(user); + currentVariableDebt = IERC20Detailed(reserve.variableDebtTokenAddress).balanceOf(user); + currentStableDebt = IERC20Detailed(reserve.stableDebtTokenAddress).balanceOf(user); + principalStableDebt = IStableDebtToken(reserve.stableDebtTokenAddress).principalBalanceOf(user); + scaledVariableDebt = IVariableDebtToken(reserve.variableDebtTokenAddress).scaledBalanceOf(user); + liquidityRate = reserve.currentLiquidityRate; + stableBorrowRate = IStableDebtToken(reserve.stableDebtTokenAddress).getUserStableRate(user); + stableRateLastUpdated = IStableDebtToken(reserve.stableDebtTokenAddress).getUserLastUpdated( + user + ); + usageAsCollateralEnabled = userConfig.isUsingAsCollateral(reserve.id); + } + + function getReserveTokensAddresses(address asset) + external + view + returns ( + address aTokenAddress, + address stableDebtTokenAddress, + address variableDebtTokenAddress + ) + { + DataTypes.ReserveData memory reserve = + ILendingPool(ADDRESSES_PROVIDER.getLendingPool()).getReserveData(asset); + + return ( + reserve.aTokenAddress, + reserve.stableDebtTokenAddress, + reserve.variableDebtTokenAddress + ); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/UiPoolDataProvider.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/UiPoolDataProvider.sol new file mode 100644 index 000000000..0356dad13 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/UiPoolDataProvider.sol @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol'; +import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; +import {IUiPoolDataProvider} from './interfaces/IUiPoolDataProvider.sol'; +import {ILendingPool} from '../interfaces/ILendingPool.sol'; +import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol'; +import {IAToken} from '../interfaces/IAToken.sol'; +import {IVariableDebtToken} from '../interfaces/IVariableDebtToken.sol'; +import {IStableDebtToken} from '../interfaces/IStableDebtToken.sol'; +import {WadRayMath} from '../protocol/libraries/math/WadRayMath.sol'; +import {ReserveConfiguration} from '../protocol/libraries/configuration/ReserveConfiguration.sol'; +import {UserConfiguration} from '../protocol/libraries/configuration/UserConfiguration.sol'; +import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; +import { + DefaultReserveInterestRateStrategy +} from '../protocol/lendingpool/DefaultReserveInterestRateStrategy.sol'; + +contract UiPoolDataProvider is IUiPoolDataProvider { + using WadRayMath for uint256; + using ReserveConfiguration for DataTypes.ReserveConfigurationMap; + using UserConfiguration for DataTypes.UserConfigurationMap; + + address public constant MOCK_USD_ADDRESS = 0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96; + + function getInterestRateStrategySlopes(DefaultReserveInterestRateStrategy interestRateStrategy) + internal + view + returns ( + uint256, + uint256, + uint256, + uint256 + ) + { + return ( + interestRateStrategy.variableRateSlope1(), + interestRateStrategy.variableRateSlope2(), + interestRateStrategy.stableRateSlope1(), + interestRateStrategy.stableRateSlope2() + ); + } + + function getReservesData(ILendingPoolAddressesProvider provider, address user) + external + view + override + returns ( + AggregatedReserveData[] memory, + UserReserveData[] memory, + uint256 + ) + { + ILendingPool lendingPool = ILendingPool(provider.getLendingPool()); + IPriceOracleGetter oracle = IPriceOracleGetter(provider.getPriceOracle()); + address[] memory reserves = lendingPool.getReservesList(); + DataTypes.UserConfigurationMap memory userConfig = lendingPool.getUserConfiguration(user); + + AggregatedReserveData[] memory reservesData = new AggregatedReserveData[](reserves.length); + UserReserveData[] memory userReservesData = + new UserReserveData[](user != address(0) ? reserves.length : 0); + + for (uint256 i = 0; i < reserves.length; i++) { + AggregatedReserveData memory reserveData = reservesData[i]; + reserveData.underlyingAsset = reserves[i]; + + // reserve current state + DataTypes.ReserveData memory baseData = + lendingPool.getReserveData(reserveData.underlyingAsset); + reserveData.liquidityIndex = baseData.liquidityIndex; + reserveData.variableBorrowIndex = baseData.variableBorrowIndex; + reserveData.liquidityRate = baseData.currentLiquidityRate; + reserveData.variableBorrowRate = baseData.currentVariableBorrowRate; + reserveData.stableBorrowRate = baseData.currentStableBorrowRate; + reserveData.lastUpdateTimestamp = baseData.lastUpdateTimestamp; + reserveData.aTokenAddress = baseData.aTokenAddress; + reserveData.stableDebtTokenAddress = baseData.stableDebtTokenAddress; + reserveData.variableDebtTokenAddress = baseData.variableDebtTokenAddress; + reserveData.interestRateStrategyAddress = baseData.interestRateStrategyAddress; + reserveData.priceInEth = oracle.getAssetPrice(reserveData.underlyingAsset); + + reserveData.availableLiquidity = IERC20Detailed(reserveData.underlyingAsset).balanceOf( + reserveData.aTokenAddress + ); + ( + reserveData.totalPrincipalStableDebt, + , + reserveData.averageStableRate, + reserveData.stableDebtLastUpdateTimestamp + ) = IStableDebtToken(reserveData.stableDebtTokenAddress).getSupplyData(); + reserveData.totalScaledVariableDebt = IVariableDebtToken(reserveData.variableDebtTokenAddress) + .scaledTotalSupply(); + + // reserve configuration + + // we're getting this info from the aToken, because some of assets can be not compliant with ETC20Detailed + reserveData.symbol = IERC20Detailed(reserveData.aTokenAddress).symbol(); + reserveData.name = ''; + + ( + reserveData.baseLTVasCollateral, + reserveData.reserveLiquidationThreshold, + reserveData.reserveLiquidationBonus, + reserveData.decimals, + reserveData.reserveFactor + ) = baseData.configuration.getParamsMemory(); + ( + reserveData.isActive, + reserveData.isFrozen, + reserveData.borrowingEnabled, + reserveData.stableBorrowRateEnabled + ) = baseData.configuration.getFlagsMemory(); + reserveData.usageAsCollateralEnabled = reserveData.baseLTVasCollateral != 0; + ( + reserveData.variableRateSlope1, + reserveData.variableRateSlope2, + reserveData.stableRateSlope1, + reserveData.stableRateSlope2 + ) = getInterestRateStrategySlopes( + DefaultReserveInterestRateStrategy(reserveData.interestRateStrategyAddress) + ); + + if (user != address(0)) { + // user reserve data + userReservesData[i].underlyingAsset = reserveData.underlyingAsset; + userReservesData[i].scaledATokenBalance = IAToken(reserveData.aTokenAddress) + .scaledBalanceOf(user); + userReservesData[i].usageAsCollateralEnabledOnUser = userConfig.isUsingAsCollateral(i); + + if (userConfig.isBorrowing(i)) { + userReservesData[i].scaledVariableDebt = IVariableDebtToken( + reserveData + .variableDebtTokenAddress + ) + .scaledBalanceOf(user); + userReservesData[i].principalStableDebt = IStableDebtToken( + reserveData + .stableDebtTokenAddress + ) + .principalBalanceOf(user); + if (userReservesData[i].principalStableDebt != 0) { + userReservesData[i].stableBorrowRate = IStableDebtToken( + reserveData + .stableDebtTokenAddress + ) + .getUserStableRate(user); + userReservesData[i].stableBorrowLastUpdateTimestamp = IStableDebtToken( + reserveData + .stableDebtTokenAddress + ) + .getUserLastUpdated(user); + } + } + } + } + return (reservesData, userReservesData, oracle.getAssetPrice(MOCK_USD_ADDRESS)); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/WETHGateway.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/WETHGateway.sol new file mode 100644 index 000000000..7cf654994 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/WETHGateway.sol @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {Ownable} from '../dependencies/openzeppelin/contracts/Ownable.sol'; +import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; +import {IWETH} from './interfaces/IWETH.sol'; +import {IWETHGateway} from './interfaces/IWETHGateway.sol'; +import {ILendingPool} from '../interfaces/ILendingPool.sol'; +import {IAToken} from '../interfaces/IAToken.sol'; +import {ReserveConfiguration} from '../protocol/libraries/configuration/ReserveConfiguration.sol'; +import {UserConfiguration} from '../protocol/libraries/configuration/UserConfiguration.sol'; +import {Helpers} from '../protocol/libraries/helpers/Helpers.sol'; +import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; + +contract WETHGateway is IWETHGateway, Ownable { + using ReserveConfiguration for DataTypes.ReserveConfigurationMap; + using UserConfiguration for DataTypes.UserConfigurationMap; + + IWETH internal immutable WETH; + + /** + * @dev Sets the WETH address and the LendingPoolAddressesProvider address. Infinite approves lending pool. + * @param weth Address of the Wrapped Ether contract + **/ + constructor(address weth) public { + WETH = IWETH(weth); + } + + function authorizeLendingPool(address lendingPool) external onlyOwner { + WETH.approve(lendingPool, uint256(-1)); + } + + /** + * @dev deposits WETH into the reserve, using native ETH. A corresponding amount of the overlying asset (aTokens) + * is minted. + * @param lendingPool address of the targeted underlying lending pool + * @param onBehalfOf address of the user who will receive the aTokens representing the deposit + * @param referralCode integrators are assigned a referral code and can potentially receive rewards. + **/ + function depositETH( + address lendingPool, + address onBehalfOf, + uint16 referralCode + ) external payable override { + WETH.deposit{value: msg.value}(); + ILendingPool(lendingPool).deposit(address(WETH), msg.value, onBehalfOf, referralCode); + } + + /** + * @dev withdraws the WETH _reserves of msg.sender. + * @param lendingPool address of the targeted underlying lending pool + * @param amount amount of aWETH to withdraw and receive native ETH + * @param to address of the user who will receive native ETH + */ + function withdrawETH( + address lendingPool, + uint256 amount, + address to + ) external override { + IAToken aWETH = IAToken(ILendingPool(lendingPool).getReserveData(address(WETH)).aTokenAddress); + uint256 userBalance = aWETH.balanceOf(msg.sender); + uint256 amountToWithdraw = amount; + + // if amount is equal to uint(-1), the user wants to redeem everything + if (amount == type(uint256).max) { + amountToWithdraw = userBalance; + } + aWETH.transferFrom(msg.sender, address(this), amountToWithdraw); + ILendingPool(lendingPool).withdraw(address(WETH), amountToWithdraw, address(this)); + WETH.withdraw(amountToWithdraw); + _safeTransferETH(to, amountToWithdraw); + } + + /** + * @dev repays a borrow on the WETH reserve, for the specified amount (or for the whole amount, if uint256(-1) is specified). + * @param lendingPool address of the targeted underlying lending pool + * @param amount the amount to repay, or uint256(-1) if the user wants to repay everything + * @param rateMode the rate mode to repay + * @param onBehalfOf the address for which msg.sender is repaying + */ + function repayETH( + address lendingPool, + uint256 amount, + uint256 rateMode, + address onBehalfOf + ) external payable override { + (uint256 stableDebt, uint256 variableDebt) = + Helpers.getUserCurrentDebtMemory( + onBehalfOf, + ILendingPool(lendingPool).getReserveData(address(WETH)) + ); + + uint256 paybackAmount = + DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.STABLE + ? stableDebt + : variableDebt; + + if (amount < paybackAmount) { + paybackAmount = amount; + } + require(msg.value >= paybackAmount, 'msg.value is less than repayment amount'); + WETH.deposit{value: paybackAmount}(); + ILendingPool(lendingPool).repay(address(WETH), msg.value, rateMode, onBehalfOf); + + // refund remaining dust eth + if (msg.value > paybackAmount) _safeTransferETH(msg.sender, msg.value - paybackAmount); + } + + /** + * @dev borrow WETH, unwraps to ETH and send both the ETH and DebtTokens to msg.sender, via `approveDelegation` and onBehalf argument in `LendingPool.borrow`. + * @param lendingPool address of the targeted underlying lending pool + * @param amount the amount of ETH to borrow + * @param interesRateMode the interest rate mode + * @param referralCode integrators are assigned a referral code and can potentially receive rewards + */ + function borrowETH( + address lendingPool, + uint256 amount, + uint256 interesRateMode, + uint16 referralCode + ) external override { + ILendingPool(lendingPool).borrow( + address(WETH), + amount, + interesRateMode, + referralCode, + msg.sender + ); + WETH.withdraw(amount); + _safeTransferETH(msg.sender, amount); + } + + /** + * @dev transfer ETH to an address, revert if it fails. + * @param to recipient of the transfer + * @param value the amount to send + */ + function _safeTransferETH(address to, uint256 value) internal { + (bool success, ) = to.call{value: value}(new bytes(0)); + require(success, 'ETH_TRANSFER_FAILED'); + } + + /** + * @dev transfer ERC20 from the utility contract, for ERC20 recovery in case of stuck tokens due + * direct transfers to the contract address. + * @param token token to transfer + * @param to recipient of the transfer + * @param amount amount to send + */ + function emergencyTokenTransfer( + address token, + address to, + uint256 amount + ) external onlyOwner { + IERC20(token).transfer(to, amount); + } + + /** + * @dev transfer native Ether from the utility contract, for native Ether recovery in case of stuck Ether + * due selfdestructs or transfer ether to pre-computated contract address before deployment. + * @param to recipient of the transfer + * @param amount amount to send + */ + function emergencyEtherTransfer(address to, uint256 amount) external onlyOwner { + _safeTransferETH(to, amount); + } + + /** + * @dev Get WETH address used by WETHGateway + */ + function getWETHAddress() external view returns (address) { + return address(WETH); + } + + /** + * @dev Only WETH contract is allowed to transfer ETH here. Prevent other addresses to send Ether to this contract. + */ + receive() external payable { + require(msg.sender == address(WETH), 'Receive not allowed'); + } + + /** + * @dev Revert fallback calls + */ + fallback() external payable { + revert('Fallback not allowed'); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/WalletBalanceProvider.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/WalletBalanceProvider.sol new file mode 100644 index 000000000..7d8ffb165 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/WalletBalanceProvider.sol @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +pragma experimental ABIEncoderV2; + +import {Address} from '../dependencies/openzeppelin/contracts/Address.sol'; +import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; + +import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol'; +import {ILendingPool} from '../interfaces/ILendingPool.sol'; +import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol'; +import {ReserveConfiguration} from '../protocol/libraries/configuration/ReserveConfiguration.sol'; +import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; + +/** + * @title WalletBalanceProvider contract + * @author Aave, influenced by https://github.com/wbobeirne/eth-balance-checker/blob/master/contracts/BalanceChecker.sol + * @notice Implements a logic of getting multiple tokens balance for one user address + * @dev NOTE: THIS CONTRACT IS NOT USED WITHIN THE AAVE PROTOCOL. It's an accessory contract used to reduce the number of calls + * towards the blockchain from the Aave backend. + **/ +contract WalletBalanceProvider { + using Address for address payable; + using Address for address; + using SafeERC20 for IERC20; + using ReserveConfiguration for DataTypes.ReserveConfigurationMap; + + address constant MOCK_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + + /** + @dev Fallback function, don't accept any ETH + **/ + receive() external payable { + //only contracts can send ETH to the core + require(msg.sender.isContract(), '22'); + } + + /** + @dev Check the token balance of a wallet in a token contract + + Returns the balance of the token for user. Avoids possible errors: + - return 0 on non-contract address + **/ + function balanceOf(address user, address token) public view returns (uint256) { + if (token == MOCK_ETH_ADDRESS) { + return user.balance; // ETH balance + // check if token is actually a contract + } else if (token.isContract()) { + return IERC20(token).balanceOf(user); + } + revert('INVALID_TOKEN'); + } + + /** + * @notice Fetches, for a list of _users and _tokens (ETH included with mock address), the balances + * @param users The list of users + * @param tokens The list of tokens + * @return And array with the concatenation of, for each user, his/her balances + **/ + function batchBalanceOf(address[] calldata users, address[] calldata tokens) + external + view + returns (uint256[] memory) + { + uint256[] memory balances = new uint256[](users.length * tokens.length); + + for (uint256 i = 0; i < users.length; i++) { + for (uint256 j = 0; j < tokens.length; j++) { + balances[i * tokens.length + j] = balanceOf(users[i], tokens[j]); + } + } + + return balances; + } + + /** + @dev provides balances of user wallet for all reserves available on the pool + */ + function getUserWalletBalances(address provider, address user) + external + view + returns (address[] memory, uint256[] memory) + { + ILendingPool pool = ILendingPool(ILendingPoolAddressesProvider(provider).getLendingPool()); + + address[] memory reserves = pool.getReservesList(); + address[] memory reservesWithEth = new address[](reserves.length + 1); + for (uint256 i = 0; i < reserves.length; i++) { + reservesWithEth[i] = reserves[i]; + } + reservesWithEth[reserves.length] = MOCK_ETH_ADDRESS; + + uint256[] memory balances = new uint256[](reservesWithEth.length); + + for (uint256 j = 0; j < reserves.length; j++) { + DataTypes.ReserveConfigurationMap memory configuration = + pool.getConfiguration(reservesWithEth[j]); + + (bool isActive, , , ) = configuration.getFlagsMemory(); + + if (!isActive) { + balances[j] = 0; + continue; + } + balances[j] = balanceOf(user, reservesWithEth[j]); + } + balances[reserves.length] = balanceOf(user, MOCK_ETH_ADDRESS); + + return (reservesWithEth, balances); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/interfaces/IUiPoolDataProvider.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/interfaces/IUiPoolDataProvider.sol new file mode 100644 index 000000000..cf556b46f --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/interfaces/IUiPoolDataProvider.sol @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol'; + +interface IUiPoolDataProvider { + struct AggregatedReserveData { + address underlyingAsset; + string name; + string symbol; + uint256 decimals; + uint256 baseLTVasCollateral; + uint256 reserveLiquidationThreshold; + uint256 reserveLiquidationBonus; + uint256 reserveFactor; + bool usageAsCollateralEnabled; + bool borrowingEnabled; + bool stableBorrowRateEnabled; + bool isActive; + bool isFrozen; + // base data + uint128 liquidityIndex; + uint128 variableBorrowIndex; + uint128 liquidityRate; + uint128 variableBorrowRate; + uint128 stableBorrowRate; + uint40 lastUpdateTimestamp; + address aTokenAddress; + address stableDebtTokenAddress; + address variableDebtTokenAddress; + address interestRateStrategyAddress; + // + uint256 availableLiquidity; + uint256 totalPrincipalStableDebt; + uint256 averageStableRate; + uint256 stableDebtLastUpdateTimestamp; + uint256 totalScaledVariableDebt; + uint256 priceInEth; + uint256 variableRateSlope1; + uint256 variableRateSlope2; + uint256 stableRateSlope1; + uint256 stableRateSlope2; + } + // + // struct ReserveData { + // uint256 averageStableBorrowRate; + // uint256 totalLiquidity; + // } + + struct UserReserveData { + address underlyingAsset; + uint256 scaledATokenBalance; + bool usageAsCollateralEnabledOnUser; + uint256 stableBorrowRate; + uint256 scaledVariableDebt; + uint256 principalStableDebt; + uint256 stableBorrowLastUpdateTimestamp; + } + + // + // struct ATokenSupplyData { + // string name; + // string symbol; + // uint8 decimals; + // uint256 totalSupply; + // address aTokenAddress; + // } + + function getReservesData(ILendingPoolAddressesProvider provider, address user) + external + view + returns ( + AggregatedReserveData[] memory, + UserReserveData[] memory, + uint256 + ); + + // function getUserReservesData(ILendingPoolAddressesProvider provider, address user) + // external + // view + // returns (UserReserveData[] memory); + // + // function getAllATokenSupply(ILendingPoolAddressesProvider provider) + // external + // view + // returns (ATokenSupplyData[] memory); + // + // function getATokenSupply(address[] calldata aTokens) + // external + // view + // returns (ATokenSupplyData[] memory); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/interfaces/IWETH.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/interfaces/IWETH.sol new file mode 100644 index 000000000..9dfb9762f --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/interfaces/IWETH.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +interface IWETH { + function deposit() external payable; + + function withdraw(uint256) external; + + function approve(address guy, uint256 wad) external returns (bool); + + function transferFrom( + address src, + address dst, + uint256 wad + ) external returns (bool); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/interfaces/IWETHGateway.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/interfaces/IWETHGateway.sol new file mode 100644 index 000000000..d0dbe1970 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/misc/interfaces/IWETHGateway.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +interface IWETHGateway { + function depositETH( + address lendingPool, + address onBehalfOf, + uint16 referralCode + ) external payable; + + function withdrawETH( + address lendingPool, + uint256 amount, + address onBehalfOf + ) external; + + function repayETH( + address lendingPool, + uint256 amount, + uint256 rateMode, + address onBehalfOf + ) external payable; + + function borrowETH( + address lendingPool, + uint256 amount, + uint256 interesRateMode, + uint16 referralCode + ) external; +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/flashloan/MockFlashLoanReceiver.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/flashloan/MockFlashLoanReceiver.sol new file mode 100644 index 000000000..e393fdc14 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/flashloan/MockFlashLoanReceiver.sol @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol'; +import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol'; + +import {FlashLoanReceiverBase} from '../../flashloan/base/FlashLoanReceiverBase.sol'; +import {MintableERC20} from '../tokens/MintableERC20.sol'; +import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol'; +import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol'; + +contract MockFlashLoanReceiver is FlashLoanReceiverBase { + using SafeERC20 for IERC20; + + ILendingPoolAddressesProvider internal _provider; + + event ExecutedWithFail(address[] _assets, uint256[] _amounts, uint256[] _premiums); + event ExecutedWithSuccess(address[] _assets, uint256[] _amounts, uint256[] _premiums); + + bool _failExecution; + uint256 _amountToApprove; + bool _simulateEOA; + + constructor(ILendingPoolAddressesProvider provider) public FlashLoanReceiverBase(provider) {} + + function setFailExecutionTransfer(bool fail) public { + _failExecution = fail; + } + + function setAmountToApprove(uint256 amountToApprove) public { + _amountToApprove = amountToApprove; + } + + function setSimulateEOA(bool flag) public { + _simulateEOA = flag; + } + + function amountToApprove() public view returns (uint256) { + return _amountToApprove; + } + + function simulateEOA() public view returns (bool) { + return _simulateEOA; + } + + function executeOperation( + address[] memory assets, + uint256[] memory amounts, + uint256[] memory premiums, + address initiator, + bytes memory params + ) public override returns (bool) { + params; + initiator; + + if (_failExecution) { + emit ExecutedWithFail(assets, amounts, premiums); + return !_simulateEOA; + } + + for (uint256 i = 0; i < assets.length; i++) { + //mint to this contract the specific amount + MintableERC20 token = MintableERC20(assets[i]); + + //check the contract has the specified balance + require( + amounts[i] <= IERC20(assets[i]).balanceOf(address(this)), + 'Invalid balance for the contract' + ); + + uint256 amountToReturn = + (_amountToApprove != 0) ? _amountToApprove : amounts[i].add(premiums[i]); + //execution does not fail - mint tokens and return them to the _destination + + token.mint(premiums[i]); + + IERC20(assets[i]).approve(address(LENDING_POOL), amountToReturn); + } + + emit ExecutedWithSuccess(assets, amounts, premiums); + + return true; + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/oracle/LendingRateOracle.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/oracle/LendingRateOracle.sol new file mode 100644 index 000000000..0f8fad345 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/oracle/LendingRateOracle.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {ILendingRateOracle} from '../../interfaces/ILendingRateOracle.sol'; +import {Ownable} from '../../dependencies/openzeppelin/contracts/Ownable.sol'; + +contract LendingRateOracle is ILendingRateOracle, Ownable { + mapping(address => uint256) borrowRates; + mapping(address => uint256) liquidityRates; + + function getMarketBorrowRate(address _asset) external view override returns (uint256) { + return borrowRates[_asset]; + } + + function setMarketBorrowRate(address _asset, uint256 _rate) external override onlyOwner { + borrowRates[_asset] = _rate; + } + + function getMarketLiquidityRate(address _asset) external view returns (uint256) { + return liquidityRates[_asset]; + } + + function setMarketLiquidityRate(address _asset, uint256 _rate) external onlyOwner { + liquidityRates[_asset] = _rate; + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/swap/MockUniswapV2Router02.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/swap/MockUniswapV2Router02.sol new file mode 100644 index 000000000..383329606 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/swap/MockUniswapV2Router02.sol @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {IUniswapV2Router02} from '../../interfaces/IUniswapV2Router02.sol'; +import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import {MintableERC20} from '../tokens/MintableERC20.sol'; + +contract MockUniswapV2Router02 is IUniswapV2Router02 { + mapping(address => uint256) internal _amountToReturn; + mapping(address => uint256) internal _amountToSwap; + mapping(address => mapping(address => mapping(uint256 => uint256))) internal _amountsIn; + mapping(address => mapping(address => mapping(uint256 => uint256))) internal _amountsOut; + uint256 internal defaultMockValue; + + function setAmountToReturn(address reserve, uint256 amount) public { + _amountToReturn[reserve] = amount; + } + + function setAmountToSwap(address reserve, uint256 amount) public { + _amountToSwap[reserve] = amount; + } + + function swapExactTokensForTokens( + uint256 amountIn, + uint256, /* amountOutMin */ + address[] calldata path, + address to, + uint256 /* deadline */ + ) external override returns (uint256[] memory amounts) { + IERC20(path[0]).transferFrom(msg.sender, address(this), amountIn); + + MintableERC20(path[1]).mint(_amountToReturn[path[0]]); + IERC20(path[1]).transfer(to, _amountToReturn[path[0]]); + + amounts = new uint256[](path.length); + amounts[0] = amountIn; + amounts[1] = _amountToReturn[path[0]]; + } + + function swapTokensForExactTokens( + uint256 amountOut, + uint256, /* amountInMax */ + address[] calldata path, + address to, + uint256 /* deadline */ + ) external override returns (uint256[] memory amounts) { + IERC20(path[0]).transferFrom(msg.sender, address(this), _amountToSwap[path[0]]); + + MintableERC20(path[1]).mint(amountOut); + IERC20(path[1]).transfer(to, amountOut); + + amounts = new uint256[](path.length); + amounts[0] = _amountToSwap[path[0]]; + amounts[1] = amountOut; + } + + function setAmountOut( + uint256 amountIn, + address reserveIn, + address reserveOut, + uint256 amountOut + ) public { + _amountsOut[reserveIn][reserveOut][amountIn] = amountOut; + } + + function setAmountIn( + uint256 amountOut, + address reserveIn, + address reserveOut, + uint256 amountIn + ) public { + _amountsIn[reserveIn][reserveOut][amountOut] = amountIn; + } + + function setDefaultMockValue(uint256 value) public { + defaultMockValue = value; + } + + function getAmountsOut(uint256 amountIn, address[] calldata path) + external + view + override + returns (uint256[] memory) + { + uint256[] memory amounts = new uint256[](path.length); + amounts[0] = amountIn; + amounts[1] = _amountsOut[path[0]][path[1]][amountIn] > 0 + ? _amountsOut[path[0]][path[1]][amountIn] + : defaultMockValue; + return amounts; + } + + function getAmountsIn(uint256 amountOut, address[] calldata path) + external + view + override + returns (uint256[] memory) + { + uint256[] memory amounts = new uint256[](path.length); + amounts[0] = _amountsIn[path[0]][path[1]][amountOut] > 0 + ? _amountsIn[path[0]][path[1]][amountOut] + : defaultMockValue; + amounts[1] = amountOut; + return amounts; + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/tokens/MintableDelegationERC20.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/tokens/MintableDelegationERC20.sol new file mode 100644 index 000000000..8137648f6 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/tokens/MintableDelegationERC20.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {ERC20} from '../../dependencies/openzeppelin/contracts/ERC20.sol'; + +/** + * @title ERC20Mintable + * @dev ERC20 minting logic + */ +contract MintableDelegationERC20 is ERC20 { + address public delegatee; + + constructor( + string memory name, + string memory symbol, + uint8 decimals + ) public ERC20(name, symbol) { + _setupDecimals(decimals); + } + + /** + * @dev Function to mint tokensp + * @param value The amount of tokens to mint. + * @return A boolean that indicates if the operation was successful. + */ + function mint(uint256 value) public returns (bool) { + _mint(msg.sender, value); + return true; + } + + function delegate(address delegateeAddress) external { + delegatee = delegateeAddress; + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/tokens/MintableERC20.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/tokens/MintableERC20.sol new file mode 100644 index 000000000..ffa9881ef --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/tokens/MintableERC20.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {ERC20} from '../../dependencies/openzeppelin/contracts/ERC20.sol'; + +/** + * @title ERC20Mintable + * @dev ERC20 minting logic + */ +contract MintableERC20 is ERC20 { + constructor( + string memory name, + string memory symbol, + uint8 decimals + ) public ERC20(name, symbol) { + _setupDecimals(decimals); + } + + /** + * @dev Function to mint tokens + * @param value The amount of tokens to mint. + * @return A boolean that indicates if the operation was successful. + */ + function mint(uint256 value) public returns (bool) { + _mint(_msgSender(), value); + return true; + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/upgradeability/MockAToken.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/upgradeability/MockAToken.sol new file mode 100644 index 000000000..47cad898b --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/upgradeability/MockAToken.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {AToken} from '../../protocol/tokenization/AToken.sol'; +import {ILendingPool} from '../../interfaces/ILendingPool.sol'; +import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol'; + +contract MockAToken is AToken { + function getRevision() internal pure override returns (uint256) { + return 0x2; + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/upgradeability/MockStableDebtToken.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/upgradeability/MockStableDebtToken.sol new file mode 100644 index 000000000..190f50d2b --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/upgradeability/MockStableDebtToken.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {StableDebtToken} from '../../protocol/tokenization/StableDebtToken.sol'; + +contract MockStableDebtToken is StableDebtToken { + function getRevision() internal pure override returns (uint256) { + return 0x2; + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/upgradeability/MockVariableDebtToken.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/upgradeability/MockVariableDebtToken.sol new file mode 100644 index 000000000..0ef28eff8 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/mocks/upgradeability/MockVariableDebtToken.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {VariableDebtToken} from '../../protocol/tokenization/VariableDebtToken.sol'; + +contract MockVariableDebtToken is VariableDebtToken { + function getRevision() internal pure override returns (uint256) { + return 0x2; + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/configuration/LendingPoolAddressesProvider.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/configuration/LendingPoolAddressesProvider.sol new file mode 100644 index 000000000..b748e27b8 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/configuration/LendingPoolAddressesProvider.sol @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {Ownable} from '../../dependencies/openzeppelin/contracts/Ownable.sol'; + +// Prettier ignore to prevent buidler flatter bug +// prettier-ignore +import {InitializableImmutableAdminUpgradeabilityProxy} from '../libraries/aave-upgradeability/InitializableImmutableAdminUpgradeabilityProxy.sol'; + +import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol'; + +/** + * @title LendingPoolAddressesProvider contract + * @dev Main registry of addresses part of or connected to the protocol, including permissioned roles + * - Acting also as factory of proxies and admin of those, so with right to change its implementations + * - Owned by the Aave Governance + * @author Aave + **/ +contract LendingPoolAddressesProvider is Ownable, ILendingPoolAddressesProvider { + string private _marketId; + mapping(bytes32 => address) private _addresses; + + bytes32 private constant LENDING_POOL = 'LENDING_POOL'; + bytes32 private constant LENDING_POOL_CONFIGURATOR = 'LENDING_POOL_CONFIGURATOR'; + bytes32 private constant POOL_ADMIN = 'POOL_ADMIN'; + bytes32 private constant EMERGENCY_ADMIN = 'EMERGENCY_ADMIN'; + bytes32 private constant LENDING_POOL_COLLATERAL_MANAGER = 'COLLATERAL_MANAGER'; + bytes32 private constant PRICE_ORACLE = 'PRICE_ORACLE'; + bytes32 private constant LENDING_RATE_ORACLE = 'LENDING_RATE_ORACLE'; + + constructor(string memory marketId) public { + _setMarketId(marketId); + } + + /** + * @dev Returns the id of the Aave market to which this contracts points to + * @return The market id + **/ + function getMarketId() external view override returns (string memory) { + return _marketId; + } + + /** + * @dev Allows to set the market which this LendingPoolAddressesProvider represents + * @param marketId The market id + */ + function setMarketId(string memory marketId) external override onlyOwner { + _setMarketId(marketId); + } + + /** + * @dev General function to update the implementation of a proxy registered with + * certain `id`. If there is no proxy registered, it will instantiate one and + * set as implementation the `implementationAddress` + * IMPORTANT Use this function carefully, only for ids that don't have an explicit + * setter function, in order to avoid unexpected consequences + * @param id The id + * @param implementationAddress The address of the new implementation + */ + function setAddressAsProxy(bytes32 id, address implementationAddress) + external + override + onlyOwner + { + _updateImpl(id, implementationAddress); + emit AddressSet(id, implementationAddress, true); + } + + /** + * @dev Sets an address for an id replacing the address saved in the addresses map + * IMPORTANT Use this function carefully, as it will do a hard replacement + * @param id The id + * @param newAddress The address to set + */ + function setAddress(bytes32 id, address newAddress) external override onlyOwner { + _addresses[id] = newAddress; + emit AddressSet(id, newAddress, false); + } + + /** + * @dev Returns an address by id + * @return The address + */ + function getAddress(bytes32 id) public view override returns (address) { + return _addresses[id]; + } + + /** + * @dev Returns the address of the LendingPool proxy + * @return The LendingPool proxy address + **/ + function getLendingPool() external view override returns (address) { + return getAddress(LENDING_POOL); + } + + /** + * @dev Updates the implementation of the LendingPool, or creates the proxy + * setting the new `pool` implementation on the first time calling it + * @param pool The new LendingPool implementation + **/ + function setLendingPoolImpl(address pool) external override onlyOwner { + _updateImpl(LENDING_POOL, pool); + emit LendingPoolUpdated(pool); + } + + /** + * @dev Returns the address of the LendingPoolConfigurator proxy + * @return The LendingPoolConfigurator proxy address + **/ + function getLendingPoolConfigurator() external view override returns (address) { + return getAddress(LENDING_POOL_CONFIGURATOR); + } + + /** + * @dev Updates the implementation of the LendingPoolConfigurator, or creates the proxy + * setting the new `configurator` implementation on the first time calling it + * @param configurator The new LendingPoolConfigurator implementation + **/ + function setLendingPoolConfiguratorImpl(address configurator) external override onlyOwner { + _updateImpl(LENDING_POOL_CONFIGURATOR, configurator); + emit LendingPoolConfiguratorUpdated(configurator); + } + + /** + * @dev Returns the address of the LendingPoolCollateralManager. Since the manager is used + * through delegateCall within the LendingPool contract, the proxy contract pattern does not work properly hence + * the addresses are changed directly + * @return The address of the LendingPoolCollateralManager + **/ + + function getLendingPoolCollateralManager() external view override returns (address) { + return getAddress(LENDING_POOL_COLLATERAL_MANAGER); + } + + /** + * @dev Updates the address of the LendingPoolCollateralManager + * @param manager The new LendingPoolCollateralManager address + **/ + function setLendingPoolCollateralManager(address manager) external override onlyOwner { + _addresses[LENDING_POOL_COLLATERAL_MANAGER] = manager; + emit LendingPoolCollateralManagerUpdated(manager); + } + + /** + * @dev The functions below are getters/setters of addresses that are outside the context + * of the protocol hence the upgradable proxy pattern is not used + **/ + + function getPoolAdmin() external view override returns (address) { + return getAddress(POOL_ADMIN); + } + + function setPoolAdmin(address admin) external override onlyOwner { + _addresses[POOL_ADMIN] = admin; + emit ConfigurationAdminUpdated(admin); + } + + function getEmergencyAdmin() external view override returns (address) { + return getAddress(EMERGENCY_ADMIN); + } + + function setEmergencyAdmin(address emergencyAdmin) external override onlyOwner { + _addresses[EMERGENCY_ADMIN] = emergencyAdmin; + emit EmergencyAdminUpdated(emergencyAdmin); + } + + function getPriceOracle() external view override returns (address) { + return getAddress(PRICE_ORACLE); + } + + function setPriceOracle(address priceOracle) external override onlyOwner { + _addresses[PRICE_ORACLE] = priceOracle; + emit PriceOracleUpdated(priceOracle); + } + + function getLendingRateOracle() external view override returns (address) { + return getAddress(LENDING_RATE_ORACLE); + } + + function setLendingRateOracle(address lendingRateOracle) external override onlyOwner { + _addresses[LENDING_RATE_ORACLE] = lendingRateOracle; + emit LendingRateOracleUpdated(lendingRateOracle); + } + + /** + * @dev Internal function to update the implementation of a specific proxied component of the protocol + * - If there is no proxy registered in the given `id`, it creates the proxy setting `newAdress` + * as implementation and calls the initialize() function on the proxy + * - If there is already a proxy registered, it just updates the implementation to `newAddress` and + * calls the initialize() function via upgradeToAndCall() in the proxy + * @param id The id of the proxy to be updated + * @param newAddress The address of the new implementation + **/ + function _updateImpl(bytes32 id, address newAddress) internal { + address payable proxyAddress = payable(_addresses[id]); + + InitializableImmutableAdminUpgradeabilityProxy proxy = + InitializableImmutableAdminUpgradeabilityProxy(proxyAddress); + bytes memory params = abi.encodeWithSignature('initialize(address)', address(this)); + + if (proxyAddress == address(0)) { + proxy = new InitializableImmutableAdminUpgradeabilityProxy(address(this)); + proxy.initialize(newAddress, params); + _addresses[id] = address(proxy); + emit ProxyCreated(id, address(proxy)); + } else { + proxy.upgradeToAndCall(newAddress, params); + } + } + + function _setMarketId(string memory marketId) internal { + _marketId = marketId; + emit MarketIdSet(marketId); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/configuration/LendingPoolAddressesProviderRegistry.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/configuration/LendingPoolAddressesProviderRegistry.sol new file mode 100644 index 000000000..3af2e52a5 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/configuration/LendingPoolAddressesProviderRegistry.sol @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {Ownable} from '../../dependencies/openzeppelin/contracts/Ownable.sol'; +import { + ILendingPoolAddressesProviderRegistry +} from '../../interfaces/ILendingPoolAddressesProviderRegistry.sol'; +import {Errors} from '../libraries/helpers/Errors.sol'; + +/** + * @title LendingPoolAddressesProviderRegistry contract + * @dev Main registry of LendingPoolAddressesProvider of multiple Aave protocol's markets + * - Used for indexing purposes of Aave protocol's markets + * - The id assigned to a LendingPoolAddressesProvider refers to the market it is connected with, + * for example with `0` for the Aave main market and `1` for the next created + * @author Aave + **/ +contract LendingPoolAddressesProviderRegistry is Ownable, ILendingPoolAddressesProviderRegistry { + mapping(address => uint256) private _addressesProviders; + address[] private _addressesProvidersList; + + /** + * @dev Returns the list of registered addresses provider + * @return The list of addresses provider, potentially containing address(0) elements + **/ + function getAddressesProvidersList() external view override returns (address[] memory) { + address[] memory addressesProvidersList = _addressesProvidersList; + + uint256 maxLength = addressesProvidersList.length; + + address[] memory activeProviders = new address[](maxLength); + + for (uint256 i = 0; i < maxLength; i++) { + if (_addressesProviders[addressesProvidersList[i]] > 0) { + activeProviders[i] = addressesProvidersList[i]; + } + } + + return activeProviders; + } + + /** + * @dev Registers an addresses provider + * @param provider The address of the new LendingPoolAddressesProvider + * @param id The id for the new LendingPoolAddressesProvider, referring to the market it belongs to + **/ + function registerAddressesProvider(address provider, uint256 id) external override onlyOwner { + require(id != 0, Errors.LPAPR_INVALID_ADDRESSES_PROVIDER_ID); + + _addressesProviders[provider] = id; + _addToAddressesProvidersList(provider); + emit AddressesProviderRegistered(provider); + } + + /** + * @dev Removes a LendingPoolAddressesProvider from the list of registered addresses provider + * @param provider The LendingPoolAddressesProvider address + **/ + function unregisterAddressesProvider(address provider) external override onlyOwner { + require(_addressesProviders[provider] > 0, Errors.LPAPR_PROVIDER_NOT_REGISTERED); + _addressesProviders[provider] = 0; + emit AddressesProviderUnregistered(provider); + } + + /** + * @dev Returns the id on a registered LendingPoolAddressesProvider + * @return The id or 0 if the LendingPoolAddressesProvider is not registered + */ + function getAddressesProviderIdByAddress(address addressesProvider) + external + view + override + returns (uint256) + { + return _addressesProviders[addressesProvider]; + } + + function _addToAddressesProvidersList(address provider) internal { + uint256 providersCount = _addressesProvidersList.length; + + for (uint256 i = 0; i < providersCount; i++) { + if (_addressesProvidersList[i] == provider) { + return; + } + } + + _addressesProvidersList.push(provider); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/lendingpool/DefaultReserveInterestRateStrategy.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/lendingpool/DefaultReserveInterestRateStrategy.sol new file mode 100644 index 000000000..6e43006e3 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/lendingpool/DefaultReserveInterestRateStrategy.sol @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol'; +import {IReserveInterestRateStrategy} from '../../interfaces/IReserveInterestRateStrategy.sol'; +import {WadRayMath} from '../libraries/math/WadRayMath.sol'; +import {PercentageMath} from '../libraries/math/PercentageMath.sol'; +import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol'; +import {ILendingRateOracle} from '../../interfaces/ILendingRateOracle.sol'; +import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol'; +import 'hardhat/console.sol'; + +/** + * @title DefaultReserveInterestRateStrategy contract + * @notice Implements the calculation of the interest rates depending on the reserve state + * @dev The model of interest rate is based on 2 slopes, one before the `OPTIMAL_UTILIZATION_RATE` + * point of utilization and another from that one to 100% + * - An instance of this same contract, can't be used across different Aave markets, due to the caching + * of the LendingPoolAddressesProvider + * @author Aave + **/ +contract DefaultReserveInterestRateStrategy is IReserveInterestRateStrategy { + using WadRayMath for uint256; + using SafeMath for uint256; + using PercentageMath for uint256; + + /** + * @dev this constant represents the utilization rate at which the pool aims to obtain most competitive borrow rates. + * Expressed in ray + **/ + uint256 public immutable OPTIMAL_UTILIZATION_RATE; + + /** + * @dev This constant represents the excess utilization rate above the optimal. It's always equal to + * 1-optimal utilization rate. Added as a constant here for gas optimizations. + * Expressed in ray + **/ + + uint256 public immutable EXCESS_UTILIZATION_RATE; + + ILendingPoolAddressesProvider public immutable addressesProvider; + + // Base variable borrow rate when Utilization rate = 0. Expressed in ray + uint256 internal immutable _baseVariableBorrowRate; + + // Slope of the variable interest curve when utilization rate > 0 and <= OPTIMAL_UTILIZATION_RATE. Expressed in ray + uint256 internal immutable _variableRateSlope1; + + // Slope of the variable interest curve when utilization rate > OPTIMAL_UTILIZATION_RATE. Expressed in ray + uint256 internal immutable _variableRateSlope2; + + // Slope of the stable interest curve when utilization rate > 0 and <= OPTIMAL_UTILIZATION_RATE. Expressed in ray + uint256 internal immutable _stableRateSlope1; + + // Slope of the stable interest curve when utilization rate > OPTIMAL_UTILIZATION_RATE. Expressed in ray + uint256 internal immutable _stableRateSlope2; + + constructor( + ILendingPoolAddressesProvider provider, + uint256 optimalUtilizationRate, + uint256 baseVariableBorrowRate, + uint256 variableRateSlope1, + uint256 variableRateSlope2, + uint256 stableRateSlope1, + uint256 stableRateSlope2 + ) public { + OPTIMAL_UTILIZATION_RATE = optimalUtilizationRate; + EXCESS_UTILIZATION_RATE = WadRayMath.ray().sub(optimalUtilizationRate); + addressesProvider = provider; + _baseVariableBorrowRate = baseVariableBorrowRate; + _variableRateSlope1 = variableRateSlope1; + _variableRateSlope2 = variableRateSlope2; + _stableRateSlope1 = stableRateSlope1; + _stableRateSlope2 = stableRateSlope2; + } + + function variableRateSlope1() external view returns (uint256) { + return _variableRateSlope1; + } + + function variableRateSlope2() external view returns (uint256) { + return _variableRateSlope2; + } + + function stableRateSlope1() external view returns (uint256) { + return _stableRateSlope1; + } + + function stableRateSlope2() external view returns (uint256) { + return _stableRateSlope2; + } + + function baseVariableBorrowRate() external view override returns (uint256) { + return _baseVariableBorrowRate; + } + + function getMaxVariableBorrowRate() external view override returns (uint256) { + return _baseVariableBorrowRate.add(_variableRateSlope1).add(_variableRateSlope2); + } + + /** + * @dev Calculates the interest rates depending on the reserve's state and configurations + * @param reserve The address of the reserve + * @param liquidityAdded The liquidity added during the operation + * @param liquidityTaken The liquidity taken during the operation + * @param totalStableDebt The total borrowed from the reserve a stable rate + * @param totalVariableDebt The total borrowed from the reserve at a variable rate + * @param averageStableBorrowRate The weighted average of all the stable rate loans + * @param reserveFactor The reserve portion of the interest that goes to the treasury of the market + * @return The liquidity rate, the stable borrow rate and the variable borrow rate + **/ + function calculateInterestRates( + address reserve, + address aToken, + uint256 liquidityAdded, + uint256 liquidityTaken, + uint256 totalStableDebt, + uint256 totalVariableDebt, + uint256 averageStableBorrowRate, + uint256 reserveFactor + ) + external + view + override + returns ( + uint256, + uint256, + uint256 + ) + { + uint256 availableLiquidity = IERC20(reserve).balanceOf(aToken); + //avoid stack too deep + availableLiquidity = availableLiquidity.add(liquidityAdded).sub(liquidityTaken); + + return + calculateInterestRates( + reserve, + availableLiquidity, + totalStableDebt, + totalVariableDebt, + averageStableBorrowRate, + reserveFactor + ); + } + + struct CalcInterestRatesLocalVars { + uint256 totalDebt; + uint256 currentVariableBorrowRate; + uint256 currentStableBorrowRate; + uint256 currentLiquidityRate; + uint256 utilizationRate; + } + + /** + * @dev Calculates the interest rates depending on the reserve's state and configurations. + * NOTE This function is kept for compatibility with the previous DefaultInterestRateStrategy interface. + * New protocol implementation uses the new calculateInterestRates() interface + * @param reserve The address of the reserve + * @param availableLiquidity The liquidity available in the corresponding aToken + * @param totalStableDebt The total borrowed from the reserve a stable rate + * @param totalVariableDebt The total borrowed from the reserve at a variable rate + * @param averageStableBorrowRate The weighted average of all the stable rate loans + * @param reserveFactor The reserve portion of the interest that goes to the treasury of the market + * @return The liquidity rate, the stable borrow rate and the variable borrow rate + **/ + function calculateInterestRates( + address reserve, + uint256 availableLiquidity, + uint256 totalStableDebt, + uint256 totalVariableDebt, + uint256 averageStableBorrowRate, + uint256 reserveFactor + ) + public + view + override + returns ( + uint256, + uint256, + uint256 + ) + { + CalcInterestRatesLocalVars memory vars; + + vars.totalDebt = totalStableDebt.add(totalVariableDebt); + vars.currentVariableBorrowRate = 0; + vars.currentStableBorrowRate = 0; + vars.currentLiquidityRate = 0; + + vars.utilizationRate = vars.totalDebt == 0 + ? 0 + : vars.totalDebt.rayDiv(availableLiquidity.add(vars.totalDebt)); + + vars.currentStableBorrowRate = ILendingRateOracle(addressesProvider.getLendingRateOracle()) + .getMarketBorrowRate(reserve); + + if (vars.utilizationRate > OPTIMAL_UTILIZATION_RATE) { + uint256 excessUtilizationRateRatio = + vars.utilizationRate.sub(OPTIMAL_UTILIZATION_RATE).rayDiv(EXCESS_UTILIZATION_RATE); + + vars.currentStableBorrowRate = vars.currentStableBorrowRate.add(_stableRateSlope1).add( + _stableRateSlope2.rayMul(excessUtilizationRateRatio) + ); + + vars.currentVariableBorrowRate = _baseVariableBorrowRate.add(_variableRateSlope1).add( + _variableRateSlope2.rayMul(excessUtilizationRateRatio) + ); + } else { + vars.currentStableBorrowRate = vars.currentStableBorrowRate.add( + _stableRateSlope1.rayMul(vars.utilizationRate.rayDiv(OPTIMAL_UTILIZATION_RATE)) + ); + vars.currentVariableBorrowRate = _baseVariableBorrowRate.add( + vars.utilizationRate.rayMul(_variableRateSlope1).rayDiv(OPTIMAL_UTILIZATION_RATE) + ); + } + + vars.currentLiquidityRate = _getOverallBorrowRate( + totalStableDebt, + totalVariableDebt, + vars + .currentVariableBorrowRate, + averageStableBorrowRate + ) + .rayMul(vars.utilizationRate) + .percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(reserveFactor)); + + return ( + vars.currentLiquidityRate, + vars.currentStableBorrowRate, + vars.currentVariableBorrowRate + ); + } + + /** + * @dev Calculates the overall borrow rate as the weighted average between the total variable debt and total stable debt + * @param totalStableDebt The total borrowed from the reserve a stable rate + * @param totalVariableDebt The total borrowed from the reserve at a variable rate + * @param currentVariableBorrowRate The current variable borrow rate of the reserve + * @param currentAverageStableBorrowRate The current weighted average of all the stable rate loans + * @return The weighted averaged borrow rate + **/ + function _getOverallBorrowRate( + uint256 totalStableDebt, + uint256 totalVariableDebt, + uint256 currentVariableBorrowRate, + uint256 currentAverageStableBorrowRate + ) internal pure returns (uint256) { + uint256 totalDebt = totalStableDebt.add(totalVariableDebt); + + if (totalDebt == 0) return 0; + + uint256 weightedVariableRate = totalVariableDebt.wadToRay().rayMul(currentVariableBorrowRate); + + uint256 weightedStableRate = totalStableDebt.wadToRay().rayMul(currentAverageStableBorrowRate); + + uint256 overallBorrowRate = + weightedVariableRate.add(weightedStableRate).rayDiv(totalDebt.wadToRay()); + + return overallBorrowRate; + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/lendingpool/LendingPool.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/lendingpool/LendingPool.sol new file mode 100644 index 000000000..3fd1daacb --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/lendingpool/LendingPool.sol @@ -0,0 +1,946 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol'; +import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol'; +import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol'; +import {Address} from '../../dependencies/openzeppelin/contracts/Address.sol'; +import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol'; +import {IAToken} from '../../interfaces/IAToken.sol'; +import {IVariableDebtToken} from '../../interfaces/IVariableDebtToken.sol'; +import {IFlashLoanReceiver} from '../../flashloan/interfaces/IFlashLoanReceiver.sol'; +import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol'; +import {IStableDebtToken} from '../../interfaces/IStableDebtToken.sol'; +import {ILendingPool} from '../../interfaces/ILendingPool.sol'; +import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol'; +import {Helpers} from '../libraries/helpers/Helpers.sol'; +import {Errors} from '../libraries/helpers/Errors.sol'; +import {WadRayMath} from '../libraries/math/WadRayMath.sol'; +import {PercentageMath} from '../libraries/math/PercentageMath.sol'; +import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol'; +import {GenericLogic} from '../libraries/logic/GenericLogic.sol'; +import {ValidationLogic} from '../libraries/logic/ValidationLogic.sol'; +import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol'; +import {UserConfiguration} from '../libraries/configuration/UserConfiguration.sol'; +import {DataTypes} from '../libraries/types/DataTypes.sol'; +import {LendingPoolStorage} from './LendingPoolStorage.sol'; + +/** + * @title LendingPool contract + * @dev Main point of interaction with an Aave protocol's market + * - Users can: + * # Deposit + * # Withdraw + * # Borrow + * # Repay + * # Swap their loans between variable and stable rate + * # Enable/disable their deposits as collateral rebalance stable rate borrow positions + * # Liquidate positions + * # Execute Flash Loans + * - To be covered by a proxy contract, owned by the LendingPoolAddressesProvider of the specific market + * - All admin functions are callable by the LendingPoolConfigurator contract defined also in the + * LendingPoolAddressesProvider + * @author Aave + **/ +contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage { + using SafeMath for uint256; + using WadRayMath for uint256; + using PercentageMath for uint256; + using SafeERC20 for IERC20; + + uint256 public constant LENDINGPOOL_REVISION = 0x2; + + modifier whenNotPaused() { + _whenNotPaused(); + _; + } + + modifier onlyLendingPoolConfigurator() { + _onlyLendingPoolConfigurator(); + _; + } + + function _whenNotPaused() internal view { + require(!_paused, Errors.LP_IS_PAUSED); + } + + function _onlyLendingPoolConfigurator() internal view { + require( + _addressesProvider.getLendingPoolConfigurator() == msg.sender, + Errors.LP_CALLER_NOT_LENDING_POOL_CONFIGURATOR + ); + } + + function getRevision() internal pure override returns (uint256) { + return LENDINGPOOL_REVISION; + } + + /** + * @dev Function is invoked by the proxy contract when the LendingPool contract is added to the + * LendingPoolAddressesProvider of the market. + * - Caching the address of the LendingPoolAddressesProvider in order to reduce gas consumption + * on subsequent operations + * @param provider The address of the LendingPoolAddressesProvider + **/ + function initialize(ILendingPoolAddressesProvider provider) public initializer { + _addressesProvider = provider; + _maxStableRateBorrowSizePercent = 2500; + _flashLoanPremiumTotal = 9; + _maxNumberOfReserves = 128; + } + + /** + * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens. + * - E.g. User deposits 100 USDC and gets in return 100 aUSDC + * @param asset The address of the underlying asset to deposit + * @param amount The amount to be deposited + * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user + * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens + * is a different wallet + * @param referralCode Code used to register the integrator originating the operation, for potential rewards. + * 0 if the action is executed directly by the user, without any middle-man + **/ + function deposit( + address asset, + uint256 amount, + address onBehalfOf, + uint16 referralCode + ) external override whenNotPaused { + DataTypes.ReserveData storage reserve = _reserves[asset]; + + ValidationLogic.validateDeposit(reserve, amount); + + address aToken = reserve.aTokenAddress; + + reserve.updateState(); + reserve.updateInterestRates(asset, aToken, amount, 0); + + IERC20(asset).safeTransferFrom(msg.sender, aToken, amount); + + bool isFirstDeposit = IAToken(aToken).mint(onBehalfOf, amount, reserve.liquidityIndex); + + if (isFirstDeposit) { + _usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true); + emit ReserveUsedAsCollateralEnabled(asset, onBehalfOf); + } + + emit Deposit(asset, msg.sender, onBehalfOf, amount, referralCode); + } + + /** + * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned + * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC + * @param asset The address of the underlying asset to withdraw + * @param amount The underlying amount to be withdrawn + * - Send the value type(uint256).max in order to withdraw the whole aToken balance + * @param to Address that will receive the underlying, same as msg.sender if the user + * wants to receive it on his own wallet, or a different address if the beneficiary is a + * different wallet + * @return The final amount withdrawn + **/ + function withdraw( + address asset, + uint256 amount, + address to + ) external override whenNotPaused returns (uint256) { + DataTypes.ReserveData storage reserve = _reserves[asset]; + + address aToken = reserve.aTokenAddress; + + uint256 userBalance = IAToken(aToken).balanceOf(msg.sender); + + uint256 amountToWithdraw = amount; + + if (amount == type(uint256).max) { + amountToWithdraw = userBalance; + } + + ValidationLogic.validateWithdraw( + asset, + amountToWithdraw, + userBalance, + _reserves, + _usersConfig[msg.sender], + _reservesList, + _reservesCount, + _addressesProvider.getPriceOracle() + ); + + reserve.updateState(); + + reserve.updateInterestRates(asset, aToken, 0, amountToWithdraw); + + if (amountToWithdraw == userBalance) { + _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, false); + emit ReserveUsedAsCollateralDisabled(asset, msg.sender); + } + + IAToken(aToken).burn(msg.sender, to, amountToWithdraw, reserve.liquidityIndex); + + emit Withdraw(asset, msg.sender, to, amountToWithdraw); + + return amountToWithdraw; + } + + /** + * @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower + * already deposited enough collateral, or he was given enough allowance by a credit delegator on the + * corresponding debt token (StableDebtToken or VariableDebtToken) + * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet + * and 100 stable/variable debt tokens, depending on the `interestRateMode` + * @param asset The address of the underlying asset to borrow + * @param amount The amount to be borrowed + * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable + * @param referralCode Code used to register the integrator originating the operation, for potential rewards. + * 0 if the action is executed directly by the user, without any middle-man + * @param onBehalfOf Address of the user who will receive the debt. Should be the address of the borrower itself + * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator + * if he has been given credit delegation allowance + **/ + function borrow( + address asset, + uint256 amount, + uint256 interestRateMode, + uint16 referralCode, + address onBehalfOf + ) external override whenNotPaused { + DataTypes.ReserveData storage reserve = _reserves[asset]; + + _executeBorrow( + ExecuteBorrowParams( + asset, + msg.sender, + onBehalfOf, + amount, + interestRateMode, + reserve.aTokenAddress, + referralCode, + true + ) + ); + } + + /** + * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned + * - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address + * @param asset The address of the borrowed underlying asset previously borrowed + * @param amount The amount to repay + * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode` + * @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable + * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the + * user calling the function if he wants to reduce/remove his own debt, or the address of any other + * other borrower whose debt should be removed + * @return The final amount repaid + **/ + function repay( + address asset, + uint256 amount, + uint256 rateMode, + address onBehalfOf + ) external override whenNotPaused returns (uint256) { + DataTypes.ReserveData storage reserve = _reserves[asset]; + + (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(onBehalfOf, reserve); + + DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode); + + ValidationLogic.validateRepay( + reserve, + amount, + interestRateMode, + onBehalfOf, + stableDebt, + variableDebt + ); + + uint256 paybackAmount = + interestRateMode == DataTypes.InterestRateMode.STABLE ? stableDebt : variableDebt; + + if (amount < paybackAmount) { + paybackAmount = amount; + } + + reserve.updateState(); + + if (interestRateMode == DataTypes.InterestRateMode.STABLE) { + IStableDebtToken(reserve.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount); + } else { + IVariableDebtToken(reserve.variableDebtTokenAddress).burn( + onBehalfOf, + paybackAmount, + reserve.variableBorrowIndex + ); + } + + address aToken = reserve.aTokenAddress; + reserve.updateInterestRates(asset, aToken, paybackAmount, 0); + + if (stableDebt.add(variableDebt).sub(paybackAmount) == 0) { + _usersConfig[onBehalfOf].setBorrowing(reserve.id, false); + } + + IERC20(asset).safeTransferFrom(msg.sender, aToken, paybackAmount); + + IAToken(aToken).handleRepayment(msg.sender, paybackAmount); + + emit Repay(asset, onBehalfOf, msg.sender, paybackAmount); + + return paybackAmount; + } + + /** + * @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa + * @param asset The address of the underlying asset borrowed + * @param rateMode The rate mode that the user wants to swap to + **/ + function swapBorrowRateMode(address asset, uint256 rateMode) external override whenNotPaused { + DataTypes.ReserveData storage reserve = _reserves[asset]; + + (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(msg.sender, reserve); + + DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode); + + ValidationLogic.validateSwapRateMode( + reserve, + _usersConfig[msg.sender], + stableDebt, + variableDebt, + interestRateMode + ); + + reserve.updateState(); + + if (interestRateMode == DataTypes.InterestRateMode.STABLE) { + IStableDebtToken(reserve.stableDebtTokenAddress).burn(msg.sender, stableDebt); + IVariableDebtToken(reserve.variableDebtTokenAddress).mint( + msg.sender, + msg.sender, + stableDebt, + reserve.variableBorrowIndex + ); + } else { + IVariableDebtToken(reserve.variableDebtTokenAddress).burn( + msg.sender, + variableDebt, + reserve.variableBorrowIndex + ); + IStableDebtToken(reserve.stableDebtTokenAddress).mint( + msg.sender, + msg.sender, + variableDebt, + reserve.currentStableBorrowRate + ); + } + + reserve.updateInterestRates(asset, reserve.aTokenAddress, 0, 0); + + emit Swap(asset, msg.sender, rateMode); + } + + /** + * @dev Rebalances the stable interest rate of a user to the current stable rate defined on the reserve. + * - Users can be rebalanced if the following conditions are satisfied: + * 1. Usage ratio is above 95% + * 2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been + * borrowed at a stable rate and depositors are not earning enough + * @param asset The address of the underlying asset borrowed + * @param user The address of the user to be rebalanced + **/ + function rebalanceStableBorrowRate(address asset, address user) external override whenNotPaused { + DataTypes.ReserveData storage reserve = _reserves[asset]; + + IERC20 stableDebtToken = IERC20(reserve.stableDebtTokenAddress); + IERC20 variableDebtToken = IERC20(reserve.variableDebtTokenAddress); + address aTokenAddress = reserve.aTokenAddress; + + uint256 stableDebt = IERC20(stableDebtToken).balanceOf(user); + + ValidationLogic.validateRebalanceStableBorrowRate( + reserve, + asset, + stableDebtToken, + variableDebtToken, + aTokenAddress + ); + + reserve.updateState(); + + IStableDebtToken(address(stableDebtToken)).burn(user, stableDebt); + IStableDebtToken(address(stableDebtToken)).mint( + user, + user, + stableDebt, + reserve.currentStableBorrowRate + ); + + reserve.updateInterestRates(asset, aTokenAddress, 0, 0); + + emit RebalanceStableBorrowRate(asset, user); + } + + /** + * @dev Allows depositors to enable/disable a specific deposited asset as collateral + * @param asset The address of the underlying asset deposited + * @param useAsCollateral `true` if the user wants to use the deposit as collateral, `false` otherwise + **/ + function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) + external + override + whenNotPaused + { + DataTypes.ReserveData storage reserve = _reserves[asset]; + + ValidationLogic.validateSetUseReserveAsCollateral( + reserve, + asset, + useAsCollateral, + _reserves, + _usersConfig[msg.sender], + _reservesList, + _reservesCount, + _addressesProvider.getPriceOracle() + ); + + _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, useAsCollateral); + + if (useAsCollateral) { + emit ReserveUsedAsCollateralEnabled(asset, msg.sender); + } else { + emit ReserveUsedAsCollateralDisabled(asset, msg.sender); + } + } + + /** + * @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1 + * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives + * a proportionally amount of the `collateralAsset` plus a bonus to cover market risk + * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation + * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation + * @param user The address of the borrower getting liquidated + * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover + * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants + * to receive the underlying collateral asset directly + **/ + function liquidationCall( + address collateralAsset, + address debtAsset, + address user, + uint256 debtToCover, + bool receiveAToken + ) external override whenNotPaused { + address collateralManager = _addressesProvider.getLendingPoolCollateralManager(); + + //solium-disable-next-line + (bool success, bytes memory result) = + collateralManager.delegatecall( + abi.encodeWithSignature( + 'liquidationCall(address,address,address,uint256,bool)', + collateralAsset, + debtAsset, + user, + debtToCover, + receiveAToken + ) + ); + + require(success, Errors.LP_LIQUIDATION_CALL_FAILED); + + (uint256 returnCode, string memory returnMessage) = abi.decode(result, (uint256, string)); + + require(returnCode == 0, string(abi.encodePacked(returnMessage))); + } + + struct FlashLoanLocalVars { + IFlashLoanReceiver receiver; + address oracle; + uint256 i; + address currentAsset; + address currentATokenAddress; + uint256 currentAmount; + uint256 currentPremium; + uint256 currentAmountPlusPremium; + address debtToken; + } + + /** + * @dev Allows smartcontracts to access the liquidity of the pool within one transaction, + * as long as the amount taken plus a fee is returned. + * IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration. + * For further details please visit https://developers.aave.com + * @param receiverAddress The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface + * @param assets The addresses of the assets being flash-borrowed + * @param amounts The amounts amounts being flash-borrowed + * @param modes Types of the debt to open if the flash loan is not returned: + * 0 -> Don't open any debt, just revert if funds can't be transferred from the receiver + * 1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address + * 2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address + * @param onBehalfOf The address that will receive the debt in the case of using on `modes` 1 or 2 + * @param params Variadic packed params to pass to the receiver as extra information + * @param referralCode Code used to register the integrator originating the operation, for potential rewards. + * 0 if the action is executed directly by the user, without any middle-man + **/ + function flashLoan( + address receiverAddress, + address[] calldata assets, + uint256[] calldata amounts, + uint256[] calldata modes, + address onBehalfOf, + bytes calldata params, + uint16 referralCode + ) external override whenNotPaused { + FlashLoanLocalVars memory vars; + + ValidationLogic.validateFlashloan(assets, amounts); + + address[] memory aTokenAddresses = new address[](assets.length); + uint256[] memory premiums = new uint256[](assets.length); + + vars.receiver = IFlashLoanReceiver(receiverAddress); + + for (vars.i = 0; vars.i < assets.length; vars.i++) { + aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress; + + premiums[vars.i] = amounts[vars.i].mul(_flashLoanPremiumTotal).div(10000); + + IAToken(aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]); + } + + require( + vars.receiver.executeOperation(assets, amounts, premiums, msg.sender, params), + Errors.LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN + ); + + for (vars.i = 0; vars.i < assets.length; vars.i++) { + vars.currentAsset = assets[vars.i]; + vars.currentAmount = amounts[vars.i]; + vars.currentPremium = premiums[vars.i]; + vars.currentATokenAddress = aTokenAddresses[vars.i]; + vars.currentAmountPlusPremium = vars.currentAmount.add(vars.currentPremium); + + if (DataTypes.InterestRateMode(modes[vars.i]) == DataTypes.InterestRateMode.NONE) { + _reserves[vars.currentAsset].updateState(); + _reserves[vars.currentAsset].cumulateToLiquidityIndex( + IERC20(vars.currentATokenAddress).totalSupply(), + vars.currentPremium + ); + _reserves[vars.currentAsset].updateInterestRates( + vars.currentAsset, + vars.currentATokenAddress, + vars.currentAmountPlusPremium, + 0 + ); + + IERC20(vars.currentAsset).safeTransferFrom( + receiverAddress, + vars.currentATokenAddress, + vars.currentAmountPlusPremium + ); + } else { + // If the user chose to not return the funds, the system checks if there is enough collateral and + // eventually opens a debt position + _executeBorrow( + ExecuteBorrowParams( + vars.currentAsset, + msg.sender, + onBehalfOf, + vars.currentAmount, + modes[vars.i], + vars.currentATokenAddress, + referralCode, + false + ) + ); + } + emit FlashLoan( + receiverAddress, + msg.sender, + vars.currentAsset, + vars.currentAmount, + vars.currentPremium, + referralCode + ); + } + } + + /** + * @dev Returns the state and configuration of the reserve + * @param asset The address of the underlying asset of the reserve + * @return The state of the reserve + **/ + function getReserveData(address asset) + external + view + override + returns (DataTypes.ReserveData memory) + { + return _reserves[asset]; + } + + /** + * @dev Returns the user account data across all the reserves + * @param user The address of the user + * @return totalCollateralETH the total collateral in ETH of the user + * @return totalDebtETH the total debt in ETH of the user + * @return availableBorrowsETH the borrowing power left of the user + * @return currentLiquidationThreshold the liquidation threshold of the user + * @return ltv the loan to value of the user + * @return healthFactor the current health factor of the user + **/ + function getUserAccountData(address user) + external + view + override + returns ( + uint256 totalCollateralETH, + uint256 totalDebtETH, + uint256 availableBorrowsETH, + uint256 currentLiquidationThreshold, + uint256 ltv, + uint256 healthFactor + ) + { + ( + totalCollateralETH, + totalDebtETH, + ltv, + currentLiquidationThreshold, + healthFactor + ) = GenericLogic.calculateUserAccountData( + user, + _reserves, + _usersConfig[user], + _reservesList, + _reservesCount, + _addressesProvider.getPriceOracle() + ); + + availableBorrowsETH = GenericLogic.calculateAvailableBorrowsETH( + totalCollateralETH, + totalDebtETH, + ltv + ); + } + + /** + * @dev Returns the configuration of the reserve + * @param asset The address of the underlying asset of the reserve + * @return The configuration of the reserve + **/ + function getConfiguration(address asset) + external + view + override + returns (DataTypes.ReserveConfigurationMap memory) + { + return _reserves[asset].configuration; + } + + /** + * @dev Returns the configuration of the user across all the reserves + * @param user The user address + * @return The configuration of the user + **/ + function getUserConfiguration(address user) + external + view + override + returns (DataTypes.UserConfigurationMap memory) + { + return _usersConfig[user]; + } + + /** + * @dev Returns the normalized income per unit of asset + * @param asset The address of the underlying asset of the reserve + * @return The reserve's normalized income + */ + function getReserveNormalizedIncome(address asset) + external + view + virtual + override + returns (uint256) + { + return _reserves[asset].getNormalizedIncome(); + } + + /** + * @dev Returns the normalized variable debt per unit of asset + * @param asset The address of the underlying asset of the reserve + * @return The reserve normalized variable debt + */ + function getReserveNormalizedVariableDebt(address asset) + external + view + override + returns (uint256) + { + return _reserves[asset].getNormalizedDebt(); + } + + /** + * @dev Returns if the LendingPool is paused + */ + function paused() external view override returns (bool) { + return _paused; + } + + /** + * @dev Returns the list of the initialized reserves + **/ + function getReservesList() external view override returns (address[] memory) { + address[] memory _activeReserves = new address[](_reservesCount); + + for (uint256 i = 0; i < _reservesCount; i++) { + _activeReserves[i] = _reservesList[i]; + } + return _activeReserves; + } + + /** + * @dev Returns the cached LendingPoolAddressesProvider connected to this contract + **/ + function getAddressesProvider() external view override returns (ILendingPoolAddressesProvider) { + return _addressesProvider; + } + + /** + * @dev Returns the percentage of available liquidity that can be borrowed at once at stable rate + */ + function MAX_STABLE_RATE_BORROW_SIZE_PERCENT() public view returns (uint256) { + return _maxStableRateBorrowSizePercent; + } + + /** + * @dev Returns the fee on flash loans + */ + function FLASHLOAN_PREMIUM_TOTAL() public view returns (uint256) { + return _flashLoanPremiumTotal; + } + + /** + * @dev Returns the maximum number of reserves supported to be listed in this LendingPool + */ + function MAX_NUMBER_RESERVES() public view returns (uint256) { + return _maxNumberOfReserves; + } + + /** + * @dev Validates and finalizes an aToken transfer + * - Only callable by the overlying aToken of the `asset` + * @param asset The address of the underlying asset of the aToken + * @param from The user from which the aTokens are transferred + * @param to The user receiving the aTokens + * @param amount The amount being transferred/withdrawn + * @param balanceFromBefore The aToken balance of the `from` user before the transfer + * @param balanceToBefore The aToken balance of the `to` user before the transfer + */ + function finalizeTransfer( + address asset, + address from, + address to, + uint256 amount, + uint256 balanceFromBefore, + uint256 balanceToBefore + ) external override whenNotPaused { + require(msg.sender == _reserves[asset].aTokenAddress, Errors.LP_CALLER_MUST_BE_AN_ATOKEN); + + ValidationLogic.validateTransfer( + from, + _reserves, + _usersConfig[from], + _reservesList, + _reservesCount, + _addressesProvider.getPriceOracle() + ); + + uint256 reserveId = _reserves[asset].id; + + if (from != to) { + if (balanceFromBefore.sub(amount) == 0) { + DataTypes.UserConfigurationMap storage fromConfig = _usersConfig[from]; + fromConfig.setUsingAsCollateral(reserveId, false); + emit ReserveUsedAsCollateralDisabled(asset, from); + } + + if (balanceToBefore == 0 && amount != 0) { + DataTypes.UserConfigurationMap storage toConfig = _usersConfig[to]; + toConfig.setUsingAsCollateral(reserveId, true); + emit ReserveUsedAsCollateralEnabled(asset, to); + } + } + } + + /** + * @dev Initializes a reserve, activating it, assigning an aToken and debt tokens and an + * interest rate strategy + * - Only callable by the LendingPoolConfigurator contract + * @param asset The address of the underlying asset of the reserve + * @param aTokenAddress The address of the aToken that will be assigned to the reserve + * @param stableDebtAddress The address of the StableDebtToken that will be assigned to the reserve + * @param aTokenAddress The address of the VariableDebtToken that will be assigned to the reserve + * @param interestRateStrategyAddress The address of the interest rate strategy contract + **/ + function initReserve( + address asset, + address aTokenAddress, + address stableDebtAddress, + address variableDebtAddress, + address interestRateStrategyAddress + ) external override onlyLendingPoolConfigurator { + require(Address.isContract(asset), Errors.LP_NOT_CONTRACT); + _reserves[asset].init( + aTokenAddress, + stableDebtAddress, + variableDebtAddress, + interestRateStrategyAddress + ); + _addReserveToList(asset); + } + + /** + * @dev Updates the address of the interest rate strategy contract + * - Only callable by the LendingPoolConfigurator contract + * @param asset The address of the underlying asset of the reserve + * @param rateStrategyAddress The address of the interest rate strategy contract + **/ + function setReserveInterestRateStrategyAddress(address asset, address rateStrategyAddress) + external + override + onlyLendingPoolConfigurator + { + _reserves[asset].interestRateStrategyAddress = rateStrategyAddress; + } + + /** + * @dev Sets the configuration bitmap of the reserve as a whole + * - Only callable by the LendingPoolConfigurator contract + * @param asset The address of the underlying asset of the reserve + * @param configuration The new configuration bitmap + **/ + function setConfiguration(address asset, uint256 configuration) + external + override + onlyLendingPoolConfigurator + { + _reserves[asset].configuration.data = configuration; + } + + /** + * @dev Set the _pause state of a reserve + * - Only callable by the LendingPoolConfigurator contract + * @param val `true` to pause the reserve, `false` to un-pause it + */ + function setPause(bool val) external override onlyLendingPoolConfigurator { + _paused = val; + if (_paused) { + emit Paused(); + } else { + emit Unpaused(); + } + } + + struct ExecuteBorrowParams { + address asset; + address user; + address onBehalfOf; + uint256 amount; + uint256 interestRateMode; + address aTokenAddress; + uint16 referralCode; + bool releaseUnderlying; + } + + function _executeBorrow(ExecuteBorrowParams memory vars) internal { + DataTypes.ReserveData storage reserve = _reserves[vars.asset]; + DataTypes.UserConfigurationMap storage userConfig = _usersConfig[vars.onBehalfOf]; + + address oracle = _addressesProvider.getPriceOracle(); + + uint256 amountInETH = + IPriceOracleGetter(oracle).getAssetPrice(vars.asset).mul(vars.amount).div( + 10**reserve.configuration.getDecimals() + ); + + ValidationLogic.validateBorrow( + vars.asset, + reserve, + vars.onBehalfOf, + vars.amount, + amountInETH, + vars.interestRateMode, + _maxStableRateBorrowSizePercent, + _reserves, + userConfig, + _reservesList, + _reservesCount, + oracle + ); + + reserve.updateState(); + + uint256 currentStableRate = 0; + + bool isFirstBorrowing = false; + if (DataTypes.InterestRateMode(vars.interestRateMode) == DataTypes.InterestRateMode.STABLE) { + currentStableRate = reserve.currentStableBorrowRate; + + isFirstBorrowing = IStableDebtToken(reserve.stableDebtTokenAddress).mint( + vars.user, + vars.onBehalfOf, + vars.amount, + currentStableRate + ); + } else { + isFirstBorrowing = IVariableDebtToken(reserve.variableDebtTokenAddress).mint( + vars.user, + vars.onBehalfOf, + vars.amount, + reserve.variableBorrowIndex + ); + } + + if (isFirstBorrowing) { + userConfig.setBorrowing(reserve.id, true); + } + + reserve.updateInterestRates( + vars.asset, + vars.aTokenAddress, + 0, + vars.releaseUnderlying ? vars.amount : 0 + ); + + if (vars.releaseUnderlying) { + IAToken(vars.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount); + } + + emit Borrow( + vars.asset, + vars.user, + vars.onBehalfOf, + vars.amount, + vars.interestRateMode, + DataTypes.InterestRateMode(vars.interestRateMode) == DataTypes.InterestRateMode.STABLE + ? currentStableRate + : reserve.currentVariableBorrowRate, + vars.referralCode + ); + } + + function _addReserveToList(address asset) internal { + uint256 reservesCount = _reservesCount; + + require(reservesCount < _maxNumberOfReserves, Errors.LP_NO_MORE_RESERVES_ALLOWED); + + bool reserveAlreadyAdded = _reserves[asset].id != 0 || _reservesList[0] == asset; + + if (!reserveAlreadyAdded) { + _reserves[asset].id = uint8(reservesCount); + _reservesList[reservesCount] = asset; + + _reservesCount = reservesCount + 1; + } + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol new file mode 100644 index 000000000..2acd52bdb --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/lendingpool/LendingPoolCollateralManager.sol @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {SafeMath} from '../../dependencies/openzeppelin/contracts//SafeMath.sol'; +import {IERC20} from '../../dependencies/openzeppelin/contracts//IERC20.sol'; +import {IAToken} from '../../interfaces/IAToken.sol'; +import {IStableDebtToken} from '../../interfaces/IStableDebtToken.sol'; +import {IVariableDebtToken} from '../../interfaces/IVariableDebtToken.sol'; +import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol'; +import {ILendingPoolCollateralManager} from '../../interfaces/ILendingPoolCollateralManager.sol'; +import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol'; +import {GenericLogic} from '../libraries/logic/GenericLogic.sol'; +import {Helpers} from '../libraries/helpers/Helpers.sol'; +import {WadRayMath} from '../libraries/math/WadRayMath.sol'; +import {PercentageMath} from '../libraries/math/PercentageMath.sol'; +import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol'; +import {Errors} from '../libraries/helpers/Errors.sol'; +import {ValidationLogic} from '../libraries/logic/ValidationLogic.sol'; +import {DataTypes} from '../libraries/types/DataTypes.sol'; +import {LendingPoolStorage} from './LendingPoolStorage.sol'; + +/** + * @title LendingPoolCollateralManager contract + * @author Aave + * @dev Implements actions involving management of collateral in the protocol, the main one being the liquidations + * IMPORTANT This contract will run always via DELEGATECALL, through the LendingPool, so the chain of inheritance + * is the same as the LendingPool, to have compatible storage layouts + **/ +contract LendingPoolCollateralManager is + ILendingPoolCollateralManager, + VersionedInitializable, + LendingPoolStorage +{ + using SafeERC20 for IERC20; + using SafeMath for uint256; + using WadRayMath for uint256; + using PercentageMath for uint256; + + uint256 internal constant LIQUIDATION_CLOSE_FACTOR_PERCENT = 5000; + + struct LiquidationCallLocalVars { + uint256 userCollateralBalance; + uint256 userStableDebt; + uint256 userVariableDebt; + uint256 maxLiquidatableDebt; + uint256 actualDebtToLiquidate; + uint256 liquidationRatio; + uint256 maxAmountCollateralToLiquidate; + uint256 userStableRate; + uint256 maxCollateralToLiquidate; + uint256 debtAmountNeeded; + uint256 healthFactor; + uint256 liquidatorPreviousATokenBalance; + IAToken collateralAtoken; + bool isCollateralEnabled; + DataTypes.InterestRateMode borrowRateMode; + uint256 errorCode; + string errorMsg; + } + + /** + * @dev As thIS contract extends the VersionedInitializable contract to match the state + * of the LendingPool contract, the getRevision() function is needed, but the value is not + * important, as the initialize() function will never be called here + */ + function getRevision() internal pure override returns (uint256) { + return 0; + } + + /** + * @dev Function to liquidate a position if its Health Factor drops below 1 + * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives + * a proportionally amount of the `collateralAsset` plus a bonus to cover market risk + * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation + * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation + * @param user The address of the borrower getting liquidated + * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover + * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants + * to receive the underlying collateral asset directly + **/ + function liquidationCall( + address collateralAsset, + address debtAsset, + address user, + uint256 debtToCover, + bool receiveAToken + ) external override returns (uint256, string memory) { + DataTypes.ReserveData storage collateralReserve = _reserves[collateralAsset]; + DataTypes.ReserveData storage debtReserve = _reserves[debtAsset]; + DataTypes.UserConfigurationMap storage userConfig = _usersConfig[user]; + + LiquidationCallLocalVars memory vars; + + (, , , , vars.healthFactor) = GenericLogic.calculateUserAccountData( + user, + _reserves, + userConfig, + _reservesList, + _reservesCount, + _addressesProvider.getPriceOracle() + ); + + (vars.userStableDebt, vars.userVariableDebt) = Helpers.getUserCurrentDebt(user, debtReserve); + + (vars.errorCode, vars.errorMsg) = ValidationLogic.validateLiquidationCall( + collateralReserve, + debtReserve, + userConfig, + vars.healthFactor, + vars.userStableDebt, + vars.userVariableDebt + ); + + if (Errors.CollateralManagerErrors(vars.errorCode) != Errors.CollateralManagerErrors.NO_ERROR) { + return (vars.errorCode, vars.errorMsg); + } + + vars.collateralAtoken = IAToken(collateralReserve.aTokenAddress); + + vars.userCollateralBalance = vars.collateralAtoken.balanceOf(user); + + vars.maxLiquidatableDebt = vars.userStableDebt.add(vars.userVariableDebt).percentMul( + LIQUIDATION_CLOSE_FACTOR_PERCENT + ); + + vars.actualDebtToLiquidate = debtToCover > vars.maxLiquidatableDebt + ? vars.maxLiquidatableDebt + : debtToCover; + + ( + vars.maxCollateralToLiquidate, + vars.debtAmountNeeded + ) = _calculateAvailableCollateralToLiquidate( + collateralReserve, + debtReserve, + collateralAsset, + debtAsset, + vars.actualDebtToLiquidate, + vars.userCollateralBalance + ); + + // If debtAmountNeeded < actualDebtToLiquidate, there isn't enough + // collateral to cover the actual amount that is being liquidated, hence we liquidate + // a smaller amount + + if (vars.debtAmountNeeded < vars.actualDebtToLiquidate) { + vars.actualDebtToLiquidate = vars.debtAmountNeeded; + } + + // If the liquidator reclaims the underlying asset, we make sure there is enough available liquidity in the + // collateral reserve + if (!receiveAToken) { + uint256 currentAvailableCollateral = + IERC20(collateralAsset).balanceOf(address(vars.collateralAtoken)); + if (currentAvailableCollateral < vars.maxCollateralToLiquidate) { + return ( + uint256(Errors.CollateralManagerErrors.NOT_ENOUGH_LIQUIDITY), + Errors.LPCM_NOT_ENOUGH_LIQUIDITY_TO_LIQUIDATE + ); + } + } + + debtReserve.updateState(); + + if (vars.userVariableDebt >= vars.actualDebtToLiquidate) { + IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn( + user, + vars.actualDebtToLiquidate, + debtReserve.variableBorrowIndex + ); + } else { + // If the user doesn't have variable debt, no need to try to burn variable debt tokens + if (vars.userVariableDebt > 0) { + IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn( + user, + vars.userVariableDebt, + debtReserve.variableBorrowIndex + ); + } + IStableDebtToken(debtReserve.stableDebtTokenAddress).burn( + user, + vars.actualDebtToLiquidate.sub(vars.userVariableDebt) + ); + } + + debtReserve.updateInterestRates( + debtAsset, + debtReserve.aTokenAddress, + vars.actualDebtToLiquidate, + 0 + ); + + if (receiveAToken) { + vars.liquidatorPreviousATokenBalance = IERC20(vars.collateralAtoken).balanceOf(msg.sender); + vars.collateralAtoken.transferOnLiquidation(user, msg.sender, vars.maxCollateralToLiquidate); + + if (vars.liquidatorPreviousATokenBalance == 0) { + DataTypes.UserConfigurationMap storage liquidatorConfig = _usersConfig[msg.sender]; + liquidatorConfig.setUsingAsCollateral(collateralReserve.id, true); + emit ReserveUsedAsCollateralEnabled(collateralAsset, msg.sender); + } + } else { + collateralReserve.updateState(); + collateralReserve.updateInterestRates( + collateralAsset, + address(vars.collateralAtoken), + 0, + vars.maxCollateralToLiquidate + ); + + // Burn the equivalent amount of aToken, sending the underlying to the liquidator + vars.collateralAtoken.burn( + user, + msg.sender, + vars.maxCollateralToLiquidate, + collateralReserve.liquidityIndex + ); + } + + // If the collateral being liquidated is equal to the user balance, + // we set the currency as not being used as collateral anymore + if (vars.maxCollateralToLiquidate == vars.userCollateralBalance) { + userConfig.setUsingAsCollateral(collateralReserve.id, false); + emit ReserveUsedAsCollateralDisabled(collateralAsset, user); + } + + // Transfers the debt asset being repaid to the aToken, where the liquidity is kept + IERC20(debtAsset).safeTransferFrom( + msg.sender, + debtReserve.aTokenAddress, + vars.actualDebtToLiquidate + ); + + emit LiquidationCall( + collateralAsset, + debtAsset, + user, + vars.actualDebtToLiquidate, + vars.maxCollateralToLiquidate, + msg.sender, + receiveAToken + ); + + return (uint256(Errors.CollateralManagerErrors.NO_ERROR), Errors.LPCM_NO_ERRORS); + } + + struct AvailableCollateralToLiquidateLocalVars { + uint256 userCompoundedBorrowBalance; + uint256 liquidationBonus; + uint256 collateralPrice; + uint256 debtAssetPrice; + uint256 maxAmountCollateralToLiquidate; + uint256 debtAssetDecimals; + uint256 collateralDecimals; + } + + /** + * @dev Calculates how much of a specific collateral can be liquidated, given + * a certain amount of debt asset. + * - This function needs to be called after all the checks to validate the liquidation have been performed, + * otherwise it might fail. + * @param collateralReserve The data of the collateral reserve + * @param debtReserve The data of the debt reserve + * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation + * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation + * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover + * @param userCollateralBalance The collateral balance for the specific `collateralAsset` of the user being liquidated + * @return collateralAmount: The maximum amount that is possible to liquidate given all the liquidation constraints + * (user balance, close factor) + * debtAmountNeeded: The amount to repay with the liquidation + **/ + function _calculateAvailableCollateralToLiquidate( + DataTypes.ReserveData storage collateralReserve, + DataTypes.ReserveData storage debtReserve, + address collateralAsset, + address debtAsset, + uint256 debtToCover, + uint256 userCollateralBalance + ) internal view returns (uint256, uint256) { + uint256 collateralAmount = 0; + uint256 debtAmountNeeded = 0; + IPriceOracleGetter oracle = IPriceOracleGetter(_addressesProvider.getPriceOracle()); + + AvailableCollateralToLiquidateLocalVars memory vars; + + vars.collateralPrice = oracle.getAssetPrice(collateralAsset); + vars.debtAssetPrice = oracle.getAssetPrice(debtAsset); + + (, , vars.liquidationBonus, vars.collateralDecimals, ) = collateralReserve + .configuration + .getParams(); + vars.debtAssetDecimals = debtReserve.configuration.getDecimals(); + + // This is the maximum possible amount of the selected collateral that can be liquidated, given the + // max amount of liquidatable debt + vars.maxAmountCollateralToLiquidate = vars + .debtAssetPrice + .mul(debtToCover) + .mul(10**vars.collateralDecimals) + .percentMul(vars.liquidationBonus) + .div(vars.collateralPrice.mul(10**vars.debtAssetDecimals)); + + if (vars.maxAmountCollateralToLiquidate > userCollateralBalance) { + collateralAmount = userCollateralBalance; + debtAmountNeeded = vars + .collateralPrice + .mul(collateralAmount) + .mul(10**vars.debtAssetDecimals) + .div(vars.debtAssetPrice.mul(10**vars.collateralDecimals)) + .percentDiv(vars.liquidationBonus); + } else { + collateralAmount = vars.maxAmountCollateralToLiquidate; + debtAmountNeeded = debtToCover; + } + return (collateralAmount, debtAmountNeeded); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/lendingpool/LendingPoolConfigurator.sol new file mode 100644 index 000000000..6b71378c5 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -0,0 +1,487 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol'; +import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol'; +import { + InitializableImmutableAdminUpgradeabilityProxy +} from '../libraries/aave-upgradeability/InitializableImmutableAdminUpgradeabilityProxy.sol'; +import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol'; +import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol'; +import {ILendingPool} from '../../interfaces/ILendingPool.sol'; +import {IERC20Detailed} from '../../dependencies/openzeppelin/contracts/IERC20Detailed.sol'; +import {Errors} from '../libraries/helpers/Errors.sol'; +import {PercentageMath} from '../libraries/math/PercentageMath.sol'; +import {DataTypes} from '../libraries/types/DataTypes.sol'; +import {IInitializableDebtToken} from '../../interfaces/IInitializableDebtToken.sol'; +import {IInitializableAToken} from '../../interfaces/IInitializableAToken.sol'; +import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol'; +import {ILendingPoolConfigurator} from '../../interfaces/ILendingPoolConfigurator.sol'; + +/** + * @title LendingPoolConfigurator contract + * @author Aave + * @dev Implements the configuration methods for the Aave protocol + **/ + +contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigurator { + using SafeMath for uint256; + using PercentageMath for uint256; + using ReserveConfiguration for DataTypes.ReserveConfigurationMap; + + ILendingPoolAddressesProvider internal addressesProvider; + ILendingPool internal pool; + + modifier onlyPoolAdmin { + require(addressesProvider.getPoolAdmin() == msg.sender, Errors.CALLER_NOT_POOL_ADMIN); + _; + } + + modifier onlyEmergencyAdmin { + require( + addressesProvider.getEmergencyAdmin() == msg.sender, + Errors.LPC_CALLER_NOT_EMERGENCY_ADMIN + ); + _; + } + + uint256 internal constant CONFIGURATOR_REVISION = 0x1; + + function getRevision() internal pure override returns (uint256) { + return CONFIGURATOR_REVISION; + } + + function initialize(ILendingPoolAddressesProvider provider) public initializer { + addressesProvider = provider; + pool = ILendingPool(addressesProvider.getLendingPool()); + } + + /** + * @dev Initializes reserves in batch + **/ + function batchInitReserve(InitReserveInput[] calldata input) external onlyPoolAdmin { + ILendingPool cachedPool = pool; + for (uint256 i = 0; i < input.length; i++) { + _initReserve(cachedPool, input[i]); + } + } + + function _initReserve(ILendingPool pool, InitReserveInput calldata input) internal { + address aTokenProxyAddress = + _initTokenWithProxy( + input.aTokenImpl, + abi.encodeWithSelector( + IInitializableAToken.initialize.selector, + pool, + input.treasury, + input.underlyingAsset, + IAaveIncentivesController(input.incentivesController), + input.underlyingAssetDecimals, + input.aTokenName, + input.aTokenSymbol, + input.params + ) + ); + + address stableDebtTokenProxyAddress = + _initTokenWithProxy( + input.stableDebtTokenImpl, + abi.encodeWithSelector( + IInitializableDebtToken.initialize.selector, + pool, + input.underlyingAsset, + IAaveIncentivesController(input.incentivesController), + input.underlyingAssetDecimals, + input.stableDebtTokenName, + input.stableDebtTokenSymbol, + input.params + ) + ); + + address variableDebtTokenProxyAddress = + _initTokenWithProxy( + input.variableDebtTokenImpl, + abi.encodeWithSelector( + IInitializableDebtToken.initialize.selector, + pool, + input.underlyingAsset, + IAaveIncentivesController(input.incentivesController), + input.underlyingAssetDecimals, + input.variableDebtTokenName, + input.variableDebtTokenSymbol, + input.params + ) + ); + + pool.initReserve( + input.underlyingAsset, + aTokenProxyAddress, + stableDebtTokenProxyAddress, + variableDebtTokenProxyAddress, + input.interestRateStrategyAddress + ); + + DataTypes.ReserveConfigurationMap memory currentConfig = + pool.getConfiguration(input.underlyingAsset); + + currentConfig.setDecimals(input.underlyingAssetDecimals); + + currentConfig.setActive(true); + currentConfig.setFrozen(false); + + pool.setConfiguration(input.underlyingAsset, currentConfig.data); + + emit ReserveInitialized( + input.underlyingAsset, + aTokenProxyAddress, + stableDebtTokenProxyAddress, + variableDebtTokenProxyAddress, + input.interestRateStrategyAddress + ); + } + + /** + * @dev Updates the aToken implementation for the reserve + **/ + function updateAToken(UpdateATokenInput calldata input) external onlyPoolAdmin { + ILendingPool cachedPool = pool; + + DataTypes.ReserveData memory reserveData = cachedPool.getReserveData(input.asset); + + (, , , uint256 decimals, ) = cachedPool.getConfiguration(input.asset).getParamsMemory(); + + bytes memory encodedCall = abi.encodeWithSelector( + IInitializableAToken.initialize.selector, + cachedPool, + input.treasury, + input.asset, + input.incentivesController, + decimals, + input.name, + input.symbol, + input.params + ); + + _upgradeTokenImplementation( + reserveData.aTokenAddress, + input.implementation, + encodedCall + ); + + emit ATokenUpgraded(input.asset, reserveData.aTokenAddress, input.implementation); + } + + /** + * @dev Updates the stable debt token implementation for the reserve + **/ + function updateStableDebtToken(UpdateDebtTokenInput calldata input) external onlyPoolAdmin { + ILendingPool cachedPool = pool; + + DataTypes.ReserveData memory reserveData = cachedPool.getReserveData(input.asset); + + (, , , uint256 decimals, ) = cachedPool.getConfiguration(input.asset).getParamsMemory(); + + bytes memory encodedCall = abi.encodeWithSelector( + IInitializableDebtToken.initialize.selector, + cachedPool, + input.asset, + input.incentivesController, + decimals, + input.name, + input.symbol, + input.params + ); + + _upgradeTokenImplementation( + reserveData.stableDebtTokenAddress, + input.implementation, + encodedCall + ); + + emit StableDebtTokenUpgraded( + input.asset, + reserveData.stableDebtTokenAddress, + input.implementation + ); + } + + /** + * @dev Updates the variable debt token implementation for the asset + **/ + function updateVariableDebtToken(UpdateDebtTokenInput calldata input) + external + onlyPoolAdmin + { + ILendingPool cachedPool = pool; + + DataTypes.ReserveData memory reserveData = cachedPool.getReserveData(input.asset); + + (, , , uint256 decimals, ) = cachedPool.getConfiguration(input.asset).getParamsMemory(); + + bytes memory encodedCall = abi.encodeWithSelector( + IInitializableDebtToken.initialize.selector, + cachedPool, + input.asset, + input.incentivesController, + decimals, + input.name, + input.symbol, + input.params + ); + + _upgradeTokenImplementation( + reserveData.variableDebtTokenAddress, + input.implementation, + encodedCall + ); + + emit VariableDebtTokenUpgraded( + input.asset, + reserveData.variableDebtTokenAddress, + input.implementation + ); + } + + /** + * @dev Enables borrowing on a reserve + * @param asset The address of the underlying asset of the reserve + * @param stableBorrowRateEnabled True if stable borrow rate needs to be enabled by default on this reserve + **/ + function enableBorrowingOnReserve(address asset, bool stableBorrowRateEnabled) + external + onlyPoolAdmin + { + DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); + + currentConfig.setBorrowingEnabled(true); + currentConfig.setStableRateBorrowingEnabled(stableBorrowRateEnabled); + + pool.setConfiguration(asset, currentConfig.data); + + emit BorrowingEnabledOnReserve(asset, stableBorrowRateEnabled); + } + + /** + * @dev Disables borrowing on a reserve + * @param asset The address of the underlying asset of the reserve + **/ + function disableBorrowingOnReserve(address asset) external onlyPoolAdmin { + DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); + + currentConfig.setBorrowingEnabled(false); + + pool.setConfiguration(asset, currentConfig.data); + emit BorrowingDisabledOnReserve(asset); + } + + /** + * @dev Configures the reserve collateralization parameters + * all the values are expressed in percentages with two decimals of precision. A valid value is 10000, which means 100.00% + * @param asset The address of the underlying asset of the reserve + * @param ltv The loan to value of the asset when used as collateral + * @param liquidationThreshold The threshold at which loans using this asset as collateral will be considered undercollateralized + * @param liquidationBonus The bonus liquidators receive to liquidate this asset. The values is always above 100%. A value of 105% + * means the liquidator will receive a 5% bonus + **/ + function configureReserveAsCollateral( + address asset, + uint256 ltv, + uint256 liquidationThreshold, + uint256 liquidationBonus + ) external onlyPoolAdmin { + DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); + + //validation of the parameters: the LTV can + //only be lower or equal than the liquidation threshold + //(otherwise a loan against the asset would cause instantaneous liquidation) + require(ltv <= liquidationThreshold, Errors.LPC_INVALID_CONFIGURATION); + + if (liquidationThreshold != 0) { + //liquidation bonus must be bigger than 100.00%, otherwise the liquidator would receive less + //collateral than needed to cover the debt + require( + liquidationBonus > PercentageMath.PERCENTAGE_FACTOR, + Errors.LPC_INVALID_CONFIGURATION + ); + + //if threshold * bonus is less than PERCENTAGE_FACTOR, it's guaranteed that at the moment + //a loan is taken there is enough collateral available to cover the liquidation bonus + require( + liquidationThreshold.percentMul(liquidationBonus) <= PercentageMath.PERCENTAGE_FACTOR, + Errors.LPC_INVALID_CONFIGURATION + ); + } else { + require(liquidationBonus == 0, Errors.LPC_INVALID_CONFIGURATION); + //if the liquidation threshold is being set to 0, + // the reserve is being disabled as collateral. To do so, + //we need to ensure no liquidity is deposited + _checkNoLiquidity(asset); + } + + currentConfig.setLtv(ltv); + currentConfig.setLiquidationThreshold(liquidationThreshold); + currentConfig.setLiquidationBonus(liquidationBonus); + + pool.setConfiguration(asset, currentConfig.data); + + emit CollateralConfigurationChanged(asset, ltv, liquidationThreshold, liquidationBonus); + } + + /** + * @dev Enable stable rate borrowing on a reserve + * @param asset The address of the underlying asset of the reserve + **/ + function enableReserveStableRate(address asset) external onlyPoolAdmin { + DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); + + currentConfig.setStableRateBorrowingEnabled(true); + + pool.setConfiguration(asset, currentConfig.data); + + emit StableRateEnabledOnReserve(asset); + } + + /** + * @dev Disable stable rate borrowing on a reserve + * @param asset The address of the underlying asset of the reserve + **/ + function disableReserveStableRate(address asset) external onlyPoolAdmin { + DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); + + currentConfig.setStableRateBorrowingEnabled(false); + + pool.setConfiguration(asset, currentConfig.data); + + emit StableRateDisabledOnReserve(asset); + } + + /** + * @dev Activates a reserve + * @param asset The address of the underlying asset of the reserve + **/ + function activateReserve(address asset) external onlyPoolAdmin { + DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); + + currentConfig.setActive(true); + + pool.setConfiguration(asset, currentConfig.data); + + emit ReserveActivated(asset); + } + + /** + * @dev Deactivates a reserve + * @param asset The address of the underlying asset of the reserve + **/ + function deactivateReserve(address asset) external onlyPoolAdmin { + _checkNoLiquidity(asset); + + DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); + + currentConfig.setActive(false); + + pool.setConfiguration(asset, currentConfig.data); + + emit ReserveDeactivated(asset); + } + + /** + * @dev Freezes a reserve. A frozen reserve doesn't allow any new deposit, borrow or rate swap + * but allows repayments, liquidations, rate rebalances and withdrawals + * @param asset The address of the underlying asset of the reserve + **/ + function freezeReserve(address asset) external onlyPoolAdmin { + DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); + + currentConfig.setFrozen(true); + + pool.setConfiguration(asset, currentConfig.data); + + emit ReserveFrozen(asset); + } + + /** + * @dev Unfreezes a reserve + * @param asset The address of the underlying asset of the reserve + **/ + function unfreezeReserve(address asset) external onlyPoolAdmin { + DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); + + currentConfig.setFrozen(false); + + pool.setConfiguration(asset, currentConfig.data); + + emit ReserveUnfrozen(asset); + } + + /** + * @dev Updates the reserve factor of a reserve + * @param asset The address of the underlying asset of the reserve + * @param reserveFactor The new reserve factor of the reserve + **/ + function setReserveFactor(address asset, uint256 reserveFactor) external onlyPoolAdmin { + DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); + + currentConfig.setReserveFactor(reserveFactor); + + pool.setConfiguration(asset, currentConfig.data); + + emit ReserveFactorChanged(asset, reserveFactor); + } + + /** + * @dev Sets the interest rate strategy of a reserve + * @param asset The address of the underlying asset of the reserve + * @param rateStrategyAddress The new address of the interest strategy contract + **/ + function setReserveInterestRateStrategyAddress(address asset, address rateStrategyAddress) + external + onlyPoolAdmin + { + pool.setReserveInterestRateStrategyAddress(asset, rateStrategyAddress); + emit ReserveInterestRateStrategyChanged(asset, rateStrategyAddress); + } + + /** + * @dev pauses or unpauses all the actions of the protocol, including aToken transfers + * @param val true if protocol needs to be paused, false otherwise + **/ + function setPoolPause(bool val) external onlyEmergencyAdmin { + pool.setPause(val); + } + + function _initTokenWithProxy(address implementation, bytes memory initParams) + internal + returns (address) + { + InitializableImmutableAdminUpgradeabilityProxy proxy = + new InitializableImmutableAdminUpgradeabilityProxy(address(this)); + + proxy.initialize(implementation, initParams); + + return address(proxy); + } + + function _upgradeTokenImplementation( + address proxyAddress, + address implementation, + bytes memory initParams + ) internal { + InitializableImmutableAdminUpgradeabilityProxy proxy = + InitializableImmutableAdminUpgradeabilityProxy(payable(proxyAddress)); + + proxy.upgradeToAndCall(implementation, initParams); + } + + function _checkNoLiquidity(address asset) internal view { + DataTypes.ReserveData memory reserveData = pool.getReserveData(asset); + + uint256 availableLiquidity = IERC20Detailed(asset).balanceOf(reserveData.aTokenAddress); + + require( + availableLiquidity == 0 && reserveData.currentLiquidityRate == 0, + Errors.LPC_RESERVE_LIQUIDITY_NOT_0 + ); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/lendingpool/LendingPoolStorage.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/lendingpool/LendingPoolStorage.sol new file mode 100644 index 000000000..586a11eea --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/lendingpool/LendingPoolStorage.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {UserConfiguration} from '../libraries/configuration/UserConfiguration.sol'; +import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol'; +import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol'; +import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol'; +import {DataTypes} from '../libraries/types/DataTypes.sol'; + +contract LendingPoolStorage { + using ReserveLogic for DataTypes.ReserveData; + using ReserveConfiguration for DataTypes.ReserveConfigurationMap; + using UserConfiguration for DataTypes.UserConfigurationMap; + + ILendingPoolAddressesProvider internal _addressesProvider; + + mapping(address => DataTypes.ReserveData) internal _reserves; + mapping(address => DataTypes.UserConfigurationMap) internal _usersConfig; + + // the list of the available reserves, structured as a mapping for gas savings reasons + mapping(uint256 => address) internal _reservesList; + + uint256 internal _reservesCount; + + bool internal _paused; + + uint256 internal _maxStableRateBorrowSizePercent; + + uint256 internal _flashLoanPremiumTotal; + + uint256 internal _maxNumberOfReserves; +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/aave-upgradeability/BaseImmutableAdminUpgradeabilityProxy.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/aave-upgradeability/BaseImmutableAdminUpgradeabilityProxy.sol new file mode 100644 index 000000000..7bf09a657 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/aave-upgradeability/BaseImmutableAdminUpgradeabilityProxy.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import '../../../dependencies/openzeppelin/upgradeability/BaseUpgradeabilityProxy.sol'; + +/** + * @title BaseImmutableAdminUpgradeabilityProxy + * @author Aave, inspired by the OpenZeppelin upgradeability proxy pattern + * @dev This contract combines an upgradeability proxy with an authorization + * mechanism for administrative tasks. The admin role is stored in an immutable, which + * helps saving transactions costs + * All external functions in this contract must be guarded by the + * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity + * feature proposal that would enable this to be done automatically. + */ +contract BaseImmutableAdminUpgradeabilityProxy is BaseUpgradeabilityProxy { + address immutable ADMIN; + + constructor(address admin) public { + ADMIN = admin; + } + + modifier ifAdmin() { + if (msg.sender == ADMIN) { + _; + } else { + _fallback(); + } + } + + /** + * @return The address of the proxy admin. + */ + function admin() external ifAdmin returns (address) { + return ADMIN; + } + + /** + * @return The address of the implementation. + */ + function implementation() external ifAdmin returns (address) { + return _implementation(); + } + + /** + * @dev Upgrade the backing implementation of the proxy. + * Only the admin can call this function. + * @param newImplementation Address of the new implementation. + */ + function upgradeTo(address newImplementation) external ifAdmin { + _upgradeTo(newImplementation); + } + + /** + * @dev Upgrade the backing implementation of the proxy and call a function + * on the new implementation. + * This is useful to initialize the proxied contract. + * @param newImplementation Address of the new implementation. + * @param data Data to send as msg.data in the low level call. + * It should include the signature and the parameters of the function to be called, as described in + * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding. + */ + function upgradeToAndCall(address newImplementation, bytes calldata data) + external + payable + ifAdmin + { + _upgradeTo(newImplementation); + (bool success, ) = newImplementation.delegatecall(data); + require(success); + } + + /** + * @dev Only fall back when the sender is not the admin. + */ + function _willFallback() internal virtual override { + require(msg.sender != ADMIN, 'Cannot call fallback function from the proxy admin'); + super._willFallback(); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/aave-upgradeability/InitializableImmutableAdminUpgradeabilityProxy.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/aave-upgradeability/InitializableImmutableAdminUpgradeabilityProxy.sol new file mode 100644 index 000000000..83f4cb16e --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/aave-upgradeability/InitializableImmutableAdminUpgradeabilityProxy.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import './BaseImmutableAdminUpgradeabilityProxy.sol'; +import '../../../dependencies/openzeppelin/upgradeability/InitializableUpgradeabilityProxy.sol'; + +/** + * @title InitializableAdminUpgradeabilityProxy + * @dev Extends BaseAdminUpgradeabilityProxy with an initializer function + */ +contract InitializableImmutableAdminUpgradeabilityProxy is + BaseImmutableAdminUpgradeabilityProxy, + InitializableUpgradeabilityProxy +{ + constructor(address admin) public BaseImmutableAdminUpgradeabilityProxy(admin) {} + + /** + * @dev Only fall back when the sender is not the admin. + */ + function _willFallback() internal override(BaseImmutableAdminUpgradeabilityProxy, Proxy) { + BaseImmutableAdminUpgradeabilityProxy._willFallback(); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/aave-upgradeability/VersionedInitializable.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/aave-upgradeability/VersionedInitializable.sol new file mode 100644 index 000000000..fd87b156b --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/aave-upgradeability/VersionedInitializable.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +/** + * @title VersionedInitializable + * + * @dev Helper contract to implement initializer functions. To use it, replace + * the constructor with a function that has the `initializer` modifier. + * WARNING: Unlike constructors, initializer functions must be manually + * invoked. This applies both to deploying an Initializable contract, as well + * as extending an Initializable contract via inheritance. + * WARNING: When used with inheritance, manual care must be taken to not invoke + * a parent initializer twice, or ensure that all initializers are idempotent, + * because this is not dealt with automatically as with constructors. + * + * @author Aave, inspired by the OpenZeppelin Initializable contract + */ +abstract contract VersionedInitializable { + /** + * @dev Indicates that the contract has been initialized. + */ + uint256 private lastInitializedRevision = 0; + + /** + * @dev Indicates that the contract is in the process of being initialized. + */ + bool private initializing; + + /** + * @dev Modifier to use in the initializer function of a contract. + */ + modifier initializer() { + uint256 revision = getRevision(); + require( + initializing || isConstructor() || revision > lastInitializedRevision, + 'Contract instance has already been initialized' + ); + + bool isTopLevelCall = !initializing; + if (isTopLevelCall) { + initializing = true; + lastInitializedRevision = revision; + } + + _; + + if (isTopLevelCall) { + initializing = false; + } + } + + /** + * @dev returns the revision number of the contract + * Needs to be defined in the inherited class as a constant. + **/ + function getRevision() internal pure virtual returns (uint256); + + /** + * @dev Returns true if and only if the function is running in the constructor + **/ + function isConstructor() private view returns (bool) { + // extcodesize checks the size of the code stored in an address, and + // address returns the current address. Since the code is still not + // deployed when running a constructor, any checks on its code size will + // yield zero, making it an effective way to detect if a contract is + // under construction or not. + uint256 cs; + //solium-disable-next-line + assembly { + cs := extcodesize(address()) + } + return cs == 0; + } + + // Reserved storage space to allow for layout changes in the future. + uint256[50] private ______gap; +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/configuration/ReserveConfiguration.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/configuration/ReserveConfiguration.sol new file mode 100644 index 000000000..638a44f2d --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/configuration/ReserveConfiguration.sol @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {Errors} from '../helpers/Errors.sol'; +import {DataTypes} from '../types/DataTypes.sol'; + +/** + * @title ReserveConfiguration library + * @author Aave + * @notice Implements the bitmap logic to handle the reserve configuration + */ +library ReserveConfiguration { + uint256 constant LTV_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000; // prettier-ignore + uint256 constant LIQUIDATION_THRESHOLD_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFF; // prettier-ignore + uint256 constant LIQUIDATION_BONUS_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFF; // prettier-ignore + uint256 constant DECIMALS_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFF; // prettier-ignore + uint256 constant ACTIVE_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFF; // prettier-ignore + uint256 constant FROZEN_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFF; // prettier-ignore + uint256 constant BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFF; // prettier-ignore + uint256 constant STABLE_BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFF; // prettier-ignore + uint256 constant RESERVE_FACTOR_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFF; // prettier-ignore + + /// @dev For the LTV, the start bit is 0 (up to 15), hence no bitshifting is needed + uint256 constant LIQUIDATION_THRESHOLD_START_BIT_POSITION = 16; + uint256 constant LIQUIDATION_BONUS_START_BIT_POSITION = 32; + uint256 constant RESERVE_DECIMALS_START_BIT_POSITION = 48; + uint256 constant IS_ACTIVE_START_BIT_POSITION = 56; + uint256 constant IS_FROZEN_START_BIT_POSITION = 57; + uint256 constant BORROWING_ENABLED_START_BIT_POSITION = 58; + uint256 constant STABLE_BORROWING_ENABLED_START_BIT_POSITION = 59; + uint256 constant RESERVE_FACTOR_START_BIT_POSITION = 64; + + uint256 constant MAX_VALID_LTV = 65535; + uint256 constant MAX_VALID_LIQUIDATION_THRESHOLD = 65535; + uint256 constant MAX_VALID_LIQUIDATION_BONUS = 65535; + uint256 constant MAX_VALID_DECIMALS = 255; + uint256 constant MAX_VALID_RESERVE_FACTOR = 65535; + + /** + * @dev Sets the Loan to Value of the reserve + * @param self The reserve configuration + * @param ltv the new ltv + **/ + function setLtv(DataTypes.ReserveConfigurationMap memory self, uint256 ltv) internal pure { + require(ltv <= MAX_VALID_LTV, Errors.RC_INVALID_LTV); + + self.data = (self.data & LTV_MASK) | ltv; + } + + /** + * @dev Gets the Loan to Value of the reserve + * @param self The reserve configuration + * @return The loan to value + **/ + function getLtv(DataTypes.ReserveConfigurationMap storage self) internal view returns (uint256) { + return self.data & ~LTV_MASK; + } + + /** + * @dev Sets the liquidation threshold of the reserve + * @param self The reserve configuration + * @param threshold The new liquidation threshold + **/ + function setLiquidationThreshold(DataTypes.ReserveConfigurationMap memory self, uint256 threshold) + internal + pure + { + require(threshold <= MAX_VALID_LIQUIDATION_THRESHOLD, Errors.RC_INVALID_LIQ_THRESHOLD); + + self.data = + (self.data & LIQUIDATION_THRESHOLD_MASK) | + (threshold << LIQUIDATION_THRESHOLD_START_BIT_POSITION); + } + + /** + * @dev Gets the liquidation threshold of the reserve + * @param self The reserve configuration + * @return The liquidation threshold + **/ + function getLiquidationThreshold(DataTypes.ReserveConfigurationMap storage self) + internal + view + returns (uint256) + { + return (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION; + } + + /** + * @dev Sets the liquidation bonus of the reserve + * @param self The reserve configuration + * @param bonus The new liquidation bonus + **/ + function setLiquidationBonus(DataTypes.ReserveConfigurationMap memory self, uint256 bonus) + internal + pure + { + require(bonus <= MAX_VALID_LIQUIDATION_BONUS, Errors.RC_INVALID_LIQ_BONUS); + + self.data = + (self.data & LIQUIDATION_BONUS_MASK) | + (bonus << LIQUIDATION_BONUS_START_BIT_POSITION); + } + + /** + * @dev Gets the liquidation bonus of the reserve + * @param self The reserve configuration + * @return The liquidation bonus + **/ + function getLiquidationBonus(DataTypes.ReserveConfigurationMap storage self) + internal + view + returns (uint256) + { + return (self.data & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION; + } + + /** + * @dev Sets the decimals of the underlying asset of the reserve + * @param self The reserve configuration + * @param decimals The decimals + **/ + function setDecimals(DataTypes.ReserveConfigurationMap memory self, uint256 decimals) + internal + pure + { + require(decimals <= MAX_VALID_DECIMALS, Errors.RC_INVALID_DECIMALS); + + self.data = (self.data & DECIMALS_MASK) | (decimals << RESERVE_DECIMALS_START_BIT_POSITION); + } + + /** + * @dev Gets the decimals of the underlying asset of the reserve + * @param self The reserve configuration + * @return The decimals of the asset + **/ + function getDecimals(DataTypes.ReserveConfigurationMap storage self) + internal + view + returns (uint256) + { + return (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION; + } + + /** + * @dev Sets the active state of the reserve + * @param self The reserve configuration + * @param active The active state + **/ + function setActive(DataTypes.ReserveConfigurationMap memory self, bool active) internal pure { + self.data = + (self.data & ACTIVE_MASK) | + (uint256(active ? 1 : 0) << IS_ACTIVE_START_BIT_POSITION); + } + + /** + * @dev Gets the active state of the reserve + * @param self The reserve configuration + * @return The active state + **/ + function getActive(DataTypes.ReserveConfigurationMap storage self) internal view returns (bool) { + return (self.data & ~ACTIVE_MASK) != 0; + } + + /** + * @dev Sets the frozen state of the reserve + * @param self The reserve configuration + * @param frozen The frozen state + **/ + function setFrozen(DataTypes.ReserveConfigurationMap memory self, bool frozen) internal pure { + self.data = + (self.data & FROZEN_MASK) | + (uint256(frozen ? 1 : 0) << IS_FROZEN_START_BIT_POSITION); + } + + /** + * @dev Gets the frozen state of the reserve + * @param self The reserve configuration + * @return The frozen state + **/ + function getFrozen(DataTypes.ReserveConfigurationMap storage self) internal view returns (bool) { + return (self.data & ~FROZEN_MASK) != 0; + } + + /** + * @dev Enables or disables borrowing on the reserve + * @param self The reserve configuration + * @param enabled True if the borrowing needs to be enabled, false otherwise + **/ + function setBorrowingEnabled(DataTypes.ReserveConfigurationMap memory self, bool enabled) + internal + pure + { + self.data = + (self.data & BORROWING_MASK) | + (uint256(enabled ? 1 : 0) << BORROWING_ENABLED_START_BIT_POSITION); + } + + /** + * @dev Gets the borrowing state of the reserve + * @param self The reserve configuration + * @return The borrowing state + **/ + function getBorrowingEnabled(DataTypes.ReserveConfigurationMap storage self) + internal + view + returns (bool) + { + return (self.data & ~BORROWING_MASK) != 0; + } + + /** + * @dev Enables or disables stable rate borrowing on the reserve + * @param self The reserve configuration + * @param enabled True if the stable rate borrowing needs to be enabled, false otherwise + **/ + function setStableRateBorrowingEnabled( + DataTypes.ReserveConfigurationMap memory self, + bool enabled + ) internal pure { + self.data = + (self.data & STABLE_BORROWING_MASK) | + (uint256(enabled ? 1 : 0) << STABLE_BORROWING_ENABLED_START_BIT_POSITION); + } + + /** + * @dev Gets the stable rate borrowing state of the reserve + * @param self The reserve configuration + * @return The stable rate borrowing state + **/ + function getStableRateBorrowingEnabled(DataTypes.ReserveConfigurationMap storage self) + internal + view + returns (bool) + { + return (self.data & ~STABLE_BORROWING_MASK) != 0; + } + + /** + * @dev Sets the reserve factor of the reserve + * @param self The reserve configuration + * @param reserveFactor The reserve factor + **/ + function setReserveFactor(DataTypes.ReserveConfigurationMap memory self, uint256 reserveFactor) + internal + pure + { + require(reserveFactor <= MAX_VALID_RESERVE_FACTOR, Errors.RC_INVALID_RESERVE_FACTOR); + + self.data = + (self.data & RESERVE_FACTOR_MASK) | + (reserveFactor << RESERVE_FACTOR_START_BIT_POSITION); + } + + /** + * @dev Gets the reserve factor of the reserve + * @param self The reserve configuration + * @return The reserve factor + **/ + function getReserveFactor(DataTypes.ReserveConfigurationMap storage self) + internal + view + returns (uint256) + { + return (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION; + } + + /** + * @dev Gets the configuration flags of the reserve + * @param self The reserve configuration + * @return The state flags representing active, frozen, borrowing enabled, stableRateBorrowing enabled + **/ + function getFlags(DataTypes.ReserveConfigurationMap storage self) + internal + view + returns ( + bool, + bool, + bool, + bool + ) + { + uint256 dataLocal = self.data; + + return ( + (dataLocal & ~ACTIVE_MASK) != 0, + (dataLocal & ~FROZEN_MASK) != 0, + (dataLocal & ~BORROWING_MASK) != 0, + (dataLocal & ~STABLE_BORROWING_MASK) != 0 + ); + } + + /** + * @dev Gets the configuration paramters of the reserve + * @param self The reserve configuration + * @return The state params representing ltv, liquidation threshold, liquidation bonus, the reserve decimals + **/ + function getParams(DataTypes.ReserveConfigurationMap storage self) + internal + view + returns ( + uint256, + uint256, + uint256, + uint256, + uint256 + ) + { + uint256 dataLocal = self.data; + + return ( + dataLocal & ~LTV_MASK, + (dataLocal & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION, + (dataLocal & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION, + (dataLocal & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION, + (dataLocal & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION + ); + } + + /** + * @dev Gets the configuration paramters of the reserve from a memory object + * @param self The reserve configuration + * @return The state params representing ltv, liquidation threshold, liquidation bonus, the reserve decimals + **/ + function getParamsMemory(DataTypes.ReserveConfigurationMap memory self) + internal + pure + returns ( + uint256, + uint256, + uint256, + uint256, + uint256 + ) + { + return ( + self.data & ~LTV_MASK, + (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION, + (self.data & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION, + (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION, + (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION + ); + } + + /** + * @dev Gets the configuration flags of the reserve from a memory object + * @param self The reserve configuration + * @return The state flags representing active, frozen, borrowing enabled, stableRateBorrowing enabled + **/ + function getFlagsMemory(DataTypes.ReserveConfigurationMap memory self) + internal + pure + returns ( + bool, + bool, + bool, + bool + ) + { + return ( + (self.data & ~ACTIVE_MASK) != 0, + (self.data & ~FROZEN_MASK) != 0, + (self.data & ~BORROWING_MASK) != 0, + (self.data & ~STABLE_BORROWING_MASK) != 0 + ); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/configuration/UserConfiguration.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/configuration/UserConfiguration.sol new file mode 100644 index 000000000..2638ddf4e --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/configuration/UserConfiguration.sol @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {Errors} from '../helpers/Errors.sol'; +import {DataTypes} from '../types/DataTypes.sol'; + +/** + * @title UserConfiguration library + * @author Aave + * @notice Implements the bitmap logic to handle the user configuration + */ +library UserConfiguration { + uint256 internal constant BORROWING_MASK = + 0x5555555555555555555555555555555555555555555555555555555555555555; + + /** + * @dev Sets if the user is borrowing the reserve identified by reserveIndex + * @param self The configuration object + * @param reserveIndex The index of the reserve in the bitmap + * @param borrowing True if the user is borrowing the reserve, false otherwise + **/ + function setBorrowing( + DataTypes.UserConfigurationMap storage self, + uint256 reserveIndex, + bool borrowing + ) internal { + require(reserveIndex < 128, Errors.UL_INVALID_INDEX); + self.data = + (self.data & ~(1 << (reserveIndex * 2))) | + (uint256(borrowing ? 1 : 0) << (reserveIndex * 2)); + } + + /** + * @dev Sets if the user is using as collateral the reserve identified by reserveIndex + * @param self The configuration object + * @param reserveIndex The index of the reserve in the bitmap + * @param usingAsCollateral True if the user is usin the reserve as collateral, false otherwise + **/ + function setUsingAsCollateral( + DataTypes.UserConfigurationMap storage self, + uint256 reserveIndex, + bool usingAsCollateral + ) internal { + require(reserveIndex < 128, Errors.UL_INVALID_INDEX); + self.data = + (self.data & ~(1 << (reserveIndex * 2 + 1))) | + (uint256(usingAsCollateral ? 1 : 0) << (reserveIndex * 2 + 1)); + } + + /** + * @dev Used to validate if a user has been using the reserve for borrowing or as collateral + * @param self The configuration object + * @param reserveIndex The index of the reserve in the bitmap + * @return True if the user has been using a reserve for borrowing or as collateral, false otherwise + **/ + function isUsingAsCollateralOrBorrowing( + DataTypes.UserConfigurationMap memory self, + uint256 reserveIndex + ) internal pure returns (bool) { + require(reserveIndex < 128, Errors.UL_INVALID_INDEX); + return (self.data >> (reserveIndex * 2)) & 3 != 0; + } + + /** + * @dev Used to validate if a user has been using the reserve for borrowing + * @param self The configuration object + * @param reserveIndex The index of the reserve in the bitmap + * @return True if the user has been using a reserve for borrowing, false otherwise + **/ + function isBorrowing(DataTypes.UserConfigurationMap memory self, uint256 reserveIndex) + internal + pure + returns (bool) + { + require(reserveIndex < 128, Errors.UL_INVALID_INDEX); + return (self.data >> (reserveIndex * 2)) & 1 != 0; + } + + /** + * @dev Used to validate if a user has been using the reserve as collateral + * @param self The configuration object + * @param reserveIndex The index of the reserve in the bitmap + * @return True if the user has been using a reserve as collateral, false otherwise + **/ + function isUsingAsCollateral(DataTypes.UserConfigurationMap memory self, uint256 reserveIndex) + internal + pure + returns (bool) + { + require(reserveIndex < 128, Errors.UL_INVALID_INDEX); + return (self.data >> (reserveIndex * 2 + 1)) & 1 != 0; + } + + /** + * @dev Used to validate if a user has been borrowing from any reserve + * @param self The configuration object + * @return True if the user has been borrowing any reserve, false otherwise + **/ + function isBorrowingAny(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) { + return self.data & BORROWING_MASK != 0; + } + + /** + * @dev Used to validate if a user has not been using any reserve + * @param self The configuration object + * @return True if the user has been borrowing any reserve, false otherwise + **/ + function isEmpty(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) { + return self.data == 0; + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/helpers/Errors.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/helpers/Errors.sol new file mode 100644 index 000000000..faf2df5dc --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/helpers/Errors.sol @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +/** + * @title Errors library + * @author Aave + * @notice Defines the error messages emitted by the different contracts of the Aave protocol + * @dev Error messages prefix glossary: + * - VL = ValidationLogic + * - MATH = Math libraries + * - CT = Common errors between tokens (AToken, VariableDebtToken and StableDebtToken) + * - AT = AToken + * - SDT = StableDebtToken + * - VDT = VariableDebtToken + * - LP = LendingPool + * - LPAPR = LendingPoolAddressesProviderRegistry + * - LPC = LendingPoolConfiguration + * - RL = ReserveLogic + * - LPCM = LendingPoolCollateralManager + * - P = Pausable + */ +library Errors { + //common errors + string public constant CALLER_NOT_POOL_ADMIN = '33'; // 'The caller must be the pool admin' + string public constant BORROW_ALLOWANCE_NOT_ENOUGH = '59'; // User borrows on behalf, but allowance are too small + + //contract specific errors + string public constant VL_INVALID_AMOUNT = '1'; // 'Amount must be greater than 0' + string public constant VL_NO_ACTIVE_RESERVE = '2'; // 'Action requires an active reserve' + string public constant VL_RESERVE_FROZEN = '3'; // 'Action cannot be performed because the reserve is frozen' + string public constant VL_CURRENT_AVAILABLE_LIQUIDITY_NOT_ENOUGH = '4'; // 'The current liquidity is not enough' + string public constant VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE = '5'; // 'User cannot withdraw more than the available balance' + string public constant VL_TRANSFER_NOT_ALLOWED = '6'; // 'Transfer cannot be allowed.' + string public constant VL_BORROWING_NOT_ENABLED = '7'; // 'Borrowing is not enabled' + string public constant VL_INVALID_INTEREST_RATE_MODE_SELECTED = '8'; // 'Invalid interest rate mode selected' + string public constant VL_COLLATERAL_BALANCE_IS_0 = '9'; // 'The collateral balance is 0' + string public constant VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD = '10'; // 'Health factor is lesser than the liquidation threshold' + string public constant VL_COLLATERAL_CANNOT_COVER_NEW_BORROW = '11'; // 'There is not enough collateral to cover a new borrow' + string public constant VL_STABLE_BORROWING_NOT_ENABLED = '12'; // stable borrowing not enabled + string public constant VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY = '13'; // collateral is (mostly) the same currency that is being borrowed + string public constant VL_AMOUNT_BIGGER_THAN_MAX_LOAN_SIZE_STABLE = '14'; // 'The requested amount is greater than the max loan size in stable rate mode + string public constant VL_NO_DEBT_OF_SELECTED_TYPE = '15'; // 'for repayment of stable debt, the user needs to have stable debt, otherwise, he needs to have variable debt' + string public constant VL_NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF = '16'; // 'To repay on behalf of an user an explicit amount to repay is needed' + string public constant VL_NO_STABLE_RATE_LOAN_IN_RESERVE = '17'; // 'User does not have a stable rate loan in progress on this reserve' + string public constant VL_NO_VARIABLE_RATE_LOAN_IN_RESERVE = '18'; // 'User does not have a variable rate loan in progress on this reserve' + string public constant VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0 = '19'; // 'The underlying balance needs to be greater than 0' + string public constant VL_DEPOSIT_ALREADY_IN_USE = '20'; // 'User deposit is already being used as collateral' + string public constant LP_NOT_ENOUGH_STABLE_BORROW_BALANCE = '21'; // 'User does not have any stable rate loan for this reserve' + string public constant LP_INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET = '22'; // 'Interest rate rebalance conditions were not met' + string public constant LP_LIQUIDATION_CALL_FAILED = '23'; // 'Liquidation call failed' + string public constant LP_NOT_ENOUGH_LIQUIDITY_TO_BORROW = '24'; // 'There is not enough liquidity available to borrow' + string public constant LP_REQUESTED_AMOUNT_TOO_SMALL = '25'; // 'The requested amount is too small for a FlashLoan.' + string public constant LP_INCONSISTENT_PROTOCOL_ACTUAL_BALANCE = '26'; // 'The actual balance of the protocol is inconsistent' + string public constant LP_CALLER_NOT_LENDING_POOL_CONFIGURATOR = '27'; // 'The caller of the function is not the lending pool configurator' + string public constant LP_INCONSISTENT_FLASHLOAN_PARAMS = '28'; + string public constant CT_CALLER_MUST_BE_LENDING_POOL = '29'; // 'The caller of this function must be a lending pool' + string public constant CT_CANNOT_GIVE_ALLOWANCE_TO_HIMSELF = '30'; // 'User cannot give allowance to himself' + string public constant CT_TRANSFER_AMOUNT_NOT_GT_0 = '31'; // 'Transferred amount needs to be greater than zero' + string public constant RL_RESERVE_ALREADY_INITIALIZED = '32'; // 'Reserve has already been initialized' + string public constant LPC_RESERVE_LIQUIDITY_NOT_0 = '34'; // 'The liquidity of the reserve needs to be 0' + string public constant LPC_INVALID_ATOKEN_POOL_ADDRESS = '35'; // 'The liquidity of the reserve needs to be 0' + string public constant LPC_INVALID_STABLE_DEBT_TOKEN_POOL_ADDRESS = '36'; // 'The liquidity of the reserve needs to be 0' + string public constant LPC_INVALID_VARIABLE_DEBT_TOKEN_POOL_ADDRESS = '37'; // 'The liquidity of the reserve needs to be 0' + string public constant LPC_INVALID_STABLE_DEBT_TOKEN_UNDERLYING_ADDRESS = '38'; // 'The liquidity of the reserve needs to be 0' + string public constant LPC_INVALID_VARIABLE_DEBT_TOKEN_UNDERLYING_ADDRESS = '39'; // 'The liquidity of the reserve needs to be 0' + string public constant LPC_INVALID_ADDRESSES_PROVIDER_ID = '40'; // 'The liquidity of the reserve needs to be 0' + string public constant LPC_INVALID_CONFIGURATION = '75'; // 'Invalid risk parameters for the reserve' + string public constant LPC_CALLER_NOT_EMERGENCY_ADMIN = '76'; // 'The caller must be the emergency admin' + string public constant LPAPR_PROVIDER_NOT_REGISTERED = '41'; // 'Provider is not registered' + string public constant LPCM_HEALTH_FACTOR_NOT_BELOW_THRESHOLD = '42'; // 'Health factor is not below the threshold' + string public constant LPCM_COLLATERAL_CANNOT_BE_LIQUIDATED = '43'; // 'The collateral chosen cannot be liquidated' + string public constant LPCM_SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER = '44'; // 'User did not borrow the specified currency' + string public constant LPCM_NOT_ENOUGH_LIQUIDITY_TO_LIQUIDATE = '45'; // "There isn't enough liquidity available to liquidate" + string public constant LPCM_NO_ERRORS = '46'; // 'No errors' + string public constant LP_INVALID_FLASHLOAN_MODE = '47'; //Invalid flashloan mode selected + string public constant MATH_MULTIPLICATION_OVERFLOW = '48'; + string public constant MATH_ADDITION_OVERFLOW = '49'; + string public constant MATH_DIVISION_BY_ZERO = '50'; + string public constant RL_LIQUIDITY_INDEX_OVERFLOW = '51'; // Liquidity index overflows uint128 + string public constant RL_VARIABLE_BORROW_INDEX_OVERFLOW = '52'; // Variable borrow index overflows uint128 + string public constant RL_LIQUIDITY_RATE_OVERFLOW = '53'; // Liquidity rate overflows uint128 + string public constant RL_VARIABLE_BORROW_RATE_OVERFLOW = '54'; // Variable borrow rate overflows uint128 + string public constant RL_STABLE_BORROW_RATE_OVERFLOW = '55'; // Stable borrow rate overflows uint128 + string public constant CT_INVALID_MINT_AMOUNT = '56'; //invalid amount to mint + string public constant LP_FAILED_REPAY_WITH_COLLATERAL = '57'; + string public constant CT_INVALID_BURN_AMOUNT = '58'; //invalid amount to burn + string public constant LP_FAILED_COLLATERAL_SWAP = '60'; + string public constant LP_INVALID_EQUAL_ASSETS_TO_SWAP = '61'; + string public constant LP_REENTRANCY_NOT_ALLOWED = '62'; + string public constant LP_CALLER_MUST_BE_AN_ATOKEN = '63'; + string public constant LP_IS_PAUSED = '64'; // 'Pool is paused' + string public constant LP_NO_MORE_RESERVES_ALLOWED = '65'; + string public constant LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN = '66'; + string public constant RC_INVALID_LTV = '67'; + string public constant RC_INVALID_LIQ_THRESHOLD = '68'; + string public constant RC_INVALID_LIQ_BONUS = '69'; + string public constant RC_INVALID_DECIMALS = '70'; + string public constant RC_INVALID_RESERVE_FACTOR = '71'; + string public constant LPAPR_INVALID_ADDRESSES_PROVIDER_ID = '72'; + string public constant VL_INCONSISTENT_FLASHLOAN_PARAMS = '73'; + string public constant LP_INCONSISTENT_PARAMS_LENGTH = '74'; + string public constant UL_INVALID_INDEX = '77'; + string public constant LP_NOT_CONTRACT = '78'; + string public constant SDT_STABLE_DEBT_OVERFLOW = '79'; + string public constant SDT_BURN_EXCEEDS_BALANCE = '80'; + + enum CollateralManagerErrors { + NO_ERROR, + NO_COLLATERAL_AVAILABLE, + COLLATERAL_CANNOT_BE_LIQUIDATED, + CURRRENCY_NOT_BORROWED, + HEALTH_FACTOR_ABOVE_THRESHOLD, + NOT_ENOUGH_LIQUIDITY, + NO_ACTIVE_RESERVE, + HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD, + INVALID_EQUAL_ASSETS_TO_SWAP, + FROZEN_RESERVE + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/helpers/Helpers.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/helpers/Helpers.sol new file mode 100644 index 000000000..2530b7b8f --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/helpers/Helpers.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol'; +import {DataTypes} from '../types/DataTypes.sol'; + +/** + * @title Helpers library + * @author Aave + */ +library Helpers { + /** + * @dev Fetches the user current stable and variable debt balances + * @param user The user address + * @param reserve The reserve data object + * @return The stable and variable debt balance + **/ + function getUserCurrentDebt(address user, DataTypes.ReserveData storage reserve) + internal + view + returns (uint256, uint256) + { + return ( + IERC20(reserve.stableDebtTokenAddress).balanceOf(user), + IERC20(reserve.variableDebtTokenAddress).balanceOf(user) + ); + } + + function getUserCurrentDebtMemory(address user, DataTypes.ReserveData memory reserve) + internal + view + returns (uint256, uint256) + { + return ( + IERC20(reserve.stableDebtTokenAddress).balanceOf(user), + IERC20(reserve.variableDebtTokenAddress).balanceOf(user) + ); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/logic/GenericLogic.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/logic/GenericLogic.sol new file mode 100644 index 000000000..e781291d0 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/logic/GenericLogic.sol @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol'; +import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol'; +import {ReserveLogic} from './ReserveLogic.sol'; +import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol'; +import {UserConfiguration} from '../configuration/UserConfiguration.sol'; +import {WadRayMath} from '../math/WadRayMath.sol'; +import {PercentageMath} from '../math/PercentageMath.sol'; +import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol'; +import {DataTypes} from '../types/DataTypes.sol'; + +/** + * @title GenericLogic library + * @author Aave + * @title Implements protocol-level logic to calculate and validate the state of a user + */ +library GenericLogic { + using ReserveLogic for DataTypes.ReserveData; + using SafeMath for uint256; + using WadRayMath for uint256; + using PercentageMath for uint256; + using ReserveConfiguration for DataTypes.ReserveConfigurationMap; + using UserConfiguration for DataTypes.UserConfigurationMap; + + uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1 ether; + + struct balanceDecreaseAllowedLocalVars { + uint256 decimals; + uint256 liquidationThreshold; + uint256 totalCollateralInETH; + uint256 totalDebtInETH; + uint256 avgLiquidationThreshold; + uint256 amountToDecreaseInETH; + uint256 collateralBalanceAfterDecrease; + uint256 liquidationThresholdAfterDecrease; + uint256 healthFactorAfterDecrease; + bool reserveUsageAsCollateralEnabled; + } + + /** + * @dev Checks if a specific balance decrease is allowed + * (i.e. doesn't bring the user borrow position health factor under HEALTH_FACTOR_LIQUIDATION_THRESHOLD) + * @param asset The address of the underlying asset of the reserve + * @param user The address of the user + * @param amount The amount to decrease + * @param reservesData The data of all the reserves + * @param userConfig The user configuration + * @param reserves The list of all the active reserves + * @param oracle The address of the oracle contract + * @return true if the decrease of the balance is allowed + **/ + function balanceDecreaseAllowed( + address asset, + address user, + uint256 amount, + mapping(address => DataTypes.ReserveData) storage reservesData, + DataTypes.UserConfigurationMap calldata userConfig, + mapping(uint256 => address) storage reserves, + uint256 reservesCount, + address oracle + ) external view returns (bool) { + if (!userConfig.isBorrowingAny() || !userConfig.isUsingAsCollateral(reservesData[asset].id)) { + return true; + } + + balanceDecreaseAllowedLocalVars memory vars; + + (, vars.liquidationThreshold, , vars.decimals, ) = reservesData[asset] + .configuration + .getParams(); + + if (vars.liquidationThreshold == 0) { + return true; + } + + ( + vars.totalCollateralInETH, + vars.totalDebtInETH, + , + vars.avgLiquidationThreshold, + + ) = calculateUserAccountData(user, reservesData, userConfig, reserves, reservesCount, oracle); + + if (vars.totalDebtInETH == 0) { + return true; + } + + vars.amountToDecreaseInETH = IPriceOracleGetter(oracle).getAssetPrice(asset).mul(amount).div( + 10**vars.decimals + ); + + vars.collateralBalanceAfterDecrease = vars.totalCollateralInETH.sub(vars.amountToDecreaseInETH); + + //if there is a borrow, there can't be 0 collateral + if (vars.collateralBalanceAfterDecrease == 0) { + return false; + } + + vars.liquidationThresholdAfterDecrease = vars + .totalCollateralInETH + .mul(vars.avgLiquidationThreshold) + .sub(vars.amountToDecreaseInETH.mul(vars.liquidationThreshold)) + .div(vars.collateralBalanceAfterDecrease); + + uint256 healthFactorAfterDecrease = + calculateHealthFactorFromBalances( + vars.collateralBalanceAfterDecrease, + vars.totalDebtInETH, + vars.liquidationThresholdAfterDecrease + ); + + return healthFactorAfterDecrease >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD; + } + + struct CalculateUserAccountDataVars { + uint256 reserveUnitPrice; + uint256 tokenUnit; + uint256 compoundedLiquidityBalance; + uint256 compoundedBorrowBalance; + uint256 decimals; + uint256 ltv; + uint256 liquidationThreshold; + uint256 i; + uint256 healthFactor; + uint256 totalCollateralInETH; + uint256 totalDebtInETH; + uint256 avgLtv; + uint256 avgLiquidationThreshold; + uint256 reservesLength; + bool healthFactorBelowThreshold; + address currentReserveAddress; + bool usageAsCollateralEnabled; + bool userUsesReserveAsCollateral; + } + + /** + * @dev Calculates the user data across the reserves. + * this includes the total liquidity/collateral/borrow balances in ETH, + * the average Loan To Value, the average Liquidation Ratio, and the Health factor. + * @param user The address of the user + * @param reservesData Data of all the reserves + * @param userConfig The configuration of the user + * @param reserves The list of the available reserves + * @param oracle The price oracle address + * @return The total collateral and total debt of the user in ETH, the avg ltv, liquidation threshold and the HF + **/ + function calculateUserAccountData( + address user, + mapping(address => DataTypes.ReserveData) storage reservesData, + DataTypes.UserConfigurationMap memory userConfig, + mapping(uint256 => address) storage reserves, + uint256 reservesCount, + address oracle + ) + internal + view + returns ( + uint256, + uint256, + uint256, + uint256, + uint256 + ) + { + CalculateUserAccountDataVars memory vars; + + if (userConfig.isEmpty()) { + return (0, 0, 0, 0, uint256(-1)); + } + for (vars.i = 0; vars.i < reservesCount; vars.i++) { + if (!userConfig.isUsingAsCollateralOrBorrowing(vars.i)) { + continue; + } + + vars.currentReserveAddress = reserves[vars.i]; + DataTypes.ReserveData storage currentReserve = reservesData[vars.currentReserveAddress]; + + (vars.ltv, vars.liquidationThreshold, , vars.decimals, ) = currentReserve + .configuration + .getParams(); + + vars.tokenUnit = 10**vars.decimals; + vars.reserveUnitPrice = IPriceOracleGetter(oracle).getAssetPrice(vars.currentReserveAddress); + + if (vars.liquidationThreshold != 0 && userConfig.isUsingAsCollateral(vars.i)) { + vars.compoundedLiquidityBalance = IERC20(currentReserve.aTokenAddress).balanceOf(user); + + uint256 liquidityBalanceETH = + vars.reserveUnitPrice.mul(vars.compoundedLiquidityBalance).div(vars.tokenUnit); + + vars.totalCollateralInETH = vars.totalCollateralInETH.add(liquidityBalanceETH); + + vars.avgLtv = vars.avgLtv.add(liquidityBalanceETH.mul(vars.ltv)); + vars.avgLiquidationThreshold = vars.avgLiquidationThreshold.add( + liquidityBalanceETH.mul(vars.liquidationThreshold) + ); + } + + if (userConfig.isBorrowing(vars.i)) { + vars.compoundedBorrowBalance = IERC20(currentReserve.stableDebtTokenAddress).balanceOf( + user + ); + vars.compoundedBorrowBalance = vars.compoundedBorrowBalance.add( + IERC20(currentReserve.variableDebtTokenAddress).balanceOf(user) + ); + + vars.totalDebtInETH = vars.totalDebtInETH.add( + vars.reserveUnitPrice.mul(vars.compoundedBorrowBalance).div(vars.tokenUnit) + ); + } + } + + vars.avgLtv = vars.totalCollateralInETH > 0 ? vars.avgLtv.div(vars.totalCollateralInETH) : 0; + vars.avgLiquidationThreshold = vars.totalCollateralInETH > 0 + ? vars.avgLiquidationThreshold.div(vars.totalCollateralInETH) + : 0; + + vars.healthFactor = calculateHealthFactorFromBalances( + vars.totalCollateralInETH, + vars.totalDebtInETH, + vars.avgLiquidationThreshold + ); + return ( + vars.totalCollateralInETH, + vars.totalDebtInETH, + vars.avgLtv, + vars.avgLiquidationThreshold, + vars.healthFactor + ); + } + + /** + * @dev Calculates the health factor from the corresponding balances + * @param totalCollateralInETH The total collateral in ETH + * @param totalDebtInETH The total debt in ETH + * @param liquidationThreshold The avg liquidation threshold + * @return The health factor calculated from the balances provided + **/ + function calculateHealthFactorFromBalances( + uint256 totalCollateralInETH, + uint256 totalDebtInETH, + uint256 liquidationThreshold + ) internal pure returns (uint256) { + if (totalDebtInETH == 0) return uint256(-1); + + return (totalCollateralInETH.percentMul(liquidationThreshold)).wadDiv(totalDebtInETH); + } + + /** + * @dev Calculates the equivalent amount in ETH that an user can borrow, depending on the available collateral and the + * average Loan To Value + * @param totalCollateralInETH The total collateral in ETH + * @param totalDebtInETH The total borrow balance + * @param ltv The average loan to value + * @return the amount available to borrow in ETH for the user + **/ + + function calculateAvailableBorrowsETH( + uint256 totalCollateralInETH, + uint256 totalDebtInETH, + uint256 ltv + ) internal pure returns (uint256) { + uint256 availableBorrowsETH = totalCollateralInETH.percentMul(ltv); + + if (availableBorrowsETH < totalDebtInETH) { + return 0; + } + + availableBorrowsETH = availableBorrowsETH.sub(totalDebtInETH); + return availableBorrowsETH; + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/logic/ReserveLogic.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/logic/ReserveLogic.sol new file mode 100644 index 000000000..3725d88c8 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/logic/ReserveLogic.sol @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol'; +import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol'; +import {SafeERC20} from '../../../dependencies/openzeppelin/contracts/SafeERC20.sol'; +import {IAToken} from '../../../interfaces/IAToken.sol'; +import {IStableDebtToken} from '../../../interfaces/IStableDebtToken.sol'; +import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol'; +import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol'; +import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol'; +import {MathUtils} from '../math/MathUtils.sol'; +import {WadRayMath} from '../math/WadRayMath.sol'; +import {PercentageMath} from '../math/PercentageMath.sol'; +import {Errors} from '../helpers/Errors.sol'; +import {DataTypes} from '../types/DataTypes.sol'; + +/** + * @title ReserveLogic library + * @author Aave + * @notice Implements the logic to update the reserves state + */ +library ReserveLogic { + using SafeMath for uint256; + using WadRayMath for uint256; + using PercentageMath for uint256; + using SafeERC20 for IERC20; + + /** + * @dev Emitted when the state of a reserve is updated + * @param asset The address of the underlying asset of the reserve + * @param liquidityRate The new liquidity rate + * @param stableBorrowRate The new stable borrow rate + * @param variableBorrowRate The new variable borrow rate + * @param liquidityIndex The new liquidity index + * @param variableBorrowIndex The new variable borrow index + **/ + event ReserveDataUpdated( + address indexed asset, + uint256 liquidityRate, + uint256 stableBorrowRate, + uint256 variableBorrowRate, + uint256 liquidityIndex, + uint256 variableBorrowIndex + ); + + using ReserveLogic for DataTypes.ReserveData; + using ReserveConfiguration for DataTypes.ReserveConfigurationMap; + + /** + * @dev Returns the ongoing normalized income for the reserve + * A value of 1e27 means there is no income. As time passes, the income is accrued + * A value of 2*1e27 means for each unit of asset one unit of income has been accrued + * @param reserve The reserve object + * @return the normalized income. expressed in ray + **/ + function getNormalizedIncome(DataTypes.ReserveData storage reserve) + internal + view + returns (uint256) + { + uint40 timestamp = reserve.lastUpdateTimestamp; + + //solium-disable-next-line + if (timestamp == uint40(block.timestamp)) { + //if the index was updated in the same block, no need to perform any calculation + return reserve.liquidityIndex; + } + + uint256 cumulated = + MathUtils.calculateLinearInterest(reserve.currentLiquidityRate, timestamp).rayMul( + reserve.liquidityIndex + ); + + return cumulated; + } + + /** + * @dev Returns the ongoing normalized variable debt for the reserve + * A value of 1e27 means there is no debt. As time passes, the income is accrued + * A value of 2*1e27 means that for each unit of debt, one unit worth of interest has been accumulated + * @param reserve The reserve object + * @return The normalized variable debt. expressed in ray + **/ + function getNormalizedDebt(DataTypes.ReserveData storage reserve) + internal + view + returns (uint256) + { + uint40 timestamp = reserve.lastUpdateTimestamp; + + //solium-disable-next-line + if (timestamp == uint40(block.timestamp)) { + //if the index was updated in the same block, no need to perform any calculation + return reserve.variableBorrowIndex; + } + + uint256 cumulated = + MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp).rayMul( + reserve.variableBorrowIndex + ); + + return cumulated; + } + + /** + * @dev Updates the liquidity cumulative index and the variable borrow index. + * @param reserve the reserve object + **/ + function updateState(DataTypes.ReserveData storage reserve) internal { + uint256 scaledVariableDebt = + IVariableDebtToken(reserve.variableDebtTokenAddress).scaledTotalSupply(); + uint256 previousVariableBorrowIndex = reserve.variableBorrowIndex; + uint256 previousLiquidityIndex = reserve.liquidityIndex; + uint40 lastUpdatedTimestamp = reserve.lastUpdateTimestamp; + + (uint256 newLiquidityIndex, uint256 newVariableBorrowIndex) = + _updateIndexes( + reserve, + scaledVariableDebt, + previousLiquidityIndex, + previousVariableBorrowIndex, + lastUpdatedTimestamp + ); + + _mintToTreasury( + reserve, + scaledVariableDebt, + previousVariableBorrowIndex, + newLiquidityIndex, + newVariableBorrowIndex, + lastUpdatedTimestamp + ); + } + + /** + * @dev Accumulates a predefined amount of asset to the reserve as a fixed, instantaneous income. Used for example to accumulate + * the flashloan fee to the reserve, and spread it between all the depositors + * @param reserve The reserve object + * @param totalLiquidity The total liquidity available in the reserve + * @param amount The amount to accomulate + **/ + function cumulateToLiquidityIndex( + DataTypes.ReserveData storage reserve, + uint256 totalLiquidity, + uint256 amount + ) internal { + uint256 amountToLiquidityRatio = amount.wadToRay().rayDiv(totalLiquidity.wadToRay()); + + uint256 result = amountToLiquidityRatio.add(WadRayMath.ray()); + + result = result.rayMul(reserve.liquidityIndex); + require(result <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW); + + reserve.liquidityIndex = uint128(result); + } + + /** + * @dev Initializes a reserve + * @param reserve The reserve object + * @param aTokenAddress The address of the overlying atoken contract + * @param interestRateStrategyAddress The address of the interest rate strategy contract + **/ + function init( + DataTypes.ReserveData storage reserve, + address aTokenAddress, + address stableDebtTokenAddress, + address variableDebtTokenAddress, + address interestRateStrategyAddress + ) external { + require(reserve.aTokenAddress == address(0), Errors.RL_RESERVE_ALREADY_INITIALIZED); + + reserve.liquidityIndex = uint128(WadRayMath.ray()); + reserve.variableBorrowIndex = uint128(WadRayMath.ray()); + reserve.aTokenAddress = aTokenAddress; + reserve.stableDebtTokenAddress = stableDebtTokenAddress; + reserve.variableDebtTokenAddress = variableDebtTokenAddress; + reserve.interestRateStrategyAddress = interestRateStrategyAddress; + } + + struct UpdateInterestRatesLocalVars { + address stableDebtTokenAddress; + uint256 availableLiquidity; + uint256 totalStableDebt; + uint256 newLiquidityRate; + uint256 newStableRate; + uint256 newVariableRate; + uint256 avgStableRate; + uint256 totalVariableDebt; + } + + /** + * @dev Updates the reserve current stable borrow rate, the current variable borrow rate and the current liquidity rate + * @param reserve The address of the reserve to be updated + * @param liquidityAdded The amount of liquidity added to the protocol (deposit or repay) in the previous action + * @param liquidityTaken The amount of liquidity taken from the protocol (redeem or borrow) + **/ + function updateInterestRates( + DataTypes.ReserveData storage reserve, + address reserveAddress, + address aTokenAddress, + uint256 liquidityAdded, + uint256 liquidityTaken + ) internal { + UpdateInterestRatesLocalVars memory vars; + + vars.stableDebtTokenAddress = reserve.stableDebtTokenAddress; + + (vars.totalStableDebt, vars.avgStableRate) = IStableDebtToken(vars.stableDebtTokenAddress) + .getTotalSupplyAndAvgRate(); + + //calculates the total variable debt locally using the scaled total supply instead + //of totalSupply(), as it's noticeably cheaper. Also, the index has been + //updated by the previous updateState() call + vars.totalVariableDebt = IVariableDebtToken(reserve.variableDebtTokenAddress) + .scaledTotalSupply() + .rayMul(reserve.variableBorrowIndex); + + ( + vars.newLiquidityRate, + vars.newStableRate, + vars.newVariableRate + ) = IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).calculateInterestRates( + reserveAddress, + aTokenAddress, + liquidityAdded, + liquidityTaken, + vars.totalStableDebt, + vars.totalVariableDebt, + vars.avgStableRate, + reserve.configuration.getReserveFactor() + ); + require(vars.newLiquidityRate <= type(uint128).max, Errors.RL_LIQUIDITY_RATE_OVERFLOW); + require(vars.newStableRate <= type(uint128).max, Errors.RL_STABLE_BORROW_RATE_OVERFLOW); + require(vars.newVariableRate <= type(uint128).max, Errors.RL_VARIABLE_BORROW_RATE_OVERFLOW); + + reserve.currentLiquidityRate = uint128(vars.newLiquidityRate); + reserve.currentStableBorrowRate = uint128(vars.newStableRate); + reserve.currentVariableBorrowRate = uint128(vars.newVariableRate); + + emit ReserveDataUpdated( + reserveAddress, + vars.newLiquidityRate, + vars.newStableRate, + vars.newVariableRate, + reserve.liquidityIndex, + reserve.variableBorrowIndex + ); + } + + struct MintToTreasuryLocalVars { + uint256 currentStableDebt; + uint256 principalStableDebt; + uint256 previousStableDebt; + uint256 currentVariableDebt; + uint256 previousVariableDebt; + uint256 avgStableRate; + uint256 cumulatedStableInterest; + uint256 totalDebtAccrued; + uint256 amountToMint; + uint256 reserveFactor; + uint40 stableSupplyUpdatedTimestamp; + } + + /** + * @dev Mints part of the repaid interest to the reserve treasury as a function of the reserveFactor for the + * specific asset. + * @param reserve The reserve reserve to be updated + * @param scaledVariableDebt The current scaled total variable debt + * @param previousVariableBorrowIndex The variable borrow index before the last accumulation of the interest + * @param newLiquidityIndex The new liquidity index + * @param newVariableBorrowIndex The variable borrow index after the last accumulation of the interest + **/ + function _mintToTreasury( + DataTypes.ReserveData storage reserve, + uint256 scaledVariableDebt, + uint256 previousVariableBorrowIndex, + uint256 newLiquidityIndex, + uint256 newVariableBorrowIndex, + uint40 timestamp + ) internal { + MintToTreasuryLocalVars memory vars; + + vars.reserveFactor = reserve.configuration.getReserveFactor(); + + if (vars.reserveFactor == 0) { + return; + } + + //fetching the principal, total stable debt and the avg stable rate + ( + vars.principalStableDebt, + vars.currentStableDebt, + vars.avgStableRate, + vars.stableSupplyUpdatedTimestamp + ) = IStableDebtToken(reserve.stableDebtTokenAddress).getSupplyData(); + + //calculate the last principal variable debt + vars.previousVariableDebt = scaledVariableDebt.rayMul(previousVariableBorrowIndex); + + //calculate the new total supply after accumulation of the index + vars.currentVariableDebt = scaledVariableDebt.rayMul(newVariableBorrowIndex); + + //calculate the stable debt until the last timestamp update + vars.cumulatedStableInterest = MathUtils.calculateCompoundedInterest( + vars.avgStableRate, + vars.stableSupplyUpdatedTimestamp, + timestamp + ); + + vars.previousStableDebt = vars.principalStableDebt.rayMul(vars.cumulatedStableInterest); + + //debt accrued is the sum of the current debt minus the sum of the debt at the last update + vars.totalDebtAccrued = vars + .currentVariableDebt + .add(vars.currentStableDebt) + .sub(vars.previousVariableDebt) + .sub(vars.previousStableDebt); + + vars.amountToMint = vars.totalDebtAccrued.percentMul(vars.reserveFactor); + + if (vars.amountToMint != 0) { + IAToken(reserve.aTokenAddress).mintToTreasury(vars.amountToMint, newLiquidityIndex); + } + } + + /** + * @dev Updates the reserve indexes and the timestamp of the update + * @param reserve The reserve reserve to be updated + * @param scaledVariableDebt The scaled variable debt + * @param liquidityIndex The last stored liquidity index + * @param variableBorrowIndex The last stored variable borrow index + **/ + function _updateIndexes( + DataTypes.ReserveData storage reserve, + uint256 scaledVariableDebt, + uint256 liquidityIndex, + uint256 variableBorrowIndex, + uint40 timestamp + ) internal returns (uint256, uint256) { + uint256 currentLiquidityRate = reserve.currentLiquidityRate; + + uint256 newLiquidityIndex = liquidityIndex; + uint256 newVariableBorrowIndex = variableBorrowIndex; + + //only cumulating if there is any income being produced + if (currentLiquidityRate > 0) { + uint256 cumulatedLiquidityInterest = + MathUtils.calculateLinearInterest(currentLiquidityRate, timestamp); + newLiquidityIndex = cumulatedLiquidityInterest.rayMul(liquidityIndex); + require(newLiquidityIndex <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW); + + reserve.liquidityIndex = uint128(newLiquidityIndex); + + //as the liquidity rate might come only from stable rate loans, we need to ensure + //that there is actual variable debt before accumulating + if (scaledVariableDebt != 0) { + uint256 cumulatedVariableBorrowInterest = + MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp); + newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(variableBorrowIndex); + require( + newVariableBorrowIndex <= type(uint128).max, + Errors.RL_VARIABLE_BORROW_INDEX_OVERFLOW + ); + reserve.variableBorrowIndex = uint128(newVariableBorrowIndex); + } + } + + //solium-disable-next-line + reserve.lastUpdateTimestamp = uint40(block.timestamp); + return (newLiquidityIndex, newVariableBorrowIndex); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/logic/ValidationLogic.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/logic/ValidationLogic.sol new file mode 100644 index 000000000..8aa449cdd --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/logic/ValidationLogic.sol @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol'; +import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol'; +import {ReserveLogic} from './ReserveLogic.sol'; +import {GenericLogic} from './GenericLogic.sol'; +import {WadRayMath} from '../math/WadRayMath.sol'; +import {PercentageMath} from '../math/PercentageMath.sol'; +import {SafeERC20} from '../../../dependencies/openzeppelin/contracts/SafeERC20.sol'; +import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol'; +import {UserConfiguration} from '../configuration/UserConfiguration.sol'; +import {Errors} from '../helpers/Errors.sol'; +import {Helpers} from '../helpers/Helpers.sol'; +import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol'; +import {DataTypes} from '../types/DataTypes.sol'; + +/** + * @title ReserveLogic library + * @author Aave + * @notice Implements functions to validate the different actions of the protocol + */ +library ValidationLogic { + using ReserveLogic for DataTypes.ReserveData; + using SafeMath for uint256; + using WadRayMath for uint256; + using PercentageMath for uint256; + using SafeERC20 for IERC20; + using ReserveConfiguration for DataTypes.ReserveConfigurationMap; + using UserConfiguration for DataTypes.UserConfigurationMap; + + uint256 public constant REBALANCE_UP_LIQUIDITY_RATE_THRESHOLD = 4000; + uint256 public constant REBALANCE_UP_USAGE_RATIO_THRESHOLD = 0.95 * 1e27; //usage ratio of 95% + + /** + * @dev Validates a deposit action + * @param reserve The reserve object on which the user is depositing + * @param amount The amount to be deposited + */ + function validateDeposit(DataTypes.ReserveData storage reserve, uint256 amount) external view { + (bool isActive, bool isFrozen, , ) = reserve.configuration.getFlags(); + + require(amount != 0, Errors.VL_INVALID_AMOUNT); + require(isActive, Errors.VL_NO_ACTIVE_RESERVE); + require(!isFrozen, Errors.VL_RESERVE_FROZEN); + } + + /** + * @dev Validates a withdraw action + * @param reserveAddress The address of the reserve + * @param amount The amount to be withdrawn + * @param userBalance The balance of the user + * @param reservesData The reserves state + * @param userConfig The user configuration + * @param reserves The addresses of the reserves + * @param reservesCount The number of reserves + * @param oracle The price oracle + */ + function validateWithdraw( + address reserveAddress, + uint256 amount, + uint256 userBalance, + mapping(address => DataTypes.ReserveData) storage reservesData, + DataTypes.UserConfigurationMap storage userConfig, + mapping(uint256 => address) storage reserves, + uint256 reservesCount, + address oracle + ) external view { + require(amount != 0, Errors.VL_INVALID_AMOUNT); + require(amount <= userBalance, Errors.VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE); + + (bool isActive, , , ) = reservesData[reserveAddress].configuration.getFlags(); + require(isActive, Errors.VL_NO_ACTIVE_RESERVE); + + require( + GenericLogic.balanceDecreaseAllowed( + reserveAddress, + msg.sender, + amount, + reservesData, + userConfig, + reserves, + reservesCount, + oracle + ), + Errors.VL_TRANSFER_NOT_ALLOWED + ); + } + + struct ValidateBorrowLocalVars { + uint256 currentLtv; + uint256 currentLiquidationThreshold; + uint256 amountOfCollateralNeededETH; + uint256 userCollateralBalanceETH; + uint256 userBorrowBalanceETH; + uint256 availableLiquidity; + uint256 healthFactor; + bool isActive; + bool isFrozen; + bool borrowingEnabled; + bool stableRateBorrowingEnabled; + } + + /** + * @dev Validates a borrow action + * @param asset The address of the asset to borrow + * @param reserve The reserve state from which the user is borrowing + * @param userAddress The address of the user + * @param amount The amount to be borrowed + * @param amountInETH The amount to be borrowed, in ETH + * @param interestRateMode The interest rate mode at which the user is borrowing + * @param maxStableLoanPercent The max amount of the liquidity that can be borrowed at stable rate, in percentage + * @param reservesData The state of all the reserves + * @param userConfig The state of the user for the specific reserve + * @param reserves The addresses of all the active reserves + * @param oracle The price oracle + */ + + function validateBorrow( + address asset, + DataTypes.ReserveData storage reserve, + address userAddress, + uint256 amount, + uint256 amountInETH, + uint256 interestRateMode, + uint256 maxStableLoanPercent, + mapping(address => DataTypes.ReserveData) storage reservesData, + DataTypes.UserConfigurationMap storage userConfig, + mapping(uint256 => address) storage reserves, + uint256 reservesCount, + address oracle + ) external view { + ValidateBorrowLocalVars memory vars; + + (vars.isActive, vars.isFrozen, vars.borrowingEnabled, vars.stableRateBorrowingEnabled) = reserve + .configuration + .getFlags(); + + require(vars.isActive, Errors.VL_NO_ACTIVE_RESERVE); + require(!vars.isFrozen, Errors.VL_RESERVE_FROZEN); + require(amount != 0, Errors.VL_INVALID_AMOUNT); + + require(vars.borrowingEnabled, Errors.VL_BORROWING_NOT_ENABLED); + + //validate interest rate mode + require( + uint256(DataTypes.InterestRateMode.VARIABLE) == interestRateMode || + uint256(DataTypes.InterestRateMode.STABLE) == interestRateMode, + Errors.VL_INVALID_INTEREST_RATE_MODE_SELECTED + ); + + ( + vars.userCollateralBalanceETH, + vars.userBorrowBalanceETH, + vars.currentLtv, + vars.currentLiquidationThreshold, + vars.healthFactor + ) = GenericLogic.calculateUserAccountData( + userAddress, + reservesData, + userConfig, + reserves, + reservesCount, + oracle + ); + + require(vars.userCollateralBalanceETH > 0, Errors.VL_COLLATERAL_BALANCE_IS_0); + + require( + vars.healthFactor > GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD, + Errors.VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD + ); + + //add the current already borrowed amount to the amount requested to calculate the total collateral needed. + vars.amountOfCollateralNeededETH = vars.userBorrowBalanceETH.add(amountInETH).percentDiv( + vars.currentLtv + ); //LTV is calculated in percentage + + require( + vars.amountOfCollateralNeededETH <= vars.userCollateralBalanceETH, + Errors.VL_COLLATERAL_CANNOT_COVER_NEW_BORROW + ); + + /** + * Following conditions need to be met if the user is borrowing at a stable rate: + * 1. Reserve must be enabled for stable rate borrowing + * 2. Users cannot borrow from the reserve if their collateral is (mostly) the same currency + * they are borrowing, to prevent abuses. + * 3. Users will be able to borrow only a portion of the total available liquidity + **/ + + if (interestRateMode == uint256(DataTypes.InterestRateMode.STABLE)) { + //check if the borrow mode is stable and if stable rate borrowing is enabled on this reserve + + require(vars.stableRateBorrowingEnabled, Errors.VL_STABLE_BORROWING_NOT_ENABLED); + + require( + !userConfig.isUsingAsCollateral(reserve.id) || + reserve.configuration.getLtv() == 0 || + amount > IERC20(reserve.aTokenAddress).balanceOf(userAddress), + Errors.VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY + ); + + vars.availableLiquidity = IERC20(asset).balanceOf(reserve.aTokenAddress); + + //calculate the max available loan size in stable rate mode as a percentage of the + //available liquidity + uint256 maxLoanSizeStable = vars.availableLiquidity.percentMul(maxStableLoanPercent); + + require(amount <= maxLoanSizeStable, Errors.VL_AMOUNT_BIGGER_THAN_MAX_LOAN_SIZE_STABLE); + } + } + + /** + * @dev Validates a repay action + * @param reserve The reserve state from which the user is repaying + * @param amountSent The amount sent for the repayment. Can be an actual value or uint(-1) + * @param onBehalfOf The address of the user msg.sender is repaying for + * @param stableDebt The borrow balance of the user + * @param variableDebt The borrow balance of the user + */ + function validateRepay( + DataTypes.ReserveData storage reserve, + uint256 amountSent, + DataTypes.InterestRateMode rateMode, + address onBehalfOf, + uint256 stableDebt, + uint256 variableDebt + ) external view { + bool isActive = reserve.configuration.getActive(); + + require(isActive, Errors.VL_NO_ACTIVE_RESERVE); + + require(amountSent > 0, Errors.VL_INVALID_AMOUNT); + + require( + (stableDebt > 0 && + DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.STABLE) || + (variableDebt > 0 && + DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.VARIABLE), + Errors.VL_NO_DEBT_OF_SELECTED_TYPE + ); + + require( + amountSent != uint256(-1) || msg.sender == onBehalfOf, + Errors.VL_NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF + ); + } + + /** + * @dev Validates a swap of borrow rate mode. + * @param reserve The reserve state on which the user is swapping the rate + * @param userConfig The user reserves configuration + * @param stableDebt The stable debt of the user + * @param variableDebt The variable debt of the user + * @param currentRateMode The rate mode of the borrow + */ + function validateSwapRateMode( + DataTypes.ReserveData storage reserve, + DataTypes.UserConfigurationMap storage userConfig, + uint256 stableDebt, + uint256 variableDebt, + DataTypes.InterestRateMode currentRateMode + ) external view { + (bool isActive, bool isFrozen, , bool stableRateEnabled) = reserve.configuration.getFlags(); + + require(isActive, Errors.VL_NO_ACTIVE_RESERVE); + require(!isFrozen, Errors.VL_RESERVE_FROZEN); + + if (currentRateMode == DataTypes.InterestRateMode.STABLE) { + require(stableDebt > 0, Errors.VL_NO_STABLE_RATE_LOAN_IN_RESERVE); + } else if (currentRateMode == DataTypes.InterestRateMode.VARIABLE) { + require(variableDebt > 0, Errors.VL_NO_VARIABLE_RATE_LOAN_IN_RESERVE); + /** + * user wants to swap to stable, before swapping we need to ensure that + * 1. stable borrow rate is enabled on the reserve + * 2. user is not trying to abuse the reserve by depositing + * more collateral than he is borrowing, artificially lowering + * the interest rate, borrowing at variable, and switching to stable + **/ + require(stableRateEnabled, Errors.VL_STABLE_BORROWING_NOT_ENABLED); + + require( + !userConfig.isUsingAsCollateral(reserve.id) || + reserve.configuration.getLtv() == 0 || + stableDebt.add(variableDebt) > IERC20(reserve.aTokenAddress).balanceOf(msg.sender), + Errors.VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY + ); + } else { + revert(Errors.VL_INVALID_INTEREST_RATE_MODE_SELECTED); + } + } + + /** + * @dev Validates a stable borrow rate rebalance action + * @param reserve The reserve state on which the user is getting rebalanced + * @param reserveAddress The address of the reserve + * @param stableDebtToken The stable debt token instance + * @param variableDebtToken The variable debt token instance + * @param aTokenAddress The address of the aToken contract + */ + function validateRebalanceStableBorrowRate( + DataTypes.ReserveData storage reserve, + address reserveAddress, + IERC20 stableDebtToken, + IERC20 variableDebtToken, + address aTokenAddress + ) external view { + (bool isActive, , , ) = reserve.configuration.getFlags(); + + require(isActive, Errors.VL_NO_ACTIVE_RESERVE); + + //if the usage ratio is below 95%, no rebalances are needed + uint256 totalDebt = + stableDebtToken.totalSupply().add(variableDebtToken.totalSupply()).wadToRay(); + uint256 availableLiquidity = IERC20(reserveAddress).balanceOf(aTokenAddress).wadToRay(); + uint256 usageRatio = totalDebt == 0 ? 0 : totalDebt.rayDiv(availableLiquidity.add(totalDebt)); + + //if the liquidity rate is below REBALANCE_UP_THRESHOLD of the max variable APR at 95% usage, + //then we allow rebalancing of the stable rate positions. + + uint256 currentLiquidityRate = reserve.currentLiquidityRate; + uint256 maxVariableBorrowRate = + IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).getMaxVariableBorrowRate(); + + require( + usageRatio >= REBALANCE_UP_USAGE_RATIO_THRESHOLD && + currentLiquidityRate <= + maxVariableBorrowRate.percentMul(REBALANCE_UP_LIQUIDITY_RATE_THRESHOLD), + Errors.LP_INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET + ); + } + + /** + * @dev Validates the action of setting an asset as collateral + * @param reserve The state of the reserve that the user is enabling or disabling as collateral + * @param reserveAddress The address of the reserve + * @param reservesData The data of all the reserves + * @param userConfig The state of the user for the specific reserve + * @param reserves The addresses of all the active reserves + * @param oracle The price oracle + */ + function validateSetUseReserveAsCollateral( + DataTypes.ReserveData storage reserve, + address reserveAddress, + bool useAsCollateral, + mapping(address => DataTypes.ReserveData) storage reservesData, + DataTypes.UserConfigurationMap storage userConfig, + mapping(uint256 => address) storage reserves, + uint256 reservesCount, + address oracle + ) external view { + uint256 underlyingBalance = IERC20(reserve.aTokenAddress).balanceOf(msg.sender); + + require(underlyingBalance > 0, Errors.VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0); + + require( + useAsCollateral || + GenericLogic.balanceDecreaseAllowed( + reserveAddress, + msg.sender, + underlyingBalance, + reservesData, + userConfig, + reserves, + reservesCount, + oracle + ), + Errors.VL_DEPOSIT_ALREADY_IN_USE + ); + } + + /** + * @dev Validates a flashloan action + * @param assets The assets being flashborrowed + * @param amounts The amounts for each asset being borrowed + **/ + function validateFlashloan(address[] memory assets, uint256[] memory amounts) internal pure { + require(assets.length == amounts.length, Errors.VL_INCONSISTENT_FLASHLOAN_PARAMS); + } + + /** + * @dev Validates the liquidation action + * @param collateralReserve The reserve data of the collateral + * @param principalReserve The reserve data of the principal + * @param userConfig The user configuration + * @param userHealthFactor The user's health factor + * @param userStableDebt Total stable debt balance of the user + * @param userVariableDebt Total variable debt balance of the user + **/ + function validateLiquidationCall( + DataTypes.ReserveData storage collateralReserve, + DataTypes.ReserveData storage principalReserve, + DataTypes.UserConfigurationMap storage userConfig, + uint256 userHealthFactor, + uint256 userStableDebt, + uint256 userVariableDebt + ) internal view returns (uint256, string memory) { + if ( + !collateralReserve.configuration.getActive() || !principalReserve.configuration.getActive() + ) { + return ( + uint256(Errors.CollateralManagerErrors.NO_ACTIVE_RESERVE), + Errors.VL_NO_ACTIVE_RESERVE + ); + } + + if (userHealthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD) { + return ( + uint256(Errors.CollateralManagerErrors.HEALTH_FACTOR_ABOVE_THRESHOLD), + Errors.LPCM_HEALTH_FACTOR_NOT_BELOW_THRESHOLD + ); + } + + bool isCollateralEnabled = + collateralReserve.configuration.getLiquidationThreshold() > 0 && + userConfig.isUsingAsCollateral(collateralReserve.id); + + //if collateral isn't enabled as collateral by user, it cannot be liquidated + if (!isCollateralEnabled) { + return ( + uint256(Errors.CollateralManagerErrors.COLLATERAL_CANNOT_BE_LIQUIDATED), + Errors.LPCM_COLLATERAL_CANNOT_BE_LIQUIDATED + ); + } + + if (userStableDebt == 0 && userVariableDebt == 0) { + return ( + uint256(Errors.CollateralManagerErrors.CURRRENCY_NOT_BORROWED), + Errors.LPCM_SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER + ); + } + + return (uint256(Errors.CollateralManagerErrors.NO_ERROR), Errors.LPCM_NO_ERRORS); + } + + /** + * @dev Validates an aToken transfer + * @param from The user from which the aTokens are being transferred + * @param reservesData The state of all the reserves + * @param userConfig The state of the user for the specific reserve + * @param reserves The addresses of all the active reserves + * @param oracle The price oracle + */ + function validateTransfer( + address from, + mapping(address => DataTypes.ReserveData) storage reservesData, + DataTypes.UserConfigurationMap storage userConfig, + mapping(uint256 => address) storage reserves, + uint256 reservesCount, + address oracle + ) internal view { + (, , , , uint256 healthFactor) = + GenericLogic.calculateUserAccountData( + from, + reservesData, + userConfig, + reserves, + reservesCount, + oracle + ); + + require( + healthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD, + Errors.VL_TRANSFER_NOT_ALLOWED + ); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/math/MathUtils.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/math/MathUtils.sol new file mode 100644 index 000000000..54ed9a61d --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/math/MathUtils.sol @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol'; +import {WadRayMath} from './WadRayMath.sol'; + +library MathUtils { + using SafeMath for uint256; + using WadRayMath for uint256; + + /// @dev Ignoring leap years + uint256 internal constant SECONDS_PER_YEAR = 365 days; + + /** + * @dev Function to calculate the interest accumulated using a linear interest rate formula + * @param rate The interest rate, in ray + * @param lastUpdateTimestamp The timestamp of the last update of the interest + * @return The interest rate linearly accumulated during the timeDelta, in ray + **/ + + function calculateLinearInterest(uint256 rate, uint40 lastUpdateTimestamp) + internal + view + returns (uint256) + { + //solium-disable-next-line + uint256 timeDifference = block.timestamp.sub(uint256(lastUpdateTimestamp)); + + return (rate.mul(timeDifference) / SECONDS_PER_YEAR).add(WadRayMath.ray()); + } + + /** + * @dev Function to calculate the interest using a compounded interest rate formula + * To avoid expensive exponentiation, the calculation is performed using a binomial approximation: + * + * (1+x)^n = 1+n*x+[n/2*(n-1)]*x^2+[n/6*(n-1)*(n-2)*x^3... + * + * The approximation slightly underpays liquidity providers and undercharges borrowers, with the advantage of great gas cost reductions + * The whitepaper contains reference to the approximation and a table showing the margin of error per different time periods + * + * @param rate The interest rate, in ray + * @param lastUpdateTimestamp The timestamp of the last update of the interest + * @return The interest rate compounded during the timeDelta, in ray + **/ + function calculateCompoundedInterest( + uint256 rate, + uint40 lastUpdateTimestamp, + uint256 currentTimestamp + ) internal pure returns (uint256) { + //solium-disable-next-line + uint256 exp = currentTimestamp.sub(uint256(lastUpdateTimestamp)); + + if (exp == 0) { + return WadRayMath.ray(); + } + + uint256 expMinusOne = exp - 1; + + uint256 expMinusTwo = exp > 2 ? exp - 2 : 0; + + uint256 ratePerSecond = rate / SECONDS_PER_YEAR; + + uint256 basePowerTwo = ratePerSecond.rayMul(ratePerSecond); + uint256 basePowerThree = basePowerTwo.rayMul(ratePerSecond); + + uint256 secondTerm = exp.mul(expMinusOne).mul(basePowerTwo) / 2; + uint256 thirdTerm = exp.mul(expMinusOne).mul(expMinusTwo).mul(basePowerThree) / 6; + + return WadRayMath.ray().add(ratePerSecond.mul(exp)).add(secondTerm).add(thirdTerm); + } + + /** + * @dev Calculates the compounded interest between the timestamp of the last update and the current block timestamp + * @param rate The interest rate (in ray) + * @param lastUpdateTimestamp The timestamp from which the interest accumulation needs to be calculated + **/ + function calculateCompoundedInterest(uint256 rate, uint40 lastUpdateTimestamp) + internal + view + returns (uint256) + { + return calculateCompoundedInterest(rate, lastUpdateTimestamp, block.timestamp); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/math/PercentageMath.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/math/PercentageMath.sol new file mode 100644 index 000000000..74d85d6bf --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/math/PercentageMath.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {Errors} from '../helpers/Errors.sol'; + +/** + * @title PercentageMath library + * @author Aave + * @notice Provides functions to perform percentage calculations + * @dev Percentages are defined by default with 2 decimals of precision (100.00). The precision is indicated by PERCENTAGE_FACTOR + * @dev Operations are rounded half up + **/ + +library PercentageMath { + uint256 constant PERCENTAGE_FACTOR = 1e4; //percentage plus two decimals + uint256 constant HALF_PERCENT = PERCENTAGE_FACTOR / 2; + + /** + * @dev Executes a percentage multiplication + * @param value The value of which the percentage needs to be calculated + * @param percentage The percentage of the value to be calculated + * @return The percentage of value + **/ + function percentMul(uint256 value, uint256 percentage) internal pure returns (uint256) { + if (value == 0 || percentage == 0) { + return 0; + } + + require( + value <= (type(uint256).max - HALF_PERCENT) / percentage, + Errors.MATH_MULTIPLICATION_OVERFLOW + ); + + return (value * percentage + HALF_PERCENT) / PERCENTAGE_FACTOR; + } + + /** + * @dev Executes a percentage division + * @param value The value of which the percentage needs to be calculated + * @param percentage The percentage of the value to be calculated + * @return The value divided the percentage + **/ + function percentDiv(uint256 value, uint256 percentage) internal pure returns (uint256) { + require(percentage != 0, Errors.MATH_DIVISION_BY_ZERO); + uint256 halfPercentage = percentage / 2; + + require( + value <= (type(uint256).max - halfPercentage) / PERCENTAGE_FACTOR, + Errors.MATH_MULTIPLICATION_OVERFLOW + ); + + return (value * PERCENTAGE_FACTOR + halfPercentage) / percentage; + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/math/WadRayMath.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/math/WadRayMath.sol new file mode 100644 index 000000000..6a304a0db --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/math/WadRayMath.sol @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {Errors} from '../helpers/Errors.sol'; + +/** + * @title WadRayMath library + * @author Aave + * @dev Provides mul and div function for wads (decimal numbers with 18 digits precision) and rays (decimals with 27 digits) + **/ + +library WadRayMath { + uint256 internal constant WAD = 1e18; + uint256 internal constant halfWAD = WAD / 2; + + uint256 internal constant RAY = 1e27; + uint256 internal constant halfRAY = RAY / 2; + + uint256 internal constant WAD_RAY_RATIO = 1e9; + + /** + * @return One ray, 1e27 + **/ + function ray() internal pure returns (uint256) { + return RAY; + } + + /** + * @return One wad, 1e18 + **/ + + function wad() internal pure returns (uint256) { + return WAD; + } + + /** + * @return Half ray, 1e27/2 + **/ + function halfRay() internal pure returns (uint256) { + return halfRAY; + } + + /** + * @return Half ray, 1e18/2 + **/ + function halfWad() internal pure returns (uint256) { + return halfWAD; + } + + /** + * @dev Multiplies two wad, rounding half up to the nearest wad + * @param a Wad + * @param b Wad + * @return The result of a*b, in wad + **/ + function wadMul(uint256 a, uint256 b) internal pure returns (uint256) { + if (a == 0 || b == 0) { + return 0; + } + + require(a <= (type(uint256).max - halfWAD) / b, Errors.MATH_MULTIPLICATION_OVERFLOW); + + return (a * b + halfWAD) / WAD; + } + + /** + * @dev Divides two wad, rounding half up to the nearest wad + * @param a Wad + * @param b Wad + * @return The result of a/b, in wad + **/ + function wadDiv(uint256 a, uint256 b) internal pure returns (uint256) { + require(b != 0, Errors.MATH_DIVISION_BY_ZERO); + uint256 halfB = b / 2; + + require(a <= (type(uint256).max - halfB) / WAD, Errors.MATH_MULTIPLICATION_OVERFLOW); + + return (a * WAD + halfB) / b; + } + + /** + * @dev Multiplies two ray, rounding half up to the nearest ray + * @param a Ray + * @param b Ray + * @return The result of a*b, in ray + **/ + function rayMul(uint256 a, uint256 b) internal pure returns (uint256) { + if (a == 0 || b == 0) { + return 0; + } + + require(a <= (type(uint256).max - halfRAY) / b, Errors.MATH_MULTIPLICATION_OVERFLOW); + + return (a * b + halfRAY) / RAY; + } + + /** + * @dev Divides two ray, rounding half up to the nearest ray + * @param a Ray + * @param b Ray + * @return The result of a/b, in ray + **/ + function rayDiv(uint256 a, uint256 b) internal pure returns (uint256) { + require(b != 0, Errors.MATH_DIVISION_BY_ZERO); + uint256 halfB = b / 2; + + require(a <= (type(uint256).max - halfB) / RAY, Errors.MATH_MULTIPLICATION_OVERFLOW); + + return (a * RAY + halfB) / b; + } + + /** + * @dev Casts ray down to wad + * @param a Ray + * @return a casted to wad, rounded half up to the nearest wad + **/ + function rayToWad(uint256 a) internal pure returns (uint256) { + uint256 halfRatio = WAD_RAY_RATIO / 2; + uint256 result = halfRatio + a; + require(result >= halfRatio, Errors.MATH_ADDITION_OVERFLOW); + + return result / WAD_RAY_RATIO; + } + + /** + * @dev Converts wad up to ray + * @param a Wad + * @return a converted in ray + **/ + function wadToRay(uint256 a) internal pure returns (uint256) { + uint256 result = a * WAD_RAY_RATIO; + require(result / WAD_RAY_RATIO == a, Errors.MATH_MULTIPLICATION_OVERFLOW); + return result; + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/types/DataTypes.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/types/DataTypes.sol new file mode 100644 index 000000000..9256788f5 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/libraries/types/DataTypes.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +library DataTypes { + // refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties. + struct ReserveData { + //stores the reserve configuration + ReserveConfigurationMap configuration; + //the liquidity index. Expressed in ray + uint128 liquidityIndex; + //variable borrow index. Expressed in ray + uint128 variableBorrowIndex; + //the current supply rate. Expressed in ray + uint128 currentLiquidityRate; + //the current variable borrow rate. Expressed in ray + uint128 currentVariableBorrowRate; + //the current stable borrow rate. Expressed in ray + uint128 currentStableBorrowRate; + uint40 lastUpdateTimestamp; + //tokens addresses + address aTokenAddress; + address stableDebtTokenAddress; + address variableDebtTokenAddress; + //address of the interest rate strategy + address interestRateStrategyAddress; + //the id of the reserve. Represents the position in the list of the active reserves + uint8 id; + } + + struct ReserveConfigurationMap { + //bit 0-15: LTV + //bit 16-31: Liq. threshold + //bit 32-47: Liq. bonus + //bit 48-55: Decimals + //bit 56: Reserve is active + //bit 57: reserve is frozen + //bit 58: borrowing is enabled + //bit 59: stable rate borrowing enabled + //bit 60-63: reserved + //bit 64-79: reserve factor + uint256 data; + } + + struct UserConfigurationMap { + uint256 data; + } + + enum InterestRateMode {NONE, STABLE, VARIABLE} +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/AToken.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/AToken.sol new file mode 100644 index 000000000..41b170ebf --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/AToken.sol @@ -0,0 +1,406 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol'; +import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol'; +import {ILendingPool} from '../../interfaces/ILendingPool.sol'; +import {IAToken} from '../../interfaces/IAToken.sol'; +import {WadRayMath} from '../libraries/math/WadRayMath.sol'; +import {Errors} from '../libraries/helpers/Errors.sol'; +import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol'; +import {IncentivizedERC20} from './IncentivizedERC20.sol'; +import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol'; + +/** + * @title Aave ERC20 AToken + * @dev Implementation of the interest bearing token for the Aave protocol + * @author Aave + */ +contract AToken is + VersionedInitializable, + IncentivizedERC20('ATOKEN_IMPL', 'ATOKEN_IMPL', 0), + IAToken +{ + using WadRayMath for uint256; + using SafeERC20 for IERC20; + + bytes public constant EIP712_REVISION = bytes('1'); + bytes32 internal constant EIP712_DOMAIN = + keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'); + bytes32 public constant PERMIT_TYPEHASH = + keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)'); + + uint256 public constant ATOKEN_REVISION = 0x1; + + /// @dev owner => next valid nonce to submit with permit() + mapping(address => uint256) public _nonces; + + bytes32 public DOMAIN_SEPARATOR; + + ILendingPool internal _pool; + address internal _treasury; + address internal _underlyingAsset; + IAaveIncentivesController internal _incentivesController; + + modifier onlyLendingPool { + require(_msgSender() == address(_pool), Errors.CT_CALLER_MUST_BE_LENDING_POOL); + _; + } + + function getRevision() internal pure virtual override returns (uint256) { + return ATOKEN_REVISION; + } + + /** + * @dev Initializes the aToken + * @param pool The address of the lending pool where this aToken will be used + * @param treasury The address of the Aave treasury, receiving the fees on this aToken + * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH) + * @param incentivesController The smart contract managing potential incentives distribution + * @param aTokenDecimals The decimals of the aToken, same as the underlying asset's + * @param aTokenName The name of the aToken + * @param aTokenSymbol The symbol of the aToken + */ + function initialize( + ILendingPool pool, + address treasury, + address underlyingAsset, + IAaveIncentivesController incentivesController, + uint8 aTokenDecimals, + string calldata aTokenName, + string calldata aTokenSymbol, + bytes calldata params + ) external override initializer { + uint256 chainId; + + //solium-disable-next-line + assembly { + chainId := chainid() + } + + DOMAIN_SEPARATOR = keccak256( + abi.encode( + EIP712_DOMAIN, + keccak256(bytes(aTokenName)), + keccak256(EIP712_REVISION), + chainId, + address(this) + ) + ); + + _setName(aTokenName); + _setSymbol(aTokenSymbol); + _setDecimals(aTokenDecimals); + + _pool = pool; + _treasury = treasury; + _underlyingAsset = underlyingAsset; + _incentivesController = incentivesController; + + emit Initialized( + underlyingAsset, + address(pool), + treasury, + address(incentivesController), + aTokenDecimals, + aTokenName, + aTokenSymbol, + params + ); + } + + /** + * @dev Burns aTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying` + * - Only callable by the LendingPool, as extra state updates there need to be managed + * @param user The owner of the aTokens, getting them burned + * @param receiverOfUnderlying The address that will receive the underlying + * @param amount The amount being burned + * @param index The new liquidity index of the reserve + **/ + function burn( + address user, + address receiverOfUnderlying, + uint256 amount, + uint256 index + ) external override onlyLendingPool { + uint256 amountScaled = amount.rayDiv(index); + require(amountScaled != 0, Errors.CT_INVALID_BURN_AMOUNT); + _burn(user, amountScaled); + + IERC20(_underlyingAsset).safeTransfer(receiverOfUnderlying, amount); + + emit Transfer(user, address(0), amount); + emit Burn(user, receiverOfUnderlying, amount, index); + } + + /** + * @dev Mints `amount` aTokens to `user` + * - Only callable by the LendingPool, as extra state updates there need to be managed + * @param user The address receiving the minted tokens + * @param amount The amount of tokens getting minted + * @param index The new liquidity index of the reserve + * @return `true` if the the previous balance of the user was 0 + */ + function mint( + address user, + uint256 amount, + uint256 index + ) external override onlyLendingPool returns (bool) { + uint256 previousBalance = super.balanceOf(user); + + uint256 amountScaled = amount.rayDiv(index); + require(amountScaled != 0, Errors.CT_INVALID_MINT_AMOUNT); + _mint(user, amountScaled); + + emit Transfer(address(0), user, amount); + emit Mint(user, amount, index); + + return previousBalance == 0; + } + + /** + * @dev Mints aTokens to the reserve treasury + * - Only callable by the LendingPool + * @param amount The amount of tokens getting minted + * @param index The new liquidity index of the reserve + */ + function mintToTreasury(uint256 amount, uint256 index) external override onlyLendingPool { + if (amount == 0) { + return; + } + + address treasury = _treasury; + + // Compared to the normal mint, we don't check for rounding errors. + // The amount to mint can easily be very small since it is a fraction of the interest ccrued. + // In that case, the treasury will experience a (very small) loss, but it + // wont cause potentially valid transactions to fail. + _mint(treasury, amount.rayDiv(index)); + + emit Transfer(address(0), treasury, amount); + emit Mint(treasury, amount, index); + } + + /** + * @dev Transfers aTokens in the event of a borrow being liquidated, in case the liquidators reclaims the aToken + * - Only callable by the LendingPool + * @param from The address getting liquidated, current owner of the aTokens + * @param to The recipient + * @param value The amount of tokens getting transferred + **/ + function transferOnLiquidation( + address from, + address to, + uint256 value + ) external override onlyLendingPool { + // Being a normal transfer, the Transfer() and BalanceTransfer() are emitted + // so no need to emit a specific event here + _transfer(from, to, value, false); + + emit Transfer(from, to, value); + } + + /** + * @dev Calculates the balance of the user: principal balance + interest generated by the principal + * @param user The user whose balance is calculated + * @return The balance of the user + **/ + function balanceOf(address user) + public + view + override(IncentivizedERC20, IERC20) + returns (uint256) + { + return super.balanceOf(user).rayMul(_pool.getReserveNormalizedIncome(_underlyingAsset)); + } + + /** + * @dev Returns the scaled balance of the user. The scaled balance is the sum of all the + * updated stored balance divided by the reserve's liquidity index at the moment of the update + * @param user The user whose balance is calculated + * @return The scaled balance of the user + **/ + function scaledBalanceOf(address user) external view override returns (uint256) { + return super.balanceOf(user); + } + + /** + * @dev Returns the scaled balance of the user and the scaled total supply. + * @param user The address of the user + * @return The scaled balance of the user + * @return The scaled balance and the scaled total supply + **/ + function getScaledUserBalanceAndSupply(address user) + external + view + override + returns (uint256, uint256) + { + return (super.balanceOf(user), super.totalSupply()); + } + + /** + * @dev calculates the total supply of the specific aToken + * since the balance of every single user increases over time, the total supply + * does that too. + * @return the current total supply + **/ + function totalSupply() public view override(IncentivizedERC20, IERC20) returns (uint256) { + uint256 currentSupplyScaled = super.totalSupply(); + + if (currentSupplyScaled == 0) { + return 0; + } + + return currentSupplyScaled.rayMul(_pool.getReserveNormalizedIncome(_underlyingAsset)); + } + + /** + * @dev Returns the scaled total supply of the variable debt token. Represents sum(debt/index) + * @return the scaled total supply + **/ + function scaledTotalSupply() public view virtual override returns (uint256) { + return super.totalSupply(); + } + + /** + * @dev Returns the address of the Aave treasury, receiving the fees on this aToken + **/ + function RESERVE_TREASURY_ADDRESS() public view returns (address) { + return _treasury; + } + + /** + * @dev Returns the address of the underlying asset of this aToken (E.g. WETH for aWETH) + **/ + function UNDERLYING_ASSET_ADDRESS() public view returns (address) { + return _underlyingAsset; + } + + /** + * @dev Returns the address of the lending pool where this aToken is used + **/ + function POOL() public view returns (ILendingPool) { + return _pool; + } + + /** + * @dev For internal usage in the logic of the parent contract IncentivizedERC20 + **/ + function _getIncentivesController() internal view override returns (IAaveIncentivesController) { + return _incentivesController; + } + + /** + * @dev Returns the address of the incentives controller contract + **/ + function getIncentivesController() external view override returns (IAaveIncentivesController) { + return _getIncentivesController(); + } + + /** + * @dev Transfers the underlying asset to `target`. Used by the LendingPool to transfer + * assets in borrow(), withdraw() and flashLoan() + * @param target The recipient of the aTokens + * @param amount The amount getting transferred + * @return The amount transferred + **/ + function transferUnderlyingTo(address target, uint256 amount) + external + override + onlyLendingPool + returns (uint256) + { + IERC20(_underlyingAsset).safeTransfer(target, amount); + return amount; + } + + /** + * @dev Invoked to execute actions on the aToken side after a repayment. + * @param user The user executing the repayment + * @param amount The amount getting repaid + **/ + function handleRepayment(address user, uint256 amount) external override onlyLendingPool {} + + /** + * @dev implements the permit function as for + * https://github.com/ethereum/EIPs/blob/8a34d644aacf0f9f8f00815307fd7dd5da07655f/EIPS/eip-2612.md + * @param owner The owner of the funds + * @param spender The spender + * @param value The amount + * @param deadline The deadline timestamp, type(uint256).max for max deadline + * @param v Signature param + * @param s Signature param + * @param r Signature param + */ + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external { + require(owner != address(0), 'INVALID_OWNER'); + //solium-disable-next-line + require(block.timestamp <= deadline, 'INVALID_EXPIRATION'); + uint256 currentValidNonce = _nonces[owner]; + bytes32 digest = + keccak256( + abi.encodePacked( + '\x19\x01', + DOMAIN_SEPARATOR, + keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline)) + ) + ); + require(owner == ecrecover(digest, v, r, s), 'INVALID_SIGNATURE'); + _nonces[owner] = currentValidNonce.add(1); + _approve(owner, spender, value); + } + + /** + * @dev Transfers the aTokens between two users. Validates the transfer + * (ie checks for valid HF after the transfer) if required + * @param from The source address + * @param to The destination address + * @param amount The amount getting transferred + * @param validate `true` if the transfer needs to be validated + **/ + function _transfer( + address from, + address to, + uint256 amount, + bool validate + ) internal { + address underlyingAsset = _underlyingAsset; + ILendingPool pool = _pool; + + uint256 index = pool.getReserveNormalizedIncome(underlyingAsset); + + uint256 fromBalanceBefore = super.balanceOf(from).rayMul(index); + uint256 toBalanceBefore = super.balanceOf(to).rayMul(index); + + super._transfer(from, to, amount.rayDiv(index)); + + if (validate) { + pool.finalizeTransfer(underlyingAsset, from, to, amount, fromBalanceBefore, toBalanceBefore); + } + + emit BalanceTransfer(from, to, amount, index); + } + + /** + * @dev Overrides the parent _transfer to force validated transfer() and transferFrom() + * @param from The source address + * @param to The destination address + * @param amount The amount getting transferred + **/ + function _transfer( + address from, + address to, + uint256 amount + ) internal override { + _transfer(from, to, amount, true); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/DelegationAwareAToken.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/DelegationAwareAToken.sol new file mode 100644 index 000000000..21f592ea1 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/DelegationAwareAToken.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {ILendingPool} from '../../interfaces/ILendingPool.sol'; +import {IDelegationToken} from '../../interfaces/IDelegationToken.sol'; +import {Errors} from '../libraries/helpers/Errors.sol'; +import {AToken} from './AToken.sol'; + +/** + * @title Aave AToken enabled to delegate voting power of the underlying asset to a different address + * @dev The underlying asset needs to be compatible with the COMP delegation interface + * @author Aave + */ +contract DelegationAwareAToken is AToken { + modifier onlyPoolAdmin { + require( + _msgSender() == ILendingPool(_pool).getAddressesProvider().getPoolAdmin(), + Errors.CALLER_NOT_POOL_ADMIN + ); + _; + } + + /** + * @dev Delegates voting power of the underlying asset to a `delegatee` address + * @param delegatee The address that will receive the delegation + **/ + function delegateUnderlyingTo(address delegatee) external onlyPoolAdmin { + IDelegationToken(_underlyingAsset).delegate(delegatee); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/IncentivizedERC20.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/IncentivizedERC20.sol new file mode 100644 index 000000000..855f63b16 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/IncentivizedERC20.sol @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {Context} from '../../dependencies/openzeppelin/contracts/Context.sol'; +import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol'; +import {IERC20Detailed} from '../../dependencies/openzeppelin/contracts/IERC20Detailed.sol'; +import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol'; +import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol'; + +/** + * @title ERC20 + * @notice Basic ERC20 implementation + * @author Aave, inspired by the Openzeppelin ERC20 implementation + **/ +abstract contract IncentivizedERC20 is Context, IERC20, IERC20Detailed { + using SafeMath for uint256; + + mapping(address => uint256) internal _balances; + + mapping(address => mapping(address => uint256)) private _allowances; + uint256 internal _totalSupply; + string private _name; + string private _symbol; + uint8 private _decimals; + + constructor( + string memory name, + string memory symbol, + uint8 decimals + ) public { + _name = name; + _symbol = symbol; + _decimals = decimals; + } + + /** + * @return The name of the token + **/ + function name() public view override returns (string memory) { + return _name; + } + + /** + * @return The symbol of the token + **/ + function symbol() public view override returns (string memory) { + return _symbol; + } + + /** + * @return The decimals of the token + **/ + function decimals() public view override returns (uint8) { + return _decimals; + } + + /** + * @return The total supply of the token + **/ + function totalSupply() public view virtual override returns (uint256) { + return _totalSupply; + } + + /** + * @return The balance of the token + **/ + function balanceOf(address account) public view virtual override returns (uint256) { + return _balances[account]; + } + + /** + * @return Abstract function implemented by the child aToken/debtToken. + * Done this way in order to not break compatibility with previous versions of aTokens/debtTokens + **/ + function _getIncentivesController() internal view virtual returns(IAaveIncentivesController); + + /** + * @dev Executes a transfer of tokens from _msgSender() to recipient + * @param recipient The recipient of the tokens + * @param amount The amount of tokens being transferred + * @return `true` if the transfer succeeds, `false` otherwise + **/ + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { + _transfer(_msgSender(), recipient, amount); + emit Transfer(_msgSender(), recipient, amount); + return true; + } + + /** + * @dev Returns the allowance of spender on the tokens owned by owner + * @param owner The owner of the tokens + * @param spender The user allowed to spend the owner's tokens + * @return The amount of owner's tokens spender is allowed to spend + **/ + function allowance(address owner, address spender) + public + view + virtual + override + returns (uint256) + { + return _allowances[owner][spender]; + } + + /** + * @dev Allows `spender` to spend the tokens owned by _msgSender() + * @param spender The user allowed to spend _msgSender() tokens + * @return `true` + **/ + function approve(address spender, uint256 amount) public virtual override returns (bool) { + _approve(_msgSender(), spender, amount); + return true; + } + + /** + * @dev Executes a transfer of token from sender to recipient, if _msgSender() is allowed to do so + * @param sender The owner of the tokens + * @param recipient The recipient of the tokens + * @param amount The amount of tokens being transferred + * @return `true` if the transfer succeeds, `false` otherwise + **/ + function transferFrom( + address sender, + address recipient, + uint256 amount + ) public virtual override returns (bool) { + _transfer(sender, recipient, amount); + _approve( + sender, + _msgSender(), + _allowances[sender][_msgSender()].sub(amount, 'ERC20: transfer amount exceeds allowance') + ); + emit Transfer(sender, recipient, amount); + return true; + } + + /** + * @dev Increases the allowance of spender to spend _msgSender() tokens + * @param spender The user allowed to spend on behalf of _msgSender() + * @param addedValue The amount being added to the allowance + * @return `true` + **/ + function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); + return true; + } + + /** + * @dev Decreases the allowance of spender to spend _msgSender() tokens + * @param spender The user allowed to spend on behalf of _msgSender() + * @param subtractedValue The amount being subtracted to the allowance + * @return `true` + **/ + function decreaseAllowance(address spender, uint256 subtractedValue) + public + virtual + returns (bool) + { + _approve( + _msgSender(), + spender, + _allowances[_msgSender()][spender].sub( + subtractedValue, + 'ERC20: decreased allowance below zero' + ) + ); + return true; + } + + function _transfer( + address sender, + address recipient, + uint256 amount + ) internal virtual { + require(sender != address(0), 'ERC20: transfer from the zero address'); + require(recipient != address(0), 'ERC20: transfer to the zero address'); + + _beforeTokenTransfer(sender, recipient, amount); + + uint256 oldSenderBalance = _balances[sender]; + _balances[sender] = oldSenderBalance.sub(amount, 'ERC20: transfer amount exceeds balance'); + uint256 oldRecipientBalance = _balances[recipient]; + _balances[recipient] = _balances[recipient].add(amount); + + if (address(_getIncentivesController()) != address(0)) { + uint256 currentTotalSupply = _totalSupply; + _getIncentivesController().handleAction(sender, currentTotalSupply, oldSenderBalance); + if (sender != recipient) { + _getIncentivesController().handleAction(recipient, currentTotalSupply, oldRecipientBalance); + } + } + } + + function _mint(address account, uint256 amount) internal virtual { + require(account != address(0), 'ERC20: mint to the zero address'); + + _beforeTokenTransfer(address(0), account, amount); + + uint256 oldTotalSupply = _totalSupply; + _totalSupply = oldTotalSupply.add(amount); + + uint256 oldAccountBalance = _balances[account]; + _balances[account] = oldAccountBalance.add(amount); + + if (address(_getIncentivesController()) != address(0)) { + _getIncentivesController().handleAction(account, oldTotalSupply, oldAccountBalance); + } + } + + function _burn(address account, uint256 amount) internal virtual { + require(account != address(0), 'ERC20: burn from the zero address'); + + _beforeTokenTransfer(account, address(0), amount); + + uint256 oldTotalSupply = _totalSupply; + _totalSupply = oldTotalSupply.sub(amount); + + uint256 oldAccountBalance = _balances[account]; + _balances[account] = oldAccountBalance.sub(amount, 'ERC20: burn amount exceeds balance'); + + if (address(_getIncentivesController()) != address(0)) { + _getIncentivesController().handleAction(account, oldTotalSupply, oldAccountBalance); + } + } + + function _approve( + address owner, + address spender, + uint256 amount + ) internal virtual { + require(owner != address(0), 'ERC20: approve from the zero address'); + require(spender != address(0), 'ERC20: approve to the zero address'); + + _allowances[owner][spender] = amount; + emit Approval(owner, spender, amount); + } + + function _setName(string memory newName) internal { + _name = newName; + } + + function _setSymbol(string memory newSymbol) internal { + _symbol = newSymbol; + } + + function _setDecimals(uint8 newDecimals) internal { + _decimals = newDecimals; + } + + function _beforeTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual {} +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/StableDebtToken.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/StableDebtToken.sol new file mode 100644 index 000000000..d9f47c8eb --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/StableDebtToken.sol @@ -0,0 +1,435 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {DebtTokenBase} from './base/DebtTokenBase.sol'; +import {MathUtils} from '../libraries/math/MathUtils.sol'; +import {WadRayMath} from '../libraries/math/WadRayMath.sol'; +import {IStableDebtToken} from '../../interfaces/IStableDebtToken.sol'; +import {ILendingPool} from '../../interfaces/ILendingPool.sol'; +import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol'; +import {Errors} from '../libraries/helpers/Errors.sol'; + +/** + * @title StableDebtToken + * @notice Implements a stable debt token to track the borrowing positions of users + * at stable rate mode + * @author Aave + **/ +contract StableDebtToken is IStableDebtToken, DebtTokenBase { + using WadRayMath for uint256; + + uint256 public constant DEBT_TOKEN_REVISION = 0x1; + + uint256 internal _avgStableRate; + mapping(address => uint40) internal _timestamps; + mapping(address => uint256) internal _usersStableRate; + uint40 internal _totalSupplyTimestamp; + + ILendingPool internal _pool; + address internal _underlyingAsset; + IAaveIncentivesController internal _incentivesController; + + /** + * @dev Initializes the debt token. + * @param pool The address of the lending pool where this aToken will be used + * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH) + * @param incentivesController The smart contract managing potential incentives distribution + * @param debtTokenDecimals The decimals of the debtToken, same as the underlying asset's + * @param debtTokenName The name of the token + * @param debtTokenSymbol The symbol of the token + */ + function initialize( + ILendingPool pool, + address underlyingAsset, + IAaveIncentivesController incentivesController, + uint8 debtTokenDecimals, + string memory debtTokenName, + string memory debtTokenSymbol, + bytes calldata params + ) public override initializer { + _setName(debtTokenName); + _setSymbol(debtTokenSymbol); + _setDecimals(debtTokenDecimals); + + _pool = pool; + _underlyingAsset = underlyingAsset; + _incentivesController = incentivesController; + + emit Initialized( + underlyingAsset, + address(pool), + address(incentivesController), + debtTokenDecimals, + debtTokenName, + debtTokenSymbol, + params + ); + } + + /** + * @dev Gets the revision of the stable debt token implementation + * @return The debt token implementation revision + **/ + function getRevision() internal pure virtual override returns (uint256) { + return DEBT_TOKEN_REVISION; + } + + /** + * @dev Returns the average stable rate across all the stable rate debt + * @return the average stable rate + **/ + function getAverageStableRate() external view virtual override returns (uint256) { + return _avgStableRate; + } + + /** + * @dev Returns the timestamp of the last user action + * @return The last update timestamp + **/ + function getUserLastUpdated(address user) external view virtual override returns (uint40) { + return _timestamps[user]; + } + + /** + * @dev Returns the stable rate of the user + * @param user The address of the user + * @return The stable rate of user + **/ + function getUserStableRate(address user) external view virtual override returns (uint256) { + return _usersStableRate[user]; + } + + /** + * @dev Calculates the current user debt balance + * @return The accumulated debt of the user + **/ + function balanceOf(address account) public view virtual override returns (uint256) { + uint256 accountBalance = super.balanceOf(account); + uint256 stableRate = _usersStableRate[account]; + if (accountBalance == 0) { + return 0; + } + uint256 cumulatedInterest = + MathUtils.calculateCompoundedInterest(stableRate, _timestamps[account]); + return accountBalance.rayMul(cumulatedInterest); + } + + struct MintLocalVars { + uint256 previousSupply; + uint256 nextSupply; + uint256 amountInRay; + uint256 newStableRate; + uint256 currentAvgStableRate; + } + + /** + * @dev Mints debt token to the `onBehalfOf` address. + * - Only callable by the LendingPool + * - The resulting rate is the weighted average between the rate of the new debt + * and the rate of the previous debt + * @param user The address receiving the borrowed underlying, being the delegatee in case + * of credit delegate, or same as `onBehalfOf` otherwise + * @param onBehalfOf The address receiving the debt tokens + * @param amount The amount of debt tokens to mint + * @param rate The rate of the debt being minted + **/ + function mint( + address user, + address onBehalfOf, + uint256 amount, + uint256 rate + ) external override onlyLendingPool returns (bool) { + MintLocalVars memory vars; + + if (user != onBehalfOf) { + _decreaseBorrowAllowance(onBehalfOf, user, amount); + } + + (, uint256 currentBalance, uint256 balanceIncrease) = _calculateBalanceIncrease(onBehalfOf); + + vars.previousSupply = totalSupply(); + vars.currentAvgStableRate = _avgStableRate; + vars.nextSupply = _totalSupply = vars.previousSupply.add(amount); + + vars.amountInRay = amount.wadToRay(); + + vars.newStableRate = _usersStableRate[onBehalfOf] + .rayMul(currentBalance.wadToRay()) + .add(vars.amountInRay.rayMul(rate)) + .rayDiv(currentBalance.add(amount).wadToRay()); + + require(vars.newStableRate <= type(uint128).max, Errors.SDT_STABLE_DEBT_OVERFLOW); + _usersStableRate[onBehalfOf] = vars.newStableRate; + + //solium-disable-next-line + _totalSupplyTimestamp = _timestamps[onBehalfOf] = uint40(block.timestamp); + + // Calculates the updated average stable rate + vars.currentAvgStableRate = _avgStableRate = vars + .currentAvgStableRate + .rayMul(vars.previousSupply.wadToRay()) + .add(rate.rayMul(vars.amountInRay)) + .rayDiv(vars.nextSupply.wadToRay()); + + _mint(onBehalfOf, amount.add(balanceIncrease), vars.previousSupply); + + emit Transfer(address(0), onBehalfOf, amount); + + emit Mint( + user, + onBehalfOf, + amount, + currentBalance, + balanceIncrease, + vars.newStableRate, + vars.currentAvgStableRate, + vars.nextSupply + ); + + return currentBalance == 0; + } + + /** + * @dev Burns debt of `user` + * @param user The address of the user getting his debt burned + * @param amount The amount of debt tokens getting burned + **/ + function burn(address user, uint256 amount) external override onlyLendingPool { + (, uint256 currentBalance, uint256 balanceIncrease) = _calculateBalanceIncrease(user); + + uint256 previousSupply = totalSupply(); + uint256 newAvgStableRate = 0; + uint256 nextSupply = 0; + uint256 userStableRate = _usersStableRate[user]; + + // Since the total supply and each single user debt accrue separately, + // there might be accumulation errors so that the last borrower repaying + // mght actually try to repay more than the available debt supply. + // In this case we simply set the total supply and the avg stable rate to 0 + if (previousSupply <= amount) { + _avgStableRate = 0; + _totalSupply = 0; + } else { + nextSupply = _totalSupply = previousSupply.sub(amount); + uint256 firstTerm = _avgStableRate.rayMul(previousSupply.wadToRay()); + uint256 secondTerm = userStableRate.rayMul(amount.wadToRay()); + + // For the same reason described above, when the last user is repaying it might + // happen that user rate * user balance > avg rate * total supply. In that case, + // we simply set the avg rate to 0 + if (secondTerm >= firstTerm) { + newAvgStableRate = _avgStableRate = _totalSupply = 0; + } else { + newAvgStableRate = _avgStableRate = firstTerm.sub(secondTerm).rayDiv(nextSupply.wadToRay()); + } + } + + if (amount == currentBalance) { + _usersStableRate[user] = 0; + _timestamps[user] = 0; + } else { + //solium-disable-next-line + _timestamps[user] = uint40(block.timestamp); + } + //solium-disable-next-line + _totalSupplyTimestamp = uint40(block.timestamp); + + if (balanceIncrease > amount) { + uint256 amountToMint = balanceIncrease.sub(amount); + _mint(user, amountToMint, previousSupply); + emit Mint( + user, + user, + amountToMint, + currentBalance, + balanceIncrease, + userStableRate, + newAvgStableRate, + nextSupply + ); + } else { + uint256 amountToBurn = amount.sub(balanceIncrease); + _burn(user, amountToBurn, previousSupply); + emit Burn(user, amountToBurn, currentBalance, balanceIncrease, newAvgStableRate, nextSupply); + } + + emit Transfer(user, address(0), amount); + } + + /** + * @dev Calculates the increase in balance since the last user interaction + * @param user The address of the user for which the interest is being accumulated + * @return The previous principal balance, the new principal balance and the balance increase + **/ + function _calculateBalanceIncrease(address user) + internal + view + returns ( + uint256, + uint256, + uint256 + ) + { + uint256 previousPrincipalBalance = super.balanceOf(user); + + if (previousPrincipalBalance == 0) { + return (0, 0, 0); + } + + // Calculation of the accrued interest since the last accumulation + uint256 balanceIncrease = balanceOf(user).sub(previousPrincipalBalance); + + return ( + previousPrincipalBalance, + previousPrincipalBalance.add(balanceIncrease), + balanceIncrease + ); + } + + /** + * @dev Returns the principal and total supply, the average borrow rate and the last supply update timestamp + **/ + function getSupplyData() + public + view + override + returns ( + uint256, + uint256, + uint256, + uint40 + ) + { + uint256 avgRate = _avgStableRate; + return (super.totalSupply(), _calcTotalSupply(avgRate), avgRate, _totalSupplyTimestamp); + } + + /** + * @dev Returns the the total supply and the average stable rate + **/ + function getTotalSupplyAndAvgRate() public view override returns (uint256, uint256) { + uint256 avgRate = _avgStableRate; + return (_calcTotalSupply(avgRate), avgRate); + } + + /** + * @dev Returns the total supply + **/ + function totalSupply() public view override returns (uint256) { + return _calcTotalSupply(_avgStableRate); + } + + /** + * @dev Returns the timestamp at which the total supply was updated + **/ + function getTotalSupplyLastUpdated() public view override returns (uint40) { + return _totalSupplyTimestamp; + } + + /** + * @dev Returns the principal debt balance of the user from + * @param user The user's address + * @return The debt balance of the user since the last burn/mint action + **/ + function principalBalanceOf(address user) external view virtual override returns (uint256) { + return super.balanceOf(user); + } + + /** + * @dev Returns the address of the underlying asset of this aToken (E.g. WETH for aWETH) + **/ + function UNDERLYING_ASSET_ADDRESS() public view returns (address) { + return _underlyingAsset; + } + + /** + * @dev Returns the address of the lending pool where this aToken is used + **/ + function POOL() public view returns (ILendingPool) { + return _pool; + } + + /** + * @dev Returns the address of the incentives controller contract + **/ + function getIncentivesController() external view override returns (IAaveIncentivesController) { + return _getIncentivesController(); + } + + /** + * @dev For internal usage in the logic of the parent contracts + **/ + function _getIncentivesController() internal view override returns (IAaveIncentivesController) { + return _incentivesController; + } + + /** + * @dev For internal usage in the logic of the parent contracts + **/ + function _getUnderlyingAssetAddress() internal view override returns (address) { + return _underlyingAsset; + } + + /** + * @dev For internal usage in the logic of the parent contracts + **/ + function _getLendingPool() internal view override returns (ILendingPool) { + return _pool; + } + + /** + * @dev Calculates the total supply + * @param avgRate The average rate at which the total supply increases + * @return The debt balance of the user since the last burn/mint action + **/ + function _calcTotalSupply(uint256 avgRate) internal view virtual returns (uint256) { + uint256 principalSupply = super.totalSupply(); + + if (principalSupply == 0) { + return 0; + } + + uint256 cumulatedInterest = + MathUtils.calculateCompoundedInterest(avgRate, _totalSupplyTimestamp); + + return principalSupply.rayMul(cumulatedInterest); + } + + /** + * @dev Mints stable debt tokens to an user + * @param account The account receiving the debt tokens + * @param amount The amount being minted + * @param oldTotalSupply the total supply before the minting event + **/ + function _mint( + address account, + uint256 amount, + uint256 oldTotalSupply + ) internal { + uint256 oldAccountBalance = _balances[account]; + _balances[account] = oldAccountBalance.add(amount); + + if (address(_incentivesController) != address(0)) { + _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance); + } + } + + /** + * @dev Burns stable debt tokens of an user + * @param account The user getting his debt burned + * @param amount The amount being burned + * @param oldTotalSupply The total supply before the burning event + **/ + function _burn( + address account, + uint256 amount, + uint256 oldTotalSupply + ) internal { + uint256 oldAccountBalance = _balances[account]; + _balances[account] = oldAccountBalance.sub(amount, Errors.SDT_BURN_EXCEEDS_BALANCE); + + if (address(_incentivesController) != address(0)) { + _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance); + } + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/VariableDebtToken.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/VariableDebtToken.sol new file mode 100644 index 000000000..70833810f --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/VariableDebtToken.sol @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {IVariableDebtToken} from '../../interfaces/IVariableDebtToken.sol'; +import {WadRayMath} from '../libraries/math/WadRayMath.sol'; +import {Errors} from '../libraries/helpers/Errors.sol'; +import {DebtTokenBase} from './base/DebtTokenBase.sol'; +import {ILendingPool} from '../../interfaces/ILendingPool.sol'; +import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol'; + +/** + * @title VariableDebtToken + * @notice Implements a variable debt token to track the borrowing positions of users + * at variable rate mode + * @author Aave + **/ +contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { + using WadRayMath for uint256; + + uint256 public constant DEBT_TOKEN_REVISION = 0x1; + + ILendingPool internal _pool; + address internal _underlyingAsset; + IAaveIncentivesController internal _incentivesController; + + /** + * @dev Initializes the debt token. + * @param pool The address of the lending pool where this aToken will be used + * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH) + * @param incentivesController The smart contract managing potential incentives distribution + * @param debtTokenDecimals The decimals of the debtToken, same as the underlying asset's + * @param debtTokenName The name of the token + * @param debtTokenSymbol The symbol of the token + */ + function initialize( + ILendingPool pool, + address underlyingAsset, + IAaveIncentivesController incentivesController, + uint8 debtTokenDecimals, + string memory debtTokenName, + string memory debtTokenSymbol, + bytes calldata params + ) public override initializer { + _setName(debtTokenName); + _setSymbol(debtTokenSymbol); + _setDecimals(debtTokenDecimals); + + _pool = pool; + _underlyingAsset = underlyingAsset; + _incentivesController = incentivesController; + + emit Initialized( + underlyingAsset, + address(pool), + address(incentivesController), + debtTokenDecimals, + debtTokenName, + debtTokenSymbol, + params + ); + } + + /** + * @dev Gets the revision of the stable debt token implementation + * @return The debt token implementation revision + **/ + function getRevision() internal pure virtual override returns (uint256) { + return DEBT_TOKEN_REVISION; + } + + /** + * @dev Calculates the accumulated debt balance of the user + * @return The debt balance of the user + **/ + function balanceOf(address user) public view virtual override returns (uint256) { + uint256 scaledBalance = super.balanceOf(user); + + if (scaledBalance == 0) { + return 0; + } + + return scaledBalance.rayMul(_pool.getReserveNormalizedVariableDebt(_underlyingAsset)); + } + + /** + * @dev Mints debt token to the `onBehalfOf` address + * - Only callable by the LendingPool + * @param user The address receiving the borrowed underlying, being the delegatee in case + * of credit delegate, or same as `onBehalfOf` otherwise + * @param onBehalfOf The address receiving the debt tokens + * @param amount The amount of debt being minted + * @param index The variable debt index of the reserve + * @return `true` if the the previous balance of the user is 0 + **/ + function mint( + address user, + address onBehalfOf, + uint256 amount, + uint256 index + ) external override onlyLendingPool returns (bool) { + if (user != onBehalfOf) { + _decreaseBorrowAllowance(onBehalfOf, user, amount); + } + + uint256 previousBalance = super.balanceOf(onBehalfOf); + uint256 amountScaled = amount.rayDiv(index); + require(amountScaled != 0, Errors.CT_INVALID_MINT_AMOUNT); + + _mint(onBehalfOf, amountScaled); + + emit Transfer(address(0), onBehalfOf, amount); + emit Mint(user, onBehalfOf, amount, index); + + return previousBalance == 0; + } + + /** + * @dev Burns user variable debt + * - Only callable by the LendingPool + * @param user The user whose debt is getting burned + * @param amount The amount getting burned + * @param index The variable debt index of the reserve + **/ + function burn( + address user, + uint256 amount, + uint256 index + ) external override onlyLendingPool { + uint256 amountScaled = amount.rayDiv(index); + require(amountScaled != 0, Errors.CT_INVALID_BURN_AMOUNT); + + _burn(user, amountScaled); + + emit Transfer(user, address(0), amount); + emit Burn(user, amount, index); + } + + /** + * @dev Returns the principal debt balance of the user from + * @return The debt balance of the user since the last burn/mint action + **/ + function scaledBalanceOf(address user) public view virtual override returns (uint256) { + return super.balanceOf(user); + } + + /** + * @dev Returns the total supply of the variable debt token. Represents the total debt accrued by the users + * @return The total supply + **/ + function totalSupply() public view virtual override returns (uint256) { + return super.totalSupply().rayMul(_pool.getReserveNormalizedVariableDebt(_underlyingAsset)); + } + + /** + * @dev Returns the scaled total supply of the variable debt token. Represents sum(debt/index) + * @return the scaled total supply + **/ + function scaledTotalSupply() public view virtual override returns (uint256) { + return super.totalSupply(); + } + + /** + * @dev Returns the principal balance of the user and principal total supply. + * @param user The address of the user + * @return The principal balance of the user + * @return The principal total supply + **/ + function getScaledUserBalanceAndSupply(address user) + external + view + override + returns (uint256, uint256) + { + return (super.balanceOf(user), super.totalSupply()); + } + + /** + * @dev Returns the address of the underlying asset of this aToken (E.g. WETH for aWETH) + **/ + function UNDERLYING_ASSET_ADDRESS() public view returns (address) { + return _underlyingAsset; + } + + /** + * @dev Returns the address of the incentives controller contract + **/ + function getIncentivesController() external view override returns (IAaveIncentivesController) { + return _getIncentivesController(); + } + + /** + * @dev Returns the address of the lending pool where this aToken is used + **/ + function POOL() public view returns (ILendingPool) { + return _pool; + } + + function _getIncentivesController() internal view override returns (IAaveIncentivesController) { + return _incentivesController; + } + + function _getUnderlyingAssetAddress() internal view override returns (address) { + return _underlyingAsset; + } + + function _getLendingPool() internal view override returns (ILendingPool) { + return _pool; + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/base/DebtTokenBase.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/base/DebtTokenBase.sol new file mode 100644 index 000000000..953b9c55a --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/contracts/protocol/tokenization/base/DebtTokenBase.sol @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {ILendingPool} from '../../../interfaces/ILendingPool.sol'; +import {ICreditDelegationToken} from '../../../interfaces/ICreditDelegationToken.sol'; +import { + VersionedInitializable +} from '../../libraries/aave-upgradeability/VersionedInitializable.sol'; +import {IncentivizedERC20} from '../IncentivizedERC20.sol'; +import {Errors} from '../../libraries/helpers/Errors.sol'; + +/** + * @title DebtTokenBase + * @notice Base contract for different types of debt tokens, like StableDebtToken or VariableDebtToken + * @author Aave + */ + +abstract contract DebtTokenBase is + IncentivizedERC20('DEBTTOKEN_IMPL', 'DEBTTOKEN_IMPL', 0), + VersionedInitializable, + ICreditDelegationToken +{ + mapping(address => mapping(address => uint256)) internal _borrowAllowances; + + /** + * @dev Only lending pool can call functions marked by this modifier + **/ + modifier onlyLendingPool { + require(_msgSender() == address(_getLendingPool()), Errors.CT_CALLER_MUST_BE_LENDING_POOL); + _; + } + + /** + * @dev delegates borrowing power to a user on the specific debt token + * @param delegatee the address receiving the delegated borrowing power + * @param amount the maximum amount being delegated. Delegation will still + * respect the liquidation constraints (even if delegated, a delegatee cannot + * force a delegator HF to go below 1) + **/ + function approveDelegation(address delegatee, uint256 amount) external override { + _borrowAllowances[_msgSender()][delegatee] = amount; + emit BorrowAllowanceDelegated(_msgSender(), delegatee, _getUnderlyingAssetAddress(), amount); + } + + /** + * @dev returns the borrow allowance of the user + * @param fromUser The user to giving allowance + * @param toUser The user to give allowance to + * @return the current allowance of toUser + **/ + function borrowAllowance(address fromUser, address toUser) + external + view + override + returns (uint256) + { + return _borrowAllowances[fromUser][toUser]; + } + + /** + * @dev Being non transferrable, the debt token does not implement any of the + * standard ERC20 functions for transfer and allowance. + **/ + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { + recipient; + amount; + revert('TRANSFER_NOT_SUPPORTED'); + } + + function allowance(address owner, address spender) + public + view + virtual + override + returns (uint256) + { + owner; + spender; + revert('ALLOWANCE_NOT_SUPPORTED'); + } + + function approve(address spender, uint256 amount) public virtual override returns (bool) { + spender; + amount; + revert('APPROVAL_NOT_SUPPORTED'); + } + + function transferFrom( + address sender, + address recipient, + uint256 amount + ) public virtual override returns (bool) { + sender; + recipient; + amount; + revert('TRANSFER_NOT_SUPPORTED'); + } + + function increaseAllowance(address spender, uint256 addedValue) + public + virtual + override + returns (bool) + { + spender; + addedValue; + revert('ALLOWANCE_NOT_SUPPORTED'); + } + + function decreaseAllowance(address spender, uint256 subtractedValue) + public + virtual + override + returns (bool) + { + spender; + subtractedValue; + revert('ALLOWANCE_NOT_SUPPORTED'); + } + + function _decreaseBorrowAllowance( + address delegator, + address delegatee, + uint256 amount + ) internal { + uint256 newAllowance = + _borrowAllowances[delegator][delegatee].sub(amount, Errors.BORROW_ALLOWANCE_NOT_ENOUGH); + + _borrowAllowances[delegator][delegatee] = newAllowance; + + emit BorrowAllowanceDelegated(delegator, delegatee, _getUnderlyingAssetAddress(), newAllowance); + } + + function _getUnderlyingAssetAddress() internal view virtual returns (address); + + function _getLendingPool() internal view virtual returns (ILendingPool); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/hardhat/console.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/hardhat/console.sol new file mode 100644 index 000000000..d65e3b412 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/complete_sources/hardhat/console.sol @@ -0,0 +1,1532 @@ +// SPDX-License-Identifier: MIT +pragma solidity >= 0.4.22 <0.9.0; + +library console { + address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67); + + function _sendLogPayload(bytes memory payload) private view { + uint256 payloadLength = payload.length; + address consoleAddress = CONSOLE_ADDRESS; + assembly { + let payloadStart := add(payload, 32) + let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0) + } + } + + function log() internal view { + _sendLogPayload(abi.encodeWithSignature("log()")); + } + + function logInt(int p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(int)", p0)); + } + + function logUint(uint p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); + } + + function logString(string memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); + } + + function logBool(bool p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); + } + + function logAddress(address p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); + } + + function logBytes(bytes memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0)); + } + + function logBytes1(bytes1 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0)); + } + + function logBytes2(bytes2 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0)); + } + + function logBytes3(bytes3 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0)); + } + + function logBytes4(bytes4 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0)); + } + + function logBytes5(bytes5 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0)); + } + + function logBytes6(bytes6 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0)); + } + + function logBytes7(bytes7 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0)); + } + + function logBytes8(bytes8 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0)); + } + + function logBytes9(bytes9 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0)); + } + + function logBytes10(bytes10 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0)); + } + + function logBytes11(bytes11 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0)); + } + + function logBytes12(bytes12 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0)); + } + + function logBytes13(bytes13 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0)); + } + + function logBytes14(bytes14 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0)); + } + + function logBytes15(bytes15 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0)); + } + + function logBytes16(bytes16 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0)); + } + + function logBytes17(bytes17 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0)); + } + + function logBytes18(bytes18 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0)); + } + + function logBytes19(bytes19 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0)); + } + + function logBytes20(bytes20 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0)); + } + + function logBytes21(bytes21 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0)); + } + + function logBytes22(bytes22 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0)); + } + + function logBytes23(bytes23 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0)); + } + + function logBytes24(bytes24 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0)); + } + + function logBytes25(bytes25 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0)); + } + + function logBytes26(bytes26 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0)); + } + + function logBytes27(bytes27 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0)); + } + + function logBytes28(bytes28 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0)); + } + + function logBytes29(bytes29 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0)); + } + + function logBytes30(bytes30 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0)); + } + + function logBytes31(bytes31 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0)); + } + + function logBytes32(bytes32 p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0)); + } + + function log(uint p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); + } + + function log(string memory p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); + } + + function log(bool p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); + } + + function log(address p0) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); + } + + function log(uint p0, uint p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1)); + } + + function log(uint p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1)); + } + + function log(uint p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1)); + } + + function log(uint p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1)); + } + + function log(string memory p0, uint p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1)); + } + + function log(string memory p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1)); + } + + function log(string memory p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); + } + + function log(string memory p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); + } + + function log(bool p0, uint p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1)); + } + + function log(bool p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1)); + } + + function log(bool p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1)); + } + + function log(bool p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1)); + } + + function log(address p0, uint p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1)); + } + + function log(address p0, string memory p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1)); + } + + function log(address p0, bool p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1)); + } + + function log(address p0, address p1) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1)); + } + + function log(uint p0, uint p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2)); + } + + function log(uint p0, uint p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2)); + } + + function log(uint p0, uint p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2)); + } + + function log(uint p0, uint p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2)); + } + + function log(uint p0, string memory p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2)); + } + + function log(uint p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2)); + } + + function log(uint p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2)); + } + + function log(uint p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2)); + } + + function log(uint p0, bool p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2)); + } + + function log(uint p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2)); + } + + function log(uint p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2)); + } + + function log(uint p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2)); + } + + function log(uint p0, address p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2)); + } + + function log(uint p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2)); + } + + function log(uint p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2)); + } + + function log(uint p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2)); + } + + function log(string memory p0, uint p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2)); + } + + function log(string memory p0, uint p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2)); + } + + function log(string memory p0, uint p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2)); + } + + function log(string memory p0, uint p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2)); + } + + function log(string memory p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2)); + } + + function log(string memory p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2)); + } + + function log(string memory p0, address p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2)); + } + + function log(string memory p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2)); + } + + function log(string memory p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2)); + } + + function log(string memory p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2)); + } + + function log(bool p0, uint p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2)); + } + + function log(bool p0, uint p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2)); + } + + function log(bool p0, uint p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2)); + } + + function log(bool p0, uint p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2)); + } + + function log(bool p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2)); + } + + function log(bool p0, bool p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2)); + } + + function log(bool p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2)); + } + + function log(bool p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2)); + } + + function log(bool p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2)); + } + + function log(bool p0, address p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2)); + } + + function log(bool p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2)); + } + + function log(bool p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2)); + } + + function log(bool p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2)); + } + + function log(address p0, uint p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2)); + } + + function log(address p0, uint p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2)); + } + + function log(address p0, uint p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2)); + } + + function log(address p0, uint p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2)); + } + + function log(address p0, string memory p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2)); + } + + function log(address p0, string memory p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2)); + } + + function log(address p0, string memory p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2)); + } + + function log(address p0, string memory p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2)); + } + + function log(address p0, bool p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2)); + } + + function log(address p0, bool p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2)); + } + + function log(address p0, bool p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2)); + } + + function log(address p0, bool p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2)); + } + + function log(address p0, address p1, uint p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2)); + } + + function log(address p0, address p1, string memory p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2)); + } + + function log(address p0, address p1, bool p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2)); + } + + function log(address p0, address p1, address p2) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2)); + } + + function log(uint p0, uint p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, uint p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3)); + } + + function log(uint p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, uint p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3)); + } + + function log(string memory p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, uint p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3)); + } + + function log(bool p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, uint p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, string memory p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, bool p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, uint p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, string memory p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, bool p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, uint p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, string memory p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, bool p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3)); + } + + function log(address p0, address p1, address p2, address p3) internal view { + _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3)); + } + +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/metadata.json b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/metadata.json new file mode 100644 index 000000000..f33e00111 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/metadata.json @@ -0,0 +1,1317 @@ +{ + "compiler": { "version": "0.6.12+commit.27d51765" }, + "language": "Solidity", + "output": { + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "reserve", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "onBehalfOf", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "borrowRateMode", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "borrowRate", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint16", + "name": "referral", + "type": "uint16" + } + ], + "name": "Borrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "reserve", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "onBehalfOf", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint16", + "name": "referral", + "type": "uint16" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "initiator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "premium", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "referralCode", + "type": "uint16" + } + ], + "name": "FlashLoan", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "collateralAsset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "debtAsset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "debtToCover", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "liquidatedCollateralAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "liquidator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "receiveAToken", + "type": "bool" + } + ], + "name": "LiquidationCall", + "type": "event" + }, + { "anonymous": false, "inputs": [], "name": "Paused", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "reserve", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "RebalanceStableBorrowRate", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "reserve", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "repayer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Repay", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "reserve", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "liquidityRate", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "stableBorrowRate", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "variableBorrowRate", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "liquidityIndex", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "variableBorrowIndex", + "type": "uint256" + } + ], + "name": "ReserveDataUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "reserve", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "ReserveUsedAsCollateralDisabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "reserve", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "ReserveUsedAsCollateralEnabled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "reserve", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "rateMode", + "type": "uint256" + } + ], + "name": "Swap", + "type": "event" + }, + { "anonymous": false, "inputs": [], "name": "Unpaused", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "reserve", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Withdraw", + "type": "event" + }, + { + "inputs": [], + "name": "FLASHLOAN_PREMIUM_TOTAL", + "outputs": [ + { "internalType": "uint256", "name": "", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "LENDINGPOOL_REVISION", + "outputs": [ + { "internalType": "uint256", "name": "", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_NUMBER_RESERVES", + "outputs": [ + { "internalType": "uint256", "name": "", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_STABLE_RATE_BORROW_SIZE_PERCENT", + "outputs": [ + { "internalType": "uint256", "name": "", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { + "internalType": "uint256", + "name": "interestRateMode", + "type": "uint256" + }, + { + "internalType": "uint16", + "name": "referralCode", + "type": "uint16" + }, + { "internalType": "address", "name": "onBehalfOf", "type": "address" } + ], + "name": "borrow", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { + "internalType": "address", + "name": "onBehalfOf", + "type": "address" + }, + { "internalType": "uint16", "name": "referralCode", "type": "uint16" } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { + "internalType": "uint256", + "name": "balanceFromBefore", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "balanceToBefore", + "type": "uint256" + } + ], + "name": "finalizeTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiverAddress", + "type": "address" + }, + { + "internalType": "address[]", + "name": "assets", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { "internalType": "uint256[]", "name": "modes", "type": "uint256[]" }, + { + "internalType": "address", + "name": "onBehalfOf", + "type": "address" + }, + { "internalType": "bytes", "name": "params", "type": "bytes" }, + { "internalType": "uint16", "name": "referralCode", "type": "uint16" } + ], + "name": "flashLoan", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getAddressesProvider", + "outputs": [ + { + "internalType": "contract ILendingPoolAddressesProvider", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset", "type": "address" } + ], + "name": "getConfiguration", + "outputs": [ + { + "components": [ + { "internalType": "uint256", "name": "data", "type": "uint256" } + ], + "internalType": "struct DataTypes.ReserveConfigurationMap", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset", "type": "address" } + ], + "name": "getReserveData", + "outputs": [ + { + "components": [ + { + "components": [ + { + "internalType": "uint256", + "name": "data", + "type": "uint256" + } + ], + "internalType": "struct DataTypes.ReserveConfigurationMap", + "name": "configuration", + "type": "tuple" + }, + { + "internalType": "uint128", + "name": "liquidityIndex", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "variableBorrowIndex", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "currentLiquidityRate", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "currentVariableBorrowRate", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "currentStableBorrowRate", + "type": "uint128" + }, + { + "internalType": "uint40", + "name": "lastUpdateTimestamp", + "type": "uint40" + }, + { + "internalType": "address", + "name": "aTokenAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "stableDebtTokenAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "variableDebtTokenAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "interestRateStrategyAddress", + "type": "address" + }, + { "internalType": "uint8", "name": "id", "type": "uint8" } + ], + "internalType": "struct DataTypes.ReserveData", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset", "type": "address" } + ], + "name": "getReserveNormalizedIncome", + "outputs": [ + { "internalType": "uint256", "name": "", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset", "type": "address" } + ], + "name": "getReserveNormalizedVariableDebt", + "outputs": [ + { "internalType": "uint256", "name": "", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getReservesList", + "outputs": [ + { "internalType": "address[]", "name": "", "type": "address[]" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "getUserAccountData", + "outputs": [ + { + "internalType": "uint256", + "name": "totalCollateralETH", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalDebtETH", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "availableBorrowsETH", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "currentLiquidationThreshold", + "type": "uint256" + }, + { "internalType": "uint256", "name": "ltv", "type": "uint256" }, + { + "internalType": "uint256", + "name": "healthFactor", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "getUserConfiguration", + "outputs": [ + { + "components": [ + { "internalType": "uint256", "name": "data", "type": "uint256" } + ], + "internalType": "struct DataTypes.UserConfigurationMap", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { + "internalType": "address", + "name": "aTokenAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "stableDebtAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "variableDebtAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "interestRateStrategyAddress", + "type": "address" + } + ], + "name": "initReserve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract ILendingPoolAddressesProvider", + "name": "provider", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "collateralAsset", + "type": "address" + }, + { "internalType": "address", "name": "debtAsset", "type": "address" }, + { "internalType": "address", "name": "user", "type": "address" }, + { + "internalType": "uint256", + "name": "debtToCover", + "type": "uint256" + }, + { "internalType": "bool", "name": "receiveAToken", "type": "bool" } + ], + "name": "liquidationCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "rebalanceStableBorrowRate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "uint256", "name": "rateMode", "type": "uint256" }, + { "internalType": "address", "name": "onBehalfOf", "type": "address" } + ], + "name": "repay", + "outputs": [ + { "internalType": "uint256", "name": "", "type": "uint256" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { + "internalType": "uint256", + "name": "configuration", + "type": "uint256" + } + ], + "name": "setConfiguration", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "bool", "name": "val", "type": "bool" }], + "name": "setPause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { + "internalType": "address", + "name": "rateStrategyAddress", + "type": "address" + } + ], + "name": "setReserveInterestRateStrategyAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "bool", "name": "useAsCollateral", "type": "bool" } + ], + "name": "setUserUseReserveAsCollateral", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "uint256", "name": "rateMode", "type": "uint256" } + ], + "name": "swapBorrowRateMode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "address", "name": "to", "type": "address" } + ], + "name": "withdraw", + "outputs": [ + { "internalType": "uint256", "name": "", "type": "uint256" } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "devdoc": { + "author": "Aave*", + "details": "Main point of interaction with an Aave protocol's market - Users can: # Deposit # Withdraw # Borrow # Repay # Swap their loans between variable and stable rate # Enable/disable their deposits as collateral rebalance stable rate borrow positions # Liquidate positions # Execute Flash Loans - To be covered by a proxy contract, owned by the LendingPoolAddressesProvider of the specific market - All admin functions are callable by the LendingPoolConfigurator contract defined also in the LendingPoolAddressesProvider", + "kind": "dev", + "methods": { + "FLASHLOAN_PREMIUM_TOTAL()": { + "details": "Returns the fee on flash loans " + }, + "MAX_NUMBER_RESERVES()": { + "details": "Returns the maximum number of reserves supported to be listed in this LendingPool" + }, + "MAX_STABLE_RATE_BORROW_SIZE_PERCENT()": { + "details": "Returns the percentage of available liquidity that can be borrowed at once at stable rate" + }, + "borrow(address,uint256,uint256,uint16,address)": { + "details": "Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower already deposited enough collateral, or he was given enough allowance by a credit delegator on the corresponding debt token (StableDebtToken or VariableDebtToken) - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet and 100 stable/variable debt tokens, depending on the `interestRateMode`", + "params": { + "amount": "The amount to be borrowed", + "asset": "The address of the underlying asset to borrow", + "interestRateMode": "The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable", + "onBehalfOf": "Address of the user who will receive the debt. Should be the address of the borrower itself calling the function if he wants to borrow against his own collateral, or the address of the credit delegator if he has been given credit delegation allowance*", + "referralCode": "Code used to register the integrator originating the operation, for potential rewards. 0 if the action is executed directly by the user, without any middle-man" + } + }, + "deposit(address,uint256,address,uint16)": { + "details": "Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens. - E.g. User deposits 100 USDC and gets in return 100 aUSDC", + "params": { + "amount": "The amount to be deposited", + "asset": "The address of the underlying asset to deposit", + "onBehalfOf": "The address that will receive the aTokens, same as msg.sender if the user wants to receive them on his own wallet, or a different address if the beneficiary of aTokens is a different wallet", + "referralCode": "Code used to register the integrator originating the operation, for potential rewards. 0 if the action is executed directly by the user, without any middle-man*" + } + }, + "finalizeTransfer(address,address,address,uint256,uint256,uint256)": { + "details": "Validates and finalizes an aToken transfer - Only callable by the overlying aToken of the `asset`", + "params": { + "amount": "The amount being transferred/withdrawn", + "asset": "The address of the underlying asset of the aToken", + "balanceFromBefore": "The aToken balance of the `from` user before the transfer", + "balanceToBefore": "The aToken balance of the `to` user before the transfer", + "from": "The user from which the aTokens are transferred", + "to": "The user receiving the aTokens" + } + }, + "flashLoan(address,address[],uint256[],uint256[],address,bytes,uint16)": { + "details": "Allows smartcontracts to access the liquidity of the pool within one transaction, as long as the amount taken plus a fee is returned. IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration. For further details please visit https://developers.aave.com", + "params": { + "amounts": "The amounts amounts being flash-borrowed", + "assets": "The addresses of the assets being flash-borrowed", + "modes": "Types of the debt to open if the flash loan is not returned: 0 -> Don't open any debt, just revert if funds can't be transferred from the receiver 1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address 2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address", + "onBehalfOf": "The address that will receive the debt in the case of using on `modes` 1 or 2", + "params": "Variadic packed params to pass to the receiver as extra information", + "receiverAddress": "The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface", + "referralCode": "Code used to register the integrator originating the operation, for potential rewards. 0 if the action is executed directly by the user, without any middle-man*" + } + }, + "getAddressesProvider()": { + "details": "Returns the cached LendingPoolAddressesProvider connected to this contract*" + }, + "getConfiguration(address)": { + "details": "Returns the configuration of the reserve", + "params": { + "asset": "The address of the underlying asset of the reserve" + }, + "returns": { "_0": "The configuration of the reserve*" } + }, + "getReserveData(address)": { + "details": "Returns the state and configuration of the reserve", + "params": { + "asset": "The address of the underlying asset of the reserve" + }, + "returns": { "_0": "The state of the reserve*" } + }, + "getReserveNormalizedIncome(address)": { + "details": "Returns the normalized income per unit of asset", + "params": { + "asset": "The address of the underlying asset of the reserve" + }, + "returns": { "_0": "The reserve's normalized income" } + }, + "getReserveNormalizedVariableDebt(address)": { + "details": "Returns the normalized variable debt per unit of asset", + "params": { + "asset": "The address of the underlying asset of the reserve" + }, + "returns": { "_0": "The reserve normalized variable debt" } + }, + "getReservesList()": { + "details": "Returns the list of the initialized reserves*" + }, + "getUserAccountData(address)": { + "details": "Returns the user account data across all the reserves", + "params": { "user": "The address of the user" }, + "returns": { + "availableBorrowsETH": "the borrowing power left of the user", + "currentLiquidationThreshold": "the liquidation threshold of the user", + "healthFactor": "the current health factor of the user*", + "ltv": "the loan to value of the user", + "totalCollateralETH": "the total collateral in ETH of the user", + "totalDebtETH": "the total debt in ETH of the user" + } + }, + "getUserConfiguration(address)": { + "details": "Returns the configuration of the user across all the reserves", + "params": { "user": "The user address" }, + "returns": { "_0": "The configuration of the user*" } + }, + "initReserve(address,address,address,address,address)": { + "details": "Initializes a reserve, activating it, assigning an aToken and debt tokens and an interest rate strategy - Only callable by the LendingPoolConfigurator contract", + "params": { + "aTokenAddress": "The address of the VariableDebtToken that will be assigned to the reserve", + "asset": "The address of the underlying asset of the reserve", + "interestRateStrategyAddress": "The address of the interest rate strategy contract*", + "stableDebtAddress": "The address of the StableDebtToken that will be assigned to the reserve" + } + }, + "initialize(address)": { + "details": "Function is invoked by the proxy contract when the LendingPool contract is added to the LendingPoolAddressesProvider of the market. - Caching the address of the LendingPoolAddressesProvider in order to reduce gas consumption on subsequent operations", + "params": { + "provider": "The address of the LendingPoolAddressesProvider*" + } + }, + "liquidationCall(address,address,address,uint256,bool)": { + "details": "Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1 - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives a proportionally amount of the `collateralAsset` plus a bonus to cover market risk", + "params": { + "collateralAsset": "The address of the underlying asset used as collateral, to receive as result of the liquidation", + "debtAsset": "The address of the underlying borrowed asset to be repaid with the liquidation", + "debtToCover": "The debt amount of borrowed `asset` the liquidator wants to cover", + "receiveAToken": "`true` if the liquidators wants to receive the collateral aTokens, `false` if he wants to receive the underlying collateral asset directly*", + "user": "The address of the borrower getting liquidated" + } + }, + "paused()": { "details": "Returns if the LendingPool is paused" }, + "rebalanceStableBorrowRate(address,address)": { + "details": "Rebalances the stable interest rate of a user to the current stable rate defined on the reserve. - Users can be rebalanced if the following conditions are satisfied: 1. Usage ratio is above 95% 2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been borrowed at a stable rate and depositors are not earning enough", + "params": { + "asset": "The address of the underlying asset borrowed", + "user": "The address of the user to be rebalanced*" + } + }, + "repay(address,uint256,uint256,address)": { + "params": { + "amount": "The amount to repay - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`", + "asset": "The address of the borrowed underlying asset previously borrowed", + "onBehalfOf": "Address of the user who will get his debt reduced/removed. Should be the address of the user calling the function if he wants to reduce/remove his own debt, or the address of any other other borrower whose debt should be removed", + "rateMode": "The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable" + }, + "returns": { "_0": "The final amount repaid*" } + }, + "setConfiguration(address,uint256)": { + "details": "Sets the configuration bitmap of the reserve as a whole - Only callable by the LendingPoolConfigurator contract", + "params": { + "asset": "The address of the underlying asset of the reserve", + "configuration": "The new configuration bitmap*" + } + }, + "setPause(bool)": { + "details": "Set the _pause state of a reserve - Only callable by the LendingPoolConfigurator contract", + "params": { + "val": "`true` to pause the reserve, `false` to un-pause it" + } + }, + "setReserveInterestRateStrategyAddress(address,address)": { + "details": "Updates the address of the interest rate strategy contract - Only callable by the LendingPoolConfigurator contract", + "params": { + "asset": "The address of the underlying asset of the reserve", + "rateStrategyAddress": "The address of the interest rate strategy contract*" + } + }, + "setUserUseReserveAsCollateral(address,bool)": { + "details": "Allows depositors to enable/disable a specific deposited asset as collateral", + "params": { + "asset": "The address of the underlying asset deposited", + "useAsCollateral": "`true` if the user wants to use the deposit as collateral, `false` otherwise*" + } + }, + "swapBorrowRateMode(address,uint256)": { + "details": "Allows a borrower to swap his debt between stable and variable mode, or viceversa", + "params": { + "asset": "The address of the underlying asset borrowed", + "rateMode": "The rate mode that the user wants to swap to*" + } + }, + "withdraw(address,uint256,address)": { + "details": "Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC", + "params": { + "amount": "The underlying amount to be withdrawn - Send the value type(uint256).max in order to withdraw the whole aToken balance", + "asset": "The address of the underlying asset to withdraw", + "to": "Address that will receive the underlying, same as msg.sender if the user wants to receive it on his own wallet, or a different address if the beneficiary is a different wallet" + }, + "returns": { "_0": "The final amount withdrawn*" } + } + }, + "title": "LendingPool contract", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "repay(address,uint256,uint256,address)": { + "notice": "Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address" + } + }, + "version": 1 + } + }, + "settings": { + "compilationTarget": { + "contracts/protocol/lendingpool/LendingPool.sol": "LendingPool" + }, + "evmVersion": "istanbul", + "libraries": {}, + "metadata": { "bytecodeHash": "ipfs" }, + "optimizer": { "enabled": true, "runs": 200 }, + "remappings": [] + }, + "sources": { + "contracts/dependencies/openzeppelin/contracts/Address.sol": { + "keccak256": "0x71a8ea40617f961a5aef4129be273aaf285eb3a1fa6ad66a53aeec0768fea56b", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://4f4ebe707d00ef5573795c4020df59fe131799b0c39a4e44a79567f249287b66", + "dweb:/ipfs/QmdjsoYUuKKkUjkjxf97SjkLNGYbaU6PDXkerhb7P83uuh" + ] + }, + "contracts/dependencies/openzeppelin/contracts/IERC20.sol": { + "keccak256": "0xe9d8994b4b126042dd702600c37635bb16a379a658bc0248bedb951befb95df7", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://895dd646d5fd895512ece68ad3faa3fa515130f2d1e4f103c83db32758ca369e", + "dweb:/ipfs/QmRGGCniMXoig91GemkXb8ookxDy8dx2WCfwWwivuFXGVN" + ] + }, + "contracts/dependencies/openzeppelin/contracts/SafeERC20.sol": { + "keccak256": "0xd2f21401b1f030221da8eb167adb22a1cff47d7f2ef23dd34a72f05543d23aca", + "license": "MIT", + "urls": [ + "bzz-raw://24b5c9a940febf40712ce6d9b21dc6e84e6f8112e8e8bcee811520e9df33ec27", + "dweb:/ipfs/QmV23WWJ55wLuWKigdR1cFQYQJaBnQhEWrUrm7o4kx6stC" + ] + }, + "contracts/dependencies/openzeppelin/contracts/SafeMath.sol": { + "keccak256": "0x491d2b7ab89a7acf1f305dc70cc3a933136bd62c6e49cfa4e70e3fd2e8dce58d", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://9031f0f7a8e1394b7f75bf5a6bfa22eddbbb8e4e7716cb31f11a90645d5cde19", + "dweb:/ipfs/QmRFfZbPnosAXi31crVuus7cTwu8FoJK1gfaKnNZX22J33" + ] + }, + "contracts/flashloan/interfaces/IFlashLoanReceiver.sol": { + "keccak256": "0xe1c2f302af5a79dcf290e08d0f8b8d6b91283ede7b1f855f6786bd92c4ec470f", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://7a203b669e2bb5290d5ab004efd5aa78ec3d68d1ab786c4f34dd85fbe421e79d", + "dweb:/ipfs/QmQ6zxo5Qa3d8eC3DfnN9VgcVct7mFT1QSLFpMrBPo66do" + ] + }, + "contracts/interfaces/IAToken.sol": { + "keccak256": "0x29dcaf9dec145b318caebd1a1545734ebae5b2e1d2f9e3750f39f57382ddff34", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://5d769acdd7c9fd1a64f68b809a72a5eaa1977bb81acd093dd07f788f522614db", + "dweb:/ipfs/QmURP4LVbyxc9FwcjHGoJpP655vdPsz2oGksU61iCt7LMZ" + ] + }, + "contracts/interfaces/IAaveIncentivesController.sol": { + "keccak256": "0xc9d3e3471450a33f0966db6e760980ed3c9f0d7d94bdddf65844555b901065e2", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://6f6ec86eb241d9c56bb6a6e79fce09056c5c08a5c232a8ac8e210f8909e883cd", + "dweb:/ipfs/QmS3NcFHek6GK5m8DVfBahazikX3hQzgfsSLZ3KV8to1Yv" + ] + }, + "contracts/interfaces/IInitializableAToken.sol": { + "keccak256": "0x1200a1b2aefcee481fbdcda5017bce8edff8072e8e543c68144f687c11e0d45f", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://d7dadeaf90754265d7f1dfdc0f229950d400ebf81ab4b5f62fc9e087ea2b7c50", + "dweb:/ipfs/QmQGS3qoBD8wpFchuD4H79MkuAGvY71yrJfT3uCLpN1JwZ" + ] + }, + "contracts/interfaces/IInitializableDebtToken.sol": { + "keccak256": "0x81138cd58dfee7d2917b7ac215dde9153f8f7f3fa6130ec3ef9a0484782d910f", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://7fa584bbae040701d0650143bc7cfd17768d393ff790ca9c174b44af2b821f4b", + "dweb:/ipfs/QmUvS8FL73GfQ9aHwZqb5N6qgtUvZKCMGGJ1Rv837naD2h" + ] + }, + "contracts/interfaces/ILendingPool.sol": { + "keccak256": "0xc89e087e3c8b3b8f5c536e5d4341c85449c176deb695200c99fb4eeb92093d8d", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://6ffe338e88a1a19c2d6e9539bdc8190fc41e0b626059236e12a875c59ea64045", + "dweb:/ipfs/QmW1sqddCUcJQGG81gtYZYRc6PRMmvfp62KhhVEmVUNKF3" + ] + }, + "contracts/interfaces/ILendingPoolAddressesProvider.sol": { + "keccak256": "0xb11c734236fb9f38a88162b487be7eea4a344b925a9c8ecf550ac2bd1b590b1c", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://b7a9d03cbf8007f82fdb0bdbd703afa6eef644e05f95bc2c4625c46279e61b19", + "dweb:/ipfs/QmQ48r9jZMyjjynXTdnvBEajnQXAs6xEuJx7LzJAnaa2iW" + ] + }, + "contracts/interfaces/IPriceOracleGetter.sol": { + "keccak256": "0x29106cf3c1d26c868870f128bb6e7f41b2b57a79882177fe8061919680d6ccee", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://151c38c46401cd52e1cdc2d7afc531ae36e96e74fa8e90c1508549bd2c24fe6e", + "dweb:/ipfs/QmSXA7HWy56BRipEpyp8T3TrqtV3p7X5Ant6ixksKscADS" + ] + }, + "contracts/interfaces/IReserveInterestRateStrategy.sol": { + "keccak256": "0x0ab046c2e0e37b742d7224033bf4005b9d78ddfc9b1f7a5a8528c07091d3f08e", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://3b216c57478c3ee9140f01fcbf76adbf2264830d9de1bf1ebae6579759c4e549", + "dweb:/ipfs/QmV66iyNj8nhzQMJNJK4gqm2XVSwDkFegn8v2Y3fiTCqrc" + ] + }, + "contracts/interfaces/IScaledBalanceToken.sol": { + "keccak256": "0x8a30667a00d1d6f95f4a44cf33d1f5869153325395fee603957882ba0a5734ed", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://cfeae32aa78f6f52fdda54dd6536b0fb5d341f8341353b40271f03a8fe957fab", + "dweb:/ipfs/QmPq5x5kxQr3NSJhBj4F2fYEUeMja4NW48LB5XHAsJJ4XY" + ] + }, + "contracts/interfaces/IStableDebtToken.sol": { + "keccak256": "0x72ad72d5d442b637ad08aee86e6aa3091db623c717cb4ffb2b76e826e6fcb097", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://1b682b64bf0594f2063a7ed196a2f6a1c76c35cc315ea447e650d3042c93dda6", + "dweb:/ipfs/QmQ15ky7qKHAvcg9qEiSgpsiV2H2mJ4mzFHkctghq6ksNB" + ] + }, + "contracts/interfaces/IVariableDebtToken.sol": { + "keccak256": "0xe881d8a4659119d87c5a9f55bd53d05e6e9c23d943519e03195575d1d6146531", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://dcbacfbaccd453f57df15dc489a43a1304e16f0c5d77b3cc8b0b650ab6e027ca", + "dweb:/ipfs/QmXyLiEfXkdYHqtw8vkjhRKgPV8d4bgVVL2Ep6JQqd1TuZ" + ] + }, + "contracts/protocol/lendingpool/LendingPool.sol": { + "keccak256": "0xd76a4e477ba30669059b80f7ae1f911baf60bdac2faa7af0a1bfc371ac695994", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://8c20cf4ab811a3bb45c4e0893bd6f1f043d1336601685f005604e306651dc7a3", + "dweb:/ipfs/QmZjt81s8y7texgEyKp1k1GzGYvuziHUB8Cdy1qgHftKLL" + ] + }, + "contracts/protocol/lendingpool/LendingPoolStorage.sol": { + "keccak256": "0xf752436a4a44721be2185f04c2b87a12b26100480d5eecb696c6d36d38a48b4c", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://7a8e0460593807f790afc066c461718e75303d824005dbf70582cfaf1157b59f", + "dweb:/ipfs/QmRo6iEMXKoxsYVLLpc5QwZbRWNA1YA9hXYQpkN7BHzTH1" + ] + }, + "contracts/protocol/libraries/aave-upgradeability/VersionedInitializable.sol": { + "keccak256": "0x316b26239f6e8059b75ddb7437a840ef85eae15802d653d11f81ccee00b1be25", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://9f1c759a8a51da7f5778d77b6fd9b1124465358596d6ea93b6c274b876153126", + "dweb:/ipfs/QmQFp6h52he8DVE4JNMDHCHh8dKngvWSTJmxc2L3QYg6WD" + ] + }, + "contracts/protocol/libraries/configuration/ReserveConfiguration.sol": { + "keccak256": "0x124285f1252cb46e9cd89b2590cebb1cfdbd1af774db939d37a0b2129e303fab", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://bff49de86e978ed1fbc553b9fb24370a34af0b3c15b6e2749c2b37f94538c4cc", + "dweb:/ipfs/QmZeEiLm8Lr9q8XB5yMVGu8TNGyZ6UYLwt3csJgBamQ2g3" + ] + }, + "contracts/protocol/libraries/configuration/UserConfiguration.sol": { + "keccak256": "0x53c4afcea96b2cb7b11d71bc7d58f0cce9d2fdf93018d2297544afb7d55ed805", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://b30ce71881b90092856126295cc494b0e1074a019dda3c1cd2cd2c6160c0aa12", + "dweb:/ipfs/Qme36YprHFbqcd1n26Xg1HQ3SVBKXdhSkyXdqVw2FDrFcb" + ] + }, + "contracts/protocol/libraries/helpers/Errors.sol": { + "keccak256": "0xa7c9fa7a4ab679ac293d28fd4a1cfb0ab1fba157ab2665b6969076f37aea5539", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://393460ee111be40884096d511eda7013903fd96b9eb00225359d0ceca173bf27", + "dweb:/ipfs/QmQUDMp3ja41iCppkT7vgkQJTFLmo4Kz2hTsDsydxMJgUT" + ] + }, + "contracts/protocol/libraries/helpers/Helpers.sol": { + "keccak256": "0xeb3b1daae775e1bcd7fcda0fa21500279fffbb615e84cd3d04da806304a244d9", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://f3d8d4fb79d770c038c6331660dc2c4df1f8413b445d934634c368c853e2c1d8", + "dweb:/ipfs/QmaUUAp9iiDi1VuxKA86X97GZ1FPZWTwxmMBjxCQddFGrX" + ] + }, + "contracts/protocol/libraries/logic/GenericLogic.sol": { + "keccak256": "0x3d7a092555708cf13a430d8f78c6989535c258d413eef1726f1df44734c5122b", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://609f0f63abab8e2b49e0cc31fb3ae1b45d6805636ddf133e691e3c18df3c13e9", + "dweb:/ipfs/QmYXYAHimJWzu29FM5AjVQAFRKP4wPTsDgZmPpoV9P9xUU" + ] + }, + "contracts/protocol/libraries/logic/ReserveLogic.sol": { + "keccak256": "0x3d525227caef23a05d5653067f49dba59795978e60cd9a85d6e76ca3b9fa67f1", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://2ba201c983524c2d03ea9337dcfde52e6dc1160d2dc3c3375983e521b0bb4dca", + "dweb:/ipfs/QmTGBA9o9wDnqP1XEm65FowaLLgYYRzENeFEk7kpYgmzEX" + ] + }, + "contracts/protocol/libraries/logic/ValidationLogic.sol": { + "keccak256": "0x635ed57302c0a73954d0122aebe4e4b80c2287186c65008629d7420134d127b5", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://102380f2b97b4ce3938936bc988e93eefd44bb37ac03953bc0ec078393bb4b42", + "dweb:/ipfs/QmVPBcgTwr3DFjeW3JgnLMJhwnDm6Tni1Y8AB3gFEZjhVJ" + ] + }, + "contracts/protocol/libraries/math/MathUtils.sol": { + "keccak256": "0x385815535695c27ceaa95b34aeab677b96a17c0d88621ddb92b543a64075ce10", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://115d2558f1c8886d832b239956cc1a3f3a07915f08521581fe09192619a8dca6", + "dweb:/ipfs/QmUnMAM7YYT5nZV3W62SFu5ufX8uN8yciXcUiiQWFo399g" + ] + }, + "contracts/protocol/libraries/math/PercentageMath.sol": { + "keccak256": "0xcb555ab691219a42c47239dfe1af7394e1aa8e68161c8bc1286d447044e9d09a", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://3bf3aa963bda262d86736f614d1d5e10b2f95db39dc502da0a265a696931560b", + "dweb:/ipfs/QmatiGsUZGHvmazuuMACeX44auMsoWMzTLpUbHBrdHb7eD" + ] + }, + "contracts/protocol/libraries/math/WadRayMath.sol": { + "keccak256": "0x3c2ac11df412318dd8dec98c6d5500d03999c42806b0051e7424e61a12be38d4", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://7b6d9cbb2408593c3ad2b920e568757a3926aa89c623749c64045b191d6bf3e3", + "dweb:/ipfs/QmPvcwrnj6KXYEZRGKHisCnHujTPXdKEvzz1RzgTPPFUa9" + ] + }, + "contracts/protocol/libraries/types/DataTypes.sol": { + "keccak256": "0xf645d3e560cec23f2e1c0367e474efa9db1e85fda88bc54fec7a925366a93540", + "license": "agpl-3.0", + "urls": [ + "bzz-raw://e60e7eb473dfcf2e762d1cb08d6cd00608d2e50410368d07fa3be5e14dd2d8ed", + "dweb:/ipfs/QmWY2YwVS4ViqeRkjQkwTHdZANsdAitDdhbzHvafdg3HyR" + ] + } + }, + "version": 1 +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/Address.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/Address.sol new file mode 100644 index 000000000..bec641a8a --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/Address.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + */ + function isContract(address account) internal view returns (bool) { + // According to EIP-1052, 0x0 is the value returned for not-yet created accounts + // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned + // for accounts without code, i.e. `keccak256('')` + bytes32 codehash; + bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; + // solhint-disable-next-line no-inline-assembly + assembly { + codehash := extcodehash(account) + } + return (codehash != accountHash && codehash != 0x0); + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, 'Address: insufficient balance'); + + // solhint-disable-next-line avoid-low-level-calls, avoid-call-value + (bool success, ) = recipient.call{value: amount}(''); + require(success, 'Address: unable to send value, recipient may have reverted'); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/DataTypes.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/DataTypes.sol new file mode 100644 index 000000000..9256788f5 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/DataTypes.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +library DataTypes { + // refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties. + struct ReserveData { + //stores the reserve configuration + ReserveConfigurationMap configuration; + //the liquidity index. Expressed in ray + uint128 liquidityIndex; + //variable borrow index. Expressed in ray + uint128 variableBorrowIndex; + //the current supply rate. Expressed in ray + uint128 currentLiquidityRate; + //the current variable borrow rate. Expressed in ray + uint128 currentVariableBorrowRate; + //the current stable borrow rate. Expressed in ray + uint128 currentStableBorrowRate; + uint40 lastUpdateTimestamp; + //tokens addresses + address aTokenAddress; + address stableDebtTokenAddress; + address variableDebtTokenAddress; + //address of the interest rate strategy + address interestRateStrategyAddress; + //the id of the reserve. Represents the position in the list of the active reserves + uint8 id; + } + + struct ReserveConfigurationMap { + //bit 0-15: LTV + //bit 16-31: Liq. threshold + //bit 32-47: Liq. bonus + //bit 48-55: Decimals + //bit 56: Reserve is active + //bit 57: reserve is frozen + //bit 58: borrowing is enabled + //bit 59: stable rate borrowing enabled + //bit 60-63: reserved + //bit 64-79: reserve factor + uint256 data; + } + + struct UserConfigurationMap { + uint256 data; + } + + enum InterestRateMode {NONE, STABLE, VARIABLE} +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/Errors.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/Errors.sol new file mode 100644 index 000000000..faf2df5dc --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/Errors.sol @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +/** + * @title Errors library + * @author Aave + * @notice Defines the error messages emitted by the different contracts of the Aave protocol + * @dev Error messages prefix glossary: + * - VL = ValidationLogic + * - MATH = Math libraries + * - CT = Common errors between tokens (AToken, VariableDebtToken and StableDebtToken) + * - AT = AToken + * - SDT = StableDebtToken + * - VDT = VariableDebtToken + * - LP = LendingPool + * - LPAPR = LendingPoolAddressesProviderRegistry + * - LPC = LendingPoolConfiguration + * - RL = ReserveLogic + * - LPCM = LendingPoolCollateralManager + * - P = Pausable + */ +library Errors { + //common errors + string public constant CALLER_NOT_POOL_ADMIN = '33'; // 'The caller must be the pool admin' + string public constant BORROW_ALLOWANCE_NOT_ENOUGH = '59'; // User borrows on behalf, but allowance are too small + + //contract specific errors + string public constant VL_INVALID_AMOUNT = '1'; // 'Amount must be greater than 0' + string public constant VL_NO_ACTIVE_RESERVE = '2'; // 'Action requires an active reserve' + string public constant VL_RESERVE_FROZEN = '3'; // 'Action cannot be performed because the reserve is frozen' + string public constant VL_CURRENT_AVAILABLE_LIQUIDITY_NOT_ENOUGH = '4'; // 'The current liquidity is not enough' + string public constant VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE = '5'; // 'User cannot withdraw more than the available balance' + string public constant VL_TRANSFER_NOT_ALLOWED = '6'; // 'Transfer cannot be allowed.' + string public constant VL_BORROWING_NOT_ENABLED = '7'; // 'Borrowing is not enabled' + string public constant VL_INVALID_INTEREST_RATE_MODE_SELECTED = '8'; // 'Invalid interest rate mode selected' + string public constant VL_COLLATERAL_BALANCE_IS_0 = '9'; // 'The collateral balance is 0' + string public constant VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD = '10'; // 'Health factor is lesser than the liquidation threshold' + string public constant VL_COLLATERAL_CANNOT_COVER_NEW_BORROW = '11'; // 'There is not enough collateral to cover a new borrow' + string public constant VL_STABLE_BORROWING_NOT_ENABLED = '12'; // stable borrowing not enabled + string public constant VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY = '13'; // collateral is (mostly) the same currency that is being borrowed + string public constant VL_AMOUNT_BIGGER_THAN_MAX_LOAN_SIZE_STABLE = '14'; // 'The requested amount is greater than the max loan size in stable rate mode + string public constant VL_NO_DEBT_OF_SELECTED_TYPE = '15'; // 'for repayment of stable debt, the user needs to have stable debt, otherwise, he needs to have variable debt' + string public constant VL_NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF = '16'; // 'To repay on behalf of an user an explicit amount to repay is needed' + string public constant VL_NO_STABLE_RATE_LOAN_IN_RESERVE = '17'; // 'User does not have a stable rate loan in progress on this reserve' + string public constant VL_NO_VARIABLE_RATE_LOAN_IN_RESERVE = '18'; // 'User does not have a variable rate loan in progress on this reserve' + string public constant VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0 = '19'; // 'The underlying balance needs to be greater than 0' + string public constant VL_DEPOSIT_ALREADY_IN_USE = '20'; // 'User deposit is already being used as collateral' + string public constant LP_NOT_ENOUGH_STABLE_BORROW_BALANCE = '21'; // 'User does not have any stable rate loan for this reserve' + string public constant LP_INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET = '22'; // 'Interest rate rebalance conditions were not met' + string public constant LP_LIQUIDATION_CALL_FAILED = '23'; // 'Liquidation call failed' + string public constant LP_NOT_ENOUGH_LIQUIDITY_TO_BORROW = '24'; // 'There is not enough liquidity available to borrow' + string public constant LP_REQUESTED_AMOUNT_TOO_SMALL = '25'; // 'The requested amount is too small for a FlashLoan.' + string public constant LP_INCONSISTENT_PROTOCOL_ACTUAL_BALANCE = '26'; // 'The actual balance of the protocol is inconsistent' + string public constant LP_CALLER_NOT_LENDING_POOL_CONFIGURATOR = '27'; // 'The caller of the function is not the lending pool configurator' + string public constant LP_INCONSISTENT_FLASHLOAN_PARAMS = '28'; + string public constant CT_CALLER_MUST_BE_LENDING_POOL = '29'; // 'The caller of this function must be a lending pool' + string public constant CT_CANNOT_GIVE_ALLOWANCE_TO_HIMSELF = '30'; // 'User cannot give allowance to himself' + string public constant CT_TRANSFER_AMOUNT_NOT_GT_0 = '31'; // 'Transferred amount needs to be greater than zero' + string public constant RL_RESERVE_ALREADY_INITIALIZED = '32'; // 'Reserve has already been initialized' + string public constant LPC_RESERVE_LIQUIDITY_NOT_0 = '34'; // 'The liquidity of the reserve needs to be 0' + string public constant LPC_INVALID_ATOKEN_POOL_ADDRESS = '35'; // 'The liquidity of the reserve needs to be 0' + string public constant LPC_INVALID_STABLE_DEBT_TOKEN_POOL_ADDRESS = '36'; // 'The liquidity of the reserve needs to be 0' + string public constant LPC_INVALID_VARIABLE_DEBT_TOKEN_POOL_ADDRESS = '37'; // 'The liquidity of the reserve needs to be 0' + string public constant LPC_INVALID_STABLE_DEBT_TOKEN_UNDERLYING_ADDRESS = '38'; // 'The liquidity of the reserve needs to be 0' + string public constant LPC_INVALID_VARIABLE_DEBT_TOKEN_UNDERLYING_ADDRESS = '39'; // 'The liquidity of the reserve needs to be 0' + string public constant LPC_INVALID_ADDRESSES_PROVIDER_ID = '40'; // 'The liquidity of the reserve needs to be 0' + string public constant LPC_INVALID_CONFIGURATION = '75'; // 'Invalid risk parameters for the reserve' + string public constant LPC_CALLER_NOT_EMERGENCY_ADMIN = '76'; // 'The caller must be the emergency admin' + string public constant LPAPR_PROVIDER_NOT_REGISTERED = '41'; // 'Provider is not registered' + string public constant LPCM_HEALTH_FACTOR_NOT_BELOW_THRESHOLD = '42'; // 'Health factor is not below the threshold' + string public constant LPCM_COLLATERAL_CANNOT_BE_LIQUIDATED = '43'; // 'The collateral chosen cannot be liquidated' + string public constant LPCM_SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER = '44'; // 'User did not borrow the specified currency' + string public constant LPCM_NOT_ENOUGH_LIQUIDITY_TO_LIQUIDATE = '45'; // "There isn't enough liquidity available to liquidate" + string public constant LPCM_NO_ERRORS = '46'; // 'No errors' + string public constant LP_INVALID_FLASHLOAN_MODE = '47'; //Invalid flashloan mode selected + string public constant MATH_MULTIPLICATION_OVERFLOW = '48'; + string public constant MATH_ADDITION_OVERFLOW = '49'; + string public constant MATH_DIVISION_BY_ZERO = '50'; + string public constant RL_LIQUIDITY_INDEX_OVERFLOW = '51'; // Liquidity index overflows uint128 + string public constant RL_VARIABLE_BORROW_INDEX_OVERFLOW = '52'; // Variable borrow index overflows uint128 + string public constant RL_LIQUIDITY_RATE_OVERFLOW = '53'; // Liquidity rate overflows uint128 + string public constant RL_VARIABLE_BORROW_RATE_OVERFLOW = '54'; // Variable borrow rate overflows uint128 + string public constant RL_STABLE_BORROW_RATE_OVERFLOW = '55'; // Stable borrow rate overflows uint128 + string public constant CT_INVALID_MINT_AMOUNT = '56'; //invalid amount to mint + string public constant LP_FAILED_REPAY_WITH_COLLATERAL = '57'; + string public constant CT_INVALID_BURN_AMOUNT = '58'; //invalid amount to burn + string public constant LP_FAILED_COLLATERAL_SWAP = '60'; + string public constant LP_INVALID_EQUAL_ASSETS_TO_SWAP = '61'; + string public constant LP_REENTRANCY_NOT_ALLOWED = '62'; + string public constant LP_CALLER_MUST_BE_AN_ATOKEN = '63'; + string public constant LP_IS_PAUSED = '64'; // 'Pool is paused' + string public constant LP_NO_MORE_RESERVES_ALLOWED = '65'; + string public constant LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN = '66'; + string public constant RC_INVALID_LTV = '67'; + string public constant RC_INVALID_LIQ_THRESHOLD = '68'; + string public constant RC_INVALID_LIQ_BONUS = '69'; + string public constant RC_INVALID_DECIMALS = '70'; + string public constant RC_INVALID_RESERVE_FACTOR = '71'; + string public constant LPAPR_INVALID_ADDRESSES_PROVIDER_ID = '72'; + string public constant VL_INCONSISTENT_FLASHLOAN_PARAMS = '73'; + string public constant LP_INCONSISTENT_PARAMS_LENGTH = '74'; + string public constant UL_INVALID_INDEX = '77'; + string public constant LP_NOT_CONTRACT = '78'; + string public constant SDT_STABLE_DEBT_OVERFLOW = '79'; + string public constant SDT_BURN_EXCEEDS_BALANCE = '80'; + + enum CollateralManagerErrors { + NO_ERROR, + NO_COLLATERAL_AVAILABLE, + COLLATERAL_CANNOT_BE_LIQUIDATED, + CURRRENCY_NOT_BORROWED, + HEALTH_FACTOR_ABOVE_THRESHOLD, + NOT_ENOUGH_LIQUIDITY, + NO_ACTIVE_RESERVE, + HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD, + INVALID_EQUAL_ASSETS_TO_SWAP, + FROZEN_RESERVE + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/GenericLogic.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/GenericLogic.sol new file mode 100644 index 000000000..e781291d0 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/GenericLogic.sol @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol'; +import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol'; +import {ReserveLogic} from './ReserveLogic.sol'; +import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol'; +import {UserConfiguration} from '../configuration/UserConfiguration.sol'; +import {WadRayMath} from '../math/WadRayMath.sol'; +import {PercentageMath} from '../math/PercentageMath.sol'; +import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol'; +import {DataTypes} from '../types/DataTypes.sol'; + +/** + * @title GenericLogic library + * @author Aave + * @title Implements protocol-level logic to calculate and validate the state of a user + */ +library GenericLogic { + using ReserveLogic for DataTypes.ReserveData; + using SafeMath for uint256; + using WadRayMath for uint256; + using PercentageMath for uint256; + using ReserveConfiguration for DataTypes.ReserveConfigurationMap; + using UserConfiguration for DataTypes.UserConfigurationMap; + + uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1 ether; + + struct balanceDecreaseAllowedLocalVars { + uint256 decimals; + uint256 liquidationThreshold; + uint256 totalCollateralInETH; + uint256 totalDebtInETH; + uint256 avgLiquidationThreshold; + uint256 amountToDecreaseInETH; + uint256 collateralBalanceAfterDecrease; + uint256 liquidationThresholdAfterDecrease; + uint256 healthFactorAfterDecrease; + bool reserveUsageAsCollateralEnabled; + } + + /** + * @dev Checks if a specific balance decrease is allowed + * (i.e. doesn't bring the user borrow position health factor under HEALTH_FACTOR_LIQUIDATION_THRESHOLD) + * @param asset The address of the underlying asset of the reserve + * @param user The address of the user + * @param amount The amount to decrease + * @param reservesData The data of all the reserves + * @param userConfig The user configuration + * @param reserves The list of all the active reserves + * @param oracle The address of the oracle contract + * @return true if the decrease of the balance is allowed + **/ + function balanceDecreaseAllowed( + address asset, + address user, + uint256 amount, + mapping(address => DataTypes.ReserveData) storage reservesData, + DataTypes.UserConfigurationMap calldata userConfig, + mapping(uint256 => address) storage reserves, + uint256 reservesCount, + address oracle + ) external view returns (bool) { + if (!userConfig.isBorrowingAny() || !userConfig.isUsingAsCollateral(reservesData[asset].id)) { + return true; + } + + balanceDecreaseAllowedLocalVars memory vars; + + (, vars.liquidationThreshold, , vars.decimals, ) = reservesData[asset] + .configuration + .getParams(); + + if (vars.liquidationThreshold == 0) { + return true; + } + + ( + vars.totalCollateralInETH, + vars.totalDebtInETH, + , + vars.avgLiquidationThreshold, + + ) = calculateUserAccountData(user, reservesData, userConfig, reserves, reservesCount, oracle); + + if (vars.totalDebtInETH == 0) { + return true; + } + + vars.amountToDecreaseInETH = IPriceOracleGetter(oracle).getAssetPrice(asset).mul(amount).div( + 10**vars.decimals + ); + + vars.collateralBalanceAfterDecrease = vars.totalCollateralInETH.sub(vars.amountToDecreaseInETH); + + //if there is a borrow, there can't be 0 collateral + if (vars.collateralBalanceAfterDecrease == 0) { + return false; + } + + vars.liquidationThresholdAfterDecrease = vars + .totalCollateralInETH + .mul(vars.avgLiquidationThreshold) + .sub(vars.amountToDecreaseInETH.mul(vars.liquidationThreshold)) + .div(vars.collateralBalanceAfterDecrease); + + uint256 healthFactorAfterDecrease = + calculateHealthFactorFromBalances( + vars.collateralBalanceAfterDecrease, + vars.totalDebtInETH, + vars.liquidationThresholdAfterDecrease + ); + + return healthFactorAfterDecrease >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD; + } + + struct CalculateUserAccountDataVars { + uint256 reserveUnitPrice; + uint256 tokenUnit; + uint256 compoundedLiquidityBalance; + uint256 compoundedBorrowBalance; + uint256 decimals; + uint256 ltv; + uint256 liquidationThreshold; + uint256 i; + uint256 healthFactor; + uint256 totalCollateralInETH; + uint256 totalDebtInETH; + uint256 avgLtv; + uint256 avgLiquidationThreshold; + uint256 reservesLength; + bool healthFactorBelowThreshold; + address currentReserveAddress; + bool usageAsCollateralEnabled; + bool userUsesReserveAsCollateral; + } + + /** + * @dev Calculates the user data across the reserves. + * this includes the total liquidity/collateral/borrow balances in ETH, + * the average Loan To Value, the average Liquidation Ratio, and the Health factor. + * @param user The address of the user + * @param reservesData Data of all the reserves + * @param userConfig The configuration of the user + * @param reserves The list of the available reserves + * @param oracle The price oracle address + * @return The total collateral and total debt of the user in ETH, the avg ltv, liquidation threshold and the HF + **/ + function calculateUserAccountData( + address user, + mapping(address => DataTypes.ReserveData) storage reservesData, + DataTypes.UserConfigurationMap memory userConfig, + mapping(uint256 => address) storage reserves, + uint256 reservesCount, + address oracle + ) + internal + view + returns ( + uint256, + uint256, + uint256, + uint256, + uint256 + ) + { + CalculateUserAccountDataVars memory vars; + + if (userConfig.isEmpty()) { + return (0, 0, 0, 0, uint256(-1)); + } + for (vars.i = 0; vars.i < reservesCount; vars.i++) { + if (!userConfig.isUsingAsCollateralOrBorrowing(vars.i)) { + continue; + } + + vars.currentReserveAddress = reserves[vars.i]; + DataTypes.ReserveData storage currentReserve = reservesData[vars.currentReserveAddress]; + + (vars.ltv, vars.liquidationThreshold, , vars.decimals, ) = currentReserve + .configuration + .getParams(); + + vars.tokenUnit = 10**vars.decimals; + vars.reserveUnitPrice = IPriceOracleGetter(oracle).getAssetPrice(vars.currentReserveAddress); + + if (vars.liquidationThreshold != 0 && userConfig.isUsingAsCollateral(vars.i)) { + vars.compoundedLiquidityBalance = IERC20(currentReserve.aTokenAddress).balanceOf(user); + + uint256 liquidityBalanceETH = + vars.reserveUnitPrice.mul(vars.compoundedLiquidityBalance).div(vars.tokenUnit); + + vars.totalCollateralInETH = vars.totalCollateralInETH.add(liquidityBalanceETH); + + vars.avgLtv = vars.avgLtv.add(liquidityBalanceETH.mul(vars.ltv)); + vars.avgLiquidationThreshold = vars.avgLiquidationThreshold.add( + liquidityBalanceETH.mul(vars.liquidationThreshold) + ); + } + + if (userConfig.isBorrowing(vars.i)) { + vars.compoundedBorrowBalance = IERC20(currentReserve.stableDebtTokenAddress).balanceOf( + user + ); + vars.compoundedBorrowBalance = vars.compoundedBorrowBalance.add( + IERC20(currentReserve.variableDebtTokenAddress).balanceOf(user) + ); + + vars.totalDebtInETH = vars.totalDebtInETH.add( + vars.reserveUnitPrice.mul(vars.compoundedBorrowBalance).div(vars.tokenUnit) + ); + } + } + + vars.avgLtv = vars.totalCollateralInETH > 0 ? vars.avgLtv.div(vars.totalCollateralInETH) : 0; + vars.avgLiquidationThreshold = vars.totalCollateralInETH > 0 + ? vars.avgLiquidationThreshold.div(vars.totalCollateralInETH) + : 0; + + vars.healthFactor = calculateHealthFactorFromBalances( + vars.totalCollateralInETH, + vars.totalDebtInETH, + vars.avgLiquidationThreshold + ); + return ( + vars.totalCollateralInETH, + vars.totalDebtInETH, + vars.avgLtv, + vars.avgLiquidationThreshold, + vars.healthFactor + ); + } + + /** + * @dev Calculates the health factor from the corresponding balances + * @param totalCollateralInETH The total collateral in ETH + * @param totalDebtInETH The total debt in ETH + * @param liquidationThreshold The avg liquidation threshold + * @return The health factor calculated from the balances provided + **/ + function calculateHealthFactorFromBalances( + uint256 totalCollateralInETH, + uint256 totalDebtInETH, + uint256 liquidationThreshold + ) internal pure returns (uint256) { + if (totalDebtInETH == 0) return uint256(-1); + + return (totalCollateralInETH.percentMul(liquidationThreshold)).wadDiv(totalDebtInETH); + } + + /** + * @dev Calculates the equivalent amount in ETH that an user can borrow, depending on the available collateral and the + * average Loan To Value + * @param totalCollateralInETH The total collateral in ETH + * @param totalDebtInETH The total borrow balance + * @param ltv The average loan to value + * @return the amount available to borrow in ETH for the user + **/ + + function calculateAvailableBorrowsETH( + uint256 totalCollateralInETH, + uint256 totalDebtInETH, + uint256 ltv + ) internal pure returns (uint256) { + uint256 availableBorrowsETH = totalCollateralInETH.percentMul(ltv); + + if (availableBorrowsETH < totalDebtInETH) { + return 0; + } + + availableBorrowsETH = availableBorrowsETH.sub(totalDebtInETH); + return availableBorrowsETH; + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/Helpers.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/Helpers.sol new file mode 100644 index 000000000..2530b7b8f --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/Helpers.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol'; +import {DataTypes} from '../types/DataTypes.sol'; + +/** + * @title Helpers library + * @author Aave + */ +library Helpers { + /** + * @dev Fetches the user current stable and variable debt balances + * @param user The user address + * @param reserve The reserve data object + * @return The stable and variable debt balance + **/ + function getUserCurrentDebt(address user, DataTypes.ReserveData storage reserve) + internal + view + returns (uint256, uint256) + { + return ( + IERC20(reserve.stableDebtTokenAddress).balanceOf(user), + IERC20(reserve.variableDebtTokenAddress).balanceOf(user) + ); + } + + function getUserCurrentDebtMemory(address user, DataTypes.ReserveData memory reserve) + internal + view + returns (uint256, uint256) + { + return ( + IERC20(reserve.stableDebtTokenAddress).balanceOf(user), + IERC20(reserve.variableDebtTokenAddress).balanceOf(user) + ); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IAToken.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IAToken.sol new file mode 100644 index 000000000..0bcc9f874 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IAToken.sol @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; +import {IScaledBalanceToken} from './IScaledBalanceToken.sol'; +import {IInitializableAToken} from './IInitializableAToken.sol'; +import {IAaveIncentivesController} from './IAaveIncentivesController.sol'; + +interface IAToken is IERC20, IScaledBalanceToken, IInitializableAToken { + /** + * @dev Emitted after the mint action + * @param from The address performing the mint + * @param value The amount being + * @param index The new liquidity index of the reserve + **/ + event Mint(address indexed from, uint256 value, uint256 index); + + /** + * @dev Mints `amount` aTokens to `user` + * @param user The address receiving the minted tokens + * @param amount The amount of tokens getting minted + * @param index The new liquidity index of the reserve + * @return `true` if the the previous balance of the user was 0 + */ + function mint( + address user, + uint256 amount, + uint256 index + ) external returns (bool); + + /** + * @dev Emitted after aTokens are burned + * @param from The owner of the aTokens, getting them burned + * @param target The address that will receive the underlying + * @param value The amount being burned + * @param index The new liquidity index of the reserve + **/ + event Burn(address indexed from, address indexed target, uint256 value, uint256 index); + + /** + * @dev Emitted during the transfer action + * @param from The user whose tokens are being transferred + * @param to The recipient + * @param value The amount being transferred + * @param index The new liquidity index of the reserve + **/ + event BalanceTransfer(address indexed from, address indexed to, uint256 value, uint256 index); + + /** + * @dev Burns aTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying` + * @param user The owner of the aTokens, getting them burned + * @param receiverOfUnderlying The address that will receive the underlying + * @param amount The amount being burned + * @param index The new liquidity index of the reserve + **/ + function burn( + address user, + address receiverOfUnderlying, + uint256 amount, + uint256 index + ) external; + + /** + * @dev Mints aTokens to the reserve treasury + * @param amount The amount of tokens getting minted + * @param index The new liquidity index of the reserve + */ + function mintToTreasury(uint256 amount, uint256 index) external; + + /** + * @dev Transfers aTokens in the event of a borrow being liquidated, in case the liquidators reclaims the aToken + * @param from The address getting liquidated, current owner of the aTokens + * @param to The recipient + * @param value The amount of tokens getting transferred + **/ + function transferOnLiquidation( + address from, + address to, + uint256 value + ) external; + + /** + * @dev Transfers the underlying asset to `target`. Used by the LendingPool to transfer + * assets in borrow(), withdraw() and flashLoan() + * @param user The recipient of the underlying + * @param amount The amount getting transferred + * @return The amount transferred + **/ + function transferUnderlyingTo(address user, uint256 amount) external returns (uint256); + + /** + * @dev Invoked to execute actions on the aToken side after a repayment. + * @param user The user executing the repayment + * @param amount The amount getting repaid + **/ + function handleRepayment(address user, uint256 amount) external; + + /** + * @dev Returns the address of the incentives controller contract + **/ + function getIncentivesController() external view returns (IAaveIncentivesController); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IAaveIncentivesController.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IAaveIncentivesController.sol new file mode 100644 index 000000000..9e5c09c4a --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IAaveIncentivesController.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +interface IAaveIncentivesController { + function handleAction( + address user, + uint256 userBalance, + uint256 totalSupply + ) external; +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IERC20.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IERC20.sol new file mode 100644 index 000000000..557e03e3f --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IERC20.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `recipient`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address recipient, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `sender` to `recipient` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address sender, + address recipient, + uint256 amount + ) external returns (bool); + + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IFlashLoanReceiver.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IFlashLoanReceiver.sol new file mode 100644 index 000000000..ec2d267ff --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IFlashLoanReceiver.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol'; +import {ILendingPool} from '../../interfaces/ILendingPool.sol'; + +/** + * @title IFlashLoanReceiver interface + * @notice Interface for the Aave fee IFlashLoanReceiver. + * @author Aave + * @dev implement this interface to develop a flashloan-compatible flashLoanReceiver contract + **/ +interface IFlashLoanReceiver { + function executeOperation( + address[] calldata assets, + uint256[] calldata amounts, + uint256[] calldata premiums, + address initiator, + bytes calldata params + ) external returns (bool); + + function ADDRESSES_PROVIDER() external view returns (ILendingPoolAddressesProvider); + + function LENDING_POOL() external view returns (ILendingPool); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IInitializableAToken.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IInitializableAToken.sol new file mode 100644 index 000000000..73fe3f093 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IInitializableAToken.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {ILendingPool} from './ILendingPool.sol'; +import {IAaveIncentivesController} from './IAaveIncentivesController.sol'; + +/** + * @title IInitializableAToken + * @notice Interface for the initialize function on AToken + * @author Aave + **/ +interface IInitializableAToken { + /** + * @dev Emitted when an aToken is initialized + * @param underlyingAsset The address of the underlying asset + * @param pool The address of the associated lending pool + * @param treasury The address of the treasury + * @param incentivesController The address of the incentives controller for this aToken + * @param aTokenDecimals the decimals of the underlying + * @param aTokenName the name of the aToken + * @param aTokenSymbol the symbol of the aToken + * @param params A set of encoded parameters for additional initialization + **/ + event Initialized( + address indexed underlyingAsset, + address indexed pool, + address treasury, + address incentivesController, + uint8 aTokenDecimals, + string aTokenName, + string aTokenSymbol, + bytes params + ); + + /** + * @dev Initializes the aToken + * @param pool The address of the lending pool where this aToken will be used + * @param treasury The address of the Aave treasury, receiving the fees on this aToken + * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH) + * @param incentivesController The smart contract managing potential incentives distribution + * @param aTokenDecimals The decimals of the aToken, same as the underlying asset's + * @param aTokenName The name of the aToken + * @param aTokenSymbol The symbol of the aToken + */ + function initialize( + ILendingPool pool, + address treasury, + address underlyingAsset, + IAaveIncentivesController incentivesController, + uint8 aTokenDecimals, + string calldata aTokenName, + string calldata aTokenSymbol, + bytes calldata params + ) external; +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IInitializableDebtToken.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IInitializableDebtToken.sol new file mode 100644 index 000000000..725a7a6e6 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IInitializableDebtToken.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {ILendingPool} from './ILendingPool.sol'; +import {IAaveIncentivesController} from './IAaveIncentivesController.sol'; + +/** + * @title IInitializableDebtToken + * @notice Interface for the initialize function common between debt tokens + * @author Aave + **/ +interface IInitializableDebtToken { + /** + * @dev Emitted when a debt token is initialized + * @param underlyingAsset The address of the underlying asset + * @param pool The address of the associated lending pool + * @param incentivesController The address of the incentives controller for this aToken + * @param debtTokenDecimals the decimals of the debt token + * @param debtTokenName the name of the debt token + * @param debtTokenSymbol the symbol of the debt token + * @param params A set of encoded parameters for additional initialization + **/ + event Initialized( + address indexed underlyingAsset, + address indexed pool, + address incentivesController, + uint8 debtTokenDecimals, + string debtTokenName, + string debtTokenSymbol, + bytes params + ); + + /** + * @dev Initializes the debt token. + * @param pool The address of the lending pool where this aToken will be used + * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH) + * @param incentivesController The smart contract managing potential incentives distribution + * @param debtTokenDecimals The decimals of the debtToken, same as the underlying asset's + * @param debtTokenName The name of the token + * @param debtTokenSymbol The symbol of the token + */ + function initialize( + ILendingPool pool, + address underlyingAsset, + IAaveIncentivesController incentivesController, + uint8 debtTokenDecimals, + string memory debtTokenName, + string memory debtTokenSymbol, + bytes calldata params + ) external; +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/ILendingPool.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/ILendingPool.sol new file mode 100644 index 000000000..4b49fffc8 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/ILendingPool.sol @@ -0,0 +1,410 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {ILendingPoolAddressesProvider} from './ILendingPoolAddressesProvider.sol'; +import {DataTypes} from '../protocol/libraries/types/DataTypes.sol'; + +interface ILendingPool { + /** + * @dev Emitted on deposit() + * @param reserve The address of the underlying asset of the reserve + * @param user The address initiating the deposit + * @param onBehalfOf The beneficiary of the deposit, receiving the aTokens + * @param amount The amount deposited + * @param referral The referral code used + **/ + event Deposit( + address indexed reserve, + address user, + address indexed onBehalfOf, + uint256 amount, + uint16 indexed referral + ); + + /** + * @dev Emitted on withdraw() + * @param reserve The address of the underlyng asset being withdrawn + * @param user The address initiating the withdrawal, owner of aTokens + * @param to Address that will receive the underlying + * @param amount The amount to be withdrawn + **/ + event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount); + + /** + * @dev Emitted on borrow() and flashLoan() when debt needs to be opened + * @param reserve The address of the underlying asset being borrowed + * @param user The address of the user initiating the borrow(), receiving the funds on borrow() or just + * initiator of the transaction on flashLoan() + * @param onBehalfOf The address that will be getting the debt + * @param amount The amount borrowed out + * @param borrowRateMode The rate mode: 1 for Stable, 2 for Variable + * @param borrowRate The numeric rate at which the user has borrowed + * @param referral The referral code used + **/ + event Borrow( + address indexed reserve, + address user, + address indexed onBehalfOf, + uint256 amount, + uint256 borrowRateMode, + uint256 borrowRate, + uint16 indexed referral + ); + + /** + * @dev Emitted on repay() + * @param reserve The address of the underlying asset of the reserve + * @param user The beneficiary of the repayment, getting his debt reduced + * @param repayer The address of the user initiating the repay(), providing the funds + * @param amount The amount repaid + **/ + event Repay( + address indexed reserve, + address indexed user, + address indexed repayer, + uint256 amount + ); + + /** + * @dev Emitted on swapBorrowRateMode() + * @param reserve The address of the underlying asset of the reserve + * @param user The address of the user swapping his rate mode + * @param rateMode The rate mode that the user wants to swap to + **/ + event Swap(address indexed reserve, address indexed user, uint256 rateMode); + + /** + * @dev Emitted on setUserUseReserveAsCollateral() + * @param reserve The address of the underlying asset of the reserve + * @param user The address of the user enabling the usage as collateral + **/ + event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user); + + /** + * @dev Emitted on setUserUseReserveAsCollateral() + * @param reserve The address of the underlying asset of the reserve + * @param user The address of the user enabling the usage as collateral + **/ + event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user); + + /** + * @dev Emitted on rebalanceStableBorrowRate() + * @param reserve The address of the underlying asset of the reserve + * @param user The address of the user for which the rebalance has been executed + **/ + event RebalanceStableBorrowRate(address indexed reserve, address indexed user); + + /** + * @dev Emitted on flashLoan() + * @param target The address of the flash loan receiver contract + * @param initiator The address initiating the flash loan + * @param asset The address of the asset being flash borrowed + * @param amount The amount flash borrowed + * @param premium The fee flash borrowed + * @param referralCode The referral code used + **/ + event FlashLoan( + address indexed target, + address indexed initiator, + address indexed asset, + uint256 amount, + uint256 premium, + uint16 referralCode + ); + + /** + * @dev Emitted when the pause is triggered. + */ + event Paused(); + + /** + * @dev Emitted when the pause is lifted. + */ + event Unpaused(); + + /** + * @dev Emitted when a borrower is liquidated. This event is emitted by the LendingPool via + * LendingPoolCollateral manager using a DELEGATECALL + * This allows to have the events in the generated ABI for LendingPool. + * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation + * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation + * @param user The address of the borrower getting liquidated + * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover + * @param liquidatedCollateralAmount The amount of collateral received by the liiquidator + * @param liquidator The address of the liquidator + * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants + * to receive the underlying collateral asset directly + **/ + event LiquidationCall( + address indexed collateralAsset, + address indexed debtAsset, + address indexed user, + uint256 debtToCover, + uint256 liquidatedCollateralAmount, + address liquidator, + bool receiveAToken + ); + + /** + * @dev Emitted when the state of a reserve is updated. NOTE: This event is actually declared + * in the ReserveLogic library and emitted in the updateInterestRates() function. Since the function is internal, + * the event will actually be fired by the LendingPool contract. The event is therefore replicated here so it + * gets added to the LendingPool ABI + * @param reserve The address of the underlying asset of the reserve + * @param liquidityRate The new liquidity rate + * @param stableBorrowRate The new stable borrow rate + * @param variableBorrowRate The new variable borrow rate + * @param liquidityIndex The new liquidity index + * @param variableBorrowIndex The new variable borrow index + **/ + event ReserveDataUpdated( + address indexed reserve, + uint256 liquidityRate, + uint256 stableBorrowRate, + uint256 variableBorrowRate, + uint256 liquidityIndex, + uint256 variableBorrowIndex + ); + + /** + * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens. + * - E.g. User deposits 100 USDC and gets in return 100 aUSDC + * @param asset The address of the underlying asset to deposit + * @param amount The amount to be deposited + * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user + * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens + * is a different wallet + * @param referralCode Code used to register the integrator originating the operation, for potential rewards. + * 0 if the action is executed directly by the user, without any middle-man + **/ + function deposit( + address asset, + uint256 amount, + address onBehalfOf, + uint16 referralCode + ) external; + + /** + * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned + * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC + * @param asset The address of the underlying asset to withdraw + * @param amount The underlying amount to be withdrawn + * - Send the value type(uint256).max in order to withdraw the whole aToken balance + * @param to Address that will receive the underlying, same as msg.sender if the user + * wants to receive it on his own wallet, or a different address if the beneficiary is a + * different wallet + * @return The final amount withdrawn + **/ + function withdraw( + address asset, + uint256 amount, + address to + ) external returns (uint256); + + /** + * @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower + * already deposited enough collateral, or he was given enough allowance by a credit delegator on the + * corresponding debt token (StableDebtToken or VariableDebtToken) + * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet + * and 100 stable/variable debt tokens, depending on the `interestRateMode` + * @param asset The address of the underlying asset to borrow + * @param amount The amount to be borrowed + * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable + * @param referralCode Code used to register the integrator originating the operation, for potential rewards. + * 0 if the action is executed directly by the user, without any middle-man + * @param onBehalfOf Address of the user who will receive the debt. Should be the address of the borrower itself + * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator + * if he has been given credit delegation allowance + **/ + function borrow( + address asset, + uint256 amount, + uint256 interestRateMode, + uint16 referralCode, + address onBehalfOf + ) external; + + /** + * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned + * - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address + * @param asset The address of the borrowed underlying asset previously borrowed + * @param amount The amount to repay + * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode` + * @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable + * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the + * user calling the function if he wants to reduce/remove his own debt, or the address of any other + * other borrower whose debt should be removed + * @return The final amount repaid + **/ + function repay( + address asset, + uint256 amount, + uint256 rateMode, + address onBehalfOf + ) external returns (uint256); + + /** + * @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa + * @param asset The address of the underlying asset borrowed + * @param rateMode The rate mode that the user wants to swap to + **/ + function swapBorrowRateMode(address asset, uint256 rateMode) external; + + /** + * @dev Rebalances the stable interest rate of a user to the current stable rate defined on the reserve. + * - Users can be rebalanced if the following conditions are satisfied: + * 1. Usage ratio is above 95% + * 2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been + * borrowed at a stable rate and depositors are not earning enough + * @param asset The address of the underlying asset borrowed + * @param user The address of the user to be rebalanced + **/ + function rebalanceStableBorrowRate(address asset, address user) external; + + /** + * @dev Allows depositors to enable/disable a specific deposited asset as collateral + * @param asset The address of the underlying asset deposited + * @param useAsCollateral `true` if the user wants to use the deposit as collateral, `false` otherwise + **/ + function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external; + + /** + * @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1 + * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives + * a proportionally amount of the `collateralAsset` plus a bonus to cover market risk + * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation + * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation + * @param user The address of the borrower getting liquidated + * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover + * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants + * to receive the underlying collateral asset directly + **/ + function liquidationCall( + address collateralAsset, + address debtAsset, + address user, + uint256 debtToCover, + bool receiveAToken + ) external; + + /** + * @dev Allows smartcontracts to access the liquidity of the pool within one transaction, + * as long as the amount taken plus a fee is returned. + * IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration. + * For further details please visit https://developers.aave.com + * @param receiverAddress The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface + * @param assets The addresses of the assets being flash-borrowed + * @param amounts The amounts amounts being flash-borrowed + * @param modes Types of the debt to open if the flash loan is not returned: + * 0 -> Don't open any debt, just revert if funds can't be transferred from the receiver + * 1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address + * 2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address + * @param onBehalfOf The address that will receive the debt in the case of using on `modes` 1 or 2 + * @param params Variadic packed params to pass to the receiver as extra information + * @param referralCode Code used to register the integrator originating the operation, for potential rewards. + * 0 if the action is executed directly by the user, without any middle-man + **/ + function flashLoan( + address receiverAddress, + address[] calldata assets, + uint256[] calldata amounts, + uint256[] calldata modes, + address onBehalfOf, + bytes calldata params, + uint16 referralCode + ) external; + + /** + * @dev Returns the user account data across all the reserves + * @param user The address of the user + * @return totalCollateralETH the total collateral in ETH of the user + * @return totalDebtETH the total debt in ETH of the user + * @return availableBorrowsETH the borrowing power left of the user + * @return currentLiquidationThreshold the liquidation threshold of the user + * @return ltv the loan to value of the user + * @return healthFactor the current health factor of the user + **/ + function getUserAccountData(address user) + external + view + returns ( + uint256 totalCollateralETH, + uint256 totalDebtETH, + uint256 availableBorrowsETH, + uint256 currentLiquidationThreshold, + uint256 ltv, + uint256 healthFactor + ); + + function initReserve( + address reserve, + address aTokenAddress, + address stableDebtAddress, + address variableDebtAddress, + address interestRateStrategyAddress + ) external; + + function setReserveInterestRateStrategyAddress(address reserve, address rateStrategyAddress) + external; + + function setConfiguration(address reserve, uint256 configuration) external; + + /** + * @dev Returns the configuration of the reserve + * @param asset The address of the underlying asset of the reserve + * @return The configuration of the reserve + **/ + function getConfiguration(address asset) + external + view + returns (DataTypes.ReserveConfigurationMap memory); + + /** + * @dev Returns the configuration of the user across all the reserves + * @param user The user address + * @return The configuration of the user + **/ + function getUserConfiguration(address user) + external + view + returns (DataTypes.UserConfigurationMap memory); + + /** + * @dev Returns the normalized income normalized income of the reserve + * @param asset The address of the underlying asset of the reserve + * @return The reserve's normalized income + */ + function getReserveNormalizedIncome(address asset) external view returns (uint256); + + /** + * @dev Returns the normalized variable debt per unit of asset + * @param asset The address of the underlying asset of the reserve + * @return The reserve normalized variable debt + */ + function getReserveNormalizedVariableDebt(address asset) external view returns (uint256); + + /** + * @dev Returns the state and configuration of the reserve + * @param asset The address of the underlying asset of the reserve + * @return The state of the reserve + **/ + function getReserveData(address asset) external view returns (DataTypes.ReserveData memory); + + function finalizeTransfer( + address asset, + address from, + address to, + uint256 amount, + uint256 balanceFromAfter, + uint256 balanceToBefore + ) external; + + function getReservesList() external view returns (address[] memory); + + function getAddressesProvider() external view returns (ILendingPoolAddressesProvider); + + function setPause(bool val) external; + + function paused() external view returns (bool); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/ILendingPoolAddressesProvider.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/ILendingPoolAddressesProvider.sol new file mode 100644 index 000000000..89b217869 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/ILendingPoolAddressesProvider.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +/** + * @title LendingPoolAddressesProvider contract + * @dev Main registry of addresses part of or connected to the protocol, including permissioned roles + * - Acting also as factory of proxies and admin of those, so with right to change its implementations + * - Owned by the Aave Governance + * @author Aave + **/ +interface ILendingPoolAddressesProvider { + event MarketIdSet(string newMarketId); + event LendingPoolUpdated(address indexed newAddress); + event ConfigurationAdminUpdated(address indexed newAddress); + event EmergencyAdminUpdated(address indexed newAddress); + event LendingPoolConfiguratorUpdated(address indexed newAddress); + event LendingPoolCollateralManagerUpdated(address indexed newAddress); + event PriceOracleUpdated(address indexed newAddress); + event LendingRateOracleUpdated(address indexed newAddress); + event ProxyCreated(bytes32 id, address indexed newAddress); + event AddressSet(bytes32 id, address indexed newAddress, bool hasProxy); + + function getMarketId() external view returns (string memory); + + function setMarketId(string calldata marketId) external; + + function setAddress(bytes32 id, address newAddress) external; + + function setAddressAsProxy(bytes32 id, address impl) external; + + function getAddress(bytes32 id) external view returns (address); + + function getLendingPool() external view returns (address); + + function setLendingPoolImpl(address pool) external; + + function getLendingPoolConfigurator() external view returns (address); + + function setLendingPoolConfiguratorImpl(address configurator) external; + + function getLendingPoolCollateralManager() external view returns (address); + + function setLendingPoolCollateralManager(address manager) external; + + function getPoolAdmin() external view returns (address); + + function setPoolAdmin(address admin) external; + + function getEmergencyAdmin() external view returns (address); + + function setEmergencyAdmin(address admin) external; + + function getPriceOracle() external view returns (address); + + function setPriceOracle(address priceOracle) external; + + function getLendingRateOracle() external view returns (address); + + function setLendingRateOracle(address lendingRateOracle) external; +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IPriceOracleGetter.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IPriceOracleGetter.sol new file mode 100644 index 000000000..c8b220707 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IPriceOracleGetter.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +/** + * @title IPriceOracleGetter interface + * @notice Interface for the Aave price oracle. + **/ + +interface IPriceOracleGetter { + /** + * @dev returns the asset price in ETH + * @param asset the address of the asset + * @return the ETH price of the asset + **/ + function getAssetPrice(address asset) external view returns (uint256); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IReserveInterestRateStrategy.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IReserveInterestRateStrategy.sol new file mode 100644 index 000000000..d902050b1 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IReserveInterestRateStrategy.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +/** + * @title IReserveInterestRateStrategyInterface interface + * @dev Interface for the calculation of the interest rates + * @author Aave + */ +interface IReserveInterestRateStrategy { + function baseVariableBorrowRate() external view returns (uint256); + + function getMaxVariableBorrowRate() external view returns (uint256); + + function calculateInterestRates( + address reserve, + uint256 availableLiquidity, + uint256 totalStableDebt, + uint256 totalVariableDebt, + uint256 averageStableBorrowRate, + uint256 reserveFactor + ) + external + view + returns ( + uint256, + uint256, + uint256 + ); + + function calculateInterestRates( + address reserve, + address aToken, + uint256 liquidityAdded, + uint256 liquidityTaken, + uint256 totalStableDebt, + uint256 totalVariableDebt, + uint256 averageStableBorrowRate, + uint256 reserveFactor + ) + external + view + returns ( + uint256 liquidityRate, + uint256 stableBorrowRate, + uint256 variableBorrowRate + ); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IScaledBalanceToken.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IScaledBalanceToken.sol new file mode 100644 index 000000000..cbf02ce08 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IScaledBalanceToken.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +interface IScaledBalanceToken { + /** + * @dev Returns the scaled balance of the user. The scaled balance is the sum of all the + * updated stored balance divided by the reserve's liquidity index at the moment of the update + * @param user The user whose balance is calculated + * @return The scaled balance of the user + **/ + function scaledBalanceOf(address user) external view returns (uint256); + + /** + * @dev Returns the scaled balance of the user and the scaled total supply. + * @param user The address of the user + * @return The scaled balance of the user + * @return The scaled balance and the scaled total supply + **/ + function getScaledUserBalanceAndSupply(address user) external view returns (uint256, uint256); + + /** + * @dev Returns the scaled total supply of the variable debt token. Represents sum(debt/index) + * @return The scaled total supply + **/ + function scaledTotalSupply() external view returns (uint256); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IStableDebtToken.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IStableDebtToken.sol new file mode 100644 index 000000000..1efef4011 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IStableDebtToken.sol @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {IInitializableDebtToken} from './IInitializableDebtToken.sol'; +import {IAaveIncentivesController} from './IAaveIncentivesController.sol'; + +/** + * @title IStableDebtToken + * @notice Defines the interface for the stable debt token + * @dev It does not inherit from IERC20 to save in code size + * @author Aave + **/ + +interface IStableDebtToken is IInitializableDebtToken { + /** + * @dev Emitted when new stable debt is minted + * @param user The address of the user who triggered the minting + * @param onBehalfOf The recipient of stable debt tokens + * @param amount The amount minted + * @param currentBalance The current balance of the user + * @param balanceIncrease The increase in balance since the last action of the user + * @param newRate The rate of the debt after the minting + * @param avgStableRate The new average stable rate after the minting + * @param newTotalSupply The new total supply of the stable debt token after the action + **/ + event Mint( + address indexed user, + address indexed onBehalfOf, + uint256 amount, + uint256 currentBalance, + uint256 balanceIncrease, + uint256 newRate, + uint256 avgStableRate, + uint256 newTotalSupply + ); + + /** + * @dev Emitted when new stable debt is burned + * @param user The address of the user + * @param amount The amount being burned + * @param currentBalance The current balance of the user + * @param balanceIncrease The the increase in balance since the last action of the user + * @param avgStableRate The new average stable rate after the burning + * @param newTotalSupply The new total supply of the stable debt token after the action + **/ + event Burn( + address indexed user, + uint256 amount, + uint256 currentBalance, + uint256 balanceIncrease, + uint256 avgStableRate, + uint256 newTotalSupply + ); + + /** + * @dev Mints debt token to the `onBehalfOf` address. + * - The resulting rate is the weighted average between the rate of the new debt + * and the rate of the previous debt + * @param user The address receiving the borrowed underlying, being the delegatee in case + * of credit delegate, or same as `onBehalfOf` otherwise + * @param onBehalfOf The address receiving the debt tokens + * @param amount The amount of debt tokens to mint + * @param rate The rate of the debt being minted + **/ + function mint( + address user, + address onBehalfOf, + uint256 amount, + uint256 rate + ) external returns (bool); + + /** + * @dev Burns debt of `user` + * - The resulting rate is the weighted average between the rate of the new debt + * and the rate of the previous debt + * @param user The address of the user getting his debt burned + * @param amount The amount of debt tokens getting burned + **/ + function burn(address user, uint256 amount) external; + + /** + * @dev Returns the average rate of all the stable rate loans. + * @return The average stable rate + **/ + function getAverageStableRate() external view returns (uint256); + + /** + * @dev Returns the stable rate of the user debt + * @return The stable rate of the user + **/ + function getUserStableRate(address user) external view returns (uint256); + + /** + * @dev Returns the timestamp of the last update of the user + * @return The timestamp + **/ + function getUserLastUpdated(address user) external view returns (uint40); + + /** + * @dev Returns the principal, the total supply and the average stable rate + **/ + function getSupplyData() + external + view + returns ( + uint256, + uint256, + uint256, + uint40 + ); + + /** + * @dev Returns the timestamp of the last update of the total supply + * @return The timestamp + **/ + function getTotalSupplyLastUpdated() external view returns (uint40); + + /** + * @dev Returns the total supply and the average stable rate + **/ + function getTotalSupplyAndAvgRate() external view returns (uint256, uint256); + + /** + * @dev Returns the principal debt balance of the user + * @return The debt balance of the user since the last burn/mint action + **/ + function principalBalanceOf(address user) external view returns (uint256); + + /** + * @dev Returns the address of the incentives controller contract + **/ + function getIncentivesController() external view returns (IAaveIncentivesController); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IVariableDebtToken.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IVariableDebtToken.sol new file mode 100644 index 000000000..2411ffbca --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/IVariableDebtToken.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {IScaledBalanceToken} from './IScaledBalanceToken.sol'; +import {IInitializableDebtToken} from './IInitializableDebtToken.sol'; +import {IAaveIncentivesController} from './IAaveIncentivesController.sol'; + +/** + * @title IVariableDebtToken + * @author Aave + * @notice Defines the basic interface for a variable debt token. + **/ +interface IVariableDebtToken is IScaledBalanceToken, IInitializableDebtToken { + /** + * @dev Emitted after the mint action + * @param from The address performing the mint + * @param onBehalfOf The address of the user on which behalf minting has been performed + * @param value The amount to be minted + * @param index The last index of the reserve + **/ + event Mint(address indexed from, address indexed onBehalfOf, uint256 value, uint256 index); + + /** + * @dev Mints debt token to the `onBehalfOf` address + * @param user The address receiving the borrowed underlying, being the delegatee in case + * of credit delegate, or same as `onBehalfOf` otherwise + * @param onBehalfOf The address receiving the debt tokens + * @param amount The amount of debt being minted + * @param index The variable debt index of the reserve + * @return `true` if the the previous balance of the user is 0 + **/ + function mint( + address user, + address onBehalfOf, + uint256 amount, + uint256 index + ) external returns (bool); + + /** + * @dev Emitted when variable debt is burnt + * @param user The user which debt has been burned + * @param amount The amount of debt being burned + * @param index The index of the user + **/ + event Burn(address indexed user, uint256 amount, uint256 index); + + /** + * @dev Burns user variable debt + * @param user The user which debt is burnt + * @param index The variable debt index of the reserve + **/ + function burn( + address user, + uint256 amount, + uint256 index + ) external; + + /** + * @dev Returns the address of the incentives controller contract + **/ + function getIncentivesController() external view returns (IAaveIncentivesController); +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/LendingPool.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/LendingPool.sol new file mode 100644 index 000000000..3fd1daacb --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/LendingPool.sol @@ -0,0 +1,946 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol'; +import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol'; +import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol'; +import {Address} from '../../dependencies/openzeppelin/contracts/Address.sol'; +import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol'; +import {IAToken} from '../../interfaces/IAToken.sol'; +import {IVariableDebtToken} from '../../interfaces/IVariableDebtToken.sol'; +import {IFlashLoanReceiver} from '../../flashloan/interfaces/IFlashLoanReceiver.sol'; +import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol'; +import {IStableDebtToken} from '../../interfaces/IStableDebtToken.sol'; +import {ILendingPool} from '../../interfaces/ILendingPool.sol'; +import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol'; +import {Helpers} from '../libraries/helpers/Helpers.sol'; +import {Errors} from '../libraries/helpers/Errors.sol'; +import {WadRayMath} from '../libraries/math/WadRayMath.sol'; +import {PercentageMath} from '../libraries/math/PercentageMath.sol'; +import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol'; +import {GenericLogic} from '../libraries/logic/GenericLogic.sol'; +import {ValidationLogic} from '../libraries/logic/ValidationLogic.sol'; +import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol'; +import {UserConfiguration} from '../libraries/configuration/UserConfiguration.sol'; +import {DataTypes} from '../libraries/types/DataTypes.sol'; +import {LendingPoolStorage} from './LendingPoolStorage.sol'; + +/** + * @title LendingPool contract + * @dev Main point of interaction with an Aave protocol's market + * - Users can: + * # Deposit + * # Withdraw + * # Borrow + * # Repay + * # Swap their loans between variable and stable rate + * # Enable/disable their deposits as collateral rebalance stable rate borrow positions + * # Liquidate positions + * # Execute Flash Loans + * - To be covered by a proxy contract, owned by the LendingPoolAddressesProvider of the specific market + * - All admin functions are callable by the LendingPoolConfigurator contract defined also in the + * LendingPoolAddressesProvider + * @author Aave + **/ +contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage { + using SafeMath for uint256; + using WadRayMath for uint256; + using PercentageMath for uint256; + using SafeERC20 for IERC20; + + uint256 public constant LENDINGPOOL_REVISION = 0x2; + + modifier whenNotPaused() { + _whenNotPaused(); + _; + } + + modifier onlyLendingPoolConfigurator() { + _onlyLendingPoolConfigurator(); + _; + } + + function _whenNotPaused() internal view { + require(!_paused, Errors.LP_IS_PAUSED); + } + + function _onlyLendingPoolConfigurator() internal view { + require( + _addressesProvider.getLendingPoolConfigurator() == msg.sender, + Errors.LP_CALLER_NOT_LENDING_POOL_CONFIGURATOR + ); + } + + function getRevision() internal pure override returns (uint256) { + return LENDINGPOOL_REVISION; + } + + /** + * @dev Function is invoked by the proxy contract when the LendingPool contract is added to the + * LendingPoolAddressesProvider of the market. + * - Caching the address of the LendingPoolAddressesProvider in order to reduce gas consumption + * on subsequent operations + * @param provider The address of the LendingPoolAddressesProvider + **/ + function initialize(ILendingPoolAddressesProvider provider) public initializer { + _addressesProvider = provider; + _maxStableRateBorrowSizePercent = 2500; + _flashLoanPremiumTotal = 9; + _maxNumberOfReserves = 128; + } + + /** + * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens. + * - E.g. User deposits 100 USDC and gets in return 100 aUSDC + * @param asset The address of the underlying asset to deposit + * @param amount The amount to be deposited + * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user + * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens + * is a different wallet + * @param referralCode Code used to register the integrator originating the operation, for potential rewards. + * 0 if the action is executed directly by the user, without any middle-man + **/ + function deposit( + address asset, + uint256 amount, + address onBehalfOf, + uint16 referralCode + ) external override whenNotPaused { + DataTypes.ReserveData storage reserve = _reserves[asset]; + + ValidationLogic.validateDeposit(reserve, amount); + + address aToken = reserve.aTokenAddress; + + reserve.updateState(); + reserve.updateInterestRates(asset, aToken, amount, 0); + + IERC20(asset).safeTransferFrom(msg.sender, aToken, amount); + + bool isFirstDeposit = IAToken(aToken).mint(onBehalfOf, amount, reserve.liquidityIndex); + + if (isFirstDeposit) { + _usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true); + emit ReserveUsedAsCollateralEnabled(asset, onBehalfOf); + } + + emit Deposit(asset, msg.sender, onBehalfOf, amount, referralCode); + } + + /** + * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned + * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC + * @param asset The address of the underlying asset to withdraw + * @param amount The underlying amount to be withdrawn + * - Send the value type(uint256).max in order to withdraw the whole aToken balance + * @param to Address that will receive the underlying, same as msg.sender if the user + * wants to receive it on his own wallet, or a different address if the beneficiary is a + * different wallet + * @return The final amount withdrawn + **/ + function withdraw( + address asset, + uint256 amount, + address to + ) external override whenNotPaused returns (uint256) { + DataTypes.ReserveData storage reserve = _reserves[asset]; + + address aToken = reserve.aTokenAddress; + + uint256 userBalance = IAToken(aToken).balanceOf(msg.sender); + + uint256 amountToWithdraw = amount; + + if (amount == type(uint256).max) { + amountToWithdraw = userBalance; + } + + ValidationLogic.validateWithdraw( + asset, + amountToWithdraw, + userBalance, + _reserves, + _usersConfig[msg.sender], + _reservesList, + _reservesCount, + _addressesProvider.getPriceOracle() + ); + + reserve.updateState(); + + reserve.updateInterestRates(asset, aToken, 0, amountToWithdraw); + + if (amountToWithdraw == userBalance) { + _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, false); + emit ReserveUsedAsCollateralDisabled(asset, msg.sender); + } + + IAToken(aToken).burn(msg.sender, to, amountToWithdraw, reserve.liquidityIndex); + + emit Withdraw(asset, msg.sender, to, amountToWithdraw); + + return amountToWithdraw; + } + + /** + * @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower + * already deposited enough collateral, or he was given enough allowance by a credit delegator on the + * corresponding debt token (StableDebtToken or VariableDebtToken) + * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet + * and 100 stable/variable debt tokens, depending on the `interestRateMode` + * @param asset The address of the underlying asset to borrow + * @param amount The amount to be borrowed + * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable + * @param referralCode Code used to register the integrator originating the operation, for potential rewards. + * 0 if the action is executed directly by the user, without any middle-man + * @param onBehalfOf Address of the user who will receive the debt. Should be the address of the borrower itself + * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator + * if he has been given credit delegation allowance + **/ + function borrow( + address asset, + uint256 amount, + uint256 interestRateMode, + uint16 referralCode, + address onBehalfOf + ) external override whenNotPaused { + DataTypes.ReserveData storage reserve = _reserves[asset]; + + _executeBorrow( + ExecuteBorrowParams( + asset, + msg.sender, + onBehalfOf, + amount, + interestRateMode, + reserve.aTokenAddress, + referralCode, + true + ) + ); + } + + /** + * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned + * - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address + * @param asset The address of the borrowed underlying asset previously borrowed + * @param amount The amount to repay + * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode` + * @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable + * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the + * user calling the function if he wants to reduce/remove his own debt, or the address of any other + * other borrower whose debt should be removed + * @return The final amount repaid + **/ + function repay( + address asset, + uint256 amount, + uint256 rateMode, + address onBehalfOf + ) external override whenNotPaused returns (uint256) { + DataTypes.ReserveData storage reserve = _reserves[asset]; + + (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(onBehalfOf, reserve); + + DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode); + + ValidationLogic.validateRepay( + reserve, + amount, + interestRateMode, + onBehalfOf, + stableDebt, + variableDebt + ); + + uint256 paybackAmount = + interestRateMode == DataTypes.InterestRateMode.STABLE ? stableDebt : variableDebt; + + if (amount < paybackAmount) { + paybackAmount = amount; + } + + reserve.updateState(); + + if (interestRateMode == DataTypes.InterestRateMode.STABLE) { + IStableDebtToken(reserve.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount); + } else { + IVariableDebtToken(reserve.variableDebtTokenAddress).burn( + onBehalfOf, + paybackAmount, + reserve.variableBorrowIndex + ); + } + + address aToken = reserve.aTokenAddress; + reserve.updateInterestRates(asset, aToken, paybackAmount, 0); + + if (stableDebt.add(variableDebt).sub(paybackAmount) == 0) { + _usersConfig[onBehalfOf].setBorrowing(reserve.id, false); + } + + IERC20(asset).safeTransferFrom(msg.sender, aToken, paybackAmount); + + IAToken(aToken).handleRepayment(msg.sender, paybackAmount); + + emit Repay(asset, onBehalfOf, msg.sender, paybackAmount); + + return paybackAmount; + } + + /** + * @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa + * @param asset The address of the underlying asset borrowed + * @param rateMode The rate mode that the user wants to swap to + **/ + function swapBorrowRateMode(address asset, uint256 rateMode) external override whenNotPaused { + DataTypes.ReserveData storage reserve = _reserves[asset]; + + (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(msg.sender, reserve); + + DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode); + + ValidationLogic.validateSwapRateMode( + reserve, + _usersConfig[msg.sender], + stableDebt, + variableDebt, + interestRateMode + ); + + reserve.updateState(); + + if (interestRateMode == DataTypes.InterestRateMode.STABLE) { + IStableDebtToken(reserve.stableDebtTokenAddress).burn(msg.sender, stableDebt); + IVariableDebtToken(reserve.variableDebtTokenAddress).mint( + msg.sender, + msg.sender, + stableDebt, + reserve.variableBorrowIndex + ); + } else { + IVariableDebtToken(reserve.variableDebtTokenAddress).burn( + msg.sender, + variableDebt, + reserve.variableBorrowIndex + ); + IStableDebtToken(reserve.stableDebtTokenAddress).mint( + msg.sender, + msg.sender, + variableDebt, + reserve.currentStableBorrowRate + ); + } + + reserve.updateInterestRates(asset, reserve.aTokenAddress, 0, 0); + + emit Swap(asset, msg.sender, rateMode); + } + + /** + * @dev Rebalances the stable interest rate of a user to the current stable rate defined on the reserve. + * - Users can be rebalanced if the following conditions are satisfied: + * 1. Usage ratio is above 95% + * 2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been + * borrowed at a stable rate and depositors are not earning enough + * @param asset The address of the underlying asset borrowed + * @param user The address of the user to be rebalanced + **/ + function rebalanceStableBorrowRate(address asset, address user) external override whenNotPaused { + DataTypes.ReserveData storage reserve = _reserves[asset]; + + IERC20 stableDebtToken = IERC20(reserve.stableDebtTokenAddress); + IERC20 variableDebtToken = IERC20(reserve.variableDebtTokenAddress); + address aTokenAddress = reserve.aTokenAddress; + + uint256 stableDebt = IERC20(stableDebtToken).balanceOf(user); + + ValidationLogic.validateRebalanceStableBorrowRate( + reserve, + asset, + stableDebtToken, + variableDebtToken, + aTokenAddress + ); + + reserve.updateState(); + + IStableDebtToken(address(stableDebtToken)).burn(user, stableDebt); + IStableDebtToken(address(stableDebtToken)).mint( + user, + user, + stableDebt, + reserve.currentStableBorrowRate + ); + + reserve.updateInterestRates(asset, aTokenAddress, 0, 0); + + emit RebalanceStableBorrowRate(asset, user); + } + + /** + * @dev Allows depositors to enable/disable a specific deposited asset as collateral + * @param asset The address of the underlying asset deposited + * @param useAsCollateral `true` if the user wants to use the deposit as collateral, `false` otherwise + **/ + function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) + external + override + whenNotPaused + { + DataTypes.ReserveData storage reserve = _reserves[asset]; + + ValidationLogic.validateSetUseReserveAsCollateral( + reserve, + asset, + useAsCollateral, + _reserves, + _usersConfig[msg.sender], + _reservesList, + _reservesCount, + _addressesProvider.getPriceOracle() + ); + + _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, useAsCollateral); + + if (useAsCollateral) { + emit ReserveUsedAsCollateralEnabled(asset, msg.sender); + } else { + emit ReserveUsedAsCollateralDisabled(asset, msg.sender); + } + } + + /** + * @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1 + * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives + * a proportionally amount of the `collateralAsset` plus a bonus to cover market risk + * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation + * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation + * @param user The address of the borrower getting liquidated + * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover + * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants + * to receive the underlying collateral asset directly + **/ + function liquidationCall( + address collateralAsset, + address debtAsset, + address user, + uint256 debtToCover, + bool receiveAToken + ) external override whenNotPaused { + address collateralManager = _addressesProvider.getLendingPoolCollateralManager(); + + //solium-disable-next-line + (bool success, bytes memory result) = + collateralManager.delegatecall( + abi.encodeWithSignature( + 'liquidationCall(address,address,address,uint256,bool)', + collateralAsset, + debtAsset, + user, + debtToCover, + receiveAToken + ) + ); + + require(success, Errors.LP_LIQUIDATION_CALL_FAILED); + + (uint256 returnCode, string memory returnMessage) = abi.decode(result, (uint256, string)); + + require(returnCode == 0, string(abi.encodePacked(returnMessage))); + } + + struct FlashLoanLocalVars { + IFlashLoanReceiver receiver; + address oracle; + uint256 i; + address currentAsset; + address currentATokenAddress; + uint256 currentAmount; + uint256 currentPremium; + uint256 currentAmountPlusPremium; + address debtToken; + } + + /** + * @dev Allows smartcontracts to access the liquidity of the pool within one transaction, + * as long as the amount taken plus a fee is returned. + * IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration. + * For further details please visit https://developers.aave.com + * @param receiverAddress The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface + * @param assets The addresses of the assets being flash-borrowed + * @param amounts The amounts amounts being flash-borrowed + * @param modes Types of the debt to open if the flash loan is not returned: + * 0 -> Don't open any debt, just revert if funds can't be transferred from the receiver + * 1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address + * 2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address + * @param onBehalfOf The address that will receive the debt in the case of using on `modes` 1 or 2 + * @param params Variadic packed params to pass to the receiver as extra information + * @param referralCode Code used to register the integrator originating the operation, for potential rewards. + * 0 if the action is executed directly by the user, without any middle-man + **/ + function flashLoan( + address receiverAddress, + address[] calldata assets, + uint256[] calldata amounts, + uint256[] calldata modes, + address onBehalfOf, + bytes calldata params, + uint16 referralCode + ) external override whenNotPaused { + FlashLoanLocalVars memory vars; + + ValidationLogic.validateFlashloan(assets, amounts); + + address[] memory aTokenAddresses = new address[](assets.length); + uint256[] memory premiums = new uint256[](assets.length); + + vars.receiver = IFlashLoanReceiver(receiverAddress); + + for (vars.i = 0; vars.i < assets.length; vars.i++) { + aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress; + + premiums[vars.i] = amounts[vars.i].mul(_flashLoanPremiumTotal).div(10000); + + IAToken(aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]); + } + + require( + vars.receiver.executeOperation(assets, amounts, premiums, msg.sender, params), + Errors.LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN + ); + + for (vars.i = 0; vars.i < assets.length; vars.i++) { + vars.currentAsset = assets[vars.i]; + vars.currentAmount = amounts[vars.i]; + vars.currentPremium = premiums[vars.i]; + vars.currentATokenAddress = aTokenAddresses[vars.i]; + vars.currentAmountPlusPremium = vars.currentAmount.add(vars.currentPremium); + + if (DataTypes.InterestRateMode(modes[vars.i]) == DataTypes.InterestRateMode.NONE) { + _reserves[vars.currentAsset].updateState(); + _reserves[vars.currentAsset].cumulateToLiquidityIndex( + IERC20(vars.currentATokenAddress).totalSupply(), + vars.currentPremium + ); + _reserves[vars.currentAsset].updateInterestRates( + vars.currentAsset, + vars.currentATokenAddress, + vars.currentAmountPlusPremium, + 0 + ); + + IERC20(vars.currentAsset).safeTransferFrom( + receiverAddress, + vars.currentATokenAddress, + vars.currentAmountPlusPremium + ); + } else { + // If the user chose to not return the funds, the system checks if there is enough collateral and + // eventually opens a debt position + _executeBorrow( + ExecuteBorrowParams( + vars.currentAsset, + msg.sender, + onBehalfOf, + vars.currentAmount, + modes[vars.i], + vars.currentATokenAddress, + referralCode, + false + ) + ); + } + emit FlashLoan( + receiverAddress, + msg.sender, + vars.currentAsset, + vars.currentAmount, + vars.currentPremium, + referralCode + ); + } + } + + /** + * @dev Returns the state and configuration of the reserve + * @param asset The address of the underlying asset of the reserve + * @return The state of the reserve + **/ + function getReserveData(address asset) + external + view + override + returns (DataTypes.ReserveData memory) + { + return _reserves[asset]; + } + + /** + * @dev Returns the user account data across all the reserves + * @param user The address of the user + * @return totalCollateralETH the total collateral in ETH of the user + * @return totalDebtETH the total debt in ETH of the user + * @return availableBorrowsETH the borrowing power left of the user + * @return currentLiquidationThreshold the liquidation threshold of the user + * @return ltv the loan to value of the user + * @return healthFactor the current health factor of the user + **/ + function getUserAccountData(address user) + external + view + override + returns ( + uint256 totalCollateralETH, + uint256 totalDebtETH, + uint256 availableBorrowsETH, + uint256 currentLiquidationThreshold, + uint256 ltv, + uint256 healthFactor + ) + { + ( + totalCollateralETH, + totalDebtETH, + ltv, + currentLiquidationThreshold, + healthFactor + ) = GenericLogic.calculateUserAccountData( + user, + _reserves, + _usersConfig[user], + _reservesList, + _reservesCount, + _addressesProvider.getPriceOracle() + ); + + availableBorrowsETH = GenericLogic.calculateAvailableBorrowsETH( + totalCollateralETH, + totalDebtETH, + ltv + ); + } + + /** + * @dev Returns the configuration of the reserve + * @param asset The address of the underlying asset of the reserve + * @return The configuration of the reserve + **/ + function getConfiguration(address asset) + external + view + override + returns (DataTypes.ReserveConfigurationMap memory) + { + return _reserves[asset].configuration; + } + + /** + * @dev Returns the configuration of the user across all the reserves + * @param user The user address + * @return The configuration of the user + **/ + function getUserConfiguration(address user) + external + view + override + returns (DataTypes.UserConfigurationMap memory) + { + return _usersConfig[user]; + } + + /** + * @dev Returns the normalized income per unit of asset + * @param asset The address of the underlying asset of the reserve + * @return The reserve's normalized income + */ + function getReserveNormalizedIncome(address asset) + external + view + virtual + override + returns (uint256) + { + return _reserves[asset].getNormalizedIncome(); + } + + /** + * @dev Returns the normalized variable debt per unit of asset + * @param asset The address of the underlying asset of the reserve + * @return The reserve normalized variable debt + */ + function getReserveNormalizedVariableDebt(address asset) + external + view + override + returns (uint256) + { + return _reserves[asset].getNormalizedDebt(); + } + + /** + * @dev Returns if the LendingPool is paused + */ + function paused() external view override returns (bool) { + return _paused; + } + + /** + * @dev Returns the list of the initialized reserves + **/ + function getReservesList() external view override returns (address[] memory) { + address[] memory _activeReserves = new address[](_reservesCount); + + for (uint256 i = 0; i < _reservesCount; i++) { + _activeReserves[i] = _reservesList[i]; + } + return _activeReserves; + } + + /** + * @dev Returns the cached LendingPoolAddressesProvider connected to this contract + **/ + function getAddressesProvider() external view override returns (ILendingPoolAddressesProvider) { + return _addressesProvider; + } + + /** + * @dev Returns the percentage of available liquidity that can be borrowed at once at stable rate + */ + function MAX_STABLE_RATE_BORROW_SIZE_PERCENT() public view returns (uint256) { + return _maxStableRateBorrowSizePercent; + } + + /** + * @dev Returns the fee on flash loans + */ + function FLASHLOAN_PREMIUM_TOTAL() public view returns (uint256) { + return _flashLoanPremiumTotal; + } + + /** + * @dev Returns the maximum number of reserves supported to be listed in this LendingPool + */ + function MAX_NUMBER_RESERVES() public view returns (uint256) { + return _maxNumberOfReserves; + } + + /** + * @dev Validates and finalizes an aToken transfer + * - Only callable by the overlying aToken of the `asset` + * @param asset The address of the underlying asset of the aToken + * @param from The user from which the aTokens are transferred + * @param to The user receiving the aTokens + * @param amount The amount being transferred/withdrawn + * @param balanceFromBefore The aToken balance of the `from` user before the transfer + * @param balanceToBefore The aToken balance of the `to` user before the transfer + */ + function finalizeTransfer( + address asset, + address from, + address to, + uint256 amount, + uint256 balanceFromBefore, + uint256 balanceToBefore + ) external override whenNotPaused { + require(msg.sender == _reserves[asset].aTokenAddress, Errors.LP_CALLER_MUST_BE_AN_ATOKEN); + + ValidationLogic.validateTransfer( + from, + _reserves, + _usersConfig[from], + _reservesList, + _reservesCount, + _addressesProvider.getPriceOracle() + ); + + uint256 reserveId = _reserves[asset].id; + + if (from != to) { + if (balanceFromBefore.sub(amount) == 0) { + DataTypes.UserConfigurationMap storage fromConfig = _usersConfig[from]; + fromConfig.setUsingAsCollateral(reserveId, false); + emit ReserveUsedAsCollateralDisabled(asset, from); + } + + if (balanceToBefore == 0 && amount != 0) { + DataTypes.UserConfigurationMap storage toConfig = _usersConfig[to]; + toConfig.setUsingAsCollateral(reserveId, true); + emit ReserveUsedAsCollateralEnabled(asset, to); + } + } + } + + /** + * @dev Initializes a reserve, activating it, assigning an aToken and debt tokens and an + * interest rate strategy + * - Only callable by the LendingPoolConfigurator contract + * @param asset The address of the underlying asset of the reserve + * @param aTokenAddress The address of the aToken that will be assigned to the reserve + * @param stableDebtAddress The address of the StableDebtToken that will be assigned to the reserve + * @param aTokenAddress The address of the VariableDebtToken that will be assigned to the reserve + * @param interestRateStrategyAddress The address of the interest rate strategy contract + **/ + function initReserve( + address asset, + address aTokenAddress, + address stableDebtAddress, + address variableDebtAddress, + address interestRateStrategyAddress + ) external override onlyLendingPoolConfigurator { + require(Address.isContract(asset), Errors.LP_NOT_CONTRACT); + _reserves[asset].init( + aTokenAddress, + stableDebtAddress, + variableDebtAddress, + interestRateStrategyAddress + ); + _addReserveToList(asset); + } + + /** + * @dev Updates the address of the interest rate strategy contract + * - Only callable by the LendingPoolConfigurator contract + * @param asset The address of the underlying asset of the reserve + * @param rateStrategyAddress The address of the interest rate strategy contract + **/ + function setReserveInterestRateStrategyAddress(address asset, address rateStrategyAddress) + external + override + onlyLendingPoolConfigurator + { + _reserves[asset].interestRateStrategyAddress = rateStrategyAddress; + } + + /** + * @dev Sets the configuration bitmap of the reserve as a whole + * - Only callable by the LendingPoolConfigurator contract + * @param asset The address of the underlying asset of the reserve + * @param configuration The new configuration bitmap + **/ + function setConfiguration(address asset, uint256 configuration) + external + override + onlyLendingPoolConfigurator + { + _reserves[asset].configuration.data = configuration; + } + + /** + * @dev Set the _pause state of a reserve + * - Only callable by the LendingPoolConfigurator contract + * @param val `true` to pause the reserve, `false` to un-pause it + */ + function setPause(bool val) external override onlyLendingPoolConfigurator { + _paused = val; + if (_paused) { + emit Paused(); + } else { + emit Unpaused(); + } + } + + struct ExecuteBorrowParams { + address asset; + address user; + address onBehalfOf; + uint256 amount; + uint256 interestRateMode; + address aTokenAddress; + uint16 referralCode; + bool releaseUnderlying; + } + + function _executeBorrow(ExecuteBorrowParams memory vars) internal { + DataTypes.ReserveData storage reserve = _reserves[vars.asset]; + DataTypes.UserConfigurationMap storage userConfig = _usersConfig[vars.onBehalfOf]; + + address oracle = _addressesProvider.getPriceOracle(); + + uint256 amountInETH = + IPriceOracleGetter(oracle).getAssetPrice(vars.asset).mul(vars.amount).div( + 10**reserve.configuration.getDecimals() + ); + + ValidationLogic.validateBorrow( + vars.asset, + reserve, + vars.onBehalfOf, + vars.amount, + amountInETH, + vars.interestRateMode, + _maxStableRateBorrowSizePercent, + _reserves, + userConfig, + _reservesList, + _reservesCount, + oracle + ); + + reserve.updateState(); + + uint256 currentStableRate = 0; + + bool isFirstBorrowing = false; + if (DataTypes.InterestRateMode(vars.interestRateMode) == DataTypes.InterestRateMode.STABLE) { + currentStableRate = reserve.currentStableBorrowRate; + + isFirstBorrowing = IStableDebtToken(reserve.stableDebtTokenAddress).mint( + vars.user, + vars.onBehalfOf, + vars.amount, + currentStableRate + ); + } else { + isFirstBorrowing = IVariableDebtToken(reserve.variableDebtTokenAddress).mint( + vars.user, + vars.onBehalfOf, + vars.amount, + reserve.variableBorrowIndex + ); + } + + if (isFirstBorrowing) { + userConfig.setBorrowing(reserve.id, true); + } + + reserve.updateInterestRates( + vars.asset, + vars.aTokenAddress, + 0, + vars.releaseUnderlying ? vars.amount : 0 + ); + + if (vars.releaseUnderlying) { + IAToken(vars.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount); + } + + emit Borrow( + vars.asset, + vars.user, + vars.onBehalfOf, + vars.amount, + vars.interestRateMode, + DataTypes.InterestRateMode(vars.interestRateMode) == DataTypes.InterestRateMode.STABLE + ? currentStableRate + : reserve.currentVariableBorrowRate, + vars.referralCode + ); + } + + function _addReserveToList(address asset) internal { + uint256 reservesCount = _reservesCount; + + require(reservesCount < _maxNumberOfReserves, Errors.LP_NO_MORE_RESERVES_ALLOWED); + + bool reserveAlreadyAdded = _reserves[asset].id != 0 || _reservesList[0] == asset; + + if (!reserveAlreadyAdded) { + _reserves[asset].id = uint8(reservesCount); + _reservesList[reservesCount] = asset; + + _reservesCount = reservesCount + 1; + } + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/LendingPoolStorage.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/LendingPoolStorage.sol new file mode 100644 index 000000000..586a11eea --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/LendingPoolStorage.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {UserConfiguration} from '../libraries/configuration/UserConfiguration.sol'; +import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol'; +import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol'; +import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol'; +import {DataTypes} from '../libraries/types/DataTypes.sol'; + +contract LendingPoolStorage { + using ReserveLogic for DataTypes.ReserveData; + using ReserveConfiguration for DataTypes.ReserveConfigurationMap; + using UserConfiguration for DataTypes.UserConfigurationMap; + + ILendingPoolAddressesProvider internal _addressesProvider; + + mapping(address => DataTypes.ReserveData) internal _reserves; + mapping(address => DataTypes.UserConfigurationMap) internal _usersConfig; + + // the list of the available reserves, structured as a mapping for gas savings reasons + mapping(uint256 => address) internal _reservesList; + + uint256 internal _reservesCount; + + bool internal _paused; + + uint256 internal _maxStableRateBorrowSizePercent; + + uint256 internal _flashLoanPremiumTotal; + + uint256 internal _maxNumberOfReserves; +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/MathUtils.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/MathUtils.sol new file mode 100644 index 000000000..54ed9a61d --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/MathUtils.sol @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol'; +import {WadRayMath} from './WadRayMath.sol'; + +library MathUtils { + using SafeMath for uint256; + using WadRayMath for uint256; + + /// @dev Ignoring leap years + uint256 internal constant SECONDS_PER_YEAR = 365 days; + + /** + * @dev Function to calculate the interest accumulated using a linear interest rate formula + * @param rate The interest rate, in ray + * @param lastUpdateTimestamp The timestamp of the last update of the interest + * @return The interest rate linearly accumulated during the timeDelta, in ray + **/ + + function calculateLinearInterest(uint256 rate, uint40 lastUpdateTimestamp) + internal + view + returns (uint256) + { + //solium-disable-next-line + uint256 timeDifference = block.timestamp.sub(uint256(lastUpdateTimestamp)); + + return (rate.mul(timeDifference) / SECONDS_PER_YEAR).add(WadRayMath.ray()); + } + + /** + * @dev Function to calculate the interest using a compounded interest rate formula + * To avoid expensive exponentiation, the calculation is performed using a binomial approximation: + * + * (1+x)^n = 1+n*x+[n/2*(n-1)]*x^2+[n/6*(n-1)*(n-2)*x^3... + * + * The approximation slightly underpays liquidity providers and undercharges borrowers, with the advantage of great gas cost reductions + * The whitepaper contains reference to the approximation and a table showing the margin of error per different time periods + * + * @param rate The interest rate, in ray + * @param lastUpdateTimestamp The timestamp of the last update of the interest + * @return The interest rate compounded during the timeDelta, in ray + **/ + function calculateCompoundedInterest( + uint256 rate, + uint40 lastUpdateTimestamp, + uint256 currentTimestamp + ) internal pure returns (uint256) { + //solium-disable-next-line + uint256 exp = currentTimestamp.sub(uint256(lastUpdateTimestamp)); + + if (exp == 0) { + return WadRayMath.ray(); + } + + uint256 expMinusOne = exp - 1; + + uint256 expMinusTwo = exp > 2 ? exp - 2 : 0; + + uint256 ratePerSecond = rate / SECONDS_PER_YEAR; + + uint256 basePowerTwo = ratePerSecond.rayMul(ratePerSecond); + uint256 basePowerThree = basePowerTwo.rayMul(ratePerSecond); + + uint256 secondTerm = exp.mul(expMinusOne).mul(basePowerTwo) / 2; + uint256 thirdTerm = exp.mul(expMinusOne).mul(expMinusTwo).mul(basePowerThree) / 6; + + return WadRayMath.ray().add(ratePerSecond.mul(exp)).add(secondTerm).add(thirdTerm); + } + + /** + * @dev Calculates the compounded interest between the timestamp of the last update and the current block timestamp + * @param rate The interest rate (in ray) + * @param lastUpdateTimestamp The timestamp from which the interest accumulation needs to be calculated + **/ + function calculateCompoundedInterest(uint256 rate, uint40 lastUpdateTimestamp) + internal + view + returns (uint256) + { + return calculateCompoundedInterest(rate, lastUpdateTimestamp, block.timestamp); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/PercentageMath.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/PercentageMath.sol new file mode 100644 index 000000000..74d85d6bf --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/PercentageMath.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {Errors} from '../helpers/Errors.sol'; + +/** + * @title PercentageMath library + * @author Aave + * @notice Provides functions to perform percentage calculations + * @dev Percentages are defined by default with 2 decimals of precision (100.00). The precision is indicated by PERCENTAGE_FACTOR + * @dev Operations are rounded half up + **/ + +library PercentageMath { + uint256 constant PERCENTAGE_FACTOR = 1e4; //percentage plus two decimals + uint256 constant HALF_PERCENT = PERCENTAGE_FACTOR / 2; + + /** + * @dev Executes a percentage multiplication + * @param value The value of which the percentage needs to be calculated + * @param percentage The percentage of the value to be calculated + * @return The percentage of value + **/ + function percentMul(uint256 value, uint256 percentage) internal pure returns (uint256) { + if (value == 0 || percentage == 0) { + return 0; + } + + require( + value <= (type(uint256).max - HALF_PERCENT) / percentage, + Errors.MATH_MULTIPLICATION_OVERFLOW + ); + + return (value * percentage + HALF_PERCENT) / PERCENTAGE_FACTOR; + } + + /** + * @dev Executes a percentage division + * @param value The value of which the percentage needs to be calculated + * @param percentage The percentage of the value to be calculated + * @return The value divided the percentage + **/ + function percentDiv(uint256 value, uint256 percentage) internal pure returns (uint256) { + require(percentage != 0, Errors.MATH_DIVISION_BY_ZERO); + uint256 halfPercentage = percentage / 2; + + require( + value <= (type(uint256).max - halfPercentage) / PERCENTAGE_FACTOR, + Errors.MATH_MULTIPLICATION_OVERFLOW + ); + + return (value * PERCENTAGE_FACTOR + halfPercentage) / percentage; + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/ReserveConfiguration.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/ReserveConfiguration.sol new file mode 100644 index 000000000..638a44f2d --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/ReserveConfiguration.sol @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {Errors} from '../helpers/Errors.sol'; +import {DataTypes} from '../types/DataTypes.sol'; + +/** + * @title ReserveConfiguration library + * @author Aave + * @notice Implements the bitmap logic to handle the reserve configuration + */ +library ReserveConfiguration { + uint256 constant LTV_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000; // prettier-ignore + uint256 constant LIQUIDATION_THRESHOLD_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFF; // prettier-ignore + uint256 constant LIQUIDATION_BONUS_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFF; // prettier-ignore + uint256 constant DECIMALS_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFF; // prettier-ignore + uint256 constant ACTIVE_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFF; // prettier-ignore + uint256 constant FROZEN_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFF; // prettier-ignore + uint256 constant BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFF; // prettier-ignore + uint256 constant STABLE_BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFF; // prettier-ignore + uint256 constant RESERVE_FACTOR_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFF; // prettier-ignore + + /// @dev For the LTV, the start bit is 0 (up to 15), hence no bitshifting is needed + uint256 constant LIQUIDATION_THRESHOLD_START_BIT_POSITION = 16; + uint256 constant LIQUIDATION_BONUS_START_BIT_POSITION = 32; + uint256 constant RESERVE_DECIMALS_START_BIT_POSITION = 48; + uint256 constant IS_ACTIVE_START_BIT_POSITION = 56; + uint256 constant IS_FROZEN_START_BIT_POSITION = 57; + uint256 constant BORROWING_ENABLED_START_BIT_POSITION = 58; + uint256 constant STABLE_BORROWING_ENABLED_START_BIT_POSITION = 59; + uint256 constant RESERVE_FACTOR_START_BIT_POSITION = 64; + + uint256 constant MAX_VALID_LTV = 65535; + uint256 constant MAX_VALID_LIQUIDATION_THRESHOLD = 65535; + uint256 constant MAX_VALID_LIQUIDATION_BONUS = 65535; + uint256 constant MAX_VALID_DECIMALS = 255; + uint256 constant MAX_VALID_RESERVE_FACTOR = 65535; + + /** + * @dev Sets the Loan to Value of the reserve + * @param self The reserve configuration + * @param ltv the new ltv + **/ + function setLtv(DataTypes.ReserveConfigurationMap memory self, uint256 ltv) internal pure { + require(ltv <= MAX_VALID_LTV, Errors.RC_INVALID_LTV); + + self.data = (self.data & LTV_MASK) | ltv; + } + + /** + * @dev Gets the Loan to Value of the reserve + * @param self The reserve configuration + * @return The loan to value + **/ + function getLtv(DataTypes.ReserveConfigurationMap storage self) internal view returns (uint256) { + return self.data & ~LTV_MASK; + } + + /** + * @dev Sets the liquidation threshold of the reserve + * @param self The reserve configuration + * @param threshold The new liquidation threshold + **/ + function setLiquidationThreshold(DataTypes.ReserveConfigurationMap memory self, uint256 threshold) + internal + pure + { + require(threshold <= MAX_VALID_LIQUIDATION_THRESHOLD, Errors.RC_INVALID_LIQ_THRESHOLD); + + self.data = + (self.data & LIQUIDATION_THRESHOLD_MASK) | + (threshold << LIQUIDATION_THRESHOLD_START_BIT_POSITION); + } + + /** + * @dev Gets the liquidation threshold of the reserve + * @param self The reserve configuration + * @return The liquidation threshold + **/ + function getLiquidationThreshold(DataTypes.ReserveConfigurationMap storage self) + internal + view + returns (uint256) + { + return (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION; + } + + /** + * @dev Sets the liquidation bonus of the reserve + * @param self The reserve configuration + * @param bonus The new liquidation bonus + **/ + function setLiquidationBonus(DataTypes.ReserveConfigurationMap memory self, uint256 bonus) + internal + pure + { + require(bonus <= MAX_VALID_LIQUIDATION_BONUS, Errors.RC_INVALID_LIQ_BONUS); + + self.data = + (self.data & LIQUIDATION_BONUS_MASK) | + (bonus << LIQUIDATION_BONUS_START_BIT_POSITION); + } + + /** + * @dev Gets the liquidation bonus of the reserve + * @param self The reserve configuration + * @return The liquidation bonus + **/ + function getLiquidationBonus(DataTypes.ReserveConfigurationMap storage self) + internal + view + returns (uint256) + { + return (self.data & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION; + } + + /** + * @dev Sets the decimals of the underlying asset of the reserve + * @param self The reserve configuration + * @param decimals The decimals + **/ + function setDecimals(DataTypes.ReserveConfigurationMap memory self, uint256 decimals) + internal + pure + { + require(decimals <= MAX_VALID_DECIMALS, Errors.RC_INVALID_DECIMALS); + + self.data = (self.data & DECIMALS_MASK) | (decimals << RESERVE_DECIMALS_START_BIT_POSITION); + } + + /** + * @dev Gets the decimals of the underlying asset of the reserve + * @param self The reserve configuration + * @return The decimals of the asset + **/ + function getDecimals(DataTypes.ReserveConfigurationMap storage self) + internal + view + returns (uint256) + { + return (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION; + } + + /** + * @dev Sets the active state of the reserve + * @param self The reserve configuration + * @param active The active state + **/ + function setActive(DataTypes.ReserveConfigurationMap memory self, bool active) internal pure { + self.data = + (self.data & ACTIVE_MASK) | + (uint256(active ? 1 : 0) << IS_ACTIVE_START_BIT_POSITION); + } + + /** + * @dev Gets the active state of the reserve + * @param self The reserve configuration + * @return The active state + **/ + function getActive(DataTypes.ReserveConfigurationMap storage self) internal view returns (bool) { + return (self.data & ~ACTIVE_MASK) != 0; + } + + /** + * @dev Sets the frozen state of the reserve + * @param self The reserve configuration + * @param frozen The frozen state + **/ + function setFrozen(DataTypes.ReserveConfigurationMap memory self, bool frozen) internal pure { + self.data = + (self.data & FROZEN_MASK) | + (uint256(frozen ? 1 : 0) << IS_FROZEN_START_BIT_POSITION); + } + + /** + * @dev Gets the frozen state of the reserve + * @param self The reserve configuration + * @return The frozen state + **/ + function getFrozen(DataTypes.ReserveConfigurationMap storage self) internal view returns (bool) { + return (self.data & ~FROZEN_MASK) != 0; + } + + /** + * @dev Enables or disables borrowing on the reserve + * @param self The reserve configuration + * @param enabled True if the borrowing needs to be enabled, false otherwise + **/ + function setBorrowingEnabled(DataTypes.ReserveConfigurationMap memory self, bool enabled) + internal + pure + { + self.data = + (self.data & BORROWING_MASK) | + (uint256(enabled ? 1 : 0) << BORROWING_ENABLED_START_BIT_POSITION); + } + + /** + * @dev Gets the borrowing state of the reserve + * @param self The reserve configuration + * @return The borrowing state + **/ + function getBorrowingEnabled(DataTypes.ReserveConfigurationMap storage self) + internal + view + returns (bool) + { + return (self.data & ~BORROWING_MASK) != 0; + } + + /** + * @dev Enables or disables stable rate borrowing on the reserve + * @param self The reserve configuration + * @param enabled True if the stable rate borrowing needs to be enabled, false otherwise + **/ + function setStableRateBorrowingEnabled( + DataTypes.ReserveConfigurationMap memory self, + bool enabled + ) internal pure { + self.data = + (self.data & STABLE_BORROWING_MASK) | + (uint256(enabled ? 1 : 0) << STABLE_BORROWING_ENABLED_START_BIT_POSITION); + } + + /** + * @dev Gets the stable rate borrowing state of the reserve + * @param self The reserve configuration + * @return The stable rate borrowing state + **/ + function getStableRateBorrowingEnabled(DataTypes.ReserveConfigurationMap storage self) + internal + view + returns (bool) + { + return (self.data & ~STABLE_BORROWING_MASK) != 0; + } + + /** + * @dev Sets the reserve factor of the reserve + * @param self The reserve configuration + * @param reserveFactor The reserve factor + **/ + function setReserveFactor(DataTypes.ReserveConfigurationMap memory self, uint256 reserveFactor) + internal + pure + { + require(reserveFactor <= MAX_VALID_RESERVE_FACTOR, Errors.RC_INVALID_RESERVE_FACTOR); + + self.data = + (self.data & RESERVE_FACTOR_MASK) | + (reserveFactor << RESERVE_FACTOR_START_BIT_POSITION); + } + + /** + * @dev Gets the reserve factor of the reserve + * @param self The reserve configuration + * @return The reserve factor + **/ + function getReserveFactor(DataTypes.ReserveConfigurationMap storage self) + internal + view + returns (uint256) + { + return (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION; + } + + /** + * @dev Gets the configuration flags of the reserve + * @param self The reserve configuration + * @return The state flags representing active, frozen, borrowing enabled, stableRateBorrowing enabled + **/ + function getFlags(DataTypes.ReserveConfigurationMap storage self) + internal + view + returns ( + bool, + bool, + bool, + bool + ) + { + uint256 dataLocal = self.data; + + return ( + (dataLocal & ~ACTIVE_MASK) != 0, + (dataLocal & ~FROZEN_MASK) != 0, + (dataLocal & ~BORROWING_MASK) != 0, + (dataLocal & ~STABLE_BORROWING_MASK) != 0 + ); + } + + /** + * @dev Gets the configuration paramters of the reserve + * @param self The reserve configuration + * @return The state params representing ltv, liquidation threshold, liquidation bonus, the reserve decimals + **/ + function getParams(DataTypes.ReserveConfigurationMap storage self) + internal + view + returns ( + uint256, + uint256, + uint256, + uint256, + uint256 + ) + { + uint256 dataLocal = self.data; + + return ( + dataLocal & ~LTV_MASK, + (dataLocal & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION, + (dataLocal & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION, + (dataLocal & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION, + (dataLocal & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION + ); + } + + /** + * @dev Gets the configuration paramters of the reserve from a memory object + * @param self The reserve configuration + * @return The state params representing ltv, liquidation threshold, liquidation bonus, the reserve decimals + **/ + function getParamsMemory(DataTypes.ReserveConfigurationMap memory self) + internal + pure + returns ( + uint256, + uint256, + uint256, + uint256, + uint256 + ) + { + return ( + self.data & ~LTV_MASK, + (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION, + (self.data & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION, + (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION, + (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION + ); + } + + /** + * @dev Gets the configuration flags of the reserve from a memory object + * @param self The reserve configuration + * @return The state flags representing active, frozen, borrowing enabled, stableRateBorrowing enabled + **/ + function getFlagsMemory(DataTypes.ReserveConfigurationMap memory self) + internal + pure + returns ( + bool, + bool, + bool, + bool + ) + { + return ( + (self.data & ~ACTIVE_MASK) != 0, + (self.data & ~FROZEN_MASK) != 0, + (self.data & ~BORROWING_MASK) != 0, + (self.data & ~STABLE_BORROWING_MASK) != 0 + ); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/ReserveLogic.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/ReserveLogic.sol new file mode 100644 index 000000000..3725d88c8 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/ReserveLogic.sol @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol'; +import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol'; +import {SafeERC20} from '../../../dependencies/openzeppelin/contracts/SafeERC20.sol'; +import {IAToken} from '../../../interfaces/IAToken.sol'; +import {IStableDebtToken} from '../../../interfaces/IStableDebtToken.sol'; +import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol'; +import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol'; +import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol'; +import {MathUtils} from '../math/MathUtils.sol'; +import {WadRayMath} from '../math/WadRayMath.sol'; +import {PercentageMath} from '../math/PercentageMath.sol'; +import {Errors} from '../helpers/Errors.sol'; +import {DataTypes} from '../types/DataTypes.sol'; + +/** + * @title ReserveLogic library + * @author Aave + * @notice Implements the logic to update the reserves state + */ +library ReserveLogic { + using SafeMath for uint256; + using WadRayMath for uint256; + using PercentageMath for uint256; + using SafeERC20 for IERC20; + + /** + * @dev Emitted when the state of a reserve is updated + * @param asset The address of the underlying asset of the reserve + * @param liquidityRate The new liquidity rate + * @param stableBorrowRate The new stable borrow rate + * @param variableBorrowRate The new variable borrow rate + * @param liquidityIndex The new liquidity index + * @param variableBorrowIndex The new variable borrow index + **/ + event ReserveDataUpdated( + address indexed asset, + uint256 liquidityRate, + uint256 stableBorrowRate, + uint256 variableBorrowRate, + uint256 liquidityIndex, + uint256 variableBorrowIndex + ); + + using ReserveLogic for DataTypes.ReserveData; + using ReserveConfiguration for DataTypes.ReserveConfigurationMap; + + /** + * @dev Returns the ongoing normalized income for the reserve + * A value of 1e27 means there is no income. As time passes, the income is accrued + * A value of 2*1e27 means for each unit of asset one unit of income has been accrued + * @param reserve The reserve object + * @return the normalized income. expressed in ray + **/ + function getNormalizedIncome(DataTypes.ReserveData storage reserve) + internal + view + returns (uint256) + { + uint40 timestamp = reserve.lastUpdateTimestamp; + + //solium-disable-next-line + if (timestamp == uint40(block.timestamp)) { + //if the index was updated in the same block, no need to perform any calculation + return reserve.liquidityIndex; + } + + uint256 cumulated = + MathUtils.calculateLinearInterest(reserve.currentLiquidityRate, timestamp).rayMul( + reserve.liquidityIndex + ); + + return cumulated; + } + + /** + * @dev Returns the ongoing normalized variable debt for the reserve + * A value of 1e27 means there is no debt. As time passes, the income is accrued + * A value of 2*1e27 means that for each unit of debt, one unit worth of interest has been accumulated + * @param reserve The reserve object + * @return The normalized variable debt. expressed in ray + **/ + function getNormalizedDebt(DataTypes.ReserveData storage reserve) + internal + view + returns (uint256) + { + uint40 timestamp = reserve.lastUpdateTimestamp; + + //solium-disable-next-line + if (timestamp == uint40(block.timestamp)) { + //if the index was updated in the same block, no need to perform any calculation + return reserve.variableBorrowIndex; + } + + uint256 cumulated = + MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp).rayMul( + reserve.variableBorrowIndex + ); + + return cumulated; + } + + /** + * @dev Updates the liquidity cumulative index and the variable borrow index. + * @param reserve the reserve object + **/ + function updateState(DataTypes.ReserveData storage reserve) internal { + uint256 scaledVariableDebt = + IVariableDebtToken(reserve.variableDebtTokenAddress).scaledTotalSupply(); + uint256 previousVariableBorrowIndex = reserve.variableBorrowIndex; + uint256 previousLiquidityIndex = reserve.liquidityIndex; + uint40 lastUpdatedTimestamp = reserve.lastUpdateTimestamp; + + (uint256 newLiquidityIndex, uint256 newVariableBorrowIndex) = + _updateIndexes( + reserve, + scaledVariableDebt, + previousLiquidityIndex, + previousVariableBorrowIndex, + lastUpdatedTimestamp + ); + + _mintToTreasury( + reserve, + scaledVariableDebt, + previousVariableBorrowIndex, + newLiquidityIndex, + newVariableBorrowIndex, + lastUpdatedTimestamp + ); + } + + /** + * @dev Accumulates a predefined amount of asset to the reserve as a fixed, instantaneous income. Used for example to accumulate + * the flashloan fee to the reserve, and spread it between all the depositors + * @param reserve The reserve object + * @param totalLiquidity The total liquidity available in the reserve + * @param amount The amount to accomulate + **/ + function cumulateToLiquidityIndex( + DataTypes.ReserveData storage reserve, + uint256 totalLiquidity, + uint256 amount + ) internal { + uint256 amountToLiquidityRatio = amount.wadToRay().rayDiv(totalLiquidity.wadToRay()); + + uint256 result = amountToLiquidityRatio.add(WadRayMath.ray()); + + result = result.rayMul(reserve.liquidityIndex); + require(result <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW); + + reserve.liquidityIndex = uint128(result); + } + + /** + * @dev Initializes a reserve + * @param reserve The reserve object + * @param aTokenAddress The address of the overlying atoken contract + * @param interestRateStrategyAddress The address of the interest rate strategy contract + **/ + function init( + DataTypes.ReserveData storage reserve, + address aTokenAddress, + address stableDebtTokenAddress, + address variableDebtTokenAddress, + address interestRateStrategyAddress + ) external { + require(reserve.aTokenAddress == address(0), Errors.RL_RESERVE_ALREADY_INITIALIZED); + + reserve.liquidityIndex = uint128(WadRayMath.ray()); + reserve.variableBorrowIndex = uint128(WadRayMath.ray()); + reserve.aTokenAddress = aTokenAddress; + reserve.stableDebtTokenAddress = stableDebtTokenAddress; + reserve.variableDebtTokenAddress = variableDebtTokenAddress; + reserve.interestRateStrategyAddress = interestRateStrategyAddress; + } + + struct UpdateInterestRatesLocalVars { + address stableDebtTokenAddress; + uint256 availableLiquidity; + uint256 totalStableDebt; + uint256 newLiquidityRate; + uint256 newStableRate; + uint256 newVariableRate; + uint256 avgStableRate; + uint256 totalVariableDebt; + } + + /** + * @dev Updates the reserve current stable borrow rate, the current variable borrow rate and the current liquidity rate + * @param reserve The address of the reserve to be updated + * @param liquidityAdded The amount of liquidity added to the protocol (deposit or repay) in the previous action + * @param liquidityTaken The amount of liquidity taken from the protocol (redeem or borrow) + **/ + function updateInterestRates( + DataTypes.ReserveData storage reserve, + address reserveAddress, + address aTokenAddress, + uint256 liquidityAdded, + uint256 liquidityTaken + ) internal { + UpdateInterestRatesLocalVars memory vars; + + vars.stableDebtTokenAddress = reserve.stableDebtTokenAddress; + + (vars.totalStableDebt, vars.avgStableRate) = IStableDebtToken(vars.stableDebtTokenAddress) + .getTotalSupplyAndAvgRate(); + + //calculates the total variable debt locally using the scaled total supply instead + //of totalSupply(), as it's noticeably cheaper. Also, the index has been + //updated by the previous updateState() call + vars.totalVariableDebt = IVariableDebtToken(reserve.variableDebtTokenAddress) + .scaledTotalSupply() + .rayMul(reserve.variableBorrowIndex); + + ( + vars.newLiquidityRate, + vars.newStableRate, + vars.newVariableRate + ) = IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).calculateInterestRates( + reserveAddress, + aTokenAddress, + liquidityAdded, + liquidityTaken, + vars.totalStableDebt, + vars.totalVariableDebt, + vars.avgStableRate, + reserve.configuration.getReserveFactor() + ); + require(vars.newLiquidityRate <= type(uint128).max, Errors.RL_LIQUIDITY_RATE_OVERFLOW); + require(vars.newStableRate <= type(uint128).max, Errors.RL_STABLE_BORROW_RATE_OVERFLOW); + require(vars.newVariableRate <= type(uint128).max, Errors.RL_VARIABLE_BORROW_RATE_OVERFLOW); + + reserve.currentLiquidityRate = uint128(vars.newLiquidityRate); + reserve.currentStableBorrowRate = uint128(vars.newStableRate); + reserve.currentVariableBorrowRate = uint128(vars.newVariableRate); + + emit ReserveDataUpdated( + reserveAddress, + vars.newLiquidityRate, + vars.newStableRate, + vars.newVariableRate, + reserve.liquidityIndex, + reserve.variableBorrowIndex + ); + } + + struct MintToTreasuryLocalVars { + uint256 currentStableDebt; + uint256 principalStableDebt; + uint256 previousStableDebt; + uint256 currentVariableDebt; + uint256 previousVariableDebt; + uint256 avgStableRate; + uint256 cumulatedStableInterest; + uint256 totalDebtAccrued; + uint256 amountToMint; + uint256 reserveFactor; + uint40 stableSupplyUpdatedTimestamp; + } + + /** + * @dev Mints part of the repaid interest to the reserve treasury as a function of the reserveFactor for the + * specific asset. + * @param reserve The reserve reserve to be updated + * @param scaledVariableDebt The current scaled total variable debt + * @param previousVariableBorrowIndex The variable borrow index before the last accumulation of the interest + * @param newLiquidityIndex The new liquidity index + * @param newVariableBorrowIndex The variable borrow index after the last accumulation of the interest + **/ + function _mintToTreasury( + DataTypes.ReserveData storage reserve, + uint256 scaledVariableDebt, + uint256 previousVariableBorrowIndex, + uint256 newLiquidityIndex, + uint256 newVariableBorrowIndex, + uint40 timestamp + ) internal { + MintToTreasuryLocalVars memory vars; + + vars.reserveFactor = reserve.configuration.getReserveFactor(); + + if (vars.reserveFactor == 0) { + return; + } + + //fetching the principal, total stable debt and the avg stable rate + ( + vars.principalStableDebt, + vars.currentStableDebt, + vars.avgStableRate, + vars.stableSupplyUpdatedTimestamp + ) = IStableDebtToken(reserve.stableDebtTokenAddress).getSupplyData(); + + //calculate the last principal variable debt + vars.previousVariableDebt = scaledVariableDebt.rayMul(previousVariableBorrowIndex); + + //calculate the new total supply after accumulation of the index + vars.currentVariableDebt = scaledVariableDebt.rayMul(newVariableBorrowIndex); + + //calculate the stable debt until the last timestamp update + vars.cumulatedStableInterest = MathUtils.calculateCompoundedInterest( + vars.avgStableRate, + vars.stableSupplyUpdatedTimestamp, + timestamp + ); + + vars.previousStableDebt = vars.principalStableDebt.rayMul(vars.cumulatedStableInterest); + + //debt accrued is the sum of the current debt minus the sum of the debt at the last update + vars.totalDebtAccrued = vars + .currentVariableDebt + .add(vars.currentStableDebt) + .sub(vars.previousVariableDebt) + .sub(vars.previousStableDebt); + + vars.amountToMint = vars.totalDebtAccrued.percentMul(vars.reserveFactor); + + if (vars.amountToMint != 0) { + IAToken(reserve.aTokenAddress).mintToTreasury(vars.amountToMint, newLiquidityIndex); + } + } + + /** + * @dev Updates the reserve indexes and the timestamp of the update + * @param reserve The reserve reserve to be updated + * @param scaledVariableDebt The scaled variable debt + * @param liquidityIndex The last stored liquidity index + * @param variableBorrowIndex The last stored variable borrow index + **/ + function _updateIndexes( + DataTypes.ReserveData storage reserve, + uint256 scaledVariableDebt, + uint256 liquidityIndex, + uint256 variableBorrowIndex, + uint40 timestamp + ) internal returns (uint256, uint256) { + uint256 currentLiquidityRate = reserve.currentLiquidityRate; + + uint256 newLiquidityIndex = liquidityIndex; + uint256 newVariableBorrowIndex = variableBorrowIndex; + + //only cumulating if there is any income being produced + if (currentLiquidityRate > 0) { + uint256 cumulatedLiquidityInterest = + MathUtils.calculateLinearInterest(currentLiquidityRate, timestamp); + newLiquidityIndex = cumulatedLiquidityInterest.rayMul(liquidityIndex); + require(newLiquidityIndex <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW); + + reserve.liquidityIndex = uint128(newLiquidityIndex); + + //as the liquidity rate might come only from stable rate loans, we need to ensure + //that there is actual variable debt before accumulating + if (scaledVariableDebt != 0) { + uint256 cumulatedVariableBorrowInterest = + MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp); + newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(variableBorrowIndex); + require( + newVariableBorrowIndex <= type(uint128).max, + Errors.RL_VARIABLE_BORROW_INDEX_OVERFLOW + ); + reserve.variableBorrowIndex = uint128(newVariableBorrowIndex); + } + } + + //solium-disable-next-line + reserve.lastUpdateTimestamp = uint40(block.timestamp); + return (newLiquidityIndex, newVariableBorrowIndex); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/SafeERC20.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/SafeERC20.sol new file mode 100644 index 000000000..86d009d7e --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/SafeERC20.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.6.12; + +import {IERC20} from './IERC20.sol'; +import {SafeMath} from './SafeMath.sol'; +import {Address} from './Address.sol'; + +/** + * @title SafeERC20 + * @dev Wrappers around ERC20 operations that throw on failure (when the token + * contract returns false). Tokens that return no value (and instead revert or + * throw on failure) are also supported, non-reverting calls are assumed to be + * successful. + * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, + * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. + */ +library SafeERC20 { + using SafeMath for uint256; + using Address for address; + + function safeTransfer( + IERC20 token, + address to, + uint256 value + ) internal { + callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); + } + + function safeTransferFrom( + IERC20 token, + address from, + address to, + uint256 value + ) internal { + callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); + } + + function safeApprove( + IERC20 token, + address spender, + uint256 value + ) internal { + require( + (value == 0) || (token.allowance(address(this), spender) == 0), + 'SafeERC20: approve from non-zero to non-zero allowance' + ); + callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); + } + + function callOptionalReturn(IERC20 token, bytes memory data) private { + require(address(token).isContract(), 'SafeERC20: call to non-contract'); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = address(token).call(data); + require(success, 'SafeERC20: low-level call failed'); + + if (returndata.length > 0) { + // Return data is optional + // solhint-disable-next-line max-line-length + require(abi.decode(returndata, (bool)), 'SafeERC20: ERC20 operation did not succeed'); + } + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/SafeMath.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/SafeMath.sol new file mode 100644 index 000000000..6f814a002 --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/SafeMath.sol @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +/** + * @dev Wrappers over Solidity's arithmetic operations with added overflow + * checks. + * + * Arithmetic operations in Solidity wrap on overflow. This can easily result + * in bugs, because programmers usually assume that an overflow raises an + * error, which is the standard behavior in high level programming languages. + * `SafeMath` restores this intuition by reverting the transaction when an + * operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + */ +library SafeMath { + /** + * @dev Returns the addition of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * - Addition cannot overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a + b; + require(c >= a, 'SafeMath: addition overflow'); + + return c; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * - Subtraction cannot overflow. + */ + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + return sub(a, b, 'SafeMath: subtraction overflow'); + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting with custom message on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * - Subtraction cannot overflow. + */ + function sub( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + require(b <= a, errorMessage); + uint256 c = a - b; + + return c; + } + + /** + * @dev Returns the multiplication of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * - Multiplication cannot overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 + if (a == 0) { + return 0; + } + + uint256 c = a * b; + require(c / a == b, 'SafeMath: multiplication overflow'); + + return c; + } + + /** + * @dev Returns the integer division of two unsigned integers. Reverts on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function div(uint256 a, uint256 b) internal pure returns (uint256) { + return div(a, b, 'SafeMath: division by zero'); + } + + /** + * @dev Returns the integer division of two unsigned integers. Reverts with custom message on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function div( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + // Solidity only automatically asserts when dividing by 0 + require(b > 0, errorMessage); + uint256 c = a / b; + // assert(a == b * c + a % b); // There is no case in which this doesn't hold + + return c; + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * Reverts when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function mod(uint256 a, uint256 b) internal pure returns (uint256) { + return mod(a, b, 'SafeMath: modulo by zero'); + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * Reverts with custom message when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * - The divisor cannot be zero. + */ + function mod( + uint256 a, + uint256 b, + string memory errorMessage + ) internal pure returns (uint256) { + require(b != 0, errorMessage); + return a % b; + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/UserConfiguration.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/UserConfiguration.sol new file mode 100644 index 000000000..2638ddf4e --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/UserConfiguration.sol @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {Errors} from '../helpers/Errors.sol'; +import {DataTypes} from '../types/DataTypes.sol'; + +/** + * @title UserConfiguration library + * @author Aave + * @notice Implements the bitmap logic to handle the user configuration + */ +library UserConfiguration { + uint256 internal constant BORROWING_MASK = + 0x5555555555555555555555555555555555555555555555555555555555555555; + + /** + * @dev Sets if the user is borrowing the reserve identified by reserveIndex + * @param self The configuration object + * @param reserveIndex The index of the reserve in the bitmap + * @param borrowing True if the user is borrowing the reserve, false otherwise + **/ + function setBorrowing( + DataTypes.UserConfigurationMap storage self, + uint256 reserveIndex, + bool borrowing + ) internal { + require(reserveIndex < 128, Errors.UL_INVALID_INDEX); + self.data = + (self.data & ~(1 << (reserveIndex * 2))) | + (uint256(borrowing ? 1 : 0) << (reserveIndex * 2)); + } + + /** + * @dev Sets if the user is using as collateral the reserve identified by reserveIndex + * @param self The configuration object + * @param reserveIndex The index of the reserve in the bitmap + * @param usingAsCollateral True if the user is usin the reserve as collateral, false otherwise + **/ + function setUsingAsCollateral( + DataTypes.UserConfigurationMap storage self, + uint256 reserveIndex, + bool usingAsCollateral + ) internal { + require(reserveIndex < 128, Errors.UL_INVALID_INDEX); + self.data = + (self.data & ~(1 << (reserveIndex * 2 + 1))) | + (uint256(usingAsCollateral ? 1 : 0) << (reserveIndex * 2 + 1)); + } + + /** + * @dev Used to validate if a user has been using the reserve for borrowing or as collateral + * @param self The configuration object + * @param reserveIndex The index of the reserve in the bitmap + * @return True if the user has been using a reserve for borrowing or as collateral, false otherwise + **/ + function isUsingAsCollateralOrBorrowing( + DataTypes.UserConfigurationMap memory self, + uint256 reserveIndex + ) internal pure returns (bool) { + require(reserveIndex < 128, Errors.UL_INVALID_INDEX); + return (self.data >> (reserveIndex * 2)) & 3 != 0; + } + + /** + * @dev Used to validate if a user has been using the reserve for borrowing + * @param self The configuration object + * @param reserveIndex The index of the reserve in the bitmap + * @return True if the user has been using a reserve for borrowing, false otherwise + **/ + function isBorrowing(DataTypes.UserConfigurationMap memory self, uint256 reserveIndex) + internal + pure + returns (bool) + { + require(reserveIndex < 128, Errors.UL_INVALID_INDEX); + return (self.data >> (reserveIndex * 2)) & 1 != 0; + } + + /** + * @dev Used to validate if a user has been using the reserve as collateral + * @param self The configuration object + * @param reserveIndex The index of the reserve in the bitmap + * @return True if the user has been using a reserve as collateral, false otherwise + **/ + function isUsingAsCollateral(DataTypes.UserConfigurationMap memory self, uint256 reserveIndex) + internal + pure + returns (bool) + { + require(reserveIndex < 128, Errors.UL_INVALID_INDEX); + return (self.data >> (reserveIndex * 2 + 1)) & 1 != 0; + } + + /** + * @dev Used to validate if a user has been borrowing from any reserve + * @param self The configuration object + * @return True if the user has been borrowing any reserve, false otherwise + **/ + function isBorrowingAny(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) { + return self.data & BORROWING_MASK != 0; + } + + /** + * @dev Used to validate if a user has not been using any reserve + * @param self The configuration object + * @return True if the user has been borrowing any reserve, false otherwise + **/ + function isEmpty(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) { + return self.data == 0; + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/ValidationLogic.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/ValidationLogic.sol new file mode 100644 index 000000000..8aa449cdd --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/ValidationLogic.sol @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol'; +import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol'; +import {ReserveLogic} from './ReserveLogic.sol'; +import {GenericLogic} from './GenericLogic.sol'; +import {WadRayMath} from '../math/WadRayMath.sol'; +import {PercentageMath} from '../math/PercentageMath.sol'; +import {SafeERC20} from '../../../dependencies/openzeppelin/contracts/SafeERC20.sol'; +import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol'; +import {UserConfiguration} from '../configuration/UserConfiguration.sol'; +import {Errors} from '../helpers/Errors.sol'; +import {Helpers} from '../helpers/Helpers.sol'; +import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol'; +import {DataTypes} from '../types/DataTypes.sol'; + +/** + * @title ReserveLogic library + * @author Aave + * @notice Implements functions to validate the different actions of the protocol + */ +library ValidationLogic { + using ReserveLogic for DataTypes.ReserveData; + using SafeMath for uint256; + using WadRayMath for uint256; + using PercentageMath for uint256; + using SafeERC20 for IERC20; + using ReserveConfiguration for DataTypes.ReserveConfigurationMap; + using UserConfiguration for DataTypes.UserConfigurationMap; + + uint256 public constant REBALANCE_UP_LIQUIDITY_RATE_THRESHOLD = 4000; + uint256 public constant REBALANCE_UP_USAGE_RATIO_THRESHOLD = 0.95 * 1e27; //usage ratio of 95% + + /** + * @dev Validates a deposit action + * @param reserve The reserve object on which the user is depositing + * @param amount The amount to be deposited + */ + function validateDeposit(DataTypes.ReserveData storage reserve, uint256 amount) external view { + (bool isActive, bool isFrozen, , ) = reserve.configuration.getFlags(); + + require(amount != 0, Errors.VL_INVALID_AMOUNT); + require(isActive, Errors.VL_NO_ACTIVE_RESERVE); + require(!isFrozen, Errors.VL_RESERVE_FROZEN); + } + + /** + * @dev Validates a withdraw action + * @param reserveAddress The address of the reserve + * @param amount The amount to be withdrawn + * @param userBalance The balance of the user + * @param reservesData The reserves state + * @param userConfig The user configuration + * @param reserves The addresses of the reserves + * @param reservesCount The number of reserves + * @param oracle The price oracle + */ + function validateWithdraw( + address reserveAddress, + uint256 amount, + uint256 userBalance, + mapping(address => DataTypes.ReserveData) storage reservesData, + DataTypes.UserConfigurationMap storage userConfig, + mapping(uint256 => address) storage reserves, + uint256 reservesCount, + address oracle + ) external view { + require(amount != 0, Errors.VL_INVALID_AMOUNT); + require(amount <= userBalance, Errors.VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE); + + (bool isActive, , , ) = reservesData[reserveAddress].configuration.getFlags(); + require(isActive, Errors.VL_NO_ACTIVE_RESERVE); + + require( + GenericLogic.balanceDecreaseAllowed( + reserveAddress, + msg.sender, + amount, + reservesData, + userConfig, + reserves, + reservesCount, + oracle + ), + Errors.VL_TRANSFER_NOT_ALLOWED + ); + } + + struct ValidateBorrowLocalVars { + uint256 currentLtv; + uint256 currentLiquidationThreshold; + uint256 amountOfCollateralNeededETH; + uint256 userCollateralBalanceETH; + uint256 userBorrowBalanceETH; + uint256 availableLiquidity; + uint256 healthFactor; + bool isActive; + bool isFrozen; + bool borrowingEnabled; + bool stableRateBorrowingEnabled; + } + + /** + * @dev Validates a borrow action + * @param asset The address of the asset to borrow + * @param reserve The reserve state from which the user is borrowing + * @param userAddress The address of the user + * @param amount The amount to be borrowed + * @param amountInETH The amount to be borrowed, in ETH + * @param interestRateMode The interest rate mode at which the user is borrowing + * @param maxStableLoanPercent The max amount of the liquidity that can be borrowed at stable rate, in percentage + * @param reservesData The state of all the reserves + * @param userConfig The state of the user for the specific reserve + * @param reserves The addresses of all the active reserves + * @param oracle The price oracle + */ + + function validateBorrow( + address asset, + DataTypes.ReserveData storage reserve, + address userAddress, + uint256 amount, + uint256 amountInETH, + uint256 interestRateMode, + uint256 maxStableLoanPercent, + mapping(address => DataTypes.ReserveData) storage reservesData, + DataTypes.UserConfigurationMap storage userConfig, + mapping(uint256 => address) storage reserves, + uint256 reservesCount, + address oracle + ) external view { + ValidateBorrowLocalVars memory vars; + + (vars.isActive, vars.isFrozen, vars.borrowingEnabled, vars.stableRateBorrowingEnabled) = reserve + .configuration + .getFlags(); + + require(vars.isActive, Errors.VL_NO_ACTIVE_RESERVE); + require(!vars.isFrozen, Errors.VL_RESERVE_FROZEN); + require(amount != 0, Errors.VL_INVALID_AMOUNT); + + require(vars.borrowingEnabled, Errors.VL_BORROWING_NOT_ENABLED); + + //validate interest rate mode + require( + uint256(DataTypes.InterestRateMode.VARIABLE) == interestRateMode || + uint256(DataTypes.InterestRateMode.STABLE) == interestRateMode, + Errors.VL_INVALID_INTEREST_RATE_MODE_SELECTED + ); + + ( + vars.userCollateralBalanceETH, + vars.userBorrowBalanceETH, + vars.currentLtv, + vars.currentLiquidationThreshold, + vars.healthFactor + ) = GenericLogic.calculateUserAccountData( + userAddress, + reservesData, + userConfig, + reserves, + reservesCount, + oracle + ); + + require(vars.userCollateralBalanceETH > 0, Errors.VL_COLLATERAL_BALANCE_IS_0); + + require( + vars.healthFactor > GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD, + Errors.VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD + ); + + //add the current already borrowed amount to the amount requested to calculate the total collateral needed. + vars.amountOfCollateralNeededETH = vars.userBorrowBalanceETH.add(amountInETH).percentDiv( + vars.currentLtv + ); //LTV is calculated in percentage + + require( + vars.amountOfCollateralNeededETH <= vars.userCollateralBalanceETH, + Errors.VL_COLLATERAL_CANNOT_COVER_NEW_BORROW + ); + + /** + * Following conditions need to be met if the user is borrowing at a stable rate: + * 1. Reserve must be enabled for stable rate borrowing + * 2. Users cannot borrow from the reserve if their collateral is (mostly) the same currency + * they are borrowing, to prevent abuses. + * 3. Users will be able to borrow only a portion of the total available liquidity + **/ + + if (interestRateMode == uint256(DataTypes.InterestRateMode.STABLE)) { + //check if the borrow mode is stable and if stable rate borrowing is enabled on this reserve + + require(vars.stableRateBorrowingEnabled, Errors.VL_STABLE_BORROWING_NOT_ENABLED); + + require( + !userConfig.isUsingAsCollateral(reserve.id) || + reserve.configuration.getLtv() == 0 || + amount > IERC20(reserve.aTokenAddress).balanceOf(userAddress), + Errors.VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY + ); + + vars.availableLiquidity = IERC20(asset).balanceOf(reserve.aTokenAddress); + + //calculate the max available loan size in stable rate mode as a percentage of the + //available liquidity + uint256 maxLoanSizeStable = vars.availableLiquidity.percentMul(maxStableLoanPercent); + + require(amount <= maxLoanSizeStable, Errors.VL_AMOUNT_BIGGER_THAN_MAX_LOAN_SIZE_STABLE); + } + } + + /** + * @dev Validates a repay action + * @param reserve The reserve state from which the user is repaying + * @param amountSent The amount sent for the repayment. Can be an actual value or uint(-1) + * @param onBehalfOf The address of the user msg.sender is repaying for + * @param stableDebt The borrow balance of the user + * @param variableDebt The borrow balance of the user + */ + function validateRepay( + DataTypes.ReserveData storage reserve, + uint256 amountSent, + DataTypes.InterestRateMode rateMode, + address onBehalfOf, + uint256 stableDebt, + uint256 variableDebt + ) external view { + bool isActive = reserve.configuration.getActive(); + + require(isActive, Errors.VL_NO_ACTIVE_RESERVE); + + require(amountSent > 0, Errors.VL_INVALID_AMOUNT); + + require( + (stableDebt > 0 && + DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.STABLE) || + (variableDebt > 0 && + DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.VARIABLE), + Errors.VL_NO_DEBT_OF_SELECTED_TYPE + ); + + require( + amountSent != uint256(-1) || msg.sender == onBehalfOf, + Errors.VL_NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF + ); + } + + /** + * @dev Validates a swap of borrow rate mode. + * @param reserve The reserve state on which the user is swapping the rate + * @param userConfig The user reserves configuration + * @param stableDebt The stable debt of the user + * @param variableDebt The variable debt of the user + * @param currentRateMode The rate mode of the borrow + */ + function validateSwapRateMode( + DataTypes.ReserveData storage reserve, + DataTypes.UserConfigurationMap storage userConfig, + uint256 stableDebt, + uint256 variableDebt, + DataTypes.InterestRateMode currentRateMode + ) external view { + (bool isActive, bool isFrozen, , bool stableRateEnabled) = reserve.configuration.getFlags(); + + require(isActive, Errors.VL_NO_ACTIVE_RESERVE); + require(!isFrozen, Errors.VL_RESERVE_FROZEN); + + if (currentRateMode == DataTypes.InterestRateMode.STABLE) { + require(stableDebt > 0, Errors.VL_NO_STABLE_RATE_LOAN_IN_RESERVE); + } else if (currentRateMode == DataTypes.InterestRateMode.VARIABLE) { + require(variableDebt > 0, Errors.VL_NO_VARIABLE_RATE_LOAN_IN_RESERVE); + /** + * user wants to swap to stable, before swapping we need to ensure that + * 1. stable borrow rate is enabled on the reserve + * 2. user is not trying to abuse the reserve by depositing + * more collateral than he is borrowing, artificially lowering + * the interest rate, borrowing at variable, and switching to stable + **/ + require(stableRateEnabled, Errors.VL_STABLE_BORROWING_NOT_ENABLED); + + require( + !userConfig.isUsingAsCollateral(reserve.id) || + reserve.configuration.getLtv() == 0 || + stableDebt.add(variableDebt) > IERC20(reserve.aTokenAddress).balanceOf(msg.sender), + Errors.VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY + ); + } else { + revert(Errors.VL_INVALID_INTEREST_RATE_MODE_SELECTED); + } + } + + /** + * @dev Validates a stable borrow rate rebalance action + * @param reserve The reserve state on which the user is getting rebalanced + * @param reserveAddress The address of the reserve + * @param stableDebtToken The stable debt token instance + * @param variableDebtToken The variable debt token instance + * @param aTokenAddress The address of the aToken contract + */ + function validateRebalanceStableBorrowRate( + DataTypes.ReserveData storage reserve, + address reserveAddress, + IERC20 stableDebtToken, + IERC20 variableDebtToken, + address aTokenAddress + ) external view { + (bool isActive, , , ) = reserve.configuration.getFlags(); + + require(isActive, Errors.VL_NO_ACTIVE_RESERVE); + + //if the usage ratio is below 95%, no rebalances are needed + uint256 totalDebt = + stableDebtToken.totalSupply().add(variableDebtToken.totalSupply()).wadToRay(); + uint256 availableLiquidity = IERC20(reserveAddress).balanceOf(aTokenAddress).wadToRay(); + uint256 usageRatio = totalDebt == 0 ? 0 : totalDebt.rayDiv(availableLiquidity.add(totalDebt)); + + //if the liquidity rate is below REBALANCE_UP_THRESHOLD of the max variable APR at 95% usage, + //then we allow rebalancing of the stable rate positions. + + uint256 currentLiquidityRate = reserve.currentLiquidityRate; + uint256 maxVariableBorrowRate = + IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).getMaxVariableBorrowRate(); + + require( + usageRatio >= REBALANCE_UP_USAGE_RATIO_THRESHOLD && + currentLiquidityRate <= + maxVariableBorrowRate.percentMul(REBALANCE_UP_LIQUIDITY_RATE_THRESHOLD), + Errors.LP_INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET + ); + } + + /** + * @dev Validates the action of setting an asset as collateral + * @param reserve The state of the reserve that the user is enabling or disabling as collateral + * @param reserveAddress The address of the reserve + * @param reservesData The data of all the reserves + * @param userConfig The state of the user for the specific reserve + * @param reserves The addresses of all the active reserves + * @param oracle The price oracle + */ + function validateSetUseReserveAsCollateral( + DataTypes.ReserveData storage reserve, + address reserveAddress, + bool useAsCollateral, + mapping(address => DataTypes.ReserveData) storage reservesData, + DataTypes.UserConfigurationMap storage userConfig, + mapping(uint256 => address) storage reserves, + uint256 reservesCount, + address oracle + ) external view { + uint256 underlyingBalance = IERC20(reserve.aTokenAddress).balanceOf(msg.sender); + + require(underlyingBalance > 0, Errors.VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0); + + require( + useAsCollateral || + GenericLogic.balanceDecreaseAllowed( + reserveAddress, + msg.sender, + underlyingBalance, + reservesData, + userConfig, + reserves, + reservesCount, + oracle + ), + Errors.VL_DEPOSIT_ALREADY_IN_USE + ); + } + + /** + * @dev Validates a flashloan action + * @param assets The assets being flashborrowed + * @param amounts The amounts for each asset being borrowed + **/ + function validateFlashloan(address[] memory assets, uint256[] memory amounts) internal pure { + require(assets.length == amounts.length, Errors.VL_INCONSISTENT_FLASHLOAN_PARAMS); + } + + /** + * @dev Validates the liquidation action + * @param collateralReserve The reserve data of the collateral + * @param principalReserve The reserve data of the principal + * @param userConfig The user configuration + * @param userHealthFactor The user's health factor + * @param userStableDebt Total stable debt balance of the user + * @param userVariableDebt Total variable debt balance of the user + **/ + function validateLiquidationCall( + DataTypes.ReserveData storage collateralReserve, + DataTypes.ReserveData storage principalReserve, + DataTypes.UserConfigurationMap storage userConfig, + uint256 userHealthFactor, + uint256 userStableDebt, + uint256 userVariableDebt + ) internal view returns (uint256, string memory) { + if ( + !collateralReserve.configuration.getActive() || !principalReserve.configuration.getActive() + ) { + return ( + uint256(Errors.CollateralManagerErrors.NO_ACTIVE_RESERVE), + Errors.VL_NO_ACTIVE_RESERVE + ); + } + + if (userHealthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD) { + return ( + uint256(Errors.CollateralManagerErrors.HEALTH_FACTOR_ABOVE_THRESHOLD), + Errors.LPCM_HEALTH_FACTOR_NOT_BELOW_THRESHOLD + ); + } + + bool isCollateralEnabled = + collateralReserve.configuration.getLiquidationThreshold() > 0 && + userConfig.isUsingAsCollateral(collateralReserve.id); + + //if collateral isn't enabled as collateral by user, it cannot be liquidated + if (!isCollateralEnabled) { + return ( + uint256(Errors.CollateralManagerErrors.COLLATERAL_CANNOT_BE_LIQUIDATED), + Errors.LPCM_COLLATERAL_CANNOT_BE_LIQUIDATED + ); + } + + if (userStableDebt == 0 && userVariableDebt == 0) { + return ( + uint256(Errors.CollateralManagerErrors.CURRRENCY_NOT_BORROWED), + Errors.LPCM_SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER + ); + } + + return (uint256(Errors.CollateralManagerErrors.NO_ERROR), Errors.LPCM_NO_ERRORS); + } + + /** + * @dev Validates an aToken transfer + * @param from The user from which the aTokens are being transferred + * @param reservesData The state of all the reserves + * @param userConfig The state of the user for the specific reserve + * @param reserves The addresses of all the active reserves + * @param oracle The price oracle + */ + function validateTransfer( + address from, + mapping(address => DataTypes.ReserveData) storage reservesData, + DataTypes.UserConfigurationMap storage userConfig, + mapping(uint256 => address) storage reserves, + uint256 reservesCount, + address oracle + ) internal view { + (, , , , uint256 healthFactor) = + GenericLogic.calculateUserAccountData( + from, + reservesData, + userConfig, + reserves, + reservesCount, + oracle + ); + + require( + healthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD, + Errors.VL_TRANSFER_NOT_ALLOWED + ); + } +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/VersionedInitializable.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/VersionedInitializable.sol new file mode 100644 index 000000000..fd87b156b --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/VersionedInitializable.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +/** + * @title VersionedInitializable + * + * @dev Helper contract to implement initializer functions. To use it, replace + * the constructor with a function that has the `initializer` modifier. + * WARNING: Unlike constructors, initializer functions must be manually + * invoked. This applies both to deploying an Initializable contract, as well + * as extending an Initializable contract via inheritance. + * WARNING: When used with inheritance, manual care must be taken to not invoke + * a parent initializer twice, or ensure that all initializers are idempotent, + * because this is not dealt with automatically as with constructors. + * + * @author Aave, inspired by the OpenZeppelin Initializable contract + */ +abstract contract VersionedInitializable { + /** + * @dev Indicates that the contract has been initialized. + */ + uint256 private lastInitializedRevision = 0; + + /** + * @dev Indicates that the contract is in the process of being initialized. + */ + bool private initializing; + + /** + * @dev Modifier to use in the initializer function of a contract. + */ + modifier initializer() { + uint256 revision = getRevision(); + require( + initializing || isConstructor() || revision > lastInitializedRevision, + 'Contract instance has already been initialized' + ); + + bool isTopLevelCall = !initializing; + if (isTopLevelCall) { + initializing = true; + lastInitializedRevision = revision; + } + + _; + + if (isTopLevelCall) { + initializing = false; + } + } + + /** + * @dev returns the revision number of the contract + * Needs to be defined in the inherited class as a constant. + **/ + function getRevision() internal pure virtual returns (uint256); + + /** + * @dev Returns true if and only if the function is running in the constructor + **/ + function isConstructor() private view returns (bool) { + // extcodesize checks the size of the code stored in an address, and + // address returns the current address. Since the code is still not + // deployed when running a constructor, any checks on its code size will + // yield zero, making it an effective way to detect if a contract is + // under construction or not. + uint256 cs; + //solium-disable-next-line + assembly { + cs := extcodesize(address()) + } + return cs == 0; + } + + // Reserved storage space to allow for layout changes in the future. + uint256[50] private ______gap; +} diff --git a/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/WadRayMath.sol b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/WadRayMath.sol new file mode 100644 index 000000000..6a304a0db --- /dev/null +++ b/packages/lib-sourcify/test/sources/ExtraFilesBytecodeMismatch/sources/WadRayMath.sol @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {Errors} from '../helpers/Errors.sol'; + +/** + * @title WadRayMath library + * @author Aave + * @dev Provides mul and div function for wads (decimal numbers with 18 digits precision) and rays (decimals with 27 digits) + **/ + +library WadRayMath { + uint256 internal constant WAD = 1e18; + uint256 internal constant halfWAD = WAD / 2; + + uint256 internal constant RAY = 1e27; + uint256 internal constant halfRAY = RAY / 2; + + uint256 internal constant WAD_RAY_RATIO = 1e9; + + /** + * @return One ray, 1e27 + **/ + function ray() internal pure returns (uint256) { + return RAY; + } + + /** + * @return One wad, 1e18 + **/ + + function wad() internal pure returns (uint256) { + return WAD; + } + + /** + * @return Half ray, 1e27/2 + **/ + function halfRay() internal pure returns (uint256) { + return halfRAY; + } + + /** + * @return Half ray, 1e18/2 + **/ + function halfWad() internal pure returns (uint256) { + return halfWAD; + } + + /** + * @dev Multiplies two wad, rounding half up to the nearest wad + * @param a Wad + * @param b Wad + * @return The result of a*b, in wad + **/ + function wadMul(uint256 a, uint256 b) internal pure returns (uint256) { + if (a == 0 || b == 0) { + return 0; + } + + require(a <= (type(uint256).max - halfWAD) / b, Errors.MATH_MULTIPLICATION_OVERFLOW); + + return (a * b + halfWAD) / WAD; + } + + /** + * @dev Divides two wad, rounding half up to the nearest wad + * @param a Wad + * @param b Wad + * @return The result of a/b, in wad + **/ + function wadDiv(uint256 a, uint256 b) internal pure returns (uint256) { + require(b != 0, Errors.MATH_DIVISION_BY_ZERO); + uint256 halfB = b / 2; + + require(a <= (type(uint256).max - halfB) / WAD, Errors.MATH_MULTIPLICATION_OVERFLOW); + + return (a * WAD + halfB) / b; + } + + /** + * @dev Multiplies two ray, rounding half up to the nearest ray + * @param a Ray + * @param b Ray + * @return The result of a*b, in ray + **/ + function rayMul(uint256 a, uint256 b) internal pure returns (uint256) { + if (a == 0 || b == 0) { + return 0; + } + + require(a <= (type(uint256).max - halfRAY) / b, Errors.MATH_MULTIPLICATION_OVERFLOW); + + return (a * b + halfRAY) / RAY; + } + + /** + * @dev Divides two ray, rounding half up to the nearest ray + * @param a Ray + * @param b Ray + * @return The result of a/b, in ray + **/ + function rayDiv(uint256 a, uint256 b) internal pure returns (uint256) { + require(b != 0, Errors.MATH_DIVISION_BY_ZERO); + uint256 halfB = b / 2; + + require(a <= (type(uint256).max - halfB) / RAY, Errors.MATH_MULTIPLICATION_OVERFLOW); + + return (a * RAY + halfB) / b; + } + + /** + * @dev Casts ray down to wad + * @param a Ray + * @return a casted to wad, rounded half up to the nearest wad + **/ + function rayToWad(uint256 a) internal pure returns (uint256) { + uint256 halfRatio = WAD_RAY_RATIO / 2; + uint256 result = halfRatio + a; + require(result >= halfRatio, Errors.MATH_ADDITION_OVERFLOW); + + return result / WAD_RAY_RATIO; + } + + /** + * @dev Converts wad up to ray + * @param a Wad + * @return a converted in ray + **/ + function wadToRay(uint256 a) internal pure returns (uint256) { + uint256 result = a * WAD_RAY_RATIO; + require(result / WAD_RAY_RATIO == a, Errors.MATH_MULTIPLICATION_OVERFLOW); + return result; + } +} diff --git a/packages/lib-sourcify/test/sources/NoAuxdata/artifact.json b/packages/lib-sourcify/test/sources/NoAuxdata/artifact.json new file mode 100644 index 000000000..f01c9bbcf --- /dev/null +++ b/packages/lib-sourcify/test/sources/NoAuxdata/artifact.json @@ -0,0 +1,36 @@ +{ + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "setValue", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "value", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x6080604052348015600f57600080fd5b50602a60008190555060fd806100266000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80633fa4f24514603757806355241077146051575b600080fd5b603d6069565b604051604891906090565b60405180910390f35b606760048036038101906063919060d5565b606f565b005b60005481565b8060008190555050565b6000819050919050565b608a816079565b82525050565b600060208201905060a360008301846083565b92915050565b600080fd5b60b5816079565b811460bf57600080fd5b50565b60008135905060cf8160ae565b92915050565b60006020828403121560e85760e760a9565b5b600060f48482850160c2565b9150509291505056" +} diff --git a/packages/lib-sourcify/test/sources/NoAuxdata/metadata.json b/packages/lib-sourcify/test/sources/NoAuxdata/metadata.json new file mode 100644 index 000000000..98646031f --- /dev/null +++ b/packages/lib-sourcify/test/sources/NoAuxdata/metadata.json @@ -0,0 +1,74 @@ +{ + "compiler": { + "version": "0.8.28+commit.7893614a" + }, + "language": "Solidity", + "output": { + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + } + ], + "name": "setValue", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "value", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "devdoc": { + "kind": "dev", + "methods": {}, + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": {}, + "version": 1 + } + }, + "settings": { + "compilationTarget": { + "sources/NoAuxdata.sol": "NoAuxdata" + }, + "evmVersion": "istanbul", + "libraries": {}, + "metadata": { + "appendCBOR": false + }, + "optimizer": { + "enabled": false, + "runs": 200 + }, + "remappings": [] + }, + "sources": { + "sources/NoAuxdata.sol": { + "keccak256": "0xd4eea0e66e920a96471e32573c7853f805b023c38987f0daeea972a3bfb79baf", + "license": "MIT", + "urls": [] + } + }, + "version": 1 +} diff --git a/packages/lib-sourcify/test/sources/NoAuxdata/sources/NoAuxdata.sol b/packages/lib-sourcify/test/sources/NoAuxdata/sources/NoAuxdata.sol new file mode 100644 index 000000000..e89cd9038 --- /dev/null +++ b/packages/lib-sourcify/test/sources/NoAuxdata/sources/NoAuxdata.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract NoAuxdata { + uint256 public value; + + constructor() { + value = 42; + } + + function setValue(uint256 _value) public { + value = _value; + } +} diff --git a/packages/lib-sourcify/test/sources/ViaIRUnoptimizedMismatch/sources/C.sol b/packages/lib-sourcify/test/sources/ViaIRUnoptimizedMismatch/sources/A similarity index 100% rename from packages/lib-sourcify/test/sources/ViaIRUnoptimizedMismatch/sources/C.sol rename to packages/lib-sourcify/test/sources/ViaIRUnoptimizedMismatch/sources/A diff --git a/packages/lib-sourcify/test/utils.ts b/packages/lib-sourcify/test/utils.ts index a6149049b..aaa0bf217 100644 --- a/packages/lib-sourcify/test/utils.ts +++ b/packages/lib-sourcify/test/utils.ts @@ -25,6 +25,7 @@ import { SolidityJsonInput, SolidityOutput, } from '../src/Compilation/SolidityTypes'; +import { Verification } from '../src/Verification/Verification'; /** * Function to deploy contracts from provider unlocked accounts * contractFolderPath must contain an artifact.json file with "abi" and "bytecode" fields @@ -183,3 +184,118 @@ export const expectMatch = ( throw e; } }; + +/** + * Helper function to verify a Verification object using its getters + * @param verification The Verification object to check + * @param expected Object containing the expected values to verify + */ +export function expectVerification( + verification: Verification, + expected: { + status?: { + runtimeMatch?: 'perfect' | 'partial' | null; + creationMatch?: 'perfect' | 'partial' | null; + }; + libraryMap?: { + runtime?: { [key: string]: string }; + creation?: { [key: string]: string }; + }; + deploymentInfo?: { + blockNumber?: number; + txIndex?: number; + deployer?: string; + }; + transformations?: { + runtime?: { + list?: any[]; + values?: any; + }; + creation?: { + list?: any[]; + values?: any; + }; + }; + abiEncodedConstructorArguments?: string; + }, +) { + try { + // Check status + if (expected.status) { + if (expected.status.runtimeMatch !== undefined) { + expect(verification.status.runtimeMatch).to.equal( + expected.status.runtimeMatch, + ); + } + if (expected.status.creationMatch !== undefined) { + expect(verification.status.creationMatch).to.equal( + expected.status.creationMatch, + ); + } + } + + // Check library map + if (expected.libraryMap) { + if (expected.libraryMap.runtime) { + expect(verification.libraryMap.runtime).to.deep.equal( + expected.libraryMap.runtime, + ); + } + if (expected.libraryMap.creation) { + expect(verification.libraryMap.creation).to.deep.equal( + expected.libraryMap.creation, + ); + } + } + + // Check deployment info + if (expected.deploymentInfo) { + if (expected.deploymentInfo.blockNumber !== undefined) { + expect(verification.deploymentInfo.blockNumber).to.equal( + expected.deploymentInfo.blockNumber, + ); + } + if (expected.deploymentInfo.txIndex !== undefined) { + expect(verification.deploymentInfo.txIndex).to.equal( + expected.deploymentInfo.txIndex, + ); + } + if (expected.deploymentInfo.deployer !== undefined) { + expect(verification.deploymentInfo.deployer).to.equal( + expected.deploymentInfo.deployer, + ); + } + } + + // Check transformations + if (expected.transformations) { + if (expected.transformations.runtime) { + if (expected.transformations.runtime.list) { + expect(verification.transformations.runtime.list).to.deep.equal( + expected.transformations.runtime.list, + ); + } + if (expected.transformations.runtime.values) { + expect(verification.transformations.runtime.values).to.deep.equal( + expected.transformations.runtime.values, + ); + } + } + if (expected.transformations.creation) { + if (expected.transformations.creation.list) { + expect(verification.transformations.creation.list).to.deep.equal( + expected.transformations.creation.list, + ); + } + if (expected.transformations.creation.values) { + expect(verification.transformations.creation.values).to.deep.equal( + expected.transformations.creation.values, + ); + } + } + } + } catch (e) { + console.log('Verification:', verification); + throw e; + } +}