Skip to content

Conversation

Subarna-Singh
Copy link
Contributor

@Subarna-Singh Subarna-Singh commented Oct 14, 2025

Closes #OPDATA 3775 - Step 1

Description

Add functionality to query decimals from contract and return it as part of the result

Steps to Test

yarn test packages/source/view-function-multi-chain/test

Input

{
  "data": {
      "endpoint": "function",
      "contract": "0x2c1d072e956AFFC0D435Cb7AC38EF18d24d9127c",
  "function": "function latestAnswer() view returns (int256)",
  "network": "ETHEREUM",
  "data": {
        "decimals": {
            "signature": "function decimals() view returns (uint8)"
        }
    }
  }
}

Output

{
    "data": {
        "result": "0x000000000000000000000000000000000000000000000000000000006c5031f8",
        "decimals": "0x0000000000000000000000000000000000000000000000000000000000000008"
    },
    "statusCode": 200,
    "result": "0x000000000000000000000000000000000000000000000000000000006c5031f8",
    "timestamps": {
        "providerDataRequestedUnixMs": 1760547417551,
        "providerDataReceivedUnixMs": 1760547421541
    },
    "meta": {
        "adapterName": "VIEW_FUNCTION_MULTI_CHAIN",
        "metrics": {
            "feedId": "{\"signature\":\"function latestanswer() view returns (int256)\",\"address\":\"0x2c1d072e956affc0d435cb7ac38ef18d24d9127c\",\"inputParams\":[],\"network\":\"ethereum\",\"data\":{\"decimals\":{\"signature\":\"function decimals() view returns (uint8)\"}}}"
        }
    }
}

Quality Assurance

  • If a new adapter was made, or an existing one was modified so that its environment variables have changed, update the relevant infra-k8s configuration file.
  • If a new adapter was made, or an existing one was modified so that its environment variables have changed, update the relevant adapter-secrets configuration file or update the soak testing blacklist.
  • If a new adapter was made, or a new endpoint was added, update the test-payload.json file with relevant requests.
  • The branch naming follows git flow (feature/x, chore/x, release/x, hotfix/x, fix/x) or is created from Jira.
  • This is related to a maximum of one Jira story or GitHub issue.
  • Types are safe (avoid TypeScript/TSLint features like any and disable, instead use more specific types).
  • All code changes have 100% unit and integration test coverage. If testing is not applicable or too difficult to justify doing, the reasoning should be documented explicitly in the PR.

Copy link

changeset-bot bot commented Oct 14, 2025

🦋 Changeset detected

Latest commit: a3eece4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@chainlink/view-function-multi-chain-adapter Minor
@chainlink/proof-of-reserves-adapter Patch
@chainlink/renvm-address-set-adapter Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@mxiao-cll
Copy link
Contributor

This is not really a good idea because we are trying to inject some contract specific logic into a generic piece of code.

@mmcallister-cll @dskloetc Any ideas on how we should do this? Maybe put the decimal abi into input and have a result2 in output?

@mmcallister-cll
Copy link
Contributor

Yeah, agree with @mxiao-cll, we shouldn't call this for every base function call, we can't guarantee every contract will have decimals

My initial ideas are:

  1. (Preferred) what's wrong with doing multiple queries where this is called ie: Promise.all([value, decimals])
  2. or a separate endpoint eg:
{
  "endpoint": "function-with-decimals"
...
}
  1. or even some sort of extension that allows for an ordered array of multiple signature inputs that outputs an ordered array of results

@dskloetc
Copy link
Contributor

  1. or even some sort of extension that allows for an ordered array of multiple signature inputs that outputs an ordered array of results

My preference is closest to this. But I would suggest a record/map where the results are returned under the same keys as the inputs. So the input could be something like:

  "data": {
    "endpoint": "function",
    "signature": "function latestAnswer() view returns (int256)",
    "address": "0x779877A7B0D9E8603169DdbD7836e478b4624789",
    "network": "ETHEREUM_GOERLI",
    "data": {
      "decimals": {
        "signature": "function decimals() view returns (int256)",
        "address": "0x779877A7B0D9E8603169DdbD7836e478b4624789",
      }
    }
  }

And then the result would be of the shape:

{
  result: "123456"
  data: {
    decimals: 18
  }
}

But we could put anything else in data and it would be mapped to the corresponding name.
Perhaps address is optional and it would reuse the address from the base request if not specified.

However, I can also see how it would be beneficial to have the decimals for free on all requests without having to change the input. But making a request that is expected to fail is a bit ugly. Do we know how many of the contracts we call do not have a decimals function? Maybe we can check what's currently in RDD?

@Subarna-Singh
Copy link
Contributor Author

Subarna-Singh commented Oct 15, 2025

  1. or even some sort of extension that allows for an ordered array of multiple signature inputs that outputs an ordered array of results

My preference is closest to this. But I would suggest a record/map where the results are returned under the same keys as the inputs. So the input could be something like:

  "data": {
    "endpoint": "function",
    "signature": "function latestAnswer() view returns (int256)",
    "address": "0x779877A7B0D9E8603169DdbD7836e478b4624789",
    "network": "ETHEREUM_GOERLI",
    "data": {
      "decimals": {
        "signature": "function decimals() view returns (int256)",
        "address": "0x779877A7B0D9E8603169DdbD7836e478b4624789",
      }
    }
  }

And then the result would be of the shape:

{
  result: "123456"
  data: {
    decimals: 18
  }
}

But we could put anything else in data and it would be mapped to the corresponding name. Perhaps address is optional and it would reuse the address from the base request if not specified.

However, I can also see how it would be beneficial to have the decimals for free on all requests without having to change the input. But making a request that is expected to fail is a bit ugly. Do we know how many of the contracts we call do not have a decimals function? Maybe we can check what's currently in RDD?

Contracts in RDD that do not support decimals()

  1. https://github.com/smartcontractkit/reference-data-directory/blob/2539836df857c8c321c1337871b851e8f7683be0/ethereum-mainnet-mantle-1/contracts/0x874effca5376D434d49021B3638d1e1BC214EC27.json

  2. https://github.com/smartcontractkit/reference-data-directory/blob/2539836df857c8c321c1337871b851e8f7683be0/ethereum-mainnet-mantle-1/contracts/0x4606b8D331f07f8167d5bcD891Ff6a4eE984B8e4.json

Comment on lines +30 to +34
data: {
description: 'Optional map of function calls',
type: 'object' as unknown as Record<string, any>,
required: false,
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

data seems to be very generic name, maybe additionalRequests?

Also it is possible to type this correctly without using any

Copy link
Contributor

@dskloetc dskloetc Oct 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggested data because in the response these fields would also appear in data.
additionalRequests is also fine.

const { address, signature, inputParams, network } = param
const { address, signature, inputParams, network, data } = param

const mainResult = await this._executeFunction({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can run these two in parallel

}

private async _processNestedDataRequest(
data: Record<string, unknown> | undefined,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be typed as Record<string, RequestParams>

): Promise<Record<string, any>> {
if (!data || typeof data !== 'object') return {}

const results: Record<string, any> = {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not use any here?

resultField: req.resultField,
}

const subRes = await this._executeFunction(nestedParam)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we run these in parallel?

expect(response.json()).toMatchSnapshot()
})

it('should return success with additional data requests ', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicated test case?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants