Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/sweet-clubs-decide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@chainlink/calculated-price-adapter': minor
---

Added support for min/max operations
24 changes: 12 additions & 12 deletions packages/composites/calculated-price/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,18 @@ Supported names for this endpoint are: `computedprice`, `impliedprice`.

### Input Params

| Required? | Name | Aliases | Description | Type | Options | Default | Depends On | Not Valid With |
| :-------: | :-------------------: | :------------------: | :----------------------------------------------------------------------------: | :------: | :------------------: | :------: | :--------: | :------------: |
| ✅ | operand1Sources | `dividendSources` | An array of source adapters to query for the operand1 value | string[] | | | | |
| ✅ | operand1Input | `dividendInput` | The JSON payload to send to the operand1 sources | string | | | | |
| | operand1MinAnswers | `dividendMinAnswers` | The minimum number of answers needed to return a value for the operand1 | number | | `1` | | |
| | operand1DecimalsField | | The field path in operand1 response data containing the decimal scaling factor | string | | | | |
| ✅ | operand2Sources | `divisorSources` | An array of source adapters to query for the operand2 value | string[] | | | | |
| ✅ | operand2Input | `divisorInput` | The JSON payload to send to the operand2 sources | string | | | | |
| | operand2MinAnswers | `divisorMinAnswers` | The minimum number of answers needed to return a value for the operand2 | number | | `1` | | |
| | operand2DecimalsField | | The field path in operand2 response data containing the decimal scaling factor | string | | | | |
| | operation | | The operation to perform on the operands | string | `divide`, `multiply` | `divide` | | |
| | outputDecimals | | Decimal scaling of the result | number | | | | |
| Required? | Name | Aliases | Description | Type | Options | Default | Depends On | Not Valid With |
| :-------: | :-------------------: | :------------------: | :----------------------------------------------------------------------------: | :------: | :--------------------------------: | :------: | :--------: | :------------: |
| ✅ | operand1Sources | `dividendSources` | An array of source adapters to query for the operand1 value | string[] | | | | |
| ✅ | operand1Input | `dividendInput` | The JSON payload to send to the operand1 sources | string | | | | |
| | operand1MinAnswers | `dividendMinAnswers` | The minimum number of answers needed to return a value for the operand1 | number | | `1` | | |
| | operand1DecimalsField | | The field path in operand1 response data containing the decimal scaling factor | string | | | | |
| ✅ | operand2Sources | `divisorSources` | An array of source adapters to query for the operand2 value | string[] | | | | |
| ✅ | operand2Input | `divisorInput` | The JSON payload to send to the operand2 sources | string | | | | |
| | operand2MinAnswers | `divisorMinAnswers` | The minimum number of answers needed to return a value for the operand2 | number | | `1` | | |
| | operand2DecimalsField | | The field path in operand2 response data containing the decimal scaling factor | string | | | | |
| | operation | | The operation to perform on the operands | string | `divide`, `max`, `min`, `multiply` | `divide` | | |
| | outputDecimals | | Decimal scaling of the result | number | | | | |

### Example

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const inputParameters = new InputParameters(
default: 'divide',
type: 'string',
description: 'The operation to perform on the operands',
options: ['divide', 'multiply'],
options: ['divide', 'multiply', 'min', 'max'],
},
outputDecimals: {
required: false,
Expand Down
25 changes: 17 additions & 8 deletions packages/composites/calculated-price/src/transport/computePrice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
export type ComputedPriceTransportTypes = BaseEndpointTypes & {
Provider: {
RequestBody: never
ResponseBody: any

Check warning on line 31 in packages/composites/calculated-price/src/transport/computePrice.ts

View workflow job for this annotation

GitHub Actions / Run linters and formatters

Unexpected any. Specify a different type
}
}

Expand Down Expand Up @@ -156,14 +156,23 @@
: operand2Median

let computedResult: Decimal
if (operation.toLowerCase() === 'divide') {
computedResult = scaledOperand1.div(scaledOperand2)
} else if (operation.toLowerCase() === 'multiply') {
computedResult = scaledOperand1.mul(scaledOperand2)
} else {
throw new AdapterError({
message: `Unsupported operation: ${operation}. This should not be possible because of input validation.`,
})
switch (operation.toLowerCase()) {
case 'divide':
computedResult = scaledOperand1.div(scaledOperand2)
break
case 'multiply':
computedResult = scaledOperand1.mul(scaledOperand2)
break
case 'min':
computedResult = Decimal.min(scaledOperand1, scaledOperand2)
break
case 'max':
computedResult = Decimal.max(scaledOperand1, scaledOperand2)
break
default:
throw new AdapterError({
message: `Unsupported operation: ${operation}. This should not be possible because of input validation.`,
})
}

// Scale result up to output decimals if decimals are defined
Expand Down Expand Up @@ -211,7 +220,7 @@
method: 'POST',
data: { data: JSON.parse(input) },
}
const result = await this.requester.request<Record<string, any>>(

Check warning on line 223 in packages/composites/calculated-price/src/transport/computePrice.ts

View workflow job for this annotation

GitHub Actions / Run linters and formatters

Unexpected any. Specify a different type
JSON.stringify(requestConfig),
requestConfig,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,38 @@ exports[`execute computePrice endpoint should return success for legacy division
}
`;

exports[`execute computePrice endpoint should return success for max 1`] = `
{
"data": {
"operand1Result": "5",
"operand2Result": "20",
"result": "20",
},
"result": "20",
"statusCode": 200,
"timestamps": {
"providerDataReceivedUnixMs": 978347471111,
"providerDataRequestedUnixMs": 978347471111,
},
}
`;

exports[`execute computePrice endpoint should return success for min 1`] = `
{
"data": {
"operand1Result": "20",
"operand2Result": "10",
"result": "10",
},
"result": "10",
"statusCode": 200,
"timestamps": {
"providerDataReceivedUnixMs": 978347471111,
"providerDataRequestedUnixMs": 978347471111,
},
}
`;

exports[`execute computePrice endpoint should return success for multiply 1`] = `
{
"data": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,70 @@ describe('execute', () => {
nock.cleanAll()
})

it('should return success for min', async () => {
const data = {
operand1Sources: ['ncfx'],
operand1Input: JSON.stringify({
from: 'LINK',
to: 'USD12',
overrides: {
coingecko: {
LINK: 'chainlink',
},
},
}),
operand2Sources: ['tiingo'],
operand2Input: JSON.stringify({
from: 'ETH',
to: 'USD12',
overrides: {
coingecko: {
ETH: 'ethereum',
},
},
}),
operation: 'min',
}
mockDPResponseSuccess('ncfx', 20)
mockDPResponseSuccess('tiingo', 10)
const response = await testAdapter.request(data)
expect(response.statusCode).toBe(200)
expect(response.json()).toMatchSnapshot()
nock.cleanAll()
})

it('should return success for max', async () => {
const data = {
operand1Sources: ['ncfx'],
operand1Input: JSON.stringify({
from: 'LINK',
to: 'USD13',
overrides: {
coingecko: {
LINK: 'chainlink',
},
},
}),
operand2Sources: ['tiingo'],
operand2Input: JSON.stringify({
from: 'ETH',
to: 'USD13',
overrides: {
coingecko: {
ETH: 'ethereum',
},
},
}),
operation: 'max',
}
mockDPResponseSuccess('ncfx', 5)
mockDPResponseSuccess('tiingo', 20)
const response = await testAdapter.request(data)
expect(response.statusCode).toBe(200)
expect(response.json()).toMatchSnapshot()
nock.cleanAll()
})

it('returns failure with mismatched operand1Decimals', async () => {
const data = {
operand1Sources: ['ncfx', 'elwood'],
Expand Down
Loading