Skip to content
Merged
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
24 changes: 13 additions & 11 deletions price_pusher/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,20 @@ The parameters above are configured per price feed in a price configuration YAML
time_difference: 60 # Time difference threshold (in seconds) to push a newer price feed.
price_deviation: 0.5 # The price deviation (%) threshold to push a newer price feed.
confidence_ratio: 1 # The confidence/price (%) threshold to push a newer price feed.
# Optional block to configure whether this feed can be early updated. If at least one feed meets the
# triggering conditions above, all other feeds who meet the early update conditions will be included in
# the submitted batch of prices. This logic takes advantage of the fact that adding a feed to a larger
# batch of updates incurs a minimal gas cost. All fields below are optional (and interpreted as infinity if omitted)
# and have the same semantics as the corresponding fields above.
early_update:
time_difference: 30
price_deviation: 0.1
confidence_ratio: 0.5
- ...
```

Two sample YAML configuration files are available in the root of this repo.

You can get the list of available price feeds from
[here](https://pyth.network/developers/price-feed-ids/).

Expand All @@ -57,7 +68,7 @@ cd price_pusher
npm run start -- evm --endpoint wss://example-rpc.com \
--pyth-contract-address 0xff1a0f4744e8582DF...... \
--price-service-endpoint https://example-pyth-price.com \
--price-config-file "path/to/price-config-file.yaml.testnet.sample.yaml" \
--price-config-file "path/to/price-config.testnet.sample.yaml" \
--mnemonic-file "path/to/mnemonic.txt" \
[--pushing-frequency 10] \
[--polling-frequency 5] \
Expand All @@ -66,7 +77,7 @@ npm run start -- evm --endpoint wss://example-rpc.com \
# For Injective
npm run start -- injective --grpc-endpoint https://grpc-endpoint.com \
--pyth-contract-address inj1z60tg0... --price-service-endpoint "https://example-pyth-price.com" \
--price-config-file "path/to/price-config-file.yaml.testnet.sample.yaml" \
--price-config-file "path/to/price-config.testnet.sample.yaml" \
--mnemonic-file "path/to/mnemonic.txt" \
[--pushing-frequency 10] \
[--polling-frequency 5] \
Expand Down Expand Up @@ -94,15 +105,6 @@ npm run start -- sui
[--polling-frequency 5] \



--endpoint https://fullnode.testnet.aptoslabs.com/v1 \
--pyth-contract-address 0x7e783b349d3e89cf5931af376ebeadbfab855b3fa239b7ada8f5a92fbea6b387 --price-service-endpoint "https://xc-testnet.pyth.network" \
--price-config-file "./price-config.testnet.sample.yaml" \
--mnemonic-file "path/to/mnemonic.txt" \
[--pushing-frequency 10] \
[--polling-frequency 5] \


# Or, run the price pusher docker image instead of building from the source
docker run public.ecr.aws/pyth-network/xc-price-pusher:v<version> -- <above-arguments>
```
Expand Down
4 changes: 4 additions & 0 deletions price_pusher/price-config.mainnet.sample.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@
time_difference: 60
price_deviation: 1
confidence_ratio: 1
early_update:
time_difference: 30
price_deviation: 0.5
confidence_ratio: 0.1
4 changes: 4 additions & 0 deletions price_pusher/price-config.testnet.sample.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@
time_difference: 60
price_deviation: 1
confidence_ratio: 1
early_update:
time_difference: 30
price_deviation: 0.5
confidence_ratio: 0.1
23 changes: 19 additions & 4 deletions price_pusher/src/controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { UnixTimestamp } from "@pythnetwork/price-service-client";
import { DurationInSeconds, sleep } from "./utils";
import { IPricePusher, IPriceListener } from "./interface";
import { PriceConfig, shouldUpdate } from "./price-config";
import { IPriceListener, IPricePusher } from "./interface";
import { PriceConfig, shouldUpdate, UpdateCondition } from "./price-config";

export class Controller {
private pushingFrequency: DurationInSeconds;
Expand All @@ -28,6 +28,9 @@ export class Controller {
await sleep(this.pushingFrequency * 1000);

for (;;) {
// We will push all prices whose update condition is YES or EARLY as long as there is
// at least one YES.
let pushThresholdMet = false;
const pricesToPush: PriceConfig[] = [];
const pubTimesToPush: UnixTimestamp[] = [];

Expand All @@ -39,12 +42,24 @@ export class Controller {
const sourceLatestPrice =
this.sourcePriceListener.getLatestPriceInfo(priceId);

if (shouldUpdate(priceConfig, sourceLatestPrice, targetLatestPrice)) {
const priceShouldUpdate = shouldUpdate(
priceConfig,
sourceLatestPrice,
targetLatestPrice
);
if (priceShouldUpdate == UpdateCondition.YES) {
pushThresholdMet = true;
}

if (
priceShouldUpdate == UpdateCondition.YES ||
priceShouldUpdate == UpdateCondition.EARLY
) {
pricesToPush.push(priceConfig);
pubTimesToPush.push((targetLatestPrice?.publishTime || 0) + 1);
}
}
if (pricesToPush.length !== 0) {
if (pushThresholdMet) {
console.log(
"Some of the above values passed the threshold. Will push the price."
);
Expand Down
5 changes: 5 additions & 0 deletions price_pusher/src/evm/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ export default {
mnemonic,
pythContractAddress
);
console.log(
`Pushing updates from wallet address: ${pythContractFactory
.createWeb3PayerProvider()
.getAddress()}`
);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

drive-by for ease of use


const evmListener = new EvmPriceListener(pythContractFactory, priceItems, {
pollingFrequency,
Expand Down
2 changes: 1 addition & 1 deletion price_pusher/src/evm/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ export class EvmPricePusher implements IPricePusher {
// the update data is valid there is no possible rejection cause other than
// the target chain price being already updated.
console.log(
"Execution reverted. With high probablity, the target chain price " +
"Execution reverted. With high probability, the target chain price " +
"has already updated, Skipping this push."
);
return;
Expand Down
58 changes: 47 additions & 11 deletions price_pusher/src/price-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ const PriceConfigFileSchema: Joi.Schema = Joi.array()
time_difference: Joi.number().required(),
price_deviation: Joi.number().required(),
confidence_ratio: Joi.number().required(),
early_update: Joi.object({
time_difference: Joi.number().optional(),
price_deviation: Joi.number().optional(),
confidence_ratio: Joi.number().optional(),
}).optional(),
})
)
.unique("id")
Expand All @@ -27,6 +32,12 @@ export type PriceConfig = {
timeDifference: DurationInSeconds;
priceDeviation: PctNumber;
confidenceRatio: PctNumber;

// An early update happens when another price has met the conditions to be pushed, so this
// price can be included in a batch update for minimal gas cost.
earlyUpdateTimeDifference: DurationInSeconds | undefined;
earlyUpdatePriceDeviation: PctNumber | undefined;
earlyUpdateConfidenceRatio: PctNumber | undefined;
};

export function readPriceConfigFile(path: string): PriceConfig[] {
Expand All @@ -44,11 +55,24 @@ export function readPriceConfigFile(path: string): PriceConfig[] {
timeDifference: priceConfigRaw.time_difference,
priceDeviation: priceConfigRaw.price_deviation,
confidenceRatio: priceConfigRaw.confidence_ratio,

earlyUpdateTimeDifference: priceConfigRaw.early_update?.time_difference,
earlyUpdatePriceDeviation: priceConfigRaw.early_update?.price_deviation,
earlyUpdateConfidenceRatio: priceConfigRaw.early_update?.confidence_ratio,
};
return priceConfig;
});
}

export enum UpdateCondition {
// This price feed must be updated
YES,
// This price feed may be updated as part of a larger batch
EARLY,
// This price feed shouldn't be updated
NO,
}

/**
* Checks whether on-chain price needs to be updated with the latest pyth price information.
*
Expand All @@ -59,25 +83,25 @@ export function shouldUpdate(
priceConfig: PriceConfig,
sourceLatestPrice: PriceInfo | undefined,
targetLatestPrice: PriceInfo | undefined
): boolean {
): UpdateCondition {
const priceId = priceConfig.id;

// There is no price to update the target with.
if (sourceLatestPrice === undefined) {
return false;
return UpdateCondition.YES;

Choose a reason for hiding this comment

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

is this a bug ?

Copy link
Collaborator

Choose a reason for hiding this comment

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

yes unfortunately. fix is in #2730

}

// It means that price never existed there. So we should push the latest price feed.
if (targetLatestPrice === undefined) {
console.log(
`${priceConfig.alias} (${priceId}) is not available on the target network. Pushing the price.`
);
return true;
return UpdateCondition.YES;
}

// The current price is not newer than the price onchain
if (sourceLatestPrice.publishTime < targetLatestPrice.publishTime) {
return false;
return UpdateCondition.NO;
}

const timeDifference =
Expand All @@ -99,19 +123,31 @@ export function shouldUpdate(
console.log("Target latest price: ", targetLatestPrice);

console.log(
`Time difference: ${timeDifference} (< ${priceConfig.timeDifference}?) OR ` +
`Time difference: ${timeDifference} (< ${priceConfig.timeDifference}? / early: < ${priceConfig.earlyUpdateTimeDifference}) OR ` +
`Price deviation: ${priceDeviationPct.toFixed(5)}% (< ${
priceConfig.priceDeviation
}%?) OR ` +
}%? / early: < ${priceConfig.earlyUpdatePriceDeviation}%?) OR ` +
`Confidence ratio: ${confidenceRatioPct.toFixed(5)}% (< ${
priceConfig.confidenceRatio
}%?)`
}%? / early: < ${priceConfig.earlyUpdatePriceDeviation}%?)`
);

const result =
if (
timeDifference >= priceConfig.timeDifference ||
priceDeviationPct >= priceConfig.priceDeviation ||
confidenceRatioPct >= priceConfig.confidenceRatio;

return result;
confidenceRatioPct >= priceConfig.confidenceRatio
) {
return UpdateCondition.YES;
} else if (
(priceConfig.earlyUpdateTimeDifference !== undefined &&
timeDifference >= priceConfig.earlyUpdateTimeDifference) ||
(priceConfig.earlyUpdatePriceDeviation !== undefined &&
priceDeviationPct >= priceConfig.earlyUpdatePriceDeviation) ||
(priceConfig.earlyUpdateConfidenceRatio !== undefined &&
confidenceRatioPct >= priceConfig.earlyUpdateConfidenceRatio)
) {
return UpdateCondition.EARLY;
} else {
return UpdateCondition.NO;
}
}