diff --git a/packages/cli/transactions2.json b/packages/cli/transactions2.json index 71b657a41..43cc7eaf8 100644 --- a/packages/cli/transactions2.json +++ b/packages/cli/transactions2.json @@ -1 +1 @@ -[{"contract":"0xFa3df877F98ac5ecd87456a7AcCaa948462412f0","address":"0xFa3df877F98ac5ecd87456a7AcCaa948462412f0","function":"removeLiquidity(uint256,uint256[],uint256)","args":["10000000000000000000000000",["0","0"],1674883684],"value":"0"},{"contract":"0x765de816845861e75a25fca122bb6898b8b1282a","address":"0x765de816845861e75a25fca122bb6898b8b1282a","function":"approve(address,uint256)","args":["0x87647780180B8f55980C7D3fFeFe08a9B29e9aE1","11000000000000000000000000"],"value":"0"},{"contract":"0x37f750B7cC259A2f741AF45294f6a16572CF5cAd","address":"0x37f750B7cC259A2f741AF45294f6a16572CF5cAd","function":"approve(address,uint256)","args":["0x87647780180B8f55980C7D3fFeFe08a9B29e9aE1","11000000000000"],"value":"0"}] \ No newline at end of file +[{"contract":"0x765de816845861e75a25fca122bb6898b8b1282a","address":"0x765de816845861e75a25fca122bb6898b8b1282a","function":"approve(address,uint256)","args":["0x87647780180B8f55980C7D3fFeFe08a9B29e9aE1","11000000000000000000000000"],"value":"0"},{"contract":"0x37f750B7cC259A2f741AF45294f6a16572CF5cAd","address":"0x37f750B7cC259A2f741AF45294f6a16572CF5cAd","function":"approve(address,uint256)","args":["0x87647780180B8f55980C7D3fFeFe08a9B29e9aE1","11000000000000"],"value":"0"}] \ No newline at end of file diff --git a/packages/sdk/explorer/package.json b/packages/sdk/explorer/package.json index 1b653310e..0808b23a1 100644 --- a/packages/sdk/explorer/package.json +++ b/packages/sdk/explorer/package.json @@ -14,8 +14,8 @@ "contractkit" ], "scripts": { - "build": "tsc -b .", - "clean": "tsc -b . --clean", + "build": "yarn run --top-level tsc -b .", + "clean": "yarn run --top-level tsc -b . --clean", "docs": "yarn run --top-level typedoc", "test": "yarn run --top-level jest --runInBand --passWithNoTests", "lint": "yarn run --top-level eslint -c .eslintrc.js ", diff --git a/packages/sdk/explorer/src/block-explorer.ts b/packages/sdk/explorer/src/block-explorer.ts index fcc1ead67..37b5940b9 100644 --- a/packages/sdk/explorer/src/block-explorer.ts +++ b/packages/sdk/explorer/src/block-explorer.ts @@ -18,7 +18,7 @@ import { mapFromPairs, obtainKitContractDetails, } from './base' -import { fetchMetadata, tryGetProxyImplementation } from './sourcify' +import { fetchMetadata, queryCeloScan, tryGetProxyImplementation } from './sourcify' const debug = debugFactory('kit:explorer:block') export interface ContractNameAndMethodAbi { @@ -324,7 +324,6 @@ export class BlockExplorer { this.kit.web3.utils.toChecksumAddress(address) ) const mapping = metadata?.toContractMapping() - console.error('mapping', mapping) if (mapping) { this.addressMapping.set(address, mapping) } @@ -363,6 +362,39 @@ export class BlockExplorer { } } } + /** + * Returns the ContractMapping for the contract at that address, or if a proxy its implementation + * by looking up the contract address in CeloScan. + * @param address + * @returns The ContractMapping for the contract at that address, or undefined + */ + getContractMappingFromCeloScan = async ( + address: string + ): Promise => { + const cached = this.addressMapping.get(address) + if (cached) { + return cached + } + let metadata = await queryCeloScan( + this.kit.connection, + this.kit.web3.utils.toChecksumAddress(address) + ) + if (metadata?.implementationAddress) { + metadata = await queryCeloScan( + this.kit.connection, + this.kit.web3.utils.toChecksumAddress(metadata?.implementationAddress) + ) + } + const mapping = metadata?.toContractMapping() + console.info('CS map', address) + mapping?.fnMapping.forEach((selector) => { + console.info(selector.name, JSON.stringify(selector)) + }) + if (mapping) { + this.addressMapping.set(address, mapping) + } + return mapping + } /** * Uses all of the strategies available to find a contract mapping that contains @@ -377,6 +409,7 @@ export class BlockExplorer { selector: string, strategies = [ this.getContractMappingFromCore, + this.getContractMappingFromCeloScan, this.getContractMappingFromSourcify, this.getContractMappingFromSourcifyAsProxy, ] diff --git a/packages/sdk/explorer/src/sourcify.ts b/packages/sdk/explorer/src/sourcify.ts index 6bc573b83..b0b4db340 100644 --- a/packages/sdk/explorer/src/sourcify.ts +++ b/packages/sdk/explorer/src/sourcify.ts @@ -53,6 +53,8 @@ export interface MetadataResponse { } settings?: { compilationTarget?: Record + implementation?: string + name?: string } } @@ -64,6 +66,7 @@ export interface MetadataResponse { export class Metadata { public abi: AbiItem[] | null = null public contractName: string | null = null + public implementationAddress: string | null = null public fnMapping: Map = new Map() private abiCoder: AbiCoder @@ -72,8 +75,9 @@ export class Metadata { constructor(connection: Connection, address: Address, response: MetadataResponse) { this.abiCoder = connection.getAbiCoder() - this.response = response as MetadataResponse + this.contractName = response.settings?.name || null + this.implementationAddress = response.settings?.implementation || null // XXX: For some reason this isn't exported as it should be // @ts-ignore this.jsonInterfaceMethodToString = connection.web3.utils._jsonInterfaceMethodToString @@ -109,7 +113,7 @@ export class Metadata { // XXX: Not sure when there are multiple compilationTargets and what should // happen then but defaulting to this for now. const contracts = Object.values(value.settings.compilationTarget) - this.contractName = contracts[0] + this.contractName = this.contractName || contracts[0] } } @@ -187,7 +191,13 @@ export async function fetchMetadata( } console.error('None found on full match, trying celoScan') const fullMatchFromCeloScan = await queryCeloScan(connection, contract) + // TODO decide if querying celoscan here is the best or if each place fetchMetada is called to do it there + // same for the implementation proxy fetching if (fullMatchFromCeloScan !== null) { + if (fullMatchFromCeloScan.implementationAddress) { + console.info('Implementation found', fullMatchFromCeloScan.implementationAddress) + return queryCeloScan(connection, fullMatchFromCeloScan.implementationAddress) + } return fullMatchFromCeloScan } console.error('No full match found, trying partial match') @@ -266,9 +276,14 @@ export async function queryCeloScan( ) if (resp.ok) { const json = (await resp.json()) as CeloScanResponse + // TODO get implementation when it is a proxy. the implementation address is in the response already. if (json.message === 'OK' && json.result[0].SourceCode.length > 2) { - const data = JSON.parse(json.result[0].ABI) as AbiItem[] - return new Metadata(connection, contract, { output: { abi: data } }) + const info = json.result[0] + const data = JSON.parse(info.ABI) as AbiItem[] + return new Metadata(connection, contract, { + output: { abi: data }, + settings: { name: info.ContractName, implementation: info.Implementation }, + }) } } return null