Skip to content

Commit

Permalink
Merge pull request #794 from oceanprotocol/issue793-hide-stake-amount…
Browse files Browse the repository at this point in the history
…s-for-prediction

Hide stake amounts for prediction epoch & Allow predictoors modify their stake amount
  • Loading branch information
alexcos20 authored Aug 7, 2023
2 parents e58562c + 5ba4ff6 commit 1ec635f
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 17 deletions.
24 changes: 22 additions & 2 deletions contracts/templates/ERC20Template3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "../utils/ERC20Roles.sol";

/**
* @title DatatokenTemplate
*
Expand Down Expand Up @@ -951,15 +950,26 @@ contract ERC20Template3 is
bytes32 s; // s of provider signed message
uint256 validUntil;
}

function getAggPredval(
uint256 epoch_start,
userAuth calldata _userAuth
) public view returns (uint256, uint256) {
_checkUserAuthorization(_userAuth);
require(isValidSubscription(_userAuth.userAddress), "No subscription");
require(toEpochStart(epoch_start) == epoch_start, "invalid epoch");
require(soonestEpochToPredict(curEpoch()) > epoch_start, "predictions not closed");
return (roundSumStakesUp[epoch_start], roundSumStakes[epoch_start]);
}

function getTotalStake(
uint256 epoch_start
) public view returns (uint256) {
require(toEpochStart(epoch_start) == epoch_start, "invalid epoch");
require(soonestEpochToPredict(curEpoch()) > epoch_start, "predictions not closed");
return roundSumStakes[epoch_start];
}

function getsubscriptionRevenueAtEpoch(
uint256 epoch_start
) public view returns (uint256) {
Expand Down Expand Up @@ -996,8 +1006,17 @@ contract ERC20Template3 is

emit PredictionSubmitted(msg.sender, epoch_start, stake);
if (submittedPredval(epoch_start, msg.sender)) {
require(predictions[epoch_start][msg.sender].stake == stake, "cannot modify stake amt");
uint256 oldStake = predictions[epoch_start][msg.sender].stake;
predictions[epoch_start][msg.sender].stake = 0; // Reentrancy precaution
if (stake > oldStake) {
uint256 payment = stake - oldStake;
IERC20(stakeToken).safeTransferFrom(msg.sender, address(this), payment);
} else if (stake < oldStake) {
uint256 refund = oldStake - stake;
IERC20(stakeToken).transfer(msg.sender, refund);
}
predictions[epoch_start][msg.sender].predictedValue = predictedValue;
predictions[epoch_start][msg.sender].stake = stake;
return;
}
predictions[epoch_start][msg.sender] = Prediction(
Expand Down Expand Up @@ -1162,6 +1181,7 @@ contract ERC20Template3 is
uint256 s_per_subscription,
uint256 _truval_submit_timeout
) internal {
require(s_per_subscription % s_per_epoch == 0, "%");
if (secondsPerEpoch == 0) {
secondsPerEpoch = s_per_epoch;
}
Expand Down
55 changes: 40 additions & 15 deletions test/unit/datatokens/ERC20Template3.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const fastForward = async (seconds) => {
await ethers.provider.send("evm_mine");
}

const sPerEpoch = 288;
const sPerEpoch = 300;
const sPerSubscription = 24 * 60 * 60;
const trueValueSubmitTimeout = 24 * 60 * 60 * 3;

Expand Down Expand Up @@ -700,6 +700,13 @@ describe("ERC20Template3", () => {
});

// PREDICTOOR
it("#toEpochStart - Should return the start of the epoch for a given timestamp", async function() {
const testTimestamp = 1691374249
const secondsPerEpoch = await erc20Token.secondsPerEpoch()
const expectedEpochStart = Math.floor(testTimestamp / secondsPerEpoch) * secondsPerEpoch;
const result = await erc20Token.toEpochStart(testTimestamp);
expect(result.toNumber()).to.equal(expectedEpochStart);
});
it("#secondsPerEpoch - secondsPerEpoch should be set", async () => {
const secondsPerEpoch = await erc20Token.secondsPerEpoch();
assert(secondsPerEpoch > 0, 'Invalid secondsPerEpoch');
Expand Down Expand Up @@ -845,15 +852,16 @@ describe("ERC20Template3", () => {
prediction = await erc20Token.getPrediction(soonestEpochToPredict, owner.address, userAuth);
expect(prediction.predictedValue).to.be.eq(!predictedValue);

await expectRevert(
erc20Token.submitPredval(predictedValue, stake + 1, soonestEpochToPredict),
"cannot modify stake"
);
await mockErc20.approve(erc20Token.address, 1);
let mockErc20BalanceBefore = await mockErc20.balanceOf(owner.address);
await erc20Token.submitPredval(predictedValue, stake + 1, soonestEpochToPredict);
let mockErc20BalanceAfter = await mockErc20.balanceOf(owner.address);
expect(mockErc20BalanceBefore).to.equal(mockErc20BalanceAfter.add(1))

await expectRevert(
erc20Token.submitPredval(predictedValue, stake - 1, soonestEpochToPredict),
"cannot modify stake"
);
mockErc20BalanceBefore = await mockErc20.balanceOf(owner.address);
await erc20Token.submitPredval(predictedValue, stake - 1, soonestEpochToPredict),
mockErc20BalanceAfter = await mockErc20.balanceOf(owner.address);
expect(mockErc20BalanceAfter).to.equal(mockErc20BalanceBefore.add(2))
});
it("#pausePredictions - should pause and resume predictions", async () => {
await erc20Token.pausePredictions();
Expand Down Expand Up @@ -1191,20 +1199,33 @@ describe("ERC20Template3", () => {

let soonestEpochToPredict = await erc20Token.soonestEpochToPredict(await blocktimestamp());
const userAuth = await authorize(user2.address)
const [numer, denom] = await erc20Token.connect(user2).getAggPredval(soonestEpochToPredict, userAuth);
await expectRevert(erc20Token.connect(user2).getAggPredval(soonestEpochToPredict, userAuth), "predictions not closed");
await expectRevert(erc20Token.getTotalStake(soonestEpochToPredict), "predictions not closed");

let curEpoch = await erc20Token.curEpoch();
const secondsPerEpoch = await erc20Token.secondsPerEpoch();
let predictedEpoch = curEpoch.add(secondsPerEpoch);
const [numer, denom] = await erc20Token.connect(user2).getAggPredval(predictedEpoch, userAuth);
const totalStake = await erc20Token.getTotalStake(predictedEpoch);
expect(numer).to.be.eq(0);
expect(denom).to.be.eq(0);
expect(totalStake).to.be.eq(0);

// user2 makes a prediction
const predictedValue = true;
const stake = web3.utils.toWei("1");
await mockErc20.transfer(user3.address, stake);
await mockErc20.connect(user3).approve(erc20Token.address, stake);
await erc20Token.connect(user3).submitPredval(predictedValue, stake, soonestEpochToPredict);

const [numer2, denom2] = await erc20Token.connect(user2).getAggPredval(soonestEpochToPredict, userAuth);

await fastForward(secondsPerEpoch.toNumber())
curEpoch = await erc20Token.curEpoch();
predictedEpoch = curEpoch.add(secondsPerEpoch);
const [numer2, denom2] = await erc20Token.connect(user2).getAggPredval(predictedEpoch, userAuth);
const totalStake2 = await erc20Token.getTotalStake(predictedEpoch);
expect(numer2).to.be.eq(web3.utils.toWei("1"));
expect(denom2).to.be.eq(web3.utils.toWei("1"));
expect(totalStake2).to.be.eq(web3.utils.toWei("1"));

// check subscription revenue
const revenue = await erc20Token.getsubscriptionRevenueAtEpoch(soonestEpochToPredict);
Expand Down Expand Up @@ -1330,7 +1351,9 @@ describe("ERC20Template3", () => {
const balAfter = await mockErc20.balanceOf(user3.address);
expect(balAfter).to.be.gt(balBefore);
const profit = balAfter.sub(balBefore);
const expectedProfit = 1 + (2 / parseInt(3600 / parseInt(300 / 24)))
const secondsPerEpoch = await erc20Token.secondsPerEpoch();
const secondsPerSubscription = await erc20Token.secondsPerSubscription();
const expectedProfit = 1 + (2 / secondsPerSubscription * secondsPerEpoch)
expect(parseFloat(web3.utils.fromWei(profit.toString()))).to.be.eq(expectedProfit);

// user tries to call payout for the same slot
Expand Down Expand Up @@ -1459,7 +1482,9 @@ describe("ERC20Template3", () => {
expect(balAfter).to.be.gt(balBefore);

const profit = balAfter.sub(balBefore);
const expectedProfit = 1 + (2 / parseInt(3600 / parseInt(300 / 24)))
const secondsPerEpoch = await erc20Token.secondsPerEpoch();
const secondsPerSubscription = await erc20Token.secondsPerSubscription();
const expectedProfit = 1 + (2 / secondsPerSubscription * secondsPerEpoch);
expect(parseFloat(web3.utils.fromWei(profit.toString()))).to.be.eq(expectedProfit);

mockErc20Balance = await mockErc20.balanceOf(user3.address)
Expand Down Expand Up @@ -1615,7 +1640,7 @@ describe("ERC20Template3", () => {
let event_2 = getEventFromTx(txReceipt_2, 'Transfer')
expect(event_2.args.from).to.be.eq(erc20Token.address);
expect(event_2.args.to).to.be.eq(freMarketFeeCollector.address);
expect(event_2.args.value).to.be.eq(6666666666666666);
expect(event_2.args.value).to.be.eq(revenue_at_block);
})
it("#redeemUnusedSlotRevenue - admin should not be able to redeem for future epoch", async () => {
const secondsPerEpoch = await erc20Token.secondsPerEpoch();
Expand Down

0 comments on commit 1ec635f

Please sign in to comment.