Skip to content

Commit

Permalink
Merge pull request #23 from moonshotcollective/week3-demo-updates
Browse files Browse the repository at this point in the history
Week3 demo updates
  • Loading branch information
ghostffcode authored Aug 5, 2022
2 parents c2beb2b + 4ea2d7e commit facfdf1
Show file tree
Hide file tree
Showing 9 changed files with 321 additions and 175 deletions.
171 changes: 122 additions & 49 deletions packages/hardhat/contracts/Staking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,26 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract Staking is Ownable {
IERC20 public token;
uint256 public start;
uint256 public duration;
mapping(address => uint256) stakes;
uint256 public latestRound;

event tokenStaked(address staker, uint256 amount);
event tokenUnstaked(address staker, uint256 amount);
struct Round {
string meta;
uint256 tvl;
uint256 start;
uint256 duration;
mapping(address => uint256) stakes;
}

mapping(uint256 => Round) rounds;

modifier canUnstake() {
event roundCreated(uint256 id);
event tokenStaked(uint256 roundId, address staker, uint256 amount);
event tokenUnstaked(uint256 roundId, address staker, uint256 amount);

modifier roundExists(uint256 roundId) {
require(
(start == 0 && duration == 0) ||
(start > block.timestamp) ||
(start < block.timestamp && block.timestamp > start + duration),
"Can't unstake during an active round"
rounds[roundId].start > 0 && roundId > 0,
"Round does not exist"
);
_;
}
Expand All @@ -27,61 +34,127 @@ contract Staking is Ownable {
token = _token;
}

function updateMeta(uint256 _start, uint256 _duration) public onlyOwner {
require(
_start > 0 && _duration > 0,
"start and duration has to be > 0"
);
require(
start + duration < block.timestamp,
"A round is currently active"
);
function createRound(
uint256 start,
uint256 duration,
string memory meta
//Removed onlyOwner modifier for testing purposes
) public {

// REMOVING these require statements because they are not necessary to test staking and locking
// if (latestRound > 0) {
// require(
// start >
// rounds[latestRound].start + rounds[latestRound].duration,
// "new rounds have to start after old rounds"
// );
// }

// require(start > block.timestamp, "new rounds should be in the future");

latestRound++;

rounds[latestRound].start = start;
rounds[latestRound].duration = duration;
rounds[latestRound].meta = meta;

emit roundCreated(latestRound);
}

// stake
function stake(uint256 roundId, uint256 amount) public {
// require(isActiveRound(roundId), "Can't stake an inactive round");

token.transferFrom(msg.sender, address(this), amount);

rounds[roundId].tvl += amount;

rounds[roundId].stakes[msg.sender] += amount;

emit tokenStaked(roundId, msg.sender, amount);
}

// unstake
function unstake(uint256 roundId, uint256 amount) public {
// require(
// !isActiveRound(roundId),
// "Can't unstake during an active round"
// );
require(
_start > block.timestamp,
"start point should be in the future"
rounds[roundId].stakes[msg.sender] >= amount,
"Not enough balance to withdraw"
);
start = _start;
duration = _duration;

rounds[roundId].tvl -= amount;

rounds[roundId].stakes[msg.sender] -= amount;

token.transfer(msg.sender, amount);

emit tokenUnstaked(roundId, msg.sender, amount);
}

function fetchMeta()
// migrateStake
function migrateStake(uint256 fromRound) public {
require(fromRound < latestRound, "Can't migrate from an active round");

uint256 balance = rounds[fromRound].stakes[msg.sender];

require(balance > 0, "Not enough balance to migrate");

rounds[fromRound].tvl -= balance;
rounds[fromRound].stakes[msg.sender] = 0;
rounds[latestRound].tvl += balance;
rounds[latestRound].stakes[msg.sender] = balance;

emit tokenUnstaked(fromRound, msg.sender, balance);
emit tokenStaked(latestRound, msg.sender, balance);
}

// VIEW
function fetchRoundMeta(uint256 roundId)
public
view
roundExists(roundId)
returns (
uint256 _start,
uint256 _duration,
bool isActiveRound
uint256 start,
uint256 duration,
uint256 tvl
)
{
return (
start,
duration,
start < block.timestamp && block.timestamp < (start + duration)
rounds[roundId].start,
rounds[roundId].duration,
rounds[roundId].tvl
);
}

// stake
function stake(uint256 amount) public {
token.transferFrom(msg.sender, address(this), amount);

stakes[msg.sender] += amount;

emit tokenStaked(msg.sender, amount);
function isActiveRound(uint256 roundId)
public
view
returns (bool isActive)
{
(uint256 start, uint256 duration, ) = fetchRoundMeta(roundId);
isActive =
start < block.timestamp &&
start + duration > block.timestamp;
}

// unstake
function unstake(uint256 amount) public canUnstake {
require(stakes[msg.sender] >= amount, "Not enough balance to withdraw");

stakes[msg.sender] -= amount;

token.transfer(msg.sender, amount);

emit tokenUnstaked(msg.sender, amount);
function getUserStakeForRound(uint256 roundId, address user)
public
view
///roundExists(roundId)
returns (uint256)
{
return rounds[roundId].stakes[user];
}

// view for stake amount
function getStakeFor(address user) public view returns (uint256) {
return stakes[user];
function getUserStakeFromLatestRound(address user)
public
view
///roundExists(roundId)
returns (uint256)
{
return rounds[latestRound].stakes[user];
}
}
19 changes: 16 additions & 3 deletions packages/react-app/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import externalContracts from "./contracts/external_contracts";
// contracts
import deployedContracts from "./contracts/hardhat_contracts.json";
import { Transactor, Web3ModalSetup } from "./helpers";
import { Home, Subgraph } from "./views";
import { Admin, Home, Subgraph } from "./views";
import { useStaticJsonRPC } from "./hooks";

const { ethers } = require("ethers");
Expand All @@ -46,7 +46,7 @@ const { ethers } = require("ethers");
*/

/// 📡 What chain are your contracts deployed to?
const initialNetwork = NETWORKS.localhost; // <------- select your target frontend network (localhost, rinkeby, xdai, mainnet)
const initialNetwork = NETWORKS.goerli; // <------- select your target frontend network (localhost, rinkeby, xdai, mainnet)

// 😬 Sorry for all the console logging
const DEBUG = true;
Expand All @@ -70,7 +70,7 @@ function App(props) {

const [injectedProvider, setInjectedProvider] = useState();
const [address, setAddress] = useState();
const [selectedNetwork, setSelectedNetwork] = useState(networkOptions[0]);
const [selectedNetwork, setSelectedNetwork] = useState(networkOptions[3]);
const location = useLocation();

const targetNetwork = NETWORKS[selectedNetwork];
Expand Down Expand Up @@ -222,6 +222,9 @@ function App(props) {
<Menu.Item key="/">
<Link to="/">App Home</Link>
</Menu.Item>
<Menu.Item key="/admin">
<Link to="/admin">Admin Control</Link>
</Menu.Item>
<Menu.Item key="/debug">
<Link to="/debug">Debug Contracts</Link>
</Menu.Item>
Expand All @@ -241,6 +244,16 @@ function App(props) {
mainnetProvider={mainnetProvider}
/>
</Route>
<Route exact path="/admin">
{/* pass in any web3 props to this Home component. For example, yourLocalBalance */}
<Admin
tx={tx}
address={address}
readContracts={readContracts}
writeContracts={writeContracts}
mainnetProvider={mainnetProvider}
/>
</Route>
<Route exact path="/debug">
{/*
🎛 this scaffolding is full of commonly used components
Expand Down
56 changes: 56 additions & 0 deletions packages/react-app/src/components/Rounds.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Button } from "antd";
import { useContractReader } from "eth-hooks";
import { ethers } from "ethers";
import moment from "moment";

const zero = ethers.BigNumber.from("0");

const Rounds = ({ tokenSymbol, address, readContracts, stake, unstake, migrate, round, latestRound }) => {
const stakedBalance = ethers.utils.formatUnits(
useContractReader(readContracts, "Staking", "getUserStakeForRound", [round, address]) || zero,
);

const [start, duration, tvl] = useContractReader(readContracts, "Staking", "fetchRoundMeta", [round]) || [];

return (
<div
style={{
paddingTop: "20px",
paddingBottom: "20px",
maxWidth: "600px",
margin: "20px auto",
border: "1px solid",
}}
>
<div>
Round: {round} of {latestRound}
</div>
<div>
stake of TVL: {stakedBalance} {tokenSymbol} staked of {ethers.utils.formatEther(tvl || zero)} {tokenSymbol}
</div>
<div>Start Timestamp: {moment.unix((start || zero).toString()).format("dddd, MMMM Do YYYY, h:mm:ss a")}</div>
<div>
End Timestamp:{" "}
{moment.unix((start || zero).add(duration || zero).toString()).format("dddd, MMMM Do YYYY, h:mm:ss a")}
</div>

<div style={{ padding: "5px", marginTop: "5px" }}>
<div style={{ width: "100%" }}>
<Button style={{ marginRight: "10px" }} onClick={() => stake(round, "50")}>
Stake 50 {tokenSymbol}
</Button>
<Button style={{ marginRight: "10px" }} onClick={() => unstake(round, "50")}>
Unstake 50 {tokenSymbol}
</Button>
{round !== latestRound && stakedBalance !== "0.0" && (
<Button style={{ marginRight: "10px" }} onClick={() => migrate(round)}>
Migrate Stake {tokenSymbol}
</Button>
)}
</div>
</div>
</div>
);
};

export default Rounds;
1 change: 1 addition & 0 deletions packages/react-app/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ export { default as FaucetHint } from "./FaucetHint";
export { default as NetworkSwitch } from "./NetworkSwitch";
export { default as MultiAddressInput } from "./MultiAddressInput";
export { default as TokenSelect } from "./TokenSelect";
export { default as Rounds } from "./Rounds";
2 changes: 1 addition & 1 deletion packages/react-app/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const BLOCKNATIVE_DAPPID = "0b58206a-f3c0-4701-a62f-73c7243e8c77";
export const ALCHEMY_KEY = "oKxs-03sij-U_N0iOlrSsZFr29-IqbuF";

const localRpcUrl = process.env.REACT_APP_CODESPACES
? `https://${window.location.hostname.replace("3000", "8545")}`
? `https://${window.location.hostname.replace("3001", "8545")}`
: "http://" + (global.window ? window.location.hostname : "localhost") + ":8545";

export const NETWORKS = {
Expand Down
Loading

0 comments on commit facfdf1

Please sign in to comment.