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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Security contact: [dev-support@ampleforth.org](mailto:dev-support@ampleforth.org
### Package organization

* [spot-contracts](./spot-contracts): SPOT protocol smart contracts.
* [spot-vaults](./spot-vaults): Vault strategies leveraging the SPOT system.
* [spot-subgraph](./spot-subgraph): Subgraph to index SPOT protocol on-chain data.

## Licensing
Expand Down
4 changes: 1 addition & 3 deletions spot-vaults/contracts/_interfaces/external/IAmpleforth.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;

import { IAmpleforthOracle } from "./IAmpleforthOracle.sol";

interface IAmpleforth {
function cpiOracle() external view returns (IAmpleforthOracle);
function getTargetRate() external returns (uint256, bool);
}
24 changes: 8 additions & 16 deletions spot-vaults/contracts/_strategies/SpotCDRPricer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IPerpetualTranche } from "@ampleforthorg/spot-contracts/contracts/_interfaces/IPerpetualTranche.sol";
import { IChainlinkOracle } from "../_interfaces/external/IChainlinkOracle.sol";
import { IAmpleforthOracle } from "../_interfaces/external/IAmpleforthOracle.sol";
import { IAMPL } from "../_interfaces/external/IAMPL.sol";
import { IAmpleforth } from "../_interfaces/external/IAmpleforth.sol";
import { ISpotPricingStrategy } from "../_interfaces/ISpotPricingStrategy.sol";

/**
Expand Down Expand Up @@ -51,32 +52,23 @@ contract SpotCDRPricer is ISpotPricingStrategy {
/// @notice Number of decimals representing the prices returned by the chainlink oracle.
uint256 public immutable USD_ORACLE_DECIMALS;

/// @notice Address of the Ampleforth CPI oracle. (provides the inflation-adjusted target price for AMPL).
IAmpleforthOracle public immutable AMPL_CPI_ORACLE;

/// @notice Number of decimals representing the prices returned by the ampleforth oracle.
uint256 public immutable AMPL_CPI_ORACLE_DECIMALS;
/// @notice Address of the Ampleforth monetary policy. (provides the inflation-adjusted target price for AMPL)
IAmpleforth public immutable AMPLEFORTH_POLICY;

//-----------------------------------------------------------------------------
// Constructor

/// @notice Contract constructor.
/// @param spot Address of the SPOT token.
/// @param usdOracle Address of the USD token market price oracle token.
/// @param cpiOracle Address of the Ampleforth CPI oracle.
constructor(
IPerpetualTranche spot,
IChainlinkOracle usdOracle,
IAmpleforthOracle cpiOracle
) {
constructor(IPerpetualTranche spot, IChainlinkOracle usdOracle) {
SPOT = spot;
AMPL = IERC20(address(spot.underlying()));

USD_ORACLE = usdOracle;
USD_ORACLE_DECIMALS = usdOracle.decimals();

AMPL_CPI_ORACLE = cpiOracle;
AMPL_CPI_ORACLE_DECIMALS = cpiOracle.DECIMALS();
AMPLEFORTH_POLICY = IAmpleforth(IAMPL(address(AMPL)).monetaryPolicy());
}

//--------------------------------------------------------------------------
Expand All @@ -95,9 +87,9 @@ contract SpotCDRPricer is ISpotPricingStrategy {
/// @return p The price of the spot token in dollar coins.
/// @return v True if the price is valid and can be used by downstream consumers.
function perpPrice() external override returns (uint256, bool) {
// NOTE: Since {DECIMALS} == {AMPL_CPI_ORACLE_DECIMALS} == 18
// NOTE: Since {DECIMALS} == {AMPLEFORTH_POLICY_DECIMALS} == 18
// we don't adjust the returned values.
(uint256 targetPrice, bool targetPriceValid) = AMPL_CPI_ORACLE.getData();
(uint256 targetPrice, bool targetPriceValid) = AMPLEFORTH_POLICY.getTargetRate();
uint256 p = targetPrice.mulDiv(SPOT.getTVL(), SPOT.totalSupply());
return (p, targetPriceValid);
}
Expand Down
25 changes: 8 additions & 17 deletions spot-vaults/test/SpotCDRPricer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,9 @@ describe("SpotCDRPricer", function () {
const accounts = await ethers.getSigners();
const deployer = accounts[0];

const amplTargetOracle = new DMock("MedianOracle");
await amplTargetOracle.deploy();
await amplTargetOracle.mockMethod("getData()", [priceFP("1.15"), true]);
await amplTargetOracle.mockMethod("DECIMALS()", [18]);

const policy = new DMock("UFragmentsPolicy");
const policy = new DMock("IAmpleforth");
await policy.deploy();
await policy.mockMethod("cpiOracle()", [amplTargetOracle.target]);
await policy.mockMethod("getTargetRate()", [priceFP("1.15"), true]);

const ampl = new DMock("UFragments");
await ampl.deploy();
Expand All @@ -42,30 +37,26 @@ describe("SpotCDRPricer", function () {
]);

const SpotCDRPricer = await ethers.getContractFactory("SpotCDRPricer");
const strategy = await SpotCDRPricer.deploy(
spot.target,
usdPriceOrcle.target,
amplTargetOracle.target,
);
const strategy = await SpotCDRPricer.deploy(spot.target, usdPriceOrcle.target);
return {
deployer,
ampl,
spot,
usdPriceOrcle,
amplTargetOracle,
policy,
strategy,
};
}

describe("init", function () {
it("should initial params", async function () {
const { strategy, ampl, spot, usdPriceOrcle, amplTargetOracle } = await loadFixture(
const { strategy, ampl, spot, usdPriceOrcle, policy } = await loadFixture(
setupContracts,
);
expect(await strategy.AMPL()).to.eq(ampl.target);
expect(await strategy.SPOT()).to.eq(spot.target);
expect(await strategy.USD_ORACLE()).to.eq(usdPriceOrcle.target);
expect(await strategy.AMPL_CPI_ORACLE()).to.eq(amplTargetOracle.target);
expect(await strategy.AMPLEFORTH_POLICY()).to.eq(policy.target);
expect(await strategy.decimals()).to.eq(18);
});
});
Expand Down Expand Up @@ -130,8 +121,8 @@ describe("SpotCDRPricer", function () {
describe("#perpPrice", function () {
describe("when AMPL target data is invalid", function () {
it("should return invalid", async function () {
const { strategy, amplTargetOracle } = await loadFixture(setupContracts);
await amplTargetOracle.mockMethod("getData()", [priceFP("1.2"), false]);
const { strategy, policy } = await loadFixture(setupContracts);
await policy.mockMethod("getTargetRate()", [priceFP("1.2"), false]);
const p = await strategy.perpPrice.staticCall();
expect(p[0]).to.eq(priceFP("1.2"));
expect(p[1]).to.eq(false);
Expand Down