diff --git a/cspell.json b/cspell.json index 0d77427e8..07237c723 100644 --- a/cspell.json +++ b/cspell.json @@ -120,6 +120,7 @@ "rpcutil", "rustup", "Sablier", + "sepolia", "setu", "Shouldset", "Sighash", diff --git a/packages/contracts/scripts/shared/constants/networks.ts b/packages/contracts/scripts/shared/constants/networks.ts index 21a162494..9e4f7ce00 100644 --- a/packages/contracts/scripts/shared/constants/networks.ts +++ b/packages/contracts/scripts/shared/constants/networks.ts @@ -2,6 +2,7 @@ export const Networks: Record = { mainnet: 1, optimism: 10, goerli: 5, + sepolia: 11155111, }; export const FALLBACK_RPC = "https://eth.ubq.fi/v1/mainnet"; diff --git a/packages/contracts/scripts/task/README.md b/packages/contracts/scripts/task/README.md new file mode 100644 index 000000000..0ceeab005 --- /dev/null +++ b/packages/contracts/scripts/task/README.md @@ -0,0 +1,23 @@ +# Dollar task scripts + +## BlocksInWeek + +BlocksInWeek task provides a close approximate of number of blocks mined in one week. + +Usage: + +Ethereum mainnet: + +``` +npx tsx scripts/task/task.ts BlocksInWeek --network=mainnet +``` + +Sepolia: + +Ethereum mainnet: + +``` +npx tsx scripts/task/task.ts BlocksInWeek --network=sepolia +``` + +Prerequisite: set ETHERSCAN_API_KEY in .env \ No newline at end of file diff --git a/packages/contracts/scripts/task/dollar/blocks-in-week.ts b/packages/contracts/scripts/task/dollar/blocks-in-week.ts new file mode 100644 index 000000000..9899a07dd --- /dev/null +++ b/packages/contracts/scripts/task/dollar/blocks-in-week.ts @@ -0,0 +1,51 @@ +import { OptionDefinition } from "command-line-args"; + +import { Networks, TaskFuncParam } from "../../shared"; +import { EtherscanProvider } from "ethers"; + +export const optionDefinitions: OptionDefinition[] = [ + { name: "task", defaultOption: true }, + { name: "network", alias: "n", type: String }, +]; + +const funcBlocksInAWeek = async (params: TaskFuncParam) => { + const { args, env } = params; + const { network } = args; + + const chainId = Networks[network] ?? undefined; + if (!chainId) { + throw new Error(`Unsupported network: ${network} Please configure it out first`); + } + + const provider = new EtherscanProvider(chainId, env.etherscanApiKey); + + console.log(`Calculating number of blocks in the last week...`); + const secondsInAWeek = 604800; // 24 * 7 * 60 * 60 seconds is one week + const currentBlockNumber = await provider.getBlockNumber(); + const currentBlockTimestamp = (await provider.getBlock(currentBlockNumber))?.timestamp; + const blockTimestampTwoBlocksAgo = (await provider.getBlock(currentBlockNumber - 2))?.timestamp; + + if (currentBlockTimestamp && blockTimestampTwoBlocksAgo) { + const avgBlockTime = (currentBlockTimestamp - blockTimestampTwoBlocksAgo) / 2; + console.log(`Recent average block time: ${avgBlockTime} seconds`); + + const oneWeekAgo = currentBlockTimestamp - secondsInAWeek; + const estimatedBlocksInAWeek = secondsInAWeek / avgBlockTime; + console.log(`Estimated blocks in a week best case ${estimatedBlocksInAWeek}`); + + let estimatedBlockNumber = currentBlockNumber - estimatedBlocksInAWeek; + let estimatedBlockTimestamp = (await provider.getBlock(estimatedBlockNumber))?.timestamp; + + if (estimatedBlockTimestamp) { + let deltaBlockTime = oneWeekAgo - estimatedBlockTimestamp; + estimatedBlockNumber += Math.trunc(deltaBlockTime / avgBlockTime); + estimatedBlockTimestamp = (await provider.getBlock(estimatedBlockNumber))?.timestamp || estimatedBlockTimestamp; + deltaBlockTime -= estimatedBlockTimestamp - oneWeekAgo; + + console.log(`Produced ${estimatedBlocksInAWeek - deltaBlockTime / avgBlockTime} blocks, ${deltaBlockTime / avgBlockTime} worst than the best case`); + } + } + + return "succeeded"; +}; +export default funcBlocksInAWeek; diff --git a/packages/contracts/scripts/task/manager.ts b/packages/contracts/scripts/task/manager.ts index 66b541df8..20a8b99bd 100644 --- a/packages/contracts/scripts/task/manager.ts +++ b/packages/contracts/scripts/task/manager.ts @@ -2,11 +2,16 @@ import { OptionDefinition } from "command-line-args"; import { TaskFuncCallBack } from "../shared"; -import PriceResetHandler, { optionDefinitions as priceResetOptions } from "./dollar/priceReset"; +import PriceResetHandler, { optionDefinitions, optionDefinitions as priceResetOptions } from "./dollar/price-reset"; +import BlocksInWeekHandler from "./dollar/blocks-in-week"; export const TASK_FUNCS: Record = { PriceReset: { handler: PriceResetHandler, options: priceResetOptions, }, + BlocksInWeek: { + handler: BlocksInWeekHandler, + options: optionDefinitions, + }, }; diff --git a/packages/contracts/scripts/task/task.ts b/packages/contracts/scripts/task/task.ts index 6acdbb273..ca54c1918 100644 --- a/packages/contracts/scripts/task/task.ts +++ b/packages/contracts/scripts/task/task.ts @@ -11,7 +11,7 @@ const main = async () => { throw new Error("You MUST put the task name in command arguments at least"); } - const envPath = path.join(__dirname, "../.env"); + const envPath = path.join(__dirname, "../../.env"); if (!fs.existsSync(envPath)) { throw new Error("Env file not found"); } @@ -26,6 +26,7 @@ const main = async () => { let args; try { args = CommandLineArgs(commandLineParseOptions); + // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { console.error(`Argument parse failed!, error: ${error}`); return; diff --git a/packages/contracts/src/dollar/libraries/LibStaking.sol b/packages/contracts/src/dollar/libraries/LibStaking.sol index d8e5b93b3..ac3dc03d0 100644 --- a/packages/contracts/src/dollar/libraries/LibStaking.sol +++ b/packages/contracts/src/dollar/libraries/LibStaking.sol @@ -267,8 +267,8 @@ library LibStaking { // deposit new shares LibChef.deposit(msg.sender, _sharesAmount, _id); // calculate end locking period block number - // 1 week = 45361 blocks = 2371753*7/366 - // n = (block + duration * 45361) + // 1 week = 49930 blocks + // n = (block number + duration * 49930) stake.endBlock = block.number + _weeks * ss.blockCountInAWeek; // should be done after masterchef withdraw diff --git a/packages/contracts/src/dollar/upgradeInitializers/DiamondInit.sol b/packages/contracts/src/dollar/upgradeInitializers/DiamondInit.sol index 2614821ab..5499fb48f 100644 --- a/packages/contracts/src/dollar/upgradeInitializers/DiamondInit.sol +++ b/packages/contracts/src/dollar/upgradeInitializers/DiamondInit.sol @@ -71,7 +71,7 @@ contract DiamondInit is Modifiers { // staking LibStaking.StakingData storage ls = LibStaking.stakingStorage(); ls.stakingDiscountMultiplier = uint256(0.001 ether); // 0.001 - ls.blockCountInAWeek = 45361; + ls.blockCountInAWeek = 49930; // reentrancy guard _initReentrancyGuard(); diff --git a/packages/contracts/test/diamond/facets/ChefFacet.t.sol b/packages/contracts/test/diamond/facets/ChefFacet.t.sol index f8a6ac5c7..d8222a857 100644 --- a/packages/contracts/test/diamond/facets/ChefFacet.t.sol +++ b/packages/contracts/test/diamond/facets/ChefFacet.t.sol @@ -311,7 +311,7 @@ contract DepositStateChefTest is DepositStateChef { // advance the block number to staking time so the withdraw is possible uint256 currentBlock = block.number; - blocks = bound(blocks, 45361, 2 ** 128 - 1); + blocks = bound(blocks, 49930, 2 ** 128 - 1); assertEq(chefFacet.totalShares(), shares); uint256 preBal = governanceToken.balanceOf(fourthAccount); diff --git a/packages/contracts/test/diamond/facets/StakingFacet.t.sol b/packages/contracts/test/diamond/facets/StakingFacet.t.sol index f9c3968f5..e23eeed35 100644 --- a/packages/contracts/test/diamond/facets/StakingFacet.t.sol +++ b/packages/contracts/test/diamond/facets/StakingFacet.t.sol @@ -312,7 +312,7 @@ contract DepositStateTest is DepositStateStaking { // advance the block number to staking time so the withdraw is possible uint256 currentBlock = block.number; - blocks = bound(blocks, 45361, 2 ** 128 - 1); + blocks = bound(blocks, 49930, 2 ** 128 - 1); assertEq(chefFacet.totalShares(), shares); uint256 preBal = governanceToken.balanceOf(fourthAccount);