Skip to content

Commit

Permalink
Add test cases for v1 Pool oracle
Browse files Browse the repository at this point in the history
  • Loading branch information
cwsnt committed Jun 12, 2021
1 parent bcd0b5c commit 243aa5d
Show file tree
Hide file tree
Showing 7 changed files with 279 additions and 37 deletions.
24 changes: 16 additions & 8 deletions contracts/feeds/IV1PoolOracle.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
pragma solidity >=0.5.0 <0.6.0;

interface IV1PoolOracle {
function read(uint256 price, uint256 timestamp) external view returns(
uint256,
uint256,
uint256,
uint256,
uint256,
uint256
);
function read(uint256 price, uint256 timestamp)
external
view
returns (
uint256,
uint256,
uint256,
uint256,
uint256,
uint256
);

function latestAnswer() external view returns (uint256);
function liquidityPool() external view returns (address);
}

interface ILiquidityPoolV1Converter {
function reserveTokens(uint256 index) external view returns (address);
}
31 changes: 13 additions & 18 deletions contracts/feeds/PriceFeedV1PoolOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ contract PriceFeedV1PoolOracle is IPriceFeedsExt, Ownable {
*
* @param _v1PoolOracleAddress The V1 Pool Oracle address.
* */
constructor(address _v1PoolOracleAddress, address _rBTCAddress, address _docAddress) public {
constructor(
address _v1PoolOracleAddress,
address _rBTCAddress,
address _docAddress
) public {
setV1PoolOracleAddress(_v1PoolOracleAddress);
setRBTCAddress(_rBTCAddress);
setDOCAddress(_docAddress);
Expand All @@ -44,35 +48,26 @@ contract PriceFeedV1PoolOracle is IPriceFeedsExt, Ownable {
function latestAnswer() external view returns (uint256) {
require(rBTCAddress != address(0), "rBTC address has not been set");
require(docAddress != address(0), "DOC address has not been set");

IV1PoolOracle _v1PoolOracle = IV1PoolOracle(v1PoolOracleAddress);
(uint256 _price) = _v1PoolOracle.latestAnswer();
// Need to check, if the requested asset is BTC
address liquidityPool = _v1PoolOracle.liquidityPool();
require(ILiquidityPoolV1Converter(liquidityPool).reserveTokens(0) != rBTCAddress || ILiquidityPoolV1Converter(liquidityPool).reserveTokens(1) != rBTCAddress, "wrBTC price feed cannot use the oracle v1 pool");

uint256 _price = _v1PoolOracle.latestAnswer();

// Need to convert to USD, since the V1 pool return value is based on BTC
uint256 priceInUSD = _convertAnswerToUsd(_price);
require(priceInUSD != 0, "price error");


return priceInUSD;
}

function _convertAnswerToUsd(uint256 _valueInBTC) private view returns (uint256){
function _convertAnswerToUsd(uint256 _valueInBTC) private view returns (uint256) {
uint256 valueInUSD;
address _priceFeeds = msg.sender;

(bool success, bytes memory data) =
_priceFeeds.staticcall(
abi.encodeWithSelector(
IPriceFeeds(_priceFeeds).queryReturn.selector,
rBTCAddress,
docAddress,
_valueInBTC
)
);
assembly {
if eq(success, 1) {
valueInUSD := mload(add(data, 32))
}
}
valueInUSD = IPriceFeeds(_priceFeeds).queryReturn(rBTCAddress, docAddress, _valueInBTC);

return valueInUSD;
}
Expand Down
3 changes: 1 addition & 2 deletions contracts/feeds/PriceFeeds.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import "../openzeppelin/Ownable.sol";
import "../interfaces/IERC20.sol";
import "./PriceFeedsConstants.sol";


interface IPriceFeedsExt {
function latestAnswer() external view returns (uint256);
}
Expand Down Expand Up @@ -391,7 +390,7 @@ contract PriceFeeds is Constants, Ownable {

uint256 destRate;
if (destToken == address(baseToken)) {
destRate = 10 ** 18;
destRate = 10**18;
} else {
IPriceFeedsExt _destFeed = pricesFeeds[destToken];
require(address(_destFeed) != address(0), "unsupported dst feed");
Expand Down
12 changes: 12 additions & 0 deletions contracts/mockup/LiquidityPoolV1ConverterMockup.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
pragma solidity 0.5.17;

import "../interfaces/IERC20.sol";

contract LiquidityPoolV1ConverterMockup {
IERC20[] public reserveTokens;

constructor(IERC20 _token0, IERC20 _token1) public {
reserveTokens.push(_token0);
reserveTokens.push(_token1);
}
}
22 changes: 14 additions & 8 deletions contracts/mockup/PriceFeedV1PoolOracleMockup.sol
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
pragma solidity 0.5.17;

contract PriceFeedV1PoolOracleMockup {
uint256 value;
uint256 value;
address public liquidityPool;

constructor(uint256 _value) public {
value = _value;
}
constructor(uint256 _value, address _liquidityPool) public {
value = _value;
liquidityPool = _liquidityPool;
}

function latestAnswer() external view returns (uint256) {
return value;
function latestAnswer() external view returns (uint256) {
return value;
}

function setValue(uint256 _value) public {
function setValue(uint256 _value) public {
value = _value;
}
}

function setLiquidityPool(address _liquidityPool) public {
liquidityPool = _liquidityPool;
}
}
2 changes: 1 addition & 1 deletion contracts/modules/Affiliates.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ contract Affiliates is State, AffiliatesEvents {
SetAffiliatesReferrerResult memory result;

result.userNotFirstTradeFlag = getUserNotFirstTradeFlag(user);
result.alreadySet = affiliatesUserReferrer[user] != address(0);
result.alreadySet = affiliatesUserReferrer[user] != address(0);
result.success = !(result.userNotFirstTradeFlag || result.alreadySet || user == referrer);
if (result.success) {
affiliatesUserReferrer[user] = referrer;
Expand Down
222 changes: 222 additions & 0 deletions tests-js/affiliates_integration.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ const LoanClosingsBase = artifacts.require("LoanClosingsBase");
const LoanClosingsWith = artifacts.require("LoanClosingsWith");

const PriceFeedsLocal = artifacts.require("PriceFeedsLocal");
const PriceFeeds = artifacts.require("PriceFeeds");
const PriceFeedV1PoolOracleMockup = artifacts.require("PriceFeedV1PoolOracleMockup");
const PriceFeedRSKOracle = artifacts.require("PriceFeedRSKOracle");
const PriceFeedRSKOracleMockup = artifacts.require("PriceFeedRSKOracleMockup");
const PriceFeedV1PoolOracle = artifacts.require("PriceFeedV1PoolOracle");
const LiquidityPoolV1ConverterMockup = artifacts.require("LiquidityPoolV1ConverterMockup");
const TestSovrynSwap = artifacts.require("TestSovrynSwap");
const SwapsImplSovrynSwap = artifacts.require("SwapsImplSovrynSwap");
const Affiliates = artifacts.require("Affiliates");
Expand Down Expand Up @@ -296,4 +302,220 @@ contract("Affiliates", (accounts) => {
amount: referrerFee2.toString(),
});
});

it("Test affiliates integration with underlying token with oracle v1Pool", async () => {

feeds = await PriceFeeds.new(testWrbtc.address, tokenSOV.address, doc.address);
testToken1 = await TestToken.new("test token 1", "TEST1", 18, wei("20000", "ether"));
testToken2 = await TestToken.new("test token 2", "TEST2", 18, wei("20000", "ether"));
testToken1Price = wei("2", "ether");
testToken2Price = wei("2", "ether");
wrBTCPrice = wei("8", "ether");
docPrice = wei("7", "ether");

// Set tetToken1 feed - price 1Z BTC
// Set v1 convert mockup
liquidityV1ConverterMockupTestToken1 = await LiquidityPoolV1ConverterMockup.new(testToken1.address, testWrbtc.address);
priceFeedsV1PoolOracleMockupTestToken1 = await PriceFeedV1PoolOracleMockup.new(testToken1Price, liquidityV1ConverterMockupTestToken1.address);
priceFeedsV1PoolOracleTestToken1 = await PriceFeedV1PoolOracle.new(priceFeedsV1PoolOracleMockupTestToken1.address, testWrbtc.address, doc.address)

liquidityV1ConverterMockupTestToken2 = await LiquidityPoolV1ConverterMockup.new(testToken2.address, testWrbtc.address);
priceFeedsV1PoolOracleMockupTestToken2 = await PriceFeedV1PoolOracleMockup.new(testToken2Price, liquidityV1ConverterMockupTestToken2.address);
priceFeedsV1PoolOracleTestToken2 = await PriceFeedV1PoolOracle.new(priceFeedsV1PoolOracleMockupTestToken2.address, testWrbtc.address, doc.address)

// // Set rBTC feed - using rsk oracle
priceFeedsV1PoolOracleMockupBTC = await PriceFeedRSKOracleMockup.new();
await priceFeedsV1PoolOracleMockupBTC.setValue(wrBTCPrice);
priceFeedsV1PoolOracleBTC = await PriceFeedRSKOracle.new(priceFeedsV1PoolOracleMockupBTC.address)

// Set rBTC feed - using v1pool oracle
// liquidityV1ConverterMockupBTC = await LiquidityPoolV1ConverterMockup.new(testWrbtc.address, testWrbtc.address);
// priceFeedsV1PoolOracleMockupBTC = await PriceFeedV1PoolOracleMockup.new(wei("8", "ether"), liquidityV1ConverterMockupBTC.address);
// priceFeedsV1PoolOracleBTC = await PriceFeedV1PoolOracle.new(priceFeedsV1PoolOracleMockupBTC.address, testWrbtc.address, doc.address);

// Set DOC feed -- price 1 BTC
liquidityV1ConverterMockupDOC = await LiquidityPoolV1ConverterMockup.new(doc.address, testWrbtc.address);
priceFeedsV1PoolOracleMockupDOC = await PriceFeedV1PoolOracleMockup.new(docPrice, liquidityV1ConverterMockupDOC.address);
priceFeedsV1PoolOracleDOC = await PriceFeedV1PoolOracle.new(priceFeedsV1PoolOracleMockupDOC.address, testWrbtc.address, doc.address)

// await feeds.setPriceFeed([testWrbtc.address, doc.address], [priceFeedsV1PoolOracle.address, priceFeedsV1PoolOracle.address])
await feeds.setPriceFeed([testToken1.address, testToken2.address, doc.address, testWrbtc.address], [priceFeedsV1PoolOracleTestToken1.address, priceFeedsV1PoolOracleTestToken2.address, priceFeedsV1PoolOracleDOC.address, priceFeedsV1PoolOracleBTC.address])

test1 = await feeds.queryRate(testToken1.address, doc.address);
expect(test1[0].toString()).to.be.equal((new BN(testToken1Price).mul(new BN(wrBTCPrice)).div(new BN(wei("1", "ether")))).toString())
test = await feeds.queryReturn(testToken1.address, doc.address, wei("2", "ether"))
expect(test.toString()).to.be.equal( (new BN(2).mul(new BN(testToken1Price).mul(new BN(wrBTCPrice)).div(new BN(wei("1", "ether"))) )).toString())

test1 = await feeds.queryRate(testToken1.address, testToken2.address);

expect(test1[0].toString()).to.be.equal((new BN(testToken1Price).div(new BN(testToken2Price)).mul(new BN(wei("1", "ether"))) ).toString())
test = await feeds.queryReturn(testToken1.address, testToken2.address, wei("2", "ether"))
expect(test.toString()).to.be.equal( new BN(2).mul((new BN(testToken1Price).div(new BN(testToken2Price)).mul(new BN(wei("1", "ether")))) ).toString() )

swapsSovryn = await SwapsImplSovrynSwap.new();
const sovrynSwapSimulator = await TestSovrynSwap.new(feeds.address);
await sovryn.setSovrynSwapContractRegistryAddress(sovrynSwapSimulator.address);
await sovryn.setSupportedTokens([doc.address, testWrbtc.address], [true, true]);
await sovryn.setPriceFeedContract(
feeds.address //priceFeeds
);
await sovryn.setSwapsImplContract(
swapsSovryn.address // swapsImpl
);
await sovryn.setFeesController(owner);
await sovryn.setWrbtcToken(testWrbtc.address);
await sovryn.setSOVTokenAddress(tokenSOV.address);

// Change the min referrals to payout to 3 for testing purposes
await sovryn.setMinReferralsToPayoutAffiliates(3);
loanTokenLogic = await LoanTokenLogicStandard.new();

const loanTokenSent = wei("21", "ether");
const leverageAmount = web3.utils.toWei("2", "ether");
// underlyingToken.approve(loanTokenV2.address, loanTokenSent*2)

let previousAffiliateRewardsHeld = await sovryn.affiliateRewardsHeld(referrer);
let tx = await loanTokenV2.marginTradeAffiliate(
constants.ZERO_BYTES32, // loanId (0 for new loans)
leverageAmount, // Leverage
loanTokenSent, // loanTokenSent
0, //
testWrbtc.address, // collateralTokenAddress
trader, // trader
referrer, // referrer address
"0x", // loanDataBytes (only required with ether)
{ from: trader }
);

await expectEvent.inTransaction(tx.receipt.rawLogs[0].transactionHash, Affiliates, "SetAffiliatesReferrer", {
user: trader,
referrer: referrer,
});

let referrerOnChain = await sovryn.affiliatesUserReferrer(trader);
expect(referrerOnChain, "Incorrect User Affiliate").to.be.equal(referrer);

notFirstTradeFlagOnChain = await sovryn.getUserNotFirstTradeFlag(trader);
expect(notFirstTradeFlagOnChain, "First trade flag is not updated").to.be.true;

let event_name = "PayTradingFeeToAffiliate";
let decode = decodeLogs(tx.receipt.rawLogs, Affiliates, event_name);
let referrerFee = await sovryn.affiliatesReferrerBalances(referrer, doc.address);
if (!decode.length) {
throw "Event PayTradingFeeToAffiliate is not fired properly";
}

let isHeld = decode[0].args["isHeld"];
let affiliateRewardsHeld = await sovryn.affiliateRewardsHeld(referrer);
let submittedAffiliatesReward = decode[0].args["sovBonusAmount"];
let submittedTokenBonusAmount = decode[0].args["tokenBonusAmount"];
expect(isHeld, "First trade affiliates reward must be in held").to.be.true;
expect(referrerFee.toString(), "Token bonus rewards is not matched").to.be.equal(submittedTokenBonusAmount.toString());

let checkedValueShouldBe = affiliateRewardsHeld - previousAffiliateRewardsHeld;
expect(checkedValueShouldBe.toString(), "Affiliates bonus rewards is not matched").to.be.equal(
submittedAffiliatesReward.toString()
);

// Check lockedSOV Balance of the referrer
let referrerBalanceInLockedSOV = await lockedSOV.getLockedBalance(referrer);
expect(referrerBalanceInLockedSOV.toString(), "Referrer balance in lockedSOV is not matched").to.be.equal(new BN(0).toString());

// Change the min referrals to payout to 1
await sovryn.setMinReferralsToPayoutAffiliates(1);

previousAffiliateRewardsHeld = await sovryn.affiliateRewardsHeld(referrer);
tx = await loanTokenV2.marginTradeAffiliate(
constants.ZERO_BYTES32, // loanId (0 for new loans)
leverageAmount, // Leverage
loanTokenSent, // loanTokenSent
0, //
testWrbtc.address, // collateralTokenAddress
trader, // trader
referrer, // referrer address
"0x", // loanDataBytes (only required with ether)
{ from: trader }
);

decode = decodeLogs(tx.receipt.rawLogs, Affiliates, event_name);
if (!decode.length) {
throw "Event PayTradingFeeToAffiliate is not fired properly";
}

isHeld = decode[0].args["isHeld"];
expect(isHeld, "First trade affiliates reward must not be in held").to.be.false;

affiliateRewardsHeld = await sovryn.affiliateRewardsHeld(referrer);
submittedAffiliatesReward = decode[0].args["sovBonusAmount"];
submittedTokenBonusAmount = decode[0].args["tokenBonusAmount"];
sovBonusAmountPaid = decode[0].args["sovBonusAmountPaid"];

expect(affiliateRewardsHeld.toString(), "affiliateRewardHeld should be zero at this point").to.be.equal(new BN(0).toString());
expect(referrerFee.toString(), "Token bonus rewards is not matched").to.be.equal(submittedTokenBonusAmount.toString());

checkSovBonusAmountPaid = new BN(submittedAffiliatesReward).add(new BN(previousAffiliateRewardsHeld));
expect(checkSovBonusAmountPaid.toString(), "Affiliates bonus rewards paid is not matched").to.be.equal(
sovBonusAmountPaid.toString()
);

checkedValueShouldBe = new BN(affiliateRewardsHeld).add(new BN(previousAffiliateRewardsHeld));
expect(checkedValueShouldBe.toString(), "Affiliates bonus rewards is not matched").to.be.equal(
submittedAffiliatesReward.toString()
);

// Check lockedSOV Balance of the referrer
referrerBalanceInLockedSOV = await lockedSOV.getLockedBalance(referrer);
expect(referrerBalanceInLockedSOV.toString(), "Referrer balance in lockedSOV is not matched").to.be.equal(
checkSovBonusAmountPaid.toString()
);

// Do withdrawal
let referrerTokenBalance = await doc.balanceOf(referrer);
let referrerFee2 = new BN(referrerFee).add(new BN(submittedTokenBonusAmount));
tx = await sovryn.withdrawAllAffiliatesReferrerTokenFees(referrer, { from: referrer });
const referrerFeeAfterWithdrawal = await sovryn.affiliatesReferrerBalances(referrer, doc.address);
let referrerTokenBalanceAfterWithdrawal = await doc.balanceOf(referrer);
expect(referrerFeeAfterWithdrawal, "Incorrect all balance after all DoC withdraw").to.be.bignumber.equal(new BN(0));
expect(referrerTokenBalanceAfterWithdrawal.sub(referrerTokenBalance).toString()).to.be.equal(referrerFee2.toString());

await expectEvent.inTransaction(tx.receipt.rawLogs[0].transactionHash, Affiliates, "WithdrawAffiliatesReferrerTokenFees", {
referrer: referrer,
receiver: referrer,
tokenAddress: doc.address,
amount: referrerFee2.toString(),
});
});

it("Should revert if wrBTC price feed is set to v1 pool oracle", async () => {
feeds = await PriceFeeds.new(testWrbtc.address, tokenSOV.address, doc.address);
testToken1 = await TestToken.new("test token 1", "TEST1", 18, wei("20000", "ether"));

// Set tetToken1 feed - price 2 BTC
// Set v1 convert mockup
liquidityV1ConverterMockupTestToken1 = await LiquidityPoolV1ConverterMockup.new(testToken1.address, testWrbtc.address);
priceFeedsV1PoolOracleMockupTestToken1 = await PriceFeedV1PoolOracleMockup.new(wei("1", "ether"), liquidityV1ConverterMockupTestToken1.address);
priceFeedsV1PoolOracleTestToken1 = await PriceFeedV1PoolOracle.new(priceFeedsV1PoolOracleMockupTestToken1.address, testWrbtc.address, doc.address)

// // Set rBTC feed - using rsk oracle
// priceFeedsV1PoolOracleMockupBTC = await PriceFeedRSKOracleMockup.new();
// await priceFeedsV1PoolOracleMockupBTC.setValue(wei("9", "ether"));
// priceFeedsV1PoolOracleBTC = await PriceFeedRSKOracle.new(priceFeedsV1PoolOracleMockupBTC.address)

// Set rBTC feed - using v1pool oracle
liquidityV1ConverterMockupBTC = await LiquidityPoolV1ConverterMockup.new(testWrbtc.address, testWrbtc.address);
priceFeedsV1PoolOracleMockupBTC = await PriceFeedV1PoolOracleMockup.new(wei("8", "ether"), liquidityV1ConverterMockupBTC.address);
priceFeedsV1PoolOracleBTC = await PriceFeedV1PoolOracle.new(priceFeedsV1PoolOracleMockupBTC.address, testWrbtc.address, doc.address);

// Set DOC feed -- price 1 BTC
liquidityV1ConverterMockupDOC = await LiquidityPoolV1ConverterMockup.new(doc.address, testWrbtc.address);
priceFeedsV1PoolOracleMockupDOC = await PriceFeedV1PoolOracleMockup.new(wei("7", "ether"), liquidityV1ConverterMockupDOC.address);
priceFeedsV1PoolOracleDOC = await PriceFeedV1PoolOracle.new(priceFeedsV1PoolOracleMockupDOC.address, testWrbtc.address, doc.address)

// await feeds.setPriceFeed([testWrbtc.address, doc.address], [priceFeedsV1PoolOracle.address, priceFeedsV1PoolOracle.address])
await feeds.setPriceFeed([testToken1.address, doc.address, testWrbtc.address], [priceFeedsV1PoolOracleTestToken1.address, priceFeedsV1PoolOracleDOC.address, priceFeedsV1PoolOracleBTC.address])

await expectRevert(feeds.queryRate(testToken1.address, doc.address), "wrBTC price feed cannot use the oracle v1 pool")
await expectRevert(feeds.queryReturn(testToken1.address, doc.address, wei("1", "ether")), "wrBTC price feed cannot use the oracle v1 pool")

});
});

0 comments on commit 243aa5d

Please sign in to comment.