Skip to content

Commit ed6b9ab

Browse files
committed
SpeedRun 2
1 parent 998e462 commit ed6b9ab

10 files changed

Lines changed: 983 additions & 12 deletions

File tree

packages/hardhat/contracts/Staker.sol

Lines changed: 100 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,114 @@ pragma solidity 0.8.20; //Do not change the solidity version as it negatively im
44
import "hardhat/console.sol";
55
import "./ExampleExternalContract.sol";
66

7+
/**
8+
* @title Staker
9+
* @dev A contract that allows users to stake Ether and execute a completion call if threshold is met
10+
* @notice This contract manages staking with a deadline and threshold mechanism
11+
*/
712
contract Staker {
13+
/// @notice Mapping to track each user's staked balance
14+
/// @dev Maps user address to their total staked amount
15+
mapping(address => uint256) public balances;
16+
17+
/// @notice The minimum threshold required to complete the staking process
18+
/// @dev Set to 1 ether as the target amount
19+
uint256 public constant threshold = 1 ether;
20+
21+
/// @notice The deadline timestamp for the staking period
22+
/// @dev Set to 72 hours from contract deployment
23+
uint256 public deadline = block.timestamp + 72 hours;
24+
25+
/// @notice Flag indicating if withdrawals are allowed
26+
/// @dev Set to true only if staking fails to meet threshold after deadline
27+
bool public openForWithdraw = false;
28+
29+
/// @notice Reference to the external contract that will be completed
30+
/// @dev Instance of ExampleExternalContract
831
ExampleExternalContract public exampleExternalContract;
932

33+
/**
34+
* @notice Emitted when a user stakes Ether
35+
* @param staker The address of the staker
36+
* @param deposit The amount of Ether staked
37+
*/
38+
event Stake(address indexed staker, uint256 deposit);
39+
40+
/**
41+
* @notice Constructor to initialize the Staker contract
42+
* @dev Sets the address of the external contract to be completed
43+
* @param exampleExternalContractAddress Address of the ExampleExternalContract
44+
*/
1045
constructor(address exampleExternalContractAddress) {
1146
exampleExternalContract = ExampleExternalContract(exampleExternalContractAddress);
1247
}
1348

14-
// Collect funds in a payable `stake()` function and track individual `balances` with a mapping:
15-
// (Make sure to add a `Stake(address,uint256)` event and emit it for the frontend `All Stakings` tab to display)
49+
/**
50+
* @notice Modifier to ensure the external contract is not already completed
51+
* @dev Reverts if the external contract's completed flag is true
52+
*/
53+
modifier notCompleted() {
54+
require(exampleExternalContract.completed() == false, "NotCompleted");
55+
_;
56+
}
57+
58+
/**
59+
* @notice Allows users to stake Ether
60+
* @dev Updates the user's balance and emits a Stake event
61+
* @dev Can only be called while the external contract is not completed
62+
* @dev Reverts if sender is the zero address
63+
*/
64+
function stake() public payable notCompleted {
65+
require(msg.sender != address(0), 'InvalidAddress');
66+
balances[msg.sender] += msg.value;
67+
emit Stake(msg.sender, msg.value);
68+
}
1669

17-
// After some `deadline` allow anyone to call an `execute()` function
18-
// If the deadline has passed and the threshold is met, it should call `exampleExternalContract.complete{value: address(this).balance}()`
70+
/**
71+
* @notice Executes the completion process after deadline
72+
* @dev If threshold is met, completes the external contract with all funds
73+
* @dev If threshold is not met, opens withdrawals for users
74+
* @dev Can only be called after deadline and while external contract is not completed
75+
*/
76+
function execute() public notCompleted {
77+
require(timeLeft() == 0, 'notFinishStake');
78+
if (address(this).balance >= threshold) {
79+
exampleExternalContract.complete{value: address(this).balance}();
80+
} else {
81+
openForWithdraw = true;
82+
}
83+
}
1984

20-
// If the `threshold` was not met, allow everyone to call a `withdraw()` function to withdraw their balance
85+
/**
86+
* @notice Allows users to withdraw their staked funds
87+
* @dev Can only be called when withdrawals are open and external contract is not completed
88+
* @dev Transfers the user's entire staked balance back to them
89+
*/
90+
function withdraw() public notCompleted {
91+
require(openForWithdraw == true, 'AwaitMoreForWithdraw');
92+
payable(msg.sender).transfer(balances[msg.sender]);
93+
}
2194

22-
// Add a `timeLeft()` view function that returns the time left before the deadline for the frontend
95+
/**
96+
* @notice Returns the time remaining until the deadline
97+
* @dev Returns 0 if deadline has passed, otherwise returns seconds remaining
98+
* @return time The number of seconds left until deadline (0 if deadline passed)
99+
*/
100+
function timeLeft() public view returns(uint256 time) {
101+
if (deadline <= block.timestamp) {
102+
time = 0;
103+
} else {
104+
time = deadline - block.timestamp;
105+
}
106+
return time;
107+
}
23108

24-
// Add the `receive()` special function that receives eth and calls stake()
25-
}
109+
/**
110+
* @notice Receive function that automatically calls stake()
111+
* @dev Allows direct Ether transfers to the contract to be treated as stakes
112+
* @dev Called when Ether is sent to the contract without any function call data
113+
*/
114+
receive() external payable {
115+
stake();
116+
}
117+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
11155111
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
{
2+
"address": "0x8aaB1AdFd7e261b0E5b852Efc4a93251a27AE3b4",
3+
"abi": [
4+
{
5+
"inputs": [],
6+
"name": "complete",
7+
"outputs": [],
8+
"stateMutability": "payable",
9+
"type": "function"
10+
},
11+
{
12+
"inputs": [],
13+
"name": "completed",
14+
"outputs": [
15+
{
16+
"internalType": "bool",
17+
"name": "",
18+
"type": "bool"
19+
}
20+
],
21+
"stateMutability": "view",
22+
"type": "function"
23+
}
24+
],
25+
"transactionHash": "0xbaec46e3f175a5f8d7c95b687f3be315035e78d1a6db659d5e4e5c951403b526",
26+
"receipt": {
27+
"to": null,
28+
"from": "0x04633931E3b18a0afb3d9a655345CA8bb713f681",
29+
"contractAddress": "0x8aaB1AdFd7e261b0E5b852Efc4a93251a27AE3b4",
30+
"transactionIndex": 110,
31+
"gasUsed": "87989",
32+
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
33+
"blockHash": "0x74941e52fa7153034c08b71b5893ecfd45e75d578ccd9d202a307ab902e96f95",
34+
"transactionHash": "0xbaec46e3f175a5f8d7c95b687f3be315035e78d1a6db659d5e4e5c951403b526",
35+
"logs": [],
36+
"blockNumber": 9021443,
37+
"cumulativeGasUsed": "48082069",
38+
"status": 1,
39+
"byzantium": true
40+
},
41+
"args": [],
42+
"numDeployments": 1,
43+
"solcInputHash": "1132e34024e82f9a9d6154344e0e4ef2",
44+
"metadata": "{\"compiler\":{\"version\":\"0.8.20+commit.a1b79de6\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"complete\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"completed\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/ExampleExternalContract.sol\":\"ExampleExternalContract\"},\"evmVersion\":\"paris\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"contracts/ExampleExternalContract.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity 0.8.20; //Do not change the solidity version as it negatively impacts submission grading\\n\\ncontract ExampleExternalContract {\\n bool public completed;\\n\\n function complete() public payable {\\n completed = true;\\n }\\n}\\n\",\"keccak256\":\"0x9d7b4fb853a08a242fd15aab47631e4df4b6a455c781c84a77e5888b9badf359\",\"license\":\"MIT\"}},\"version\":1}",
45+
"bytecode": "0x6080604052348015600f57600080fd5b5060a08061001e6000396000f3fe60806040526004361060265760003560e01c8063522e117714602b5780639d9a7fe914603e575b600080fd5b603c6000805460ff19166001179055565b005b348015604957600080fd5b5060005460569060ff1681565b604051901515815260200160405180910390f3fea2646970667358221220c4e80ad9d447948c5d9a429b3c3d2e7bcc49fed3f8ed1ebeb527207c5cc811bd64736f6c63430008140033",
46+
"deployedBytecode": "0x60806040526004361060265760003560e01c8063522e117714602b5780639d9a7fe914603e575b600080fd5b603c6000805460ff19166001179055565b005b348015604957600080fd5b5060005460569060ff1681565b604051901515815260200160405180910390f3fea2646970667358221220c4e80ad9d447948c5d9a429b3c3d2e7bcc49fed3f8ed1ebeb527207c5cc811bd64736f6c63430008140033",
47+
"devdoc": {
48+
"kind": "dev",
49+
"methods": {},
50+
"version": 1
51+
},
52+
"userdoc": {
53+
"kind": "user",
54+
"methods": {},
55+
"version": 1
56+
},
57+
"storageLayout": {
58+
"storage": [
59+
{
60+
"astId": 3,
61+
"contract": "contracts/ExampleExternalContract.sol:ExampleExternalContract",
62+
"label": "completed",
63+
"offset": 0,
64+
"slot": "0",
65+
"type": "t_bool"
66+
}
67+
],
68+
"types": {
69+
"t_bool": {
70+
"encoding": "inplace",
71+
"label": "bool",
72+
"numberOfBytes": "1"
73+
}
74+
}
75+
}
76+
}

packages/hardhat/deployments/sepolia/Staker.json

Lines changed: 329 additions & 0 deletions
Large diffs are not rendered by default.

packages/hardhat/deployments/sepolia/solcInputs/1132e34024e82f9a9d6154344e0e4ef2.json

Lines changed: 42 additions & 0 deletions
Large diffs are not rendered by default.

packages/hardhat/deployments/sepolia/solcInputs/7e498d3369b761ed87d8ef3ed18a9ae8.json

Lines changed: 42 additions & 0 deletions
Large diffs are not rendered by default.

packages/hardhat/deployments/sepolia/solcInputs/9f8528b66368ff16ca409754cd3c5627.json

Lines changed: 42 additions & 0 deletions
Large diffs are not rendered by default.

packages/nextjs/app/stakings/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ const Stakings: NextPage = () => {
4545
return (
4646
<tr key={index}>
4747
<td>
48-
<Address address={event.args?.[0]} />
48+
<Address address={event.args?.staker} />
4949
</td>
50-
<td>{formatEther(event.args?.[1] || 0n)} ETH</td>
50+
<td>{formatEther(event.args?.deposit || 0n)} ETH</td>
5151
</tr>
5252
);
5353
})

0 commit comments

Comments
 (0)