Skip to content

Commit

Permalink
Merge pull request #28 from 0xBcamp/proxy
Browse files Browse the repository at this point in the history
Proxy
  • Loading branch information
0xwetzo authored Feb 10, 2024
2 parents ba4a5e1 + dad91cb commit 98d8927
Show file tree
Hide file tree
Showing 7 changed files with 514 additions and 269 deletions.
301 changes: 197 additions & 104 deletions packages/hardhat/contracts/Tournament.sol

Large diffs are not rendered by default.

26 changes: 13 additions & 13 deletions packages/hardhat/contracts/TournamentFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "./Tournament.sol";
// Use openzeppelin to inherit battle-tested implementations (ERC20, ERC721, etc)
// import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";

contract TournamentFactory {
// State Variables
Expand All @@ -14,6 +15,12 @@ contract TournamentFactory {
mapping(address => address) public TournamentPartner;
address public owner;

address public implementationContract;

constructor (address _owner) {
owner = _owner;
implementationContract = address(new Tournament());
}
//// VRF deployment to Avax. @todo make structs for each chain? Pass in struct to createTournament() for vrf constructor args.
// uint64 subscriptionId = 1341;
// bytes32 gasLane = 0x354d2f95da55398f44b7cff77da56283d9c6c829a4bdf1bbcaf2ad6a4d081f61;
Expand All @@ -25,12 +32,6 @@ contract TournamentFactory {
address tournament
);

// Constructor: Called once on contract deployment
// Check packages/hardhat/deploy/00_deploy_your_contract.ts
constructor(address _owner) {
owner = _owner;
}

// Modifier: used to define a set of rules that must be met before or after a function is executed
// Check the withdraw() function
modifier isOwner() {
Expand All @@ -47,8 +48,6 @@ contract TournamentFactory {
* @param _LPTokenAmount (uint256) - amount of the ERC-20 LP token to stake in order to participate
* @param _startTime (uint256) - block timestamp at which the tournament starts
* @param _endTime (uint256) - block timestamp at which the tournament ends
*
* @return newTournament (Tournament) - instance of the new tournament
*/
function createTournament(
string memory _name,
Expand All @@ -60,8 +59,9 @@ contract TournamentFactory {
bytes32 _gasLane,
uint32 _callbackGasLimit,
address _vrfCoordinatorV2
) public returns (Tournament newTournament) {
newTournament = new Tournament(
) public {
address instance = Clones.clone(implementationContract);
Tournament(instance).initialize(
owner,
_name,
_poolIncentivized,
Expand All @@ -73,9 +73,9 @@ contract TournamentFactory {
_callbackGasLimit,
_vrfCoordinatorV2
);
TournamentArray.push(address(newTournament));
TournamentMap[address(newTournament)] = newTournament;
emit TournamentCreated(address(newTournament));
TournamentArray.push(instance);
TournamentMap[instance] = Tournament(instance);
emit TournamentCreated(instance);
}


Expand Down
140 changes: 140 additions & 0 deletions packages/hardhat/contracts/VRFConsumerBaseV2Upgradeable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/proxy/utils/Initializable.sol";

/** ****************************************************************************
* @notice Interface for contracts using VRF randomness
* *****************************************************************************
* @dev PURPOSE
*
* @dev Reggie the Random Oracle (not his real job) wants to provide randomness
* @dev to Vera the verifier in such a way that Vera can be sure he's not
* @dev making his output up to suit himself. Reggie provides Vera a public key
* @dev to which he knows the secret key. Each time Vera provides a seed to
* @dev Reggie, he gives back a value which is computed completely
* @dev deterministically from the seed and the secret key.
*
* @dev Reggie provides a proof by which Vera can verify that the output was
* @dev correctly computed once Reggie tells it to her, but without that proof,
* @dev the output is indistinguishable to her from a uniform random sample
* @dev from the output space.
*
* @dev The purpose of this contract is to make it easy for unrelated contracts
* @dev to talk to Vera the verifier about the work Reggie is doing, to provide
* @dev simple access to a verifiable source of randomness. It ensures 2 things:
* @dev 1. The fulfillment came from the VRFCoordinator
* @dev 2. The consumer contract implements fulfillRandomWords.
* *****************************************************************************
* @dev USAGE
*
* @dev Calling contracts must inherit from VRFConsumerBase, and can
* @dev initialize VRFConsumerBase's attributes in their constructor as
* @dev shown:
*
* @dev contract VRFConsumer {
* @dev constructor(<other arguments>, address _vrfCoordinator, address _link)
* @dev VRFConsumerBase(_vrfCoordinator) public {
* @dev <initialization with other arguments goes here>
* @dev }
* @dev }
*
* @dev The oracle will have given you an ID for the VRF keypair they have
* @dev committed to (let's call it keyHash). Create subscription, fund it
* @dev and your consumer contract as a consumer of it (see VRFCoordinatorInterface
* @dev subscription management functions).
* @dev Call requestRandomWords(keyHash, subId, minimumRequestConfirmations,
* @dev callbackGasLimit, numWords),
* @dev see (VRFCoordinatorInterface for a description of the arguments).
*
* @dev Once the VRFCoordinator has received and validated the oracle's response
* @dev to your request, it will call your contract's fulfillRandomWords method.
*
* @dev The randomness argument to fulfillRandomWords is a set of random words
* @dev generated from your requestId and the blockHash of the request.
*
* @dev If your contract could have concurrent requests open, you can use the
* @dev requestId returned from requestRandomWords to track which response is associated
* @dev with which randomness request.
* @dev See "SECURITY CONSIDERATIONS" for principles to keep in mind,
* @dev if your contract could have multiple requests in flight simultaneously.
*
* @dev Colliding `requestId`s are cryptographically impossible as long as seeds
* @dev differ.
*
* *****************************************************************************
* @dev SECURITY CONSIDERATIONS
*
* @dev A method with the ability to call your fulfillRandomness method directly
* @dev could spoof a VRF response with any random value, so it's critical that
* @dev it cannot be directly called by anything other than this base contract
* @dev (specifically, by the VRFConsumerBase.rawFulfillRandomness method).
*
* @dev For your users to trust that your contract's random behavior is free
* @dev from malicious interference, it's best if you can write it so that all
* @dev behaviors implied by a VRF response are executed *during* your
* @dev fulfillRandomness method. If your contract must store the response (or
* @dev anything derived from it) and use it later, you must ensure that any
* @dev user-significant behavior which depends on that stored value cannot be
* @dev manipulated by a subsequent VRF request.
*
* @dev Similarly, both miners and the VRF oracle itself have some influence
* @dev over the order in which VRF responses appear on the blockchain, so if
* @dev your contract could have multiple VRF requests in flight simultaneously,
* @dev you must ensure that the order in which the VRF responses arrive cannot
* @dev be used to manipulate your contract's user-significant behavior.
*
* @dev Since the block hash of the block which contains the requestRandomness
* @dev call is mixed into the input to the VRF *last*, a sufficiently powerful
* @dev miner could, in principle, fork the blockchain to evict the block
* @dev containing the request, forcing the request to be included in a
* @dev different block with a different hash, and therefore a different input
* @dev to the VRF. However, such an attack would incur a substantial economic
* @dev cost. This cost scales with the number of blocks the VRF oracle waits
* @dev until it calls responds to a request. It is for this reason that
* @dev that you can signal to an oracle you'd like them to wait longer before
* @dev responding to the request (however this is not enforced in the contract
* @dev and so remains effective only in the case of unmodified oracle software).
*/
abstract contract VRFConsumerBaseV2Upgradeable is Initializable {
error OnlyCoordinatorCanFulfill(address have, address want);
address private vrfCoordinator;

function __VRFConsumerBaseV2Upgradeable_init(
address _vrfCoordinator
) internal onlyInitializing {
vrfCoordinator = _vrfCoordinator;
}

/**
* @notice fulfillRandomness handles the VRF response. Your contract must
* @notice implement it. See "SECURITY CONSIDERATIONS" above for important
* @notice principles to keep in mind when implementing your fulfillRandomness
* @notice method.
*
* @dev VRFConsumerBaseV2 expects its subcontracts to have a method with this
* @dev signature, and will call it once it has verified the proof
* @dev associated with the randomness. (It is triggered via a call to
* @dev rawFulfillRandomness, below.)
*
* @param requestId The Id initially returned by requestRandomness
* @param randomWords the VRF output expanded to the requested number of words
*/
function fulfillRandomWords(
uint256 requestId,
uint256[] memory randomWords
) internal virtual;

// rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF
// proof. rawFulfillRandomness then calls fulfillRandomness, after validating
// the origin of the call
function rawFulfillRandomWords(
uint256 requestId,
uint256[] memory randomWords
) external {
if (msg.sender != vrfCoordinator) {
revert OnlyCoordinatorCanFulfill(msg.sender, vrfCoordinator);
}
fulfillRandomWords(requestId, randomWords);
}
}
27 changes: 14 additions & 13 deletions packages/hardhat/deploy/01_deploy_your_contract.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { DeployFunction } from "hardhat-deploy/types";
import { Contract, randomBytes } from "ethers";
import { Contract } from "ethers";

/**
* Deploys a contract named "YourContract" using the deployer account and
Expand All @@ -24,27 +24,28 @@ const deployContracts: DeployFunction = async function (hre: HardhatRuntimeEnvir
const UniswapV2Pair = await hre.ethers.getContract<Contract>("UniswapV2Pair", deployer);
const Vyper_contract = await hre.ethers.getContract<Contract>("Vyper_contract", deployer); // yearn

// await deploy("VRFConsumerBaseV2Upgradeable", {
// from: deployer,
// // Contract constructor arguments
// args: [],
// log: true,
// // autoMine: can be passed to the deploy function to make the deployment process faster on local networks by
// // automatically mining the contract deployment transaction. There is no effect on live networks.
// autoMine: true,
// });

await deploy("Tournament", {
from: deployer,
// Contract constructor arguments
args: [
deployer,
"Tournament",
"0x0000000000000000000000000000000000000000",
1,
0,
Math.round(Date.now() / 1000 + 60 * 60 * 24 * 15),
0,
randomBytes(32),
0,
"0x0000000000000000000000000000000000000000",
],
args: [],
log: true,
// autoMine: can be passed to the deploy function to make the deployment process faster on local networks by
// automatically mining the contract deployment transaction. There is no effect on live networks.
autoMine: true,
});

// const implementation = await hre.ethers.getContract<Contract>("Tournament", deployer);

await deploy("TournamentFactory", {
from: deployer,
// Contract constructor arguments
Expand Down
28 changes: 15 additions & 13 deletions packages/hardhat/test/foundry/TournamentTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,17 @@ contract TournamentTest is Test {

string memory name = "Yearn Tournament";
// Deploy Tournament contract
tournamentY = new Tournament(
tournamentY = new Tournament();
tournamentY.initialize(
owner, name, address(mockYLP), LPTokenAmount,
startTime, endTime, subId, gasLane,
callbackGasLimit, address(mockVRF)
);

name = "Uniswap Tournament";
// Deploy Tournament contract
tournamentU = new Tournament(
tournamentU = new Tournament();
tournamentU.initialize(
owner, name, address(mockUniLP), LPTokenAmount,
startTime, endTime, subId, gasLane,
callbackGasLimit, address(mockVRF)
Expand Down Expand Up @@ -500,16 +502,16 @@ contract TournamentTest is Test {
assertEq(tournamentU.getPricePerShare(), uint(_fuzz0) * uint(_fuzz1) / 1000 ether);
}

function test_LPTokenAmountOfPlayer_notValuated() public {
function test_withdrawAmountFromDeposit_notValuated() public {
vm.warp(duringTime);

stakeForTest(player1);

assertEq(tournamentY.LPTokenAmountOfPlayer(player1), LPTokenAmount);
assertEq(tournamentU.LPTokenAmountOfPlayer(player1), LPTokenAmount);
assertEq(tournamentY.withdrawAmountFromDeposit(player1), LPTokenAmount);
assertEq(tournamentU.withdrawAmountFromDeposit(player1), LPTokenAmount);
}

function test_LPTokenAmountOfPlayer_valuated() public {
function test_withdrawAmountFromDeposit_valuated() public {
vm.warp(duringTime);

stakeForTest(player1);
Expand All @@ -518,29 +520,29 @@ contract TournamentTest is Test {
mockUniLP.setReserves(1500 ether, 2000 ether);
mockUniLP.setTotalSupply(2000 ether);

assertEq(tournamentY.LPTokenAmountOfPlayer(player1), LPTokenAmount * 10 / 15);
assertEq(tournamentU.LPTokenAmountOfPlayer(player1), LPTokenAmount * 10 / 15);
assertEq(tournamentY.withdrawAmountFromDeposit(player1), LPTokenAmount * 10 / 15);
assertEq(tournamentU.withdrawAmountFromDeposit(player1), LPTokenAmount * 10 / 15);

mockUniLP.setReserves(2000 ether, 1000 ether);
mockUniLP.setTotalSupply(1000 ether);

assertEq(tournamentU.LPTokenAmountOfPlayer(player1), LPTokenAmount / 2);
assertEq(tournamentU.withdrawAmountFromDeposit(player1), LPTokenAmount / 2);
}

function test_LPTokenAmountOfPlayer_devaluated() public {
function test_withdrawAmountFromDeposit_devaluated() public {
vm.warp(duringTime);

stakeForTest(player1);

mockYLP.setPricePerShare(50000);
mockUniLP.setReserves(500 ether, 500 ether);

assertEq(tournamentY.LPTokenAmountOfPlayer(player1), LPTokenAmount);
assertEq(tournamentU.LPTokenAmountOfPlayer(player1), LPTokenAmount);
assertEq(tournamentY.withdrawAmountFromDeposit(player1), LPTokenAmount);
assertEq(tournamentU.withdrawAmountFromDeposit(player1), LPTokenAmount);

mockUniLP.setTotalSupply(2000 ether);

assertEq(tournamentU.LPTokenAmountOfPlayer(player1), LPTokenAmount);
assertEq(tournamentU.withdrawAmountFromDeposit(player1), LPTokenAmount);
}

function test_unstakeLPToken_before() public {
Expand Down
10 changes: 5 additions & 5 deletions packages/nextjs/components/tournament/withdraw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ export const Withdraw = () => {
functionName: "getLPDecimals",
});

const { data: LPTokenAmountOfPlayer } = useContractRead({
const { data: withdrawAmountFromDeposit } = useContractRead({
abi: DeployedContracts[31337].Tournament.abi,
address: params.addr,
functionName: "LPTokenAmountOfPlayer",
functionName: "withdrawAmountFromDeposit",
args: [connectedAddress],
});

Expand Down Expand Up @@ -65,7 +65,7 @@ export const Withdraw = () => {
},
});

if (Number(LPTokenAmountOfPlayer) == 0) {
if (Number(withdrawAmountFromDeposit) == 0) {
return (
<div className="flex items-center flex-col flex-grow pt-10">
<div className="px-5">
Expand Down Expand Up @@ -102,14 +102,14 @@ export const Withdraw = () => {
<span className="block text-4xl font-bold">and get your rewards</span>
</h1>
<p className="text-center text-lg">
{Number(formatUnits(LPTokenAmountOfPlayer || 0n, Number(LPTokenDecimals.data) || 18)).toFixed(2)}{" "}
{Number(formatUnits(withdrawAmountFromDeposit || 0n, Number(LPTokenDecimals.data) || 18)).toFixed(2)}{" "}
{LPTokenSymbol.isLoading ? "..." : LPTokenSymbol.data}
can be withdrawn from your deposit and you earned{" "}
{Number(formatUnits(prizeAmount || 0n, Number(LPTokenDecimals.data) || 18)).toFixed(2)}{" "}
{LPTokenSymbol.isLoading ? "..." : LPTokenSymbol.data}.
<button
className="btn btn-secondary"
disabled={(LPTokenAmountOfPlayer || 0n) + (prizeAmount || 0n) == 0n}
disabled={(withdrawAmountFromDeposit || 0n) + (prizeAmount || 0n) == 0n}
onClick={() => handleWithdraw()}
>
Unstake and receive earned rewards
Expand Down
Loading

0 comments on commit 98d8927

Please sign in to comment.