Skip to content

Commit

Permalink
Merge 6373d7e into 444c276
Browse files Browse the repository at this point in the history
  • Loading branch information
kamescg committed Sep 30, 2021
2 parents 444c276 + 6373d7e commit b6efc70
Show file tree
Hide file tree
Showing 13 changed files with 515 additions and 277 deletions.
57 changes: 33 additions & 24 deletions contracts/interfaces/IPrizePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,7 @@ interface IPrizePool {
function depositTo(
address to,
uint256 amount
)
external;
) external;

/// @notice Withdraw assets from the Prize Pool instantly. A fairness fee may be charged for an early exit.
/// @param from The address to redeem tokens from.
Expand All @@ -100,6 +99,12 @@ interface IPrizePool {
uint256 amount
) external returns (uint256);

/// @notice Called by the prize strategy to award prizes.
/// @dev The amount awarded must be less than the awardBalance()
/// @param to The address of the winner that receives the award
/// @param amount The amount of assets to be awarded
function award( address to, uint256 amount) external;

/// @notice Returns the balance that is available to award.
/// @dev captureAwardBalance() should be called first
/// @return The total amount of assets to be awarded for the current prize
Expand All @@ -119,17 +124,37 @@ interface IPrizePool {
/// @return The underlying balance of assets
function balance() external returns (uint256);

/**
* @notice Read internal Ticket accounted balance.
* @return uint256 accountBalance
*/
function getAccountedBalance() external view returns (uint256);
/**
* @notice Read internal balanceCap variable
*/
function getBalanceCap() external view returns (uint256);
/**
* @notice Read internal liquidityCap variable
*/
function getLiquidityCap() external view returns (uint256);
/**
* @notice Read ticket variable
*/
function getTicket() external view returns (IControlledToken);
/**
* @notice Read token variable
*/
function getToken() external view returns (address);
/**
* @notice Read prizeStrategy variable
*/
function getPrizeStrategy() external view returns (address);

/// @dev Checks if a specific token is controlled by the Prize Pool
/// @param _controlledToken The address of the token to check
/// @return True if the token is a controlled token, false otherwise
function isControlled(IControlledToken _controlledToken) external view returns (bool);

/// @notice Called by the prize strategy to award prizes.
/// @dev The amount awarded must be less than the awardBalance()
/// @param to The address of the winner that receives the award
/// @param amount The amount of assets to be awarded
function award( address to, uint256 amount) external;

/// @notice Called by the Prize-Strategy to transfer out external ERC20 tokens
/// @dev Used to transfer out tokens held by the Prize Pool. Could be liquidated, or anything.
/// @param to The address of the winner that receives the award
Expand Down Expand Up @@ -172,22 +197,6 @@ interface IPrizePool {
/// @return True if ticket has been successfully set.
function setTicket(IControlledToken _ticket) external returns (bool);

/// @dev Returns the address of the prize pool ticket.
/// @return The address of the prize pool ticket.
function ticket() external view returns (IControlledToken);

/// @dev Returns the address of the prize pool ticket.
/// @return The address of the prize pool ticket.
function getTicket() external view returns (IControlledToken);

/// @dev Returns the address of the underlying ERC20 asset
/// @return The address of the asset
function token() external view returns (address);

/// @notice The total of all controlled tokens
/// @return The current total of all tokens
function accountedBalance() external view returns (uint256);

/// @notice Delegate the votes for a Compound COMP-like token held by the prize pool
/// @param _compLike The COMP-like token held by the prize pool that should be delegated
/// @param _to The address to delegate to
Expand Down
12 changes: 12 additions & 0 deletions contracts/interfaces/IReserve.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,19 @@ pragma solidity 0.8.6;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IReserve {

/**
* @notice Emit when checkpoint is created.
* @param reserveAccumulated Total depsosited
* @param withdrawAccumulated Total withdrawn
*/

event Checkpoint(uint256 reserveAccumulated, uint256 withdrawAccumulated);
/**
* @notice Emit when the withdrawTo function has executed.
* @param recipient Address receiving funds
* @param amount Amount of tokens transfered.
*/
event Withdrawn(address indexed recipient, uint256 amount);

/**
Expand Down
41 changes: 26 additions & 15 deletions contracts/prize-pool/PrizePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@ abstract contract PrizePool is IPrizePool, Ownable, ReentrancyGuard, IERC721Rece
string constant public VERSION = "4.0.0";

/// @notice Prize Pool ticket. Can only be set once by calling `setTicket()`.
IControlledToken public override ticket;
IControlledToken internal ticket;

/// @notice The Prize Strategy that this Prize Pool is bound to.
address public prizeStrategy;
address internal prizeStrategy;

/// @notice The total amount of tickets a user can hold.
uint256 public balanceCap;
uint256 internal balanceCap;

/// @notice The total amount of funds that the prize pool can hold.
uint256 public liquidityCap;
uint256 internal liquidityCap;

/// @notice the The awardable balance
uint256 internal _currentAwardBalance;
Expand Down Expand Up @@ -69,11 +69,6 @@ abstract contract PrizePool is IPrizePool, Ownable, ReentrancyGuard, IERC721Rece

/* ============ External Functions ============ */

/// @inheritdoc IPrizePool
function token() external override view returns (address) {
return address(_token());
}

/// @inheritdoc IPrizePool
function balance() external override returns (uint256) {
return _balance();
Expand All @@ -93,12 +88,33 @@ abstract contract PrizePool is IPrizePool, Ownable, ReentrancyGuard, IERC721Rece
function isControlled(IControlledToken _controlledToken) external override view returns (bool) {
return _isControlled(_controlledToken);
}

/// @inheritdoc IPrizePool
function getAccountedBalance() external override view returns (uint256) {
return _ticketTotalSupply();
}
/// @inheritdoc IPrizePool
function getBalanceCap() external override view returns (uint256) {
return balanceCap;
}
/// @inheritdoc IPrizePool
function getLiquidityCap() external override view returns (uint256) {
return liquidityCap;
}
/// @inheritdoc IPrizePool
function getTicket() external override view returns (IControlledToken) {
return ticket;
}

/// @inheritdoc IPrizePool
function getPrizeStrategy() external override view returns (address) {
return prizeStrategy;
}

/// @inheritdoc IPrizePool
function getToken() external override view returns (address) {
return address(_token());
}

/// @inheritdoc IPrizePool
function captureAwardBalance() external override nonReentrant returns (uint256) {

Expand Down Expand Up @@ -256,11 +272,6 @@ abstract contract PrizePool is IPrizePool, Ownable, ReentrancyGuard, IERC721Rece
_setPrizeStrategy(_prizeStrategy);
}

/// @inheritdoc IPrizePool
function accountedBalance() external override view returns (uint256) {
return _ticketTotalSupply();
}

/// @inheritdoc IPrizePool
function compLikeDelegate(ICompLike _compLike, address _to) external override onlyOwner {
if (_compLike.balanceOf(address(this)) > 0) {
Expand Down
2 changes: 1 addition & 1 deletion contracts/prize-strategy/PrizeSplitStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ contract PrizeSplitStrategy is PrizeSplit, IStrategy {
* @param _amount Amount of minted tokens.
*/
function _awardPrizeSplitAmount(address _to, uint256 _amount) override internal {
IControlledToken _ticket = prizePool.ticket();
IControlledToken _ticket = prizePool.getTicket();
prizePool.award(_to, _amount);
emit PrizeSplitAwarded(_to, _amount, _ticket);
}
Expand Down
10 changes: 9 additions & 1 deletion contracts/test/PrizePoolHarness.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ contract PrizePoolHarness is PrizePool {
function _currentTime() internal override view returns (uint256) {
return currentTime;
}

function internalCurrentTime() external view returns (uint256) {
return super._currentTime();
}

function _canAwardExternal(address _externalToken) internal override view returns (bool) {
return stubYieldSource.canAwardExternal(_externalToken);
Expand All @@ -55,4 +59,8 @@ contract PrizePoolHarness is PrizePool {
function _redeem(uint256 redeemAmount) internal override returns (uint256) {
return stubYieldSource.redeemToken(redeemAmount);
}
}

function setCurrentAwardBalance(uint256 amount) external {
_currentAwardBalance = amount;
}
}
4 changes: 0 additions & 4 deletions contracts/test/libraries/GasLib.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.6;

import "hardhat/console.sol";

library GasLib {
struct Tracker {
uint256 startingGas;
Expand All @@ -18,14 +16,12 @@ library GasLib {

function mark(Tracker memory tracker, string memory message) internal view returns (Tracker memory) {
uint256 diff = tracker.deltaGas - gasleft();
console.log(message, diff);
tracker.deltaGas = gasleft();

return tracker;
}

function done(Tracker memory tracker, string memory message) internal view {
uint256 diff = tracker.startingGas - gasleft();
console.log(message, diff);
}
}
114 changes: 114 additions & 0 deletions test/PrizeSplitStrategy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { expect } from 'chai';
import { deployMockContract, MockContract } from 'ethereum-waffle';
import { ethers, artifacts } from 'hardhat';
import { Artifact } from 'hardhat/types';
import { Signer } from '@ethersproject/abstract-signer';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { Contract, ContractFactory } from 'ethers';

const { getSigners } = ethers;
const debug = require('debug')('ptv4:PrizeSplitStrategy');
const toWei = (val: string | number) => ethers.utils.parseEther('' + val)

describe('PrizeSplitStrategy', () => {
let wallet1: SignerWithAddress;
let wallet2: SignerWithAddress;
let wallet3: SignerWithAddress;
let wallet4: SignerWithAddress;
let prizeSplitStrategy: Contract;
let ticket: Contract;
let PrizePool: Artifact;
let prizePool: MockContract;

let prizeSplitStrategyFactory: ContractFactory
let erc20MintableFactory: ContractFactory

before(async () => {
[wallet1, wallet2, wallet3, wallet4] = await getSigners();

prizeSplitStrategyFactory = await ethers.getContractFactory(
'PrizeSplitStrategy',
);

erc20MintableFactory = await ethers.getContractFactory(
'ERC20Mintable',
);

PrizePool = await artifacts.readArtifact('PrizePool');
});

beforeEach(async () => {
debug('mocking ticket and prizePool...');
ticket = await erc20MintableFactory.deploy('Ticket', 'TICK');
prizePool = await deployMockContract(wallet1 as Signer, PrizePool.abi);
await prizePool.mock.getTicket.returns(ticket.address);

debug('deploy prizeSplitStrategy...');
prizeSplitStrategy = await prizeSplitStrategyFactory.deploy(wallet1.address, prizePool.address);
});

describe('distribute()', () => {
it('should award 100% of the captured balance to the PrizeReserve', async () => {
await prizeSplitStrategy.setPrizeSplits([
{
target: wallet2.address,
percentage: 1000
},
])

await prizePool.mock.captureAwardBalance.returns(toWei('100'))
await prizePool.mock.award.withArgs(wallet2.address, toWei('100')).returns()

// Distribute Award
const distribute = await prizeSplitStrategy.distribute()

// Verify Contract Events
await expect(distribute)
.to.emit(prizeSplitStrategy, 'Distributed')
.withArgs(toWei('100'))

await expect(distribute)
.to.emit(prizeSplitStrategy, 'PrizeSplitAwarded')
.withArgs(wallet2.address, toWei('100'), ticket.address)
});

it('should award (50%/50%) the captured balance to the PrizeReserve and a secondary account.', async () => {
await prizeSplitStrategy.setPrizeSplits([
{
target: wallet2.address,
percentage: 500,
token: 1,
},
{
target: wallet3.address,
percentage: 500,
token: 0,
},
])

await prizePool.mock.captureAwardBalance.returns(toWei('100'))

// Mock PrizeReserve Award Sponsorhip
await prizePool.mock.award.withArgs(wallet2.address, toWei('50')).returns()

// Mock Secondary Wallet Award Sponsorhip
await prizePool.mock.award.withArgs(wallet3.address, toWei('50')).returns()

// Distribute Award
const distribute = await prizeSplitStrategy.distribute()

// Verify Contract Events
await expect(distribute)
.to.emit(prizeSplitStrategy, 'Distributed')
.withArgs(toWei('100'))

await expect(distribute)
.to.emit(prizeSplitStrategy, 'PrizeSplitAwarded')
.withArgs(wallet2.address, toWei('50'), ticket.address)

await expect(distribute)
.to.emit(prizeSplitStrategy, 'PrizeSplitAwarded')
.withArgs(wallet3.address, toWei('50'), ticket.address)
});
})
})
Loading

0 comments on commit b6efc70

Please sign in to comment.