Skip to content

Commit

Permalink
fix: added PendleOracle
Browse files Browse the repository at this point in the history
  • Loading branch information
web3rover committed Mar 1, 2024
1 parent 827911d commit 8e734b7
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 1 deletion.
6 changes: 6 additions & 0 deletions contracts/interfaces/IPendlePtOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.13;

interface IPendlePtOracle {
function getPtToAssetRate(address market, uint32 duration) external view returns (uint256);
}
52 changes: 52 additions & 0 deletions contracts/oracles/PendleOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.13;

import { IPendlePtOracle } from "../interfaces/IPendlePtOracle.sol";
import { LiquidStakedTokenOracle } from "./common/LiquidStakedTokenOracle.sol";
import { ensureNonzeroAddress, ensureNonzeroValue } from "@venusprotocol/solidity-utilities/contracts/validators.sol";

/**
* @title PendleOracle
* @author Venus
* @notice This oracle fetches the price of an pendle token
*/
contract PendleOracle is LiquidStakedTokenOracle {
/// @notice Address of the PT oracle
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
IPendlePtOracle public immutable PT_ORACLE;

/// @notice Address of the market
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address public immutable MARKET;

/// @notice Twap duration for the oracle
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
uint32 public immutable TWAP_DURATION;

/// @notice Constructor for the implementation contract.
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(
address market,
address ptOracle,
address ptToken,
address underlyingToken,
address resilientOracle,
uint32 twapDuration
) LiquidStakedTokenOracle(ptToken, underlyingToken, resilientOracle) {
ensureNonzeroAddress(market);
ensureNonzeroAddress(ptOracle);
ensureNonzeroValue(twapDuration);

MARKET = market;
PT_ORACLE = IPendlePtOracle(ptOracle);
TWAP_DURATION = twapDuration;
}

/**
* @notice Fetches the amount of FRAX for 1 sFrax
* @return amount The amount of FRAX for sFrax
*/
function getUnderlyingAmount() internal view override returns (uint256) {
return PT_ORACLE.getPtToAssetRate(MARKET, TWAP_DURATION);
}
}
2 changes: 1 addition & 1 deletion helpers/deploymentConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export const ADDRESSES: PreconfiguredAddresses = {
frxETH: "0x5e8422345238f34275888049021821e8e08caa1f",
weETH: "0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee",
eETH: "0x35fA164735182de50811E8e2E824cFb9B6118ac2",
PTeETH: "0xc69Ad9baB1dEE23F4605a82b3354F8E40d1E5966",
PTweETH: "0xc69Ad9baB1dEE23F4605a82b3354F8E40d1E5966",
PTweETHMarket: "0xF32e58F92e60f4b0A37A69b95d642A471365EAe8",
PTOracle: "0xbbd487268A295531d299c125F3e5f749884A3e30",
},
Expand Down
129 changes: 129 additions & 0 deletions test/PendleOracle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { smock } from "@defi-wonderland/smock";
import chai from "chai";
import { parseUnits } from "ethers/lib/utils";
import { ethers } from "hardhat";

import { ADDRESSES } from "../helpers/deploymentConfig";
import { BEP20Harness, IPendlePtOracle, ResilientOracleInterface } from "../typechain-types";
import { addr0000 } from "./utils/data";

const { expect } = chai;
chai.use(smock.matchers);

const { PTweETH, PTweETHMarket, PTOracle, eETH } = ADDRESSES.ethereum;
const eETH_PRICE = parseUnits("3400", 18);
const PRICE_DENOMINATOR = parseUnits("1", 18);
const EETH_AMOUNT_FOR_ONE_WEETH = parseUnits("0.923601422168630818", 18);
const DURATION = 3600; // 1 hour

describe("WstETHOracle unit tests", () => {
let ptWeETHMock;
let resilientOracleMock;
let pendleOracle;
let pendleOracleFactory;
let ptOracleMock;
before(async () => {
// To initialize the provider we need to hit the node with any request
await ethers.getSigners();
resilientOracleMock = await smock.fake<ResilientOracleInterface>("ResilientOracleInterface");
resilientOracleMock.getPrice.returns(eETH_PRICE);

ptWeETHMock = await smock.fake<BEP20Harness>("BEP20Harness", { address: PTweETH });
ptWeETHMock.decimals.returns(18);

ptOracleMock = await smock.fake<IPendlePtOracle>("IPendlePtOracle", { address: PTOracle });
ptOracleMock.getPtToAssetRate.returns(EETH_AMOUNT_FOR_ONE_WEETH);

pendleOracleFactory = await ethers.getContractFactory("PendleOracle");
});

describe("deployment", () => {
it("revert if market address is 0", async () => {
await expect(
pendleOracleFactory.deploy(
addr0000,
ptOracleMock.address,
ptWeETHMock.address,
eETH,
resilientOracleMock.address,
DURATION,
),
).to.be.reverted;
});
it("revert if ptOracle address is 0", async () => {
await expect(
pendleOracleFactory.deploy(
PTweETHMarket,
addr0000,
ptWeETHMock.address,
eETH,
resilientOracleMock.address,
DURATION,
),
).to.be.reverted;
});
it("revert if ptWeETH address is 0", async () => {
await expect(
pendleOracleFactory.deploy(
PTweETHMarket,
ptOracleMock.address,
addr0000,
eETH,
resilientOracleMock.address,
DURATION,
),
).to.be.reverted;
});
it("revert if eETH address is 0", async () => {
await expect(
pendleOracleFactory.deploy(
PTweETHMarket,
ptOracleMock.address,
ptWeETHMock.address,
addr0000,
resilientOracleMock.address,
DURATION,
),
).to.be.reverted;
});
it("revert if ResilientOracle address is 0", async () => {
await expect(
pendleOracleFactory.deploy(PTweETHMarket, ptOracleMock.address, ptWeETHMock.address, eETH, addr0000, DURATION),
).to.be.reverted;
});
it("revert if TWAP duration is 0", async () => {
await expect(
pendleOracleFactory.deploy(
PTweETHMarket,
ptOracleMock.address,
ptWeETHMock.address,
eETH,
resilientOracleMock.address,
0,
),
).to.be.reverted;
});

it("should deploy contract", async () => {
pendleOracle = await pendleOracleFactory.deploy(
PTweETHMarket,
ptOracleMock.address,
ptWeETHMock.address,
eETH,
resilientOracleMock.address,
DURATION,
);
});
});

describe("getPrice", () => {
it("revert if wstETH address is wrong", async () => {
await expect(pendleOracle.getPrice(addr0000)).to.be.revertedWith("wrong token address");
});

// it("should get correct price", async () => {
// const expectedPrice = STETH_USD_PRICE.mul(STETH_AMOUNT_FOR_ONE_WSTETH).div(PRICE_DENOMINATOR);
// expect(await wstETHOracle.getPrice(WSTETH)).to.equal(expectedPrice);
// });
});
});

0 comments on commit 8e734b7

Please sign in to comment.