diff --git a/contracts/oracles/StableUsdtPriceFeed.sol b/contracts/oracles/StableUsdtPriceFeed.sol new file mode 100644 index 00000000..4dfff7dc --- /dev/null +++ b/contracts/oracles/StableUsdtPriceFeed.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: BSD-3-Clause +pragma solidity 0.8.25; + +import { ResilientOracleInterface } from "../interfaces/OracleInterface.sol"; + +/** + * @title StableUsdtPriceFeed + * @dev This contract is used to get the price of USDT from a Resilient Oracle + * and bounds the price to a certain range. + */ +contract StableUsdtPriceFeed { + ResilientOracleInterface public resilientOracle; + + address public constant USDT_TOKEN_ADDR = 0x55d398326f99059fF775485246999027B3197955; + uint256 public constant UPPER_BOUND = 1020000000000000000; // 1.02 USD + uint256 public constant LOWER_BOUND = 980000000000000000; // 0.98 USD + + constructor(address _resilientOracle) { + require(_resilientOracle != address(0), "Zero address provided"); + resilientOracle = ResilientOracleInterface(_resilientOracle); + } + + function latestAnswer() external view returns (int256 answer) { + // get price + uint256 price = getPrice(); + // cast price to int256 + answer = int256(price); + } + + function latestRoundData() + external + view + returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) + { + // get price + uint256 _answer = getPrice(); + // mock timestamp to latest block timestamp + uint256 timestamp = block.timestamp; + // mock roundId to timestamp + roundId = uint80(timestamp); + return (roundId, int256(_answer), timestamp, timestamp, roundId); + } + + function decimals() external pure returns (uint8) { + return 18; + } + + function description() external pure returns (string memory) { + return "Stabilized USDT Price Feed"; + } + + function version() external pure returns (uint256) { + return 1; + } + + /** + * @dev Get the price from the Resilient Oracle, and bound it to the range + * @return price The price of USDT in 18 decimals + */ + function getPrice() private view returns (uint256 price) { + // get USDT price (18 decimals) + price = resilientOracle.getPrice(USDT_TOKEN_ADDR); + price = price < LOWER_BOUND ? LOWER_BOUND : (price > UPPER_BOUND ? UPPER_BOUND : price); + } +} diff --git a/deploy/26-deploy-stable-usdt-price-feed.ts b/deploy/26-deploy-stable-usdt-price-feed.ts new file mode 100644 index 00000000..8c0a2f6a --- /dev/null +++ b/deploy/26-deploy-stable-usdt-price-feed.ts @@ -0,0 +1,30 @@ +import hre from "hardhat"; +import { DeployFunction } from "hardhat-deploy/dist/types"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; + +const func: DeployFunction = async function ({ getNamedAccounts, deployments }: HardhatRuntimeEnvironment) { + const { deploy } = deployments; + const { deployer } = await getNamedAccounts(); + + // Get the ResilientOracle contract + const resilientOracle = await hre.ethers.getContract("ResilientOracle"); + + await deploy("StableUsdtPriceFeed", { + contract: "StableUsdtPriceFeed", + from: deployer, + log: true, + deterministicDeployment: false, + skipIfAlreadyDeployed: true, + args: [resilientOracle.address], + }); + + const stableUsdtPriceFeed = await hre.ethers.getContract("StableUsdtPriceFeed"); + console.log(`StableUsdtPriceFeed deployed at: ${stableUsdtPriceFeed.address}`); + console.log(`Resilient Oracle address: ${resilientOracle.address}`); +}; + +func.tags = ["stable-usdt-price-feed"]; +func.skip = async (hr: HardhatRuntimeEnvironment) => + hr.network.name !== "bscmainnet" && hr.network.name !== "bsctestnet"; + +export default func; diff --git a/deployments/bscmainnet.json b/deployments/bscmainnet.json index dae67d41..c565f483 100644 --- a/deployments/bscmainnet.json +++ b/deployments/bscmainnet.json @@ -14512,6 +14512,159 @@ } ] }, + "StableUsdtPriceFeed": { + "address": "0xf373A7a76De77f7A86ea65BE15577AEA8bE02A36", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_resilientOracle", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "LOWER_BOUND", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "UPPER_BOUND", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "USDT_TOKEN_ADDR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "answer", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "resilientOracle", + "outputs": [ + { + "internalType": "contract ResilientOracleInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + } + ] + }, "StkBNBOracle": { "address": "0xdBAFD16c5eA8C29D1e94a5c26b31bFAC94331Ac6", "abi": [ diff --git a/deployments/bscmainnet/StableUsdtPriceFeed.json b/deployments/bscmainnet/StableUsdtPriceFeed.json new file mode 100644 index 00000000..3ae48d56 --- /dev/null +++ b/deployments/bscmainnet/StableUsdtPriceFeed.json @@ -0,0 +1,206 @@ +{ + "address": "0xf373A7a76De77f7A86ea65BE15577AEA8bE02A36", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_resilientOracle", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "LOWER_BOUND", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "UPPER_BOUND", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "USDT_TOKEN_ADDR", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "description", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "latestAnswer", + "outputs": [ + { + "internalType": "int256", + "name": "answer", + "type": "int256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "latestRoundData", + "outputs": [ + { + "internalType": "uint80", + "name": "roundId", + "type": "uint80" + }, + { + "internalType": "int256", + "name": "answer", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "startedAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "updatedAt", + "type": "uint256" + }, + { + "internalType": "uint80", + "name": "answeredInRound", + "type": "uint80" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "resilientOracle", + "outputs": [ + { + "internalType": "contract ResilientOracleInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + } + ], + "transactionHash": "0x89795a751cb39e8b26218ebb17e70e6b119bc3fa43c19f75d9ae05af5e1cb42b", + "receipt": { + "to": null, + "from": "0x24c30C9C84b8a3C71A521ad30007ED47372331b3", + "contractAddress": "0xf373A7a76De77f7A86ea65BE15577AEA8bE02A36", + "transactionIndex": 96, + "gasUsed": "249180", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x80e6ebf36b4946c5402a277ee66b4cf639e024a7e733a4be5b17469b58c24d26", + "transactionHash": "0x89795a751cb39e8b26218ebb17e70e6b119bc3fa43c19f75d9ae05af5e1cb42b", + "logs": [], + "blockNumber": 74516176, + "cumulativeGasUsed": "13273162", + "status": 1, + "byzantium": true + }, + "args": ["0x6592b5DE802159F3E74B2486b091D11a8256ab8A"], + "numDeployments": 3, + "solcInputHash": "18625204e1f090ab1a9c8ce54eea57c0", + "metadata": "{\"compiler\":{\"version\":\"0.8.25+commit.b61c2a91\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_resilientOracle\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"LOWER_BOUND\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"UPPER_BOUND\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"USDT_TOKEN_ADDR\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"description\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestAnswer\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"answer\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestRoundData\",\"outputs\":[{\"internalType\":\"uint80\",\"name\":\"roundId\",\"type\":\"uint80\"},{\"internalType\":\"int256\",\"name\":\"answer\",\"type\":\"int256\"},{\"internalType\":\"uint256\",\"name\":\"startedAt\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"updatedAt\",\"type\":\"uint256\"},{\"internalType\":\"uint80\",\"name\":\"answeredInRound\",\"type\":\"uint80\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"resilientOracle\",\"outputs\":[{\"internalType\":\"contract ResilientOracleInterface\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}],\"devdoc\":{\"details\":\"This contract is used to get the price of USDT from a Resilient Oracle and bounds the price to a certain range.\",\"kind\":\"dev\",\"methods\":{},\"title\":\"StableUsdtPriceFeed\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/oracles/StableUsdtPriceFeed.sol\":\"StableUsdtPriceFeed\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"contracts/interfaces/OracleInterface.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity ^0.8.25;\\n\\ninterface OracleInterface {\\n function getPrice(address asset) external view returns (uint256);\\n}\\n\\ninterface ResilientOracleInterface is OracleInterface {\\n function updatePrice(address vToken) external;\\n\\n function updateAssetPrice(address asset) external;\\n\\n function getUnderlyingPrice(address vToken) external view returns (uint256);\\n}\\n\\ninterface BoundValidatorInterface {\\n function validatePriceWithAnchorPrice(\\n address asset,\\n uint256 reporterPrice,\\n uint256 anchorPrice\\n ) external view returns (bool);\\n}\\n\",\"keccak256\":\"0xd3bbb7c9eef19e8f467342df6034ef95399a00964646fb8c82b438968ae3a8c0\",\"license\":\"BSD-3-Clause\"},\"contracts/oracles/StableUsdtPriceFeed.sol\":{\"content\":\"// SPDX-License-Identifier: BSD-3-Clause\\npragma solidity 0.8.25;\\n\\nimport { ResilientOracleInterface } from \\\"../interfaces/OracleInterface.sol\\\";\\n\\n/**\\n * @title StableUsdtPriceFeed\\n * @dev This contract is used to get the price of USDT from a Resilient Oracle\\n * and bounds the price to a certain range.\\n */\\ncontract StableUsdtPriceFeed {\\n ResilientOracleInterface public resilientOracle;\\n\\n address public constant USDT_TOKEN_ADDR = 0x55d398326f99059fF775485246999027B3197955;\\n uint256 public constant UPPER_BOUND = 1020000000000000000; // 1.02 USD\\n uint256 public constant LOWER_BOUND = 980000000000000000; // 0.98 USD\\n\\n constructor(address _resilientOracle) {\\n require(_resilientOracle != address(0), \\\"Zero address provided\\\");\\n resilientOracle = ResilientOracleInterface(_resilientOracle);\\n }\\n\\n function latestAnswer() external view returns (int256 answer) {\\n // get price\\n uint256 price = getPrice();\\n // cast price to int256\\n answer = int256(price);\\n }\\n\\n function latestRoundData()\\n external\\n view\\n returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)\\n {\\n // get price\\n uint256 _answer = getPrice();\\n // mock timestamp to latest block timestamp\\n uint256 timestamp = block.timestamp;\\n // mock roundId to timestamp\\n roundId = uint80(timestamp);\\n return (roundId, int256(_answer), timestamp, timestamp, roundId);\\n }\\n\\n function decimals() external pure returns (uint8) {\\n return 18;\\n }\\n\\n function description() external pure returns (string memory) {\\n return \\\"Stabilized USDT Price Feed\\\";\\n }\\n\\n function version() external pure returns (uint256) {\\n return 1;\\n }\\n\\n /**\\n * @dev Get the price from the Resilient Oracle, and bound it to the range\\n * @return price The price of USDT in 18 decimals\\n */\\n function getPrice() private view returns (uint256 price) {\\n // get USDT price (18 decimals)\\n price = resilientOracle.getPrice(USDT_TOKEN_ADDR);\\n price = price < LOWER_BOUND ? LOWER_BOUND : (price > UPPER_BOUND ? UPPER_BOUND : price);\\n }\\n}\\n\",\"keccak256\":\"0x96c69dc61f78ad276dd7111371a61033be477122d8bfa26b5f410b1fbdff3ae7\",\"license\":\"BSD-3-Clause\"}},\"version\":1}", + "bytecode": "0x6080604052348015600e575f80fd5b506040516103f33803806103f3833981016040819052602b9160a7565b6001600160a01b03811660845760405162461bcd60e51b815260206004820152601560248201527f5a65726f20616464726573732070726f76696465640000000000000000000000604482015260640160405180910390fd5b5f80546001600160a01b0319166001600160a01b039290921691909117905560d2565b5f6020828403121560b6575f80fd5b81516001600160a01b038116811460cb575f80fd5b9392505050565b610314806100df5f395ff3fe608060405234801561000f575f80fd5b5060043610610090575f3560e01c80636198e135116100635780636198e135146100ef5780637284e416146100fe5780638d1d47f11461013d578063de719b7614610158578063feaf968c14610167575f80fd5b8063313ce5671461009457806350d25bcd146100a857806354fd4d50146100be57806360de3604146100c5575b5f80fd5b604051601281526020015b60405180910390f35b6100b06101a6565b60405190815260200161009f565b60016100b0565b5f546100d7906001600160a01b031681565b6040516001600160a01b03909116815260200161009f565b6100b0670d99a8cec7e2000081565b604080518082018252601a81527f53746162696c697a6564205553445420507269636520466565640000000000006020820152905161009f9190610292565b6100d77355d398326f99059ff775485246999027b319795581565b6100b0670e27c49886e6000081565b61016f6101b6565b6040805169ffffffffffffffffffff968716815260208101959095528401929092526060830152909116608082015260a00161009f565b5f806101b06101d7565b92915050565b5f805f805f806101c46101d7565b4297909650879550859450849350915050565b5f80546040516341976e0960e01b81527355d398326f99059ff775485246999027b319795560048201526001600160a01b03909116906341976e0990602401602060405180830381865afa158015610231573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061025591906102c7565b9050670d99a8cec7e20000811061028557670e27c49886e6000081116102785790565b50670e27c49886e6000090565b50670d99a8cec7e2000090565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f602082840312156102d7575f80fd5b505191905056fea26469706673582212203e450a15977976ec3a49a75bbb7030ba80e4627115738ccfd55efe1f36b16fa664736f6c63430008190033", + "deployedBytecode": "0x608060405234801561000f575f80fd5b5060043610610090575f3560e01c80636198e135116100635780636198e135146100ef5780637284e416146100fe5780638d1d47f11461013d578063de719b7614610158578063feaf968c14610167575f80fd5b8063313ce5671461009457806350d25bcd146100a857806354fd4d50146100be57806360de3604146100c5575b5f80fd5b604051601281526020015b60405180910390f35b6100b06101a6565b60405190815260200161009f565b60016100b0565b5f546100d7906001600160a01b031681565b6040516001600160a01b03909116815260200161009f565b6100b0670d99a8cec7e2000081565b604080518082018252601a81527f53746162696c697a6564205553445420507269636520466565640000000000006020820152905161009f9190610292565b6100d77355d398326f99059ff775485246999027b319795581565b6100b0670e27c49886e6000081565b61016f6101b6565b6040805169ffffffffffffffffffff968716815260208101959095528401929092526060830152909116608082015260a00161009f565b5f806101b06101d7565b92915050565b5f805f805f806101c46101d7565b4297909650879550859450849350915050565b5f80546040516341976e0960e01b81527355d398326f99059ff775485246999027b319795560048201526001600160a01b03909116906341976e0990602401602060405180830381865afa158015610231573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061025591906102c7565b9050670d99a8cec7e20000811061028557670e27c49886e6000081116102785790565b50670e27c49886e6000090565b50670d99a8cec7e2000090565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f602082840312156102d7575f80fd5b505191905056fea26469706673582212203e450a15977976ec3a49a75bbb7030ba80e4627115738ccfd55efe1f36b16fa664736f6c63430008190033", + "devdoc": { + "details": "This contract is used to get the price of USDT from a Resilient Oracle and bounds the price to a certain range.", + "kind": "dev", + "methods": {}, + "title": "StableUsdtPriceFeed", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": {}, + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 49, + "contract": "contracts/oracles/StableUsdtPriceFeed.sol:StableUsdtPriceFeed", + "label": "resilientOracle", + "offset": 0, + "slot": "0", + "type": "t_contract(ResilientOracleInterface)29" + } + ], + "types": { + "t_contract(ResilientOracleInterface)29": { + "encoding": "inplace", + "label": "contract ResilientOracleInterface", + "numberOfBytes": "20" + } + } + } +} diff --git a/deployments/bscmainnet/solcInputs/18625204e1f090ab1a9c8ce54eea57c0.json b/deployments/bscmainnet/solcInputs/18625204e1f090ab1a9c8ce54eea57c0.json new file mode 100644 index 00000000..030cc6f0 --- /dev/null +++ b/deployments/bscmainnet/solcInputs/18625204e1f090ab1a9c8ce54eea57c0.json @@ -0,0 +1,40 @@ +{ + "language": "Solidity", + "sources": { + "contracts/interfaces/OracleInterface.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\ninterface OracleInterface {\n function getPrice(address asset) external view returns (uint256);\n}\n\ninterface ResilientOracleInterface is OracleInterface {\n function updatePrice(address vToken) external;\n\n function updateAssetPrice(address asset) external;\n\n function getUnderlyingPrice(address vToken) external view returns (uint256);\n}\n\ninterface BoundValidatorInterface {\n function validatePriceWithAnchorPrice(\n address asset,\n uint256 reporterPrice,\n uint256 anchorPrice\n ) external view returns (bool);\n}\n" + }, + "contracts/oracles/StableUsdtPriceFeed.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { ResilientOracleInterface } from \"../interfaces/OracleInterface.sol\";\n\n/**\n * @title StableUsdtPriceFeed\n * @dev This contract is used to get the price of USDT from a Resilient Oracle\n * and bounds the price to a certain range.\n */\ncontract StableUsdtPriceFeed {\n ResilientOracleInterface public resilientOracle;\n\n address public constant USDT_TOKEN_ADDR = 0x55d398326f99059fF775485246999027B3197955;\n uint256 public constant UPPER_BOUND = 1020000000000000000; // 1.02 USD\n uint256 public constant LOWER_BOUND = 980000000000000000; // 0.98 USD\n\n constructor(address _resilientOracle) {\n require(_resilientOracle != address(0), \"Zero address provided\");\n resilientOracle = ResilientOracleInterface(_resilientOracle);\n }\n\n function latestAnswer() external view returns (int256 answer) {\n // get price\n uint256 price = getPrice();\n // cast price to int256\n answer = int256(price);\n }\n\n function latestRoundData()\n external\n view\n returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)\n {\n // get price\n uint256 _answer = getPrice();\n // mock timestamp to latest block timestamp\n uint256 timestamp = block.timestamp;\n // mock roundId to timestamp\n roundId = uint80(timestamp);\n return (roundId, int256(_answer), timestamp, timestamp, roundId);\n }\n\n function decimals() external pure returns (uint8) {\n return 18;\n }\n\n function description() external pure returns (string memory) {\n return \"Stabilized USDT Price Feed\";\n }\n\n function version() external pure returns (uint256) {\n return 1;\n }\n\n /**\n * @dev Get the price from the Resilient Oracle, and bound it to the range\n * @return price The price of USDT in 18 decimals\n */\n function getPrice() private view returns (uint256 price) {\n // get USDT price (18 decimals)\n price = resilientOracle.getPrice(USDT_TOKEN_ADDR);\n price = price < LOWER_BOUND ? LOWER_BOUND : (price > UPPER_BOUND ? UPPER_BOUND : price);\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200, + "details": { + "yul": true + } + }, + "evmVersion": "cancun", + "outputSelection": { + "*": { + "*": [ + "storageLayout", + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "evm.gasEstimates" + ], + "": ["ast"] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} diff --git a/deployments/bscmainnet/solcInputs/3783e79f330d89bf7845ddf14f4fec22.json b/deployments/bscmainnet/solcInputs/3783e79f330d89bf7845ddf14f4fec22.json new file mode 100644 index 00000000..058187d5 --- /dev/null +++ b/deployments/bscmainnet/solcInputs/3783e79f330d89bf7845ddf14f4fec22.json @@ -0,0 +1,40 @@ +{ + "language": "Solidity", + "sources": { + "contracts/interfaces/OracleInterface.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\ninterface OracleInterface {\n function getPrice(address asset) external view returns (uint256);\n}\n\ninterface ResilientOracleInterface is OracleInterface {\n function updatePrice(address vToken) external;\n\n function updateAssetPrice(address asset) external;\n\n function getUnderlyingPrice(address vToken) external view returns (uint256);\n}\n\ninterface BoundValidatorInterface {\n function validatePriceWithAnchorPrice(\n address asset,\n uint256 reporterPrice,\n uint256 anchorPrice\n ) external view returns (bool);\n}\n" + }, + "contracts/oracles/StableUsdtPriceFeed.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { ResilientOracleInterface } from \"../interfaces/OracleInterface.sol\";\n\n/**\n * @title StableUsdtPriceFeed\n * @dev This contract is used to get the price of USDT from a Resilient Oracle\n * and bounds the price to a certain range.\n */\ncontract StableUsdtPriceFeed {\n\n ResilientOracleInterface public resilientOracle;\n\n address public constant USDT_TOKEN_ADDR = 0x55d398326f99059fF775485246999027B3197955;\n uint256 public constant UPPER_BOUND = 102000000; // 1.02 USD\n uint256 public constant LOWER_BOUND = 98000000; // 0.98 USD\n\n constructor(address _resilientOracle) {\n require(_resilientOracle != address(0), \"Zero address provided\");\n resilientOracle = ResilientOracleInterface(_resilientOracle);\n }\n\n function decimals() external pure returns (uint8) {\n return 8;\n }\n\n function description() external pure returns (string memory) {\n return \"Stabilized USDT Price Feed\";\n }\n\n function version() external pure returns (uint256) {\n return 1;\n }\n\n function latestAnswer() external view returns (int256 answer) {\n // get price\n uint256 price = getPrice();\n // cast price to int256\n answer = int256(price);\n }\n\n function latestRoundData()\n external\n view\n returns (\n uint80 roundId,\n int256 answer,\n uint256 startedAt,\n uint256 updatedAt,\n uint80 answeredInRound\n ) {\n // get price\n uint256 _answer = getPrice();\n // mock timestamp to latest block timestamp\n uint256 timestamp = block.timestamp;\n // mock roundId to timestamp\n roundId = uint80(timestamp);\n return (\n roundId,\n int256(_answer),\n timestamp,\n timestamp,\n roundId\n );\n }\n\n /**\n * @dev Get the price from the Resilient Oracle, and bound it to the range\n * @return price The price of USDT in 8 decimals\n */\n function getPrice() private view returns (uint256 price) {\n // get USDT price (8 decimals)\n price = resilientOracle.getPrice(USDT_TOKEN_ADDR);\n price = price < LOWER_BOUND ? LOWER_BOUND : (price > UPPER_BOUND ? UPPER_BOUND : price);\n }\n\n}" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200, + "details": { + "yul": true + } + }, + "evmVersion": "cancun", + "outputSelection": { + "*": { + "*": [ + "storageLayout", + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "evm.gasEstimates" + ], + "": ["ast"] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} diff --git a/deployments/bscmainnet/solcInputs/7662c78da4eeb92ea0be37cb6d15b86b.json b/deployments/bscmainnet/solcInputs/7662c78da4eeb92ea0be37cb6d15b86b.json new file mode 100644 index 00000000..b48ea846 --- /dev/null +++ b/deployments/bscmainnet/solcInputs/7662c78da4eeb92ea0be37cb6d15b86b.json @@ -0,0 +1,40 @@ +{ + "language": "Solidity", + "sources": { + "contracts/interfaces/OracleInterface.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity ^0.8.25;\n\ninterface OracleInterface {\n function getPrice(address asset) external view returns (uint256);\n}\n\ninterface ResilientOracleInterface is OracleInterface {\n function updatePrice(address vToken) external;\n\n function updateAssetPrice(address asset) external;\n\n function getUnderlyingPrice(address vToken) external view returns (uint256);\n}\n\ninterface BoundValidatorInterface {\n function validatePriceWithAnchorPrice(\n address asset,\n uint256 reporterPrice,\n uint256 anchorPrice\n ) external view returns (bool);\n}\n" + }, + "contracts/oracles/StableUsdtPriceFeed.sol": { + "content": "// SPDX-License-Identifier: BSD-3-Clause\npragma solidity 0.8.25;\n\nimport { ResilientOracleInterface } from \"../interfaces/OracleInterface.sol\";\n\n/**\n * @title StableUsdtPriceFeed\n * @dev This contract is used to get the price of USDT from a Resilient Oracle\n * and bounds the price to a certain range.\n */\ncontract StableUsdtPriceFeed {\n ResilientOracleInterface public resilientOracle;\n\n address public constant USDT_TOKEN_ADDR = 0x55d398326f99059fF775485246999027B3197955;\n uint256 public constant UPPER_BOUND = 1020000000000000000; // 1.02 USD\n uint256 public constant LOWER_BOUND = 980000000000000000; // 0.98 USD\n\n constructor(address _resilientOracle) {\n require(_resilientOracle != address(0), \"Zero address provided\");\n resilientOracle = ResilientOracleInterface(_resilientOracle);\n }\n\n function latestAnswer() external view returns (int256 answer) {\n // get price\n uint256 price = getPrice();\n // cast price to int256\n answer = int256(price);\n }\n\n function latestRoundData()\n external\n view\n returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)\n {\n // get price\n uint256 _answer = getPrice();\n // mock timestamp to latest block timestamp\n uint256 timestamp = block.timestamp;\n // mock roundId to timestamp\n roundId = uint80(timestamp);\n return (roundId, int256(_answer), timestamp, timestamp, roundId);\n }\n\n function decimals() external pure returns (uint8) {\n return 8;\n }\n\n function description() external pure returns (string memory) {\n return \"Stabilized USDT Price Feed\";\n }\n\n function version() external pure returns (uint256) {\n return 1;\n }\n\n /**\n * @dev Get the price from the Resilient Oracle, and bound it to the range\n * @return price The price of USDT in 18 decimals\n */\n function getPrice() private view returns (uint256 price) {\n // get USDT price (18 decimals)\n price = resilientOracle.getPrice(USDT_TOKEN_ADDR);\n price = price < LOWER_BOUND ? LOWER_BOUND : (price > UPPER_BOUND ? UPPER_BOUND : price);\n }\n}\n" + } + }, + "settings": { + "optimizer": { + "enabled": true, + "runs": 200, + "details": { + "yul": true + } + }, + "evmVersion": "cancun", + "outputSelection": { + "*": { + "*": [ + "storageLayout", + "abi", + "evm.bytecode", + "evm.deployedBytecode", + "evm.methodIdentifiers", + "metadata", + "devdoc", + "userdoc", + "evm.gasEstimates" + ], + "": ["ast"] + } + }, + "metadata": { + "useLiteralContent": true + } + } +} diff --git a/deployments/bscmainnet_addresses.json b/deployments/bscmainnet_addresses.json index 391276cb..ca429cdb 100644 --- a/deployments/bscmainnet_addresses.json +++ b/deployments/bscmainnet_addresses.json @@ -48,6 +48,7 @@ "SolvBTC.BBN_OneJumpRedStoneOracle": "0x98B9bC5a1e7E439ebEB0BEdB7e9f6b24fEc1E8B4", "SolvBTC.BBN_OneJumpRedStoneOracle_Implementation": "0x98eD7290a3D52FA5639dd76C16ADE3074ba664dd", "SolvBTC.BBN_OneJumpRedStoneOracle_Proxy": "0x98B9bC5a1e7E439ebEB0BEdB7e9f6b24fEc1E8B4", + "StableUsdtPriceFeed": "0xf373A7a76De77f7A86ea65BE15577AEA8bE02A36", "StkBNBOracle": "0xdBAFD16c5eA8C29D1e94a5c26b31bFAC94331Ac6", "StkBNBOracle_Implementation": "0xA7C432c50D310c805c8342488921A108b585397F", "StkBNBOracle_Proxy": "0xdBAFD16c5eA8C29D1e94a5c26b31bFAC94331Ac6",