Skip to content

Commit

Permalink
add fetching of implementation contract from celoscan
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronmgdr committed Mar 19, 2024
1 parent 4b68b2a commit c9e1547
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 9 deletions.
2 changes: 1 addition & 1 deletion packages/cli/transactions2.json
Original file line number Diff line number Diff line change
@@ -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"}]
[{"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"}]
4 changes: 2 additions & 2 deletions packages/sdk/explorer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 ",
Expand Down
37 changes: 35 additions & 2 deletions packages/sdk/explorer/src/block-explorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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<ContractMapping | undefined> => {
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
Expand All @@ -377,6 +409,7 @@ export class BlockExplorer {
selector: string,
strategies = [
this.getContractMappingFromCore,
this.getContractMappingFromCeloScan,
this.getContractMappingFromSourcify,
this.getContractMappingFromSourcifyAsProxy,
]
Expand Down
23 changes: 19 additions & 4 deletions packages/sdk/explorer/src/sourcify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ export interface MetadataResponse {
}
settings?: {
compilationTarget?: Record<string, string>
implementation?: string
name?: string
}
}

Expand All @@ -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<string, ABIDefinition> = new Map()

private abiCoder: AbiCoder
Expand All @@ -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
Expand Down Expand Up @@ -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]
}
}

Expand Down Expand Up @@ -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')
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit c9e1547

Please sign in to comment.