From 20c46355f4c3eb494a22997613405b9fea6fe84d Mon Sep 17 00:00:00 2001 From: wow-alpaca Date: Fri, 15 Mar 2024 16:37:53 +0700 Subject: [PATCH 1/5] chore: pyth paradeen feed script --- deploy/trade-mining/paradeen-feed-pyth.ts | 125 ++++++++++++++++++++++ deploy/utils/price.ts | 19 ++++ package.json | 1 + 3 files changed, 145 insertions(+) create mode 100644 deploy/trade-mining/paradeen-feed-pyth.ts create mode 100644 deploy/utils/price.ts diff --git a/deploy/trade-mining/paradeen-feed-pyth.ts b/deploy/trade-mining/paradeen-feed-pyth.ts new file mode 100644 index 0000000..80f5a59 --- /dev/null +++ b/deploy/trade-mining/paradeen-feed-pyth.ts @@ -0,0 +1,125 @@ +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { DeployFunction } from "hardhat-deploy/types"; +import { ethers } from "hardhat"; +import { getConfig } from "../utils/config"; +import { BigNumber } from "ethers"; +import { AP__factory, Paradeen__factory } from "../../typechain"; +import * as readlineSync from "readline-sync"; +import { getCoinGeckoPriceUSD } from "../utils/price"; +import { formatEther } from "ethers/lib/utils"; + +interface FeedParadeenParams { + weekTimestamp: BigNumber; + amount: BigNumber; +} + +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + const config = getConfig(); + const signer = (await ethers.getSigners())[0]; + const pythExponent = BigNumber.from(10).pow(6); + + // contracts + const ap = AP__factory.connect(config.TradeMining.AP, ethers.provider); + const paradeen = Paradeen__factory.connect( + config.TradeMining.paradeen, + signer + ); + + // constants + const E18 = BigNumber.from(10).pow(18); + const WEEK = BigNumber.from(604800); + + // configs + const tradingFeeBps = 9; + const weeklyFeeThreshold = BigNumber.from(20000).mul(E18); + + console.log(`> Prepare data...`); + + const currentBlock = await ethers.provider.getBlock("latest"); + const weekCursor = BigNumber.from(currentBlock.timestamp).div(WEEK).mul(WEEK); + + const weeklyTradingVolume = await ap.weeklyTotalSupply(weekCursor); + const tradingFeeCollected = weeklyTradingVolume + .mul(tradingFeeBps) + .mul(2) // account closing volume assure + .div(10000); + const pythPrice = await getCoinGeckoPriceUSD("pyth-network"); + + console.log(`> Weekly trading volume: ${formatEther(weeklyTradingVolume)}`); + console.log(`> Trading fee collected: ${formatEther(tradingFeeCollected)}`); + console.log(`> Pyth price: ${formatEther(pythPrice)}\n`); + + let amountToFeed = BigNumber.from(0); + + if (tradingFeeCollected.lt(weeklyFeeThreshold)) { + console.log( + `> Trading fee collected is < ${formatEther(weeklyFeeThreshold)} USD` + ); + amountToFeed = tradingFeeCollected.mul(E18).div(pythPrice); + } else { + console.log( + `> Trading fee collected is > ${formatEther(weeklyFeeThreshold)} USD` + ); + amountToFeed = weeklyFeeThreshold.mul(E18).div(pythPrice); + } + + console.log(`> Amount to feed: ${formatEther(amountToFeed)} Pyth`); + + const PARAMS_INPUT: Array = [ + { + weekTimestamp: weekCursor, + amount: amountToFeed.mul(pythExponent).div(E18), + }, + ]; + + // Check feed timestamp + const weeklyReward = await paradeen.tokensPerWeek(weekCursor); + if (weeklyReward.gt(0)) { + console.log( + `> Weekly reward for ${weekCursor}: ${formatEther(weeklyReward)}` + ); + console.log(`> Already fed for this week`); + const goNext = readlineSync.question("Confirm to re-feed? (y/n): "); + switch (goNext.toLowerCase()) { + case "y": + break; + case "n": + console.log("Aborting"); + return; + default: + console.log("Invalid input"); + return; + } + } + + // Ask for confirmation + console.table(PARAMS_INPUT); + const confirm = readlineSync.question("Confirm? (y/n): "); + switch (confirm.toLowerCase()) { + case "y": + break; + case "n": + console.log("Aborting"); + return; + default: + console.log("Invalid input"); + return; + } + + const timestamps = PARAMS_INPUT.map((p) => + p.weekTimestamp.div(WEEK).mul(WEEK) + ); + const amounts = PARAMS_INPUT.map((p) => p.amount); + + console.log("> Feeding rewards to Paradeen"); + const tx = await paradeen.feed(timestamps, amounts, { + gasLimit: 10000000000, + }); + console.log(`> ⛓ Tx submitted: ${tx.hash}`); + console.log(`> Waiting tx to be mined...`); + await tx.wait(); + console.log(`> Tx mined!`); +}; + +export default func; +func.tags = ["FeedParadeenPyth"]; diff --git a/deploy/utils/price.ts b/deploy/utils/price.ts new file mode 100644 index 0000000..b67ffe9 --- /dev/null +++ b/deploy/utils/price.ts @@ -0,0 +1,19 @@ +import axios from "axios"; +import { BigNumber } from "ethers"; +import { parseEther } from "ethers/lib/utils"; + +export async function getCoinGeckoPriceUSD( + assetId: string +): Promise { + let tokenPrice = BigNumber.from(0); + try { + const response = await axios.get( + `https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&ids=${assetId}` + ); + tokenPrice = parseEther(response.data[0]["current_price"].toString()); + } catch (error) { + throw new Error(`Error fetching price for ${assetId}: ${error}`); + } + + return tokenPrice; +} diff --git a/package.json b/package.json index f45c1e2..bbeb0c4 100644 --- a/package.json +++ b/package.json @@ -184,6 +184,7 @@ "deploy:mainnet:trade-mining:ap-token:upgrade:ap-token": "hardhat --network mainnet deploy --no-compile --reset --tags UpgradeAPToken", "deploy:mainnet:trade-mining:paradeen": "hardhat --network mainnet deploy --no-compile --reset --tags Paradeen", "deploy:mainnet:trade-mining:feed-paradeen": "hardhat --network mainnet deploy --no-compile --reset --tags FeedParadeen", + "deploy:mainnet:trade-mining:feed-paradeen-pyth": "hardhat --network mainnet deploy --no-compile --reset --tags FeedParadeenPyth", "deploy:mainnet:trade-mining:set_mineable": "hardhat --network mainnet deploy --no-compile --reset --tags SetMiningManagerMinable", "deploy:mainnet:trade-mining:set_miner_manager_auth": "hardhat --network mainnet deploy --no-compile --reset --tags SetMiningManagerAuth", "deploy:mainnet:trade-mining:set_miner_manager_point": "hardhat --network mainnet deploy --no-compile --reset --tags SetMiningManagerPoint", From 0cea1472a31b320e8523a276c34b3b4cfbae7928 Mon Sep 17 00:00:00 2001 From: wow-alpaca Date: Fri, 15 Mar 2024 16:48:19 +0700 Subject: [PATCH 2/5] chore: clean up --- deploy/trade-mining/paradeen-feed-pyth.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/deploy/trade-mining/paradeen-feed-pyth.ts b/deploy/trade-mining/paradeen-feed-pyth.ts index 80f5a59..784a2fe 100644 --- a/deploy/trade-mining/paradeen-feed-pyth.ts +++ b/deploy/trade-mining/paradeen-feed-pyth.ts @@ -28,6 +28,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { // constants const E18 = BigNumber.from(10).pow(18); const WEEK = BigNumber.from(604800); + const MAX_BPS = BigNumber.from(10000); // configs const tradingFeeBps = 9; @@ -42,7 +43,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const tradingFeeCollected = weeklyTradingVolume .mul(tradingFeeBps) .mul(2) // account closing volume assure - .div(10000); + .div(MAX_BPS); const pythPrice = await getCoinGeckoPriceUSD("pyth-network"); console.log(`> Weekly trading volume: ${formatEther(weeklyTradingVolume)}`); @@ -112,9 +113,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const amounts = PARAMS_INPUT.map((p) => p.amount); console.log("> Feeding rewards to Paradeen"); - const tx = await paradeen.feed(timestamps, amounts, { - gasLimit: 10000000000, - }); + const tx = await paradeen.feed(timestamps, amounts); console.log(`> ⛓ Tx submitted: ${tx.hash}`); console.log(`> Waiting tx to be mined...`); await tx.wait(); From 5ed29387a3ee6dffb3ca854232f75c5ae8639dad Mon Sep 17 00:00:00 2001 From: wow-alpaca Date: Fri, 15 Mar 2024 16:54:57 +0700 Subject: [PATCH 3/5] chore: update json --- contracts.json | 2 +- contracts.tenderly.json | 1 + deploy/trade-mining/paradeen-feed-pyth.ts | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/contracts.json b/contracts.json index 93acd8a..40c8fe9 100644 --- a/contracts.json +++ b/contracts.json @@ -89,4 +89,4 @@ "AP": "0x2Fb74F8E1e9EFaEEc31e57946e0C1bC6853ca4f1", "rewardToken": "0xb0188B0bb2cD4a6D2744637fC83C94a284B247Da" } -} \ No newline at end of file +} diff --git a/contracts.tenderly.json b/contracts.tenderly.json index 2b30f16..7dbef12 100644 --- a/contracts.tenderly.json +++ b/contracts.tenderly.json @@ -86,6 +86,7 @@ "TradeMining": { "tradeMiningManager": "0x480DC16F44401235E20f849384f764f50B3Adb46", "paradeen": "0x06ed8e9DD7e3CAee9461145Cc8D7747d0Da23b21", + "pythParadeen": "", "AP": "0xF12Ef0f68521761192e04343a013980947223Ca3", "rewardToken": "0x55d398326f99059fF775485246999027B3197955" } diff --git a/deploy/trade-mining/paradeen-feed-pyth.ts b/deploy/trade-mining/paradeen-feed-pyth.ts index 784a2fe..4821cd2 100644 --- a/deploy/trade-mining/paradeen-feed-pyth.ts +++ b/deploy/trade-mining/paradeen-feed-pyth.ts @@ -21,7 +21,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { // contracts const ap = AP__factory.connect(config.TradeMining.AP, ethers.provider); const paradeen = Paradeen__factory.connect( - config.TradeMining.paradeen, + config.TradeMining.pythParadeen, signer ); @@ -50,7 +50,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { console.log(`> Trading fee collected: ${formatEther(tradingFeeCollected)}`); console.log(`> Pyth price: ${formatEther(pythPrice)}\n`); - let amountToFeed = BigNumber.from(0); + let amountToFeed: BigNumber; if (tradingFeeCollected.lt(weeklyFeeThreshold)) { console.log( From b3d42ef23af85c123461cc8cf4ee5a2c8fa765d3 Mon Sep 17 00:00:00 2001 From: wow-alpaca Date: Fri, 15 Mar 2024 17:02:57 +0700 Subject: [PATCH 4/5] chore: check if campaign is ended --- deploy/trade-mining/paradeen-feed-pyth.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/deploy/trade-mining/paradeen-feed-pyth.ts b/deploy/trade-mining/paradeen-feed-pyth.ts index 4821cd2..682cd18 100644 --- a/deploy/trade-mining/paradeen-feed-pyth.ts +++ b/deploy/trade-mining/paradeen-feed-pyth.ts @@ -33,6 +33,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { // configs const tradingFeeBps = 9; const weeklyFeeThreshold = BigNumber.from(20000).mul(E18); + const endTimestamp = 1716422400; // May 23, 2024 console.log(`> Prepare data...`); @@ -93,6 +94,11 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { } } + if (weekCursor.gt(endTimestamp)) { + console.log("> The campaign has ended, no need to feed"); + return; + } + // Ask for confirmation console.table(PARAMS_INPUT); const confirm = readlineSync.question("Confirm? (y/n): "); From 039e80c8438a8bf342d3d631e8d8dc7cc7a1b64b Mon Sep 17 00:00:00 2001 From: wow-alpaca Date: Fri, 15 Mar 2024 18:02:00 +0700 Subject: [PATCH 5/5] chore: approve on script --- deploy/trade-mining/paradeen-feed-pyth.ts | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/deploy/trade-mining/paradeen-feed-pyth.ts b/deploy/trade-mining/paradeen-feed-pyth.ts index 682cd18..3b9b446 100644 --- a/deploy/trade-mining/paradeen-feed-pyth.ts +++ b/deploy/trade-mining/paradeen-feed-pyth.ts @@ -3,7 +3,11 @@ import { DeployFunction } from "hardhat-deploy/types"; import { ethers } from "hardhat"; import { getConfig } from "../utils/config"; import { BigNumber } from "ethers"; -import { AP__factory, Paradeen__factory } from "../../typechain"; +import { + AP__factory, + ERC20__factory, + Paradeen__factory, +} from "../../typechain"; import * as readlineSync from "readline-sync"; import { getCoinGeckoPriceUSD } from "../utils/price"; import { formatEther } from "ethers/lib/utils"; @@ -20,6 +24,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { // contracts const ap = AP__factory.connect(config.TradeMining.AP, ethers.provider); + const pyth = ERC20__factory.connect(config.TradeMining.rewardToken, signer); const paradeen = Paradeen__factory.connect( config.TradeMining.pythParadeen, signer @@ -77,11 +82,16 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { // Check feed timestamp const weeklyReward = await paradeen.tokensPerWeek(weekCursor); if (weeklyReward.gt(0)) { + console.log("===================================="); console.log( - `> Weekly reward for ${weekCursor}: ${formatEther(weeklyReward)}` + `> Weekly reward for ${weekCursor}: ${formatEther( + weeklyReward.mul(E18).div(pythExponent) + )} Pyth` ); console.log(`> Already fed for this week`); - const goNext = readlineSync.question("Confirm to re-feed? (y/n): "); + const goNext = readlineSync.question( + "Confirm to re-feed (increase)? (y/n): " + ); switch (goNext.toLowerCase()) { case "y": break; @@ -118,6 +128,10 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { ); const amounts = PARAMS_INPUT.map((p) => p.amount); + console.log("> Approving Pyth to Paradeen..."); + const approvedTx = await pyth.approve(paradeen.address, amountToFeed); + console.log(`> ⛓ Tx submitted: ${approvedTx.hash}`); + console.log("> Feeding rewards to Paradeen"); const tx = await paradeen.feed(timestamps, amounts); console.log(`> ⛓ Tx submitted: ${tx.hash}`);