Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add extra payout per user #97

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
18 changes: 17 additions & 1 deletion contracts/core/AMM/SportsAMMV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ contract SportsAMMV2 is Initializable, ProxyOwned, ProxyPausable, ProxyReentranc
// the contract that processes betting with StakedTHALES
address public stakingThalesBettingProxy;

// support bonus payouts for some users/contracts (e.g. stakingThalesBettingProxy)
mapping(address => uint) public addedPayoutPercentagePerUser;

struct TradeDataQuoteInternal {
uint _buyInAmount;
bool _shouldCheckRisks;
Expand Down Expand Up @@ -456,7 +459,9 @@ contract SportsAMMV2 is Initializable, ProxyOwned, ProxyPausable, ProxyReentranc
uint totalQuote;
uint payout;
uint fees;
uint addedPayoutPercentage = addedPayoutPercentagePerCollateral[_tradeDataInternal._collateral];
uint addedPayoutPercentage = addedPayoutPercentagePerUser[_tradeDataInternal._recipient] > 0
? addedPayoutPercentagePerUser[_tradeDataInternal._recipient]
: addedPayoutPercentagePerCollateral[_tradeDataInternal._collateral];
if (!_tradeDataInternal._isLive) {
(totalQuote, payout, fees, , ) = _tradeQuote(
_tradeData,
Expand Down Expand Up @@ -802,10 +807,20 @@ contract SportsAMMV2 is Initializable, ProxyOwned, ProxyPausable, ProxyReentranc
/// @param _collateral to add extra payout for
/// @param _addedPayout percentage amount for extra payout
function setAddedPayoutPercentagePerCollateral(address _collateral, uint _addedPayout) external onlyOwner {
require(_addedPayout <= 3e16, "Bonus payout can't exceed 3%");
addedPayoutPercentagePerCollateral[_collateral] = _addedPayout;
emit SetAddedPayoutPercentagePerCollateral(_collateral, _addedPayout);
}

/// @notice sets additional payout percentage for certain _users
/// @param _user to add extra payout for
/// @param _addedPayout percentage amount for extra payout
function setAddedPayoutPercentagePerUser(address _user, uint _addedPayout) external onlyOwner {
require(_addedPayout <= 3e16, "Bonus payout can't exceed 3%");
addedPayoutPercentagePerUser[_user] = _addedPayout;
emit SetAddedPayoutPercentagePerUser(_user, _addedPayout);
}

/// @notice sets dedicated SafeBox per collateral
/// @param _collateral to set dedicated SafeBox for
/// @param _safeBox for the given collateral
Expand Down Expand Up @@ -864,5 +879,6 @@ contract SportsAMMV2 is Initializable, ProxyOwned, ProxyPausable, ProxyReentranc
event SetFreeBetsHolder(address freeBetsHolder);
event SetStakingThalesBettingProxy(address _stakingThalesBettingProxy);
event SetAddedPayoutPercentagePerCollateral(address _collateral, uint _addedPayout);
event SetAddedPayoutPercentagePerUser(address _user, uint _addedPayout);
event SetSafeBoxPerCollateral(address _collateral, address _safeBox);
}
56 changes: 56 additions & 0 deletions scripts/abi/SportsAMMV2.json
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,25 @@
"name": "SetAddedPayoutPercentagePerCollateral",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "_user",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "_addedPayout",
"type": "uint256"
}
],
"name": "SetAddedPayoutPercentagePerUser",
"type": "event"
},
{
"anonymous": false,
"inputs": [
Expand Down Expand Up @@ -587,6 +606,25 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "addedPayoutPercentagePerUser",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
Expand Down Expand Up @@ -1006,6 +1044,24 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_user",
"type": "address"
},
{
"internalType": "uint256",
"name": "_addedPayout",
"type": "uint256"
}
],
"name": "setAddedPayoutPercentagePerUser",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
Expand Down
4 changes: 4 additions & 0 deletions test/constants/overtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ const BUY_IN_AMOUNT = ethers.parseEther('10');
const BUY_IN_AMOUNT_SIX_DECIMALS = Number(10000000);
const ETH_BUY_IN_AMOUNT = ethers.parseEther('0.0028571428571429');
const ADDITIONAL_SLIPPAGE = ethers.parseEther('0.02');
const BONUS_PAYOUT = ethers.parseEther('0.03');
const BONUS_PAYOUT_OUT_OF_RANGE = ethers.parseEther('0.04');

const DEFAULT_AMOUNT = ethers.parseEther('10000');
const DEFAULT_AMOUNT_SIX_DECIMALS = Number('10000000000');
Expand Down Expand Up @@ -93,6 +95,8 @@ module.exports = {
BUY_IN_AMOUNT_SIX_DECIMALS,
ETH_BUY_IN_AMOUNT,
ADDITIONAL_SLIPPAGE,
BONUS_PAYOUT,
BONUS_PAYOUT_OUT_OF_RANGE,
DEFAULT_AMOUNT,
DEFAULT_AMOUNT_SIX_DECIMALS,
ETH_DEFAULT_AMOUNT,
Expand Down
121 changes: 121 additions & 0 deletions test/contracts/Overtime/SportsAMMV2/QuotesAndTradesTHALESCollateral.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ const {
const {
BUY_IN_AMOUNT,
ADDITIONAL_SLIPPAGE,
BONUS_PAYOUT,
BONUS_PAYOUT_OUT_OF_RANGE,
RESULT_TYPE,
SPORT_ID_NBA,
} = require('../../../constants/overtime');
Expand Down Expand Up @@ -112,6 +114,71 @@ describe('SportsAMMV2 Quotes And Trades', () => {
expect(safeBoxTHALESBalance).greaterThan(0);
});

it('Should revert if bonus is set too high', async () => {
const firstTraderAddress = await firstTrader.getAddress();
expect(
sportsAMMV2.setAddedPayoutPercentagePerUser(firstTraderAddress, BONUS_PAYOUT_OUT_OF_RANGE)
).to.be.revertedWith("Bonus payout can't exceed 3%");
});

it('Should buy a ticket (1 market) in THALES with extra payout for user', async () => {
const firstTraderAddress = await firstTrader.getAddress();
await sportsAMMV2.setAddedPayoutPercentagePerUser(firstTraderAddress, BONUS_PAYOUT);

const quote = await sportsAMMV2.tradeQuote(
tradeDataCurrentRound,
BUY_IN_AMOUNT,
ZERO_ADDRESS,
false
);

const quoteTHALES = await sportsAMMV2.tradeQuote(
tradeDataCurrentRound,
BUY_IN_AMOUNT,
collateralTHALESAddress,
false
);

console.log('Normal quote is: ' + quote.totalQuote);
console.log('THALES quote is: ' + quoteTHALES.totalQuote);

expect(quoteTHALES.payout).greaterThan(quote.payout);

await sportsAMMV2
.connect(firstTrader)
.trade(
tradeDataCurrentRound,
BUY_IN_AMOUNT,
quote.totalQuote,
ADDITIONAL_SLIPPAGE,
ZERO_ADDRESS,
collateralTHALESAddress,
false
);

await sportsAMMV2ResultManager.setResultTypesPerMarketTypes([0], [RESULT_TYPE.ExactPosition]);

await sportsAMMV2ResultManager.setResultsPerMarkets(
[tradeDataCurrentRound[0].gameId],
[tradeDataCurrentRound[0].typeId],
[tradeDataCurrentRound[0].playerId],
[[0]]
);

const activeTickets = await sportsAMMV2Manager.getActiveTickets(0, 100);
const ticketAddress = activeTickets[0];

const TicketContract = await ethers.getContractFactory('Ticket');
const userTicket = await TicketContract.attach(ticketAddress);

const marketData = await userTicket.markets(0);

console.log('Normal quote is: ' + quote.totalQuote);
console.log('Bonus payour for this user is: ' + marketData.odd);

expect(quoteTHALES.totalQuote).greaterThan(marketData.odd);
});

it('Should buy a ticket (1 market) in THALES with Referrer', async () => {
const quote = await sportsAMMV2.tradeQuote(
tradeDataCurrentRound,
Expand Down Expand Up @@ -300,6 +367,60 @@ describe('SportsAMMV2 Quotes And Trades', () => {
expect(await userTicket.isLive()).to.eq(true);
});

it('Should buy a live trade with bonus user payout', async () => {
const firstTraderAddress = await firstTrader.getAddress();
await sportsAMMV2.setAddedPayoutPercentagePerUser(firstTraderAddress, BONUS_PAYOUT);

const quote = await sportsAMMV2.tradeQuote(
tradeDataCurrentRound,
BUY_IN_AMOUNT,
ZERO_ADDRESS,
false
);

const quoteTHALES = await sportsAMMV2.tradeQuote(
tradeDataCurrentRound,
BUY_IN_AMOUNT,
collateralTHALESAddress,
false
);

await sportsAMMV2RiskManager.setLiveTradingPerSportAndTypeEnabled(SPORT_ID_NBA, 0, true);

await liveTradingProcessor.connect(firstTrader).requestLiveTrade({
_gameId: tradeDataCurrentRound[0].gameId,
_sportId: tradeDataCurrentRound[0].sportId,
_typeId: tradeDataCurrentRound[0].typeId,
_line: tradeDataCurrentRound[0].line,
_position: tradeDataCurrentRound[0].position,
_buyInAmount: BUY_IN_AMOUNT,
_expectedQuote: quote.totalQuote,
_additionalSlippage: ADDITIONAL_SLIPPAGE,
_referrer: ZERO_ADDRESS,
_collateral: collateralTHALESAddress,
});

let requestId = await liveTradingProcessor.counterToRequestId(0);
console.log('requestId is ' + requestId);

await mockChainlinkOracle.fulfillLiveTrade(requestId, true, quote.totalQuote);

const activeTickets = await sportsAMMV2Manager.getActiveTickets(0, 100);
const ticketAddress = activeTickets[0];

const TicketContract = await ethers.getContractFactory('Ticket');
const userTicket = await TicketContract.attach(ticketAddress);

const marketData = await userTicket.markets(0);

console.log('Normal quote is: ' + quote.totalQuote);
console.log('User live quote is: ' + marketData.odd);

expect(quoteTHALES.totalQuote).greaterThan(marketData.odd);

expect(await userTicket.isLive()).to.eq(true);
});

it('Should buy a ticket (1 market) in THALES which gets cancelled', async () => {
const quoteTHALES = await sportsAMMV2.tradeQuote(
tradeDataCurrentRound,
Expand Down