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
65 changes: 65 additions & 0 deletions contracts/oracles/StableUsdtPriceFeed.sol
Original file line number Diff line number Diff line change
@@ -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);
}
}
30 changes: 30 additions & 0 deletions deploy/26-deploy-stable-usdt-price-feed.ts
Original file line number Diff line number Diff line change
@@ -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;
153 changes: 153 additions & 0 deletions deployments/bscmainnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down
Loading
Loading