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
6 changes: 3 additions & 3 deletions core/contracts/common/implementation/FixedPoint.sol
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,9 @@ library FixedPoint {

/** @dev Divides with truncation two `Unsigned`s, reverting on overflow or division by 0, and ceil's the resultant product rather than the default floor behavior. */
function divCeil(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
uint256 divRaw = a.rawValue.mul(FP_SCALING_FACTOR);
uint256 divFloor = divRaw.div(b.rawValue);
uint256 mod = divRaw.mod(b.rawValue);
uint256 aScaled = a.rawValue.mul(FP_SCALING_FACTOR);
uint256 divFloor = aScaled.div(b.rawValue);
uint256 mod = aScaled.mod(b.rawValue);
if (mod != 0) {
return Unsigned(divFloor.add(1));
} else {
Expand Down
4 changes: 2 additions & 2 deletions core/contracts/common/implementation/Withdrawable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ abstract contract Withdrawable is MultiRole {
* @dev Either this method or `setWithdrawRole` must be called by the derived class for this contract to function
* properly.
*/
function createWithdrawRole(uint256 roleId, uint256 managingRoleId, address owner) internal {
function createWithdrawRole(uint256 roleId, uint256 managingRoleId, address withdrawerAddress) internal {
_roleId = roleId;
_createExclusiveRole(roleId, managingRoleId, owner);
_createExclusiveRole(roleId, managingRoleId, withdrawerAddress);
}

/**
Expand Down
35 changes: 19 additions & 16 deletions core/contracts/oracle/implementation/Store.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ contract Store is StoreInterface, Withdrawable, Testable {

enum Roles { Owner, Withdrawer }

FixedPoint.Unsigned public fixedOracleFeePerSecond; // Percentage of 1 E.g., .1 is 10% Oracle fee.
FixedPoint.Unsigned public weeklyDelayFee; // Percentage of 1 E.g., .1 is 10% weekly delay fee.
FixedPoint.Unsigned public fixedOracleFeePerSecondPerPfc; // Percentage of 1 E.g., .1 is 10% Oracle fee.
FixedPoint.Unsigned public weeklyDelayFeePerSecondPerPfc; // Percentage of 1 E.g., .1 is 10% weekly delay fee.

mapping(address => FixedPoint.Unsigned) public finalFees;
uint256 public constant SECONDS_PER_WEEK = 604800;
Expand All @@ -36,8 +36,8 @@ contract Store is StoreInterface, Withdrawable, Testable {
* EVENTS *
****************************************/

event NewFixedOracleFeePerSecond(FixedPoint.Unsigned newOracleFee);
event NewWeeklyDelayFee(FixedPoint.Unsigned newWeeklyDelayFee);
event NewFixedOracleFeePerSecondPerPfc(FixedPoint.Unsigned newOracleFee);
event NewWeeklyDelayFeePerSecondPerPfc(FixedPoint.Unsigned newWeeklyDelayFeePerSecondPerPfc);
event NewFinalFee(FixedPoint.Unsigned newFinalFee);

/**
Expand Down Expand Up @@ -97,15 +97,15 @@ contract Store is StoreInterface, Withdrawable, Testable {
uint256 timeDiff = endTime.sub(startTime);

// Multiply by the unscaled `timeDiff` first, to get more accurate results.
regularFee = pfc.mul(timeDiff).mul(fixedOracleFeePerSecond);
regularFee = pfc.mul(timeDiff).mul(fixedOracleFeePerSecondPerPfc);

// Compute how long ago the start time was to compute the delay penalty.
uint paymentDelay = getCurrentTime().sub(startTime);

// Compute the additional percentage (per second) that will be charged because of the penalty.
// Note: if less than a week has gone by since the startTime, paymentDelay / SECONDS_PER_WEEK will truncate to
// 0, causing no penalty to be charged.
FixedPoint.Unsigned memory penaltyPercentagePerSecond = weeklyDelayFee.mul(paymentDelay.div(SECONDS_PER_WEEK));
FixedPoint.Unsigned memory penaltyPercentagePerSecond = weeklyDelayFeePerSecondPerPfc.mul(paymentDelay.div(SECONDS_PER_WEEK));

// Apply the penaltyPercentagePerSecond to the payment period.
latePenalty = pfc.mul(timeDiff).mul(penaltyPercentagePerSecond);
Expand All @@ -130,26 +130,29 @@ contract Store is StoreInterface, Withdrawable, Testable {

/**
* @notice Sets a new oracle fee per second.
* @param newOracleFee new fee per second charged to use the oracle.
* @param newFixedOracleFeePerSecondPerPfc new fee per second charged to use the oracle.
*/
function setFixedOracleFeePerSecond(FixedPoint.Unsigned memory newOracleFee)
Comment thread
chrismaree marked this conversation as resolved.
function setFixedOracleFeePerSecondPerPfc(FixedPoint.Unsigned memory newFixedOracleFeePerSecondPerPfc)
public
onlyRoleHolder(uint(Roles.Owner))
{
// Oracle fees at or over 100% don't make sense.
require(newOracleFee.isLessThan(1));
fixedOracleFeePerSecond = newOracleFee;
emit NewFixedOracleFeePerSecond(newOracleFee);
require(newFixedOracleFeePerSecondPerPfc.isLessThan(1));
fixedOracleFeePerSecondPerPfc = newFixedOracleFeePerSecondPerPfc;
emit NewFixedOracleFeePerSecondPerPfc(newFixedOracleFeePerSecondPerPfc);
}

/**
* @notice Sets a new weekly delay fee.
* @param newWeeklyDelayFee fee escalation per week of late fee payment.
* @param newWeeklyDelayFeePerSecondPerPfc fee escalation per week of late fee payment.
*/
function setWeeklyDelayFee(FixedPoint.Unsigned memory newWeeklyDelayFee) public onlyRoleHolder(uint(Roles.Owner)) {
require(newWeeklyDelayFee.isLessThan(1), "weekly delay fee must be < 100%");
weeklyDelayFee = newWeeklyDelayFee;
emit NewWeeklyDelayFee(newWeeklyDelayFee);
function setWeeklyDelayFeePerSecondPerPfc(FixedPoint.Unsigned memory newWeeklyDelayFeePerSecondPerPfc)
public
onlyRoleHolder(uint(Roles.Owner))
{
require(newWeeklyDelayFeePerSecondPerPfc.isLessThan(1), "weekly delay fee must be < 100%");
weeklyDelayFeePerSecondPerPfc = newWeeklyDelayFeePerSecondPerPfc;
emit NewWeeklyDelayFeePerSecondPerPfc(newWeeklyDelayFeePerSecondPerPfc);
}

/**
Expand Down
6 changes: 3 additions & 3 deletions core/scripts/IntegrationTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ contract("IntegrationTest", function(accounts) {
let liquidationsObject = [];

// STEP: 0.a) set the oracle fee
await store.setFixedOracleFeePerSecond({ rawValue: dvmRegularFee.toString() }, { from: contractCreator });
await store.setFixedOracleFeePerSecondPerPfc({ rawValue: dvmRegularFee.toString() }, { from: contractCreator });

// STEP: 0.b: seed liquidator
console.log("Seeding liquidator");
Expand Down Expand Up @@ -377,7 +377,7 @@ contract("IntegrationTest", function(accounts) {
let maxCollateralLocked = 0;

// STEP: 0.a) set the oracle fee
await store.setFixedOracleFeePerSecond({ rawValue: dvmRegularFee.toString() }, { from: contractCreator });
await store.setFixedOracleFeePerSecondPerPfc({ rawValue: dvmRegularFee.toString() }, { from: contractCreator });

// STEP: 0.b): seed one over sized position
console.log("Seeding liquidator");
Expand Down Expand Up @@ -590,7 +590,7 @@ contract("IntegrationTest", function(accounts) {
let maxCollateralLocked = 0;

// STEP: 0.a) set the oracle fee
await store.setFixedOracleFeePerSecond({ rawValue: dvmRegularFee.toString() }, { from: contractCreator });
await store.setFixedOracleFeePerSecondPerPfc({ rawValue: dvmRegularFee.toString() }, { from: contractCreator });

let sponsor;
let tokenHolder;
Expand Down
19 changes: 11 additions & 8 deletions core/scripts/PrecisionErrors.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,10 @@ async function runExport() {
{ from: sponsor }
);
// 2) Set fee rate per second.
await store.setFixedOracleFeePerSecond({ rawValue: toWei(testConfig.feeRatePerSecond) }, { from: contractDeployer });
await store.setFixedOracleFeePerSecondPerPfc(
{ rawValue: toWei(testConfig.feeRatePerSecond) },
{ from: contractDeployer }
);
// 3) Move time in the contract forward by 1 second to capture unit fee.
startTime = await emp.getCurrentTime();
await emp.setCurrentTime(startTime.addn(1), { from: contractDeployer });
Expand Down Expand Up @@ -306,7 +309,7 @@ async function runExport() {
console.log(`** Depositing another ${fromWei(testConfig.expectedFeesCollected)} **`);
await emp.deposit({ rawValue: testConfig.expectedFeesCollected }, { from: sponsor });
// 1) Set fee rate per second.
await store.setFixedOracleFeePerSecond(
await store.setFixedOracleFeePerSecondPerPfc(
{ rawValue: toWei(testConfig.modifiedFeeRatePerSecond) },
{ from: contractDeployer }
);
Expand Down Expand Up @@ -345,7 +348,7 @@ async function runExport() {
*/

// Reset store fees.
await store.setFixedOracleFeePerSecond({ rawValue: toWei("0") }, { from: contractDeployer });
await store.setFixedOracleFeePerSecondPerPfc({ rawValue: toWei("0") }, { from: contractDeployer });

console.groupEnd();
/** ***************************************************************************
Expand Down Expand Up @@ -411,7 +414,7 @@ async function runExport() {
await collateral.approve(emp.address, toWei("999999999"), { from: sponsor });
await emp.create({ rawValue: testConfig.sponsorCollateralAmount }, { rawValue: toWei("100") }, { from: sponsor });
// 2) Set fee rate per second.
await store.setFixedOracleFeePerSecond({ rawValue: testConfig.feePerSecond }, { from: contractDeployer });
await store.setFixedOracleFeePerSecondPerPfc({ rawValue: testConfig.feePerSecond }, { from: contractDeployer });
// 3) Move time in the contract forward by 1 second to capture unit fee.
startTime = await emp.getCurrentTime();
await emp.setCurrentTime(startTime.addn(1));
Expand Down Expand Up @@ -478,7 +481,7 @@ async function runExport() {
*/

// Reset store fees.
await store.setFixedOracleFeePerSecond({ rawValue: toWei("0") }, { from: contractDeployer });
await store.setFixedOracleFeePerSecondPerPfc({ rawValue: toWei("0") }, { from: contractDeployer });

console.groupEnd();
/** ***************************************************************************
Expand Down Expand Up @@ -571,7 +574,7 @@ async function runExport() {
await collateral.approve(emp.address, toWei("999999999"), { from: sponsor });
await emp.create({ rawValue: testConfig.sponsorCollateralAmount }, { rawValue: toWei("100") }, { from: sponsor });
// 2) Set fee rate per second.
await store.setFixedOracleFeePerSecond({ rawValue: testConfig.feePerSecond }, { from: contractDeployer });
await store.setFixedOracleFeePerSecondPerPfc({ rawValue: testConfig.feePerSecond }, { from: contractDeployer });
// 3) Move time in the contract forward by 1 second to capture unit fee.
startTime = await emp.getCurrentTime();
await emp.setCurrentTime(startTime.addn(1));
Expand Down Expand Up @@ -609,7 +612,7 @@ async function runExport() {
* @notice RUN THE TEST TEN TIMES
*/
// Need to set fees to 0 so as not to change the fee multiplier.
await store.setFixedOracleFeePerSecond({ rawValue: toWei("0") }, { from: contractDeployer });
await store.setFixedOracleFeePerSecondPerPfc({ rawValue: toWei("0") }, { from: contractDeployer });
for (let i = 0; i < 10; i++) {
// Note: Must call `requestWithdrawal()` instead of `withdraw()` because we are the only position. I didn't create another
// less-collateralized position because it would modify the total collateral and therefore the fee multiplier.
Expand Down Expand Up @@ -1120,7 +1123,7 @@ async function runExport() {
*/

// Reset store fees.
await store.setFixedOracleFeePerSecond({ rawValue: toWei("0") }, { from: contractDeployer });
await store.setFixedOracleFeePerSecondPerPfc({ rawValue: toWei("0") }, { from: contractDeployer });

console.groupEnd();
/** ***************************************************************************
Expand Down
12 changes: 6 additions & 6 deletions core/test/financial-templates/Liquidatable.js
Original file line number Diff line number Diff line change
Expand Up @@ -992,7 +992,7 @@ contract("Liquidatable", function(accounts) {
});
it("Fees on liquidation", async () => {
// Charge a 10% fee per second.
await store.setFixedOracleFeePerSecond({ rawValue: toWei("0.1") });
await store.setFixedOracleFeePerSecondPerPfc({ rawValue: toWei("0.1") });

// Advance time to charge fee.
let currentTime = await liquidationContract.getCurrentTime();
Expand Down Expand Up @@ -1052,7 +1052,7 @@ contract("Liquidatable", function(accounts) {
);

// Clean up store fees.
await store.setFixedOracleFeePerSecond({ rawValue: "0" });
await store.setFixedOracleFeePerSecondPerPfc({ rawValue: "0" });
});
});
describe("Dispute failed", () => {
Expand Down Expand Up @@ -1481,7 +1481,7 @@ contract("Liquidatable", function(accounts) {
});
it("Fees on liquidation", async () => {
// Charge a 10% fee per second.
await store.setFixedOracleFeePerSecond({ rawValue: toWei("0.1") });
await store.setFixedOracleFeePerSecondPerPfc({ rawValue: toWei("0.1") });

// Advance time to charge fee.
let currentTime = await USDCLiquidationContract.getCurrentTime();
Expand Down Expand Up @@ -1561,7 +1561,7 @@ contract("Liquidatable", function(accounts) {
);

// Clean up store fees.
await store.setFixedOracleFeePerSecond({ rawValue: "0" });
await store.setFixedOracleFeePerSecondPerPfc({ rawValue: "0" });
});
});
describe("Dispute failed", () => {
Expand Down Expand Up @@ -1613,15 +1613,15 @@ contract("Liquidatable", function(accounts) {
// = 0.033... repeating, which cannot be represented precisely by a fixed point.
// --> 0.04 * 30 wei = 1.2 wei, which gets truncated to 1 wei, so 1 wei of fees are paid
const regularFee = toWei("0.04");
await store.setFixedOracleFeePerSecond({ rawValue: regularFee });
await store.setFixedOracleFeePerSecondPerPfc({ rawValue: regularFee });

// Advance the contract one second and make the contract pay its regular fees
let startTime = await _liquidationContract.getCurrentTime();
await _liquidationContract.setCurrentTime(startTime.addn(1));
await _liquidationContract.payFees();

// Set the store fees back to 0 to prevent fee multiplier from changing for remainder of the test.
await store.setFixedOracleFeePerSecond({ rawValue: "0" });
await store.setFixedOracleFeePerSecondPerPfc({ rawValue: "0" });

// Set allowance for contract to pull synthetic tokens from liquidator
await syntheticToken.increaseAllowance(_liquidationContract.address, numTokens, { from: liquidator });
Expand Down
8 changes: 4 additions & 4 deletions core/test/financial-templates/PricelessPositionManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,7 @@ contract("PricelessPositionManager", function(accounts) {
await pricelessPositionManager.create({ rawValue: toWei("1") }, { rawValue: toWei("1") }, { from: sponsor });

// Set store fees to 1% per second.
await store.setFixedOracleFeePerSecond({ rawValue: toWei("0.01") });
await store.setFixedOracleFeePerSecondPerPfc({ rawValue: toWei("0.01") });

// Move time in the contract forward by 1 second to capture a 1% fee.
const startTime = await pricelessPositionManager.getCurrentTime();
Expand Down Expand Up @@ -750,7 +750,7 @@ contract("PricelessPositionManager", function(accounts) {
assert.equal((await pricelessPositionManager.getCollateral(sponsor)).toString(), toWei("98.99"));

// Set the store fees back to 0 to prevent it from affecting other tests.
await store.setFixedOracleFeePerSecond({ rawValue: "0" });
await store.setFixedOracleFeePerSecondPerPfc({ rawValue: "0" });
});

it("Final fees", async function() {
Expand Down Expand Up @@ -887,15 +887,15 @@ contract("PricelessPositionManager", function(accounts) {
// = 0.033... repeating, which cannot be represented precisely by a fixed point.
// --> 0.04 * 30 wei = 1.2 wei, which gets truncated to 1 wei, so 1 wei of fees are paid
const regularFee = toWei("0.04");
await store.setFixedOracleFeePerSecond({ rawValue: regularFee });
await store.setFixedOracleFeePerSecondPerPfc({ rawValue: regularFee });

// Advance the contract one second and make the contract pay its regular fees
let startTime = await pricelessPositionManager.getCurrentTime();
await pricelessPositionManager.setCurrentTime(startTime.addn(1));
await pricelessPositionManager.payFees();

// Set the store fees back to 0 to prevent fee multiplier from changing for remainder of the test.
await store.setFixedOracleFeePerSecond({ rawValue: "0" });
await store.setFixedOracleFeePerSecondPerPfc({ rawValue: "0" });
});
it("Fee multiplier is set properly with precision loss, and fees are paid as expected", async () => {
// Absent any rounding errors, `getCollateral` should return (initial-collateral - final-fees) = 30 wei - 1 wei = 29 wei.
Expand Down
23 changes: 14 additions & 9 deletions core/test/oracle/Store.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ contract("Store", function(accounts) {
it("Compute fees basic check", async function() {
// Set fee to 10%
let newFee = { rawValue: web3.utils.toWei("0.1", "ether") };
await store.setFixedOracleFeePerSecond(newFee, { from: owner });
await store.setFixedOracleFeePerSecondPerPfc(newFee, { from: owner });

let pfc = { rawValue: web3.utils.toWei("2", "ether") };

Expand All @@ -42,7 +42,7 @@ contract("Store", function(accounts) {
it("Compute fees at 20%", async function() {
// Change fee to 20%
let newFee = { rawValue: web3.utils.toWei("0.2", "ether") };
await store.setFixedOracleFeePerSecond(newFee, { from: owner });
await store.setFixedOracleFeePerSecondPerPfc(newFee, { from: owner });

let pfc = { rawValue: web3.utils.toWei("2", "ether") };

Expand All @@ -60,14 +60,16 @@ contract("Store", function(accounts) {

// Disallow setting fees higher than 100%.
let highFee = { rawValue: web3.utils.toWei("1", "ether") };
assert(await didContractThrow(store.setFixedOracleFeePerSecond(highFee, { from: owner })));
assert(await didContractThrow(store.setFixedOracleFeePerSecondPerPfc(highFee, { from: owner })));

// Can set weekly late fees to less than 100%.
await store.setWeeklyDelayFee({ rawValue: web3.utils.toWei("0.99", "ether") }, { from: owner });
await store.setWeeklyDelayFeePerSecondPerPfc({ rawValue: web3.utils.toWei("0.99", "ether") }, { from: owner });

// Disallow setting fees >= 100%.
assert(
await didContractThrow(store.setWeeklyDelayFee({ rawValue: web3.utils.toWei("1", "ether") }, { from: owner }))
await didContractThrow(
store.setWeeklyDelayFeePerSecondPerPfc({ rawValue: web3.utils.toWei("1", "ether") }, { from: owner })
)
);

// TODO Check that only permitted role can change the fee
Expand All @@ -90,10 +92,13 @@ contract("Store", function(accounts) {

it("Weekly delay fees", async function() {
// Add weekly delay fee and confirm
const result = await store.setWeeklyDelayFee({ rawValue: web3.utils.toWei("0.5", "ether") }, { from: owner });
const result = await store.setWeeklyDelayFeePerSecondPerPfc(
{ rawValue: web3.utils.toWei("0.5", "ether") },
{ from: owner }
);

truffleAssert.eventEmitted(result, "NewWeeklyDelayFee", ev => {
return ev.newWeeklyDelayFee.rawValue === web3.utils.toWei("0.5", "ether");
truffleAssert.eventEmitted(result, "NewWeeklyDelayFeePerSecondPerPfc", ev => {
return ev.newWeeklyDelayFeePerSecondPerPfc.rawValue === web3.utils.toWei("0.5", "ether");
});
});

Expand Down Expand Up @@ -222,7 +227,7 @@ contract("Store", function(accounts) {
});

it("Late penalty based on current time", async function() {
await store.setWeeklyDelayFee({ rawValue: web3.utils.toWei("0.1", "ether") }, { from: owner });
await store.setWeeklyDelayFeePerSecondPerPfc({ rawValue: web3.utils.toWei("0.1", "ether") }, { from: owner });

const startTime = await store.getCurrentTime();

Expand Down
Loading