diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..cd573be
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "wake.compiler.solc.remappings": []
+}
\ No newline at end of file
diff --git a/.wake/extension/local-local-chain-70d5e538-ca92-4743-bfb8-ef79176e875b.json b/.wake/extension/local-local-chain-70d5e538-ca92-4743-bfb8-ef79176e875b.json
new file mode 100644
index 0000000..7c5aee3
--- /dev/null
+++ b/.wake/extension/local-local-chain-70d5e538-ca92-4743-bfb8-ef79176e875b.json
@@ -0,0 +1 @@
+{"type":"local_node","id":"local-chain-70d5e538-ca92-4743-bfb8-ef79176e875b","displayName":"Local Chain 1","state":{"accounts":[{"address":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","balance":1e+22},{"address":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","balance":1e+22},{"address":"0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc","balance":1e+22},{"address":"0x90f79bf6eb2c4f870365e785982e1f101e93b906","balance":1e+22},{"address":"0x15d34aaf54267db7d7c367839aaf71a00a2c6a65","balance":1e+22},{"address":"0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc","balance":1e+22},{"address":"0x976ea74026e726554db657fa54763abd0c3a0aa9","balance":1e+22},{"address":"0x14dc79964da2c08b23698b3d3cc7ca32193d9955","balance":1e+22},{"address":"0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f","balance":1e+22},{"address":"0xa0ee7a142d267c1f36714e4a8f75612f20a79720","balance":1e+22}],"deployment":[],"history":[]},"network":{"wakeDump":{"metadata":{"labels":{},"deployedLibraries":{}},"chainDump":"0x1f8b08000000000000ffed56cd6e23370c7e9739e7a03fea27c7a0e8f6d04351604fc5c2a0282a1e743c637826dd1481dfbdd4d84991c05874d1b914a80c181a4ae24752d447be747998e8f7eefea51b9f0e994fdd7da79e5577d7d1d48f1967be08fee190734b7fe079c1c3713da845f288f36ee80ffd729150221bdbcea6bdf205c0e68484ab82d2d7dad3d3b0fcf966cbf1c47f9c702c387d9735dfb052bcce3b7e269ee75db34f94ef8ea79eb845e22a5ff7c86277afae07e4e3ba499fcf771d124d4fe332b733e2982b1452f2aea02115b3b13ec56c8b250a84d6e8644b4a006baca7b169697a71c0752e1a8c669d5851ca988d7b3595a6725996f9bc4c277c6c46367cc184621d6205677c28399440d6876893c88296788a291efda698e2978e1c35db0a546a1070b2c583cc3229cc15b2288d754b4c4bce5129257b4c4a55341922946292a8b1459b8ace249b694b4cc790b273d1856c43821021c6641461883557e3496597c07f03f31d42a8ff6e88a35e692f7647f9372a1a1ba2b2108dcc5c85a83468f0ca36537534b5408eb20b0ca8f693c34e4e53b5377c0d2aa5908222d0519b42169556148ad21914aba243cb6d8a5bc637a91a52ae9eb321576310ff8025ca291ad6552bcd72a349f94d3183670c4e19cfc17800271905a122b8e02de6a29ae7886953cc24402a148d0099c8f8040431a3f65512ab449db242e18e2d31513107d4ce14e105d25548413b76186b002fcfc52894eb366a4bcc6a532d9e41239618abaf8ed8638ed10423b923299c93317eb3fb14d02c9566b716b0dd87eab50a85987f7be9f68ca5adbc74473cf1b8fc84f37eab3232efd17e1e69e0f952db0a912b1c0b53801250bc870c3e644f549cc662b571a0737251b250dbaada1dd5e25471c9ba201a0ffdf8eac67798b1e0c2bf4ed3b2955f8b54db1969e9a7717ed30b5e58bfeaa0258b01d0d71aad03f649ca5d8d72f3d94516c69084272ca484ad8ca919d87a9b9d69adc08989fbe3326f69eb303dce0fc3341d3651f8fff86f8fdb0de40776903eeee71b0da9883fcf5cdeb6bd6f657d6093915a1af3b33c901f70c1575e3af4cf5bd2ca951e6f28bbb6cd3f32ffc2a74f387f6c9ebff6cbbe9cf02b0e9bbedbd6fd7efa109d4b97fc7059f9bb555f39f68191a6f1a191f0764ffdfc9e9784dcbfdc75d3e1c0a7757efe726b7ddfb79ad1130ebb9525453c3e0dc3f92f1ed5a33df90c0000"},"type":"Local Chain","config":{"sessionId":"local-chain-70d5e538-ca92-4743-bfb8-ef79176e875b","type":"anvil","uri":"ws://127.0.0.1:38109"}},"stateFingerprint":"3a82e3a6d1ac2e4daae286769e6107faa655d321408b0b1f9df23ede1a63b531","persistence":{"isDirty":false,"isAutosaveEnabled":true,"lastSaveTimestamp":1742912204383}}
\ No newline at end of file
diff --git a/README.md b/README.md
index 2f03b34..3250727 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,7 @@ Go to **Participant Registration** section and register to be the workshop parti
```
| 🦄 | Name | Github username | Your current occupation |
+| 😊 | Aliyu Adeniji | AbuTuraab | Freelance Developer |
```
- Step 5: `Commit` your code and push to the forked Github repository
diff --git a/challenge-1-vesting/.vscode/settings.json b/challenge-1-vesting/.vscode/settings.json
new file mode 100644
index 0000000..cd573be
--- /dev/null
+++ b/challenge-1-vesting/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "wake.compiler.solc.remappings": []
+}
\ No newline at end of file
diff --git a/challenge-1-vesting/contracts/TokenVesting.sol b/challenge-1-vesting/contracts/TokenVesting.sol
index 43d4c3a..6913372 100644
--- a/challenge-1-vesting/contracts/TokenVesting.sol
+++ b/challenge-1-vesting/contracts/TokenVesting.sol
@@ -31,17 +31,26 @@ import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
contract TokenVesting is Ownable(msg.sender), Pausable, ReentrancyGuard {
struct VestingSchedule {
// TODO: Define the vesting schedule struct
+ uint256 totalAmount;
+ uint256 startTime;
+ uint256 cliffDuration;
+ uint256 vestingDuration;
+ uint256 amountClaimed;
+ bool revoked;
}
// Token being vested
// TODO: Add state variables
+ IERC20 public token;
// Mapping from beneficiary to vesting schedule
// TODO: Add state variables
+ mapping(address => VestingSchedule) public vestingSchedules;
// Whitelist of beneficiaries
// TODO: Add state variables
+ mapping(address => bool) public whitelist;
// Events
event VestingScheduleCreated(address indexed beneficiary, uint256 amount);
@@ -52,6 +61,7 @@ contract TokenVesting is Ownable(msg.sender), Pausable, ReentrancyGuard {
constructor(address tokenAddress) {
// TODO: Initialize the contract
+ token = IERC20(tokenAddress);
}
@@ -80,21 +90,92 @@ contract TokenVesting is Ownable(msg.sender), Pausable, ReentrancyGuard {
uint256 startTime
) external onlyOwner onlyWhitelisted(beneficiary) whenNotPaused {
// TODO: Implement vesting schedule creation
+ require(vestingSchedules[beneficiary].totalAmount == 0, "Schedule already exists");
+ require(amount > 0, "Amount is zero");
+ require(vestingDuration > 0, "Duration is zero");
+ require(cliffDuration < vestingDuration, "Cliff is too long");
+
+ VestingSchedule memory schedule = VestingSchedule({
+ totalAmount: amount,
+ startTime: startTime,
+ cliffDuration: cliffDuration,
+ vestingDuration: vestingDuration,
+ amountClaimed: 0,
+ revoked: false
+ });
+
+ vestingSchedules[beneficiary] = schedule;
+
+ require(token.transferFrom(owner(), address(this), amount), "Token transfer failed");
+
+ emit VestingScheduleCreated(beneficiary, amount);
}
function calculateVestedAmount(
address beneficiary
) public view returns (uint256) {
// TODO: Implement vested amount calculation
+ VestingSchedule memory schedule = vestingSchedules[beneficiary];
+
+ if (schedule.totalAmount == 0 || schedule.revoked) {
+ return 0;
+ }
+
+ if (block.timestamp < schedule.startTime + schedule.cliffDuration) {
+ return 0;
+ }
+
+ if (block.timestamp >= schedule.startTime + schedule.vestingDuration) {
+ return schedule.totalAmount;
+ }
+
+ uint256 timeFromStart = block.timestamp - schedule.startTime;
+
+ uint256 vestedAmount = (schedule.totalAmount * timeFromStart) / schedule.vestingDuration;
+
+ return vestedAmount;
+
}
function claimVestedTokens() external nonReentrant whenNotPaused {
// TODO: Implement token claiming
+ VestingSchedule storage schedule = vestingSchedules[msg.sender];
+
+ require(schedule.totalAmount > 0, "No schedule found");
+ require(!schedule.revoked, "Vesting revoked");
+
+ uint256 vestedAmount = calculateVestedAmount(msg.sender);
+ uint256 claimableAmount = vestedAmount - schedule.amountClaimed;
+
+ require(claimableAmount > 0, "No tokens to claim");
+
+ schedule.amountClaimed = vestedAmount;
+
+ require(token.transfer(msg.sender, claimableAmount), "Token transfer failed");
+
+ emit TokensClaimed(msg.sender, claimableAmount);
+
}
function revokeVesting(address beneficiary) external onlyOwner {
// TODO: Implement vesting revocation
+ VestingSchedule storage schedule = vestingSchedules[beneficiary];
+
+ require(schedule.totalAmount > 0, "No schedule found");
+ require(!schedule.revoked, "Already revoked");
+
+ uint256 vestedAmount = calculateVestedAmount(beneficiary);
+ uint256 unclaimedAmount = schedule.totalAmount - vestedAmount;
+
+ schedule.revoked = true;
+
+ if (unclaimedAmount > 0) {
+ require(token.transfer(owner(), unclaimedAmount), "Token transfer failed");
+ }
+
+ emit VestingRevoked(beneficiary);
+
}
function pause() external onlyOwner {
diff --git a/challenge-1-vesting/hardhat.config.ts b/challenge-1-vesting/hardhat.config.ts
index 24ee97a..f3cba0b 100644
--- a/challenge-1-vesting/hardhat.config.ts
+++ b/challenge-1-vesting/hardhat.config.ts
@@ -7,7 +7,7 @@ const config: HardhatUserConfig = {
solidity: {
version: "0.8.20",
settings: {
- optimizer: {
+ optimizer: {
enabled: true,
runs: 1, // Lower optimization runs for simpler bytecode
},
diff --git a/challenge-2-yield-farm/contracts/.vscode/settings.json b/challenge-2-yield-farm/contracts/.vscode/settings.json
new file mode 100644
index 0000000..cd573be
--- /dev/null
+++ b/challenge-2-yield-farm/contracts/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "wake.compiler.solc.remappings": []
+}
\ No newline at end of file
diff --git a/challenge-2-yield-farm/contracts/.wake/extension/local-local-chain-365d6f08-2272-4784-816b-d579d3778672.json b/challenge-2-yield-farm/contracts/.wake/extension/local-local-chain-365d6f08-2272-4784-816b-d579d3778672.json
new file mode 100644
index 0000000..4d13452
--- /dev/null
+++ b/challenge-2-yield-farm/contracts/.wake/extension/local-local-chain-365d6f08-2272-4784-816b-d579d3778672.json
@@ -0,0 +1 @@
+{"type":"local_node","id":"local-chain-365d6f08-2272-4784-816b-d579d3778672","displayName":"Local Chain 1","state":{"accounts":[{"address":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","balance":1e+22},{"address":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8","balance":1e+22},{"address":"0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc","balance":1e+22},{"address":"0x90f79bf6eb2c4f870365e785982e1f101e93b906","balance":1e+22},{"address":"0x15d34aaf54267db7d7c367839aaf71a00a2c6a65","balance":1e+22},{"address":"0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc","balance":1e+22},{"address":"0x976ea74026e726554db657fa54763abd0c3a0aa9","balance":1e+22},{"address":"0x14dc79964da2c08b23698b3d3cc7ca32193d9955","balance":1e+22},{"address":"0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f","balance":1e+22},{"address":"0xa0ee7a142d267c1f36714e4a8f75612f20a79720","balance":1e+22}],"deployment":[],"history":[]},"network":{"wakeDump":{"metadata":{"labels":{},"deployedLibraries":{}},"chainDump":"0x1f8b08000000000000ffed56cd6a24470c7e973efb507faa1f1f4dc8e6904308ec292c834a52d94d7abacd743beb60e6dda39e193bd80c4b96f425901a18aa5555fa2495ea935eba3a4cf47b77fbd28d4ffb2a87eeb633cfa6bbe968eac78ab39c05ff70e8b9a5dfcbbce0fef174d0aae41ee7ddd0effbe52ca1423eaf3b57ed4dce00be16243c29e0beb59e9e86e5cf375b1e0ff2c70147c6e9bbacf98695ea75ddc933c93cef56fb54f9eef1d093ac91b8c84f7b74b1bb359703fa71d9648fc79b0e89a6a77199d733ea58604aa5c4c0e8c8e4ea7c2cb97af64489d03b5b3c9702708af534ae5a56bd38e069ae1a9c155bc450a9585d783595263e2feb7c5ea603deaf46aef88a09ec036283e062e29a38918f29fba2b264359e6a4ac4b829a6fa65b3642bbe01714b0a4e9e23e8ac92c1daa0aad2dcb6c4f4140231738d588c69e82a64607645d578b6ae6170c557da123308941a420ea9fa542065c8b938439872abcd4532351488dfc07c8790dabf1bea683436aadd59ff9dc9cea76c3c64a7b3d0201b0b16a2f1aba936bbc650b3ee020766fde9e1a0a7a9f92bbe26534a2ac910d86c1d9347630d2536b68211c336adb94d79cbf816d352a92d4a75145a4eea1f8846b96427b65963456fb498b829668a8229181725b9081034a320358490a2c7ca66f51cb16c8a5914c824b60850895c2c40902bdad834b138db520d2a776c89894624a10d8e9517c83625051b24606e09a23e176750afdb992d319b2f8da38045e49c5b6c812462cdd925a7b9a3295c8b7371b3fb54d0aa9566772a60bb0fd5eb245462feeda57b10e475e5a57bc4838ccb4f383f6c5546e607f49f471a643ed736260a2c9985127042f51e2ac4542311078becad0b606b0959b3d0fa66d63b6a1c0c87e243528dfb7e7c75e33bcc5870915fa769d9caaf45abed8cb4f4d338bfe985a8acdf6cb29ac500185bcb3e80c4a2e5ae65bdf91ab2286368c2133219652be75a05f1d1d7e0d656e02024fde3326f69eb30ddcf77c334ed3751f8fff86f8feb0de40776d03eeee72b0da98a3fcfc26fdbdeb7b23149246faa2ec8b33e901f70c1575edaf7cf5bd2ca851eaf28bbb4cd3f8afc22874f387f6c9ebff6cb031ff02b0e9bbedbb5fbfdf4213ae72ef9eebcf277ab7ee2d83b419ac6bb9584b77beac7f7bca4e4fee5a69bf67b399ce6c72fd7d61ffab566f484c3eec4922a1e9f86e1f817dd6f73c4f90c0000"},"type":"Local Chain","config":{"sessionId":"local-chain-365d6f08-2272-4784-816b-d579d3778672","type":"anvil","uri":"ws://127.0.0.1:35161"}},"stateFingerprint":"3a82e3a6d1ac2e4daae286769e6107faa655d321408b0b1f9df23ede1a63b531","persistence":{"isDirty":false,"isAutosaveEnabled":true,"lastSaveTimestamp":1743176468522}}
\ No newline at end of file
diff --git a/challenge-2-yield-farm/contracts/yeild.sol b/challenge-2-yield-farm/contracts/yeild.sol
index 421496a..3bd8582 100644
--- a/challenge-2-yield-farm/contracts/yeild.sol
+++ b/challenge-2-yield-farm/contracts/yeild.sol
@@ -71,6 +71,12 @@ contract YieldFarm is ReentrancyGuard, Ownable {
uint256 _rewardRate
) Ownable(msg.sender) {
// TODO: Initialize contract state
+
+ lpToken = IERC20(_lpToken);
+ rewardToken = IERC20(_rewardToken);
+ rewardRate = _rewardRate;
+
+ lastUpdateTime = block.timestamp;
}
function updateReward(address _user) internal {
@@ -90,6 +96,16 @@ contract YieldFarm is ReentrancyGuard, Ownable {
// 1. Calculate rewards since last update
// 2. Apply boost multiplier
// 3. Return total pending rewards
+
+ if (totalStaked ==0){
+ return rewardPerTokenStored;
+ }
+
+ uint256 timeSinceLastUpdate = block.timestamp - lastUpdateTime;
+ uint256 rewardsSinceLastUpdate = timeSinceLastUpdate * rewardRate;
+ uint256 totalRewards = rewardPerTokenStored + (rewardsSinceLastUpdate * 1e18) / totalStaked;
+
+ return totalRewards;
}
function earned(address _user) public view returns (uint256) {
@@ -98,6 +114,14 @@ contract YieldFarm is ReentrancyGuard, Ownable {
// 1. Calculate rewards since last update
// 2. Apply boost multiplier
// 3. Return total pending rewards
+
+ UserInfo storage user = userInfo[_user];
+
+
+ uint256 userRewardPerToken = rewardPerToken();
+ uint256 rewards = (user.amount * userRewardPerToken) / 1e18;
+
+ return rewards - user.rewardDebt + user.pendingRewards;
}
/**
@@ -111,6 +135,19 @@ contract YieldFarm is ReentrancyGuard, Ownable {
// 2. Transfer LP tokens from user
// 3. Update user info and total staked amount
// 4. Emit Staked event
+
+ updateReward(msg.sender);
+
+ lpToken.transferFrom(msg.sender, address(this), _amount);
+
+ require(_amount > 0, "Cannot stake 0");
+
+ UserInfo storage user = userInfo[msg.sender];
+ user.amount += _amount;
+ user.startTime = block.timestamp;
+ totalStaked += _amount;
+
+ emit Staked(msg.sender, _amount);
}
/**
@@ -124,6 +161,19 @@ contract YieldFarm is ReentrancyGuard, Ownable {
// 2. Transfer LP tokens to user
// 3. Update user info and total staked amount
// 4. Emit Withdrawn event
+
+ updateReward(msg.sender);
+
+ UserInfo storage user = userInfo[msg.sender];
+ require(user.amount >= _amount, "Insufficient balance");
+
+ user.amount -= _amount;
+ user.rewardDebt = (user.amount * rewardPerTokenStored) / 1e18;
+ totalStaked -= _amount;
+
+ lpToken.transfer(msg.sender, _amount);
+
+ emit Withdrawn(msg.sender, _amount);
}
/**
@@ -136,6 +186,17 @@ contract YieldFarm is ReentrancyGuard, Ownable {
// 2. Transfer rewards to user
// 3. Update user reward debt
// 4. Emit RewardsClaimed event
+
+ updateReward(msg.sender);
+
+ UserInfo storage user = userInfo[msg.sender];
+ uint256 rewards = user.pendingRewards;
+ user.pendingRewards = 0;
+ user.rewardDebt = (user.amount * rewardPerTokenStored) / 1e18;
+
+ rewardToken.transfer(msg.sender, rewards);
+
+ emit RewardsClaimed(msg.sender, rewards);
}
/**
@@ -147,6 +208,19 @@ contract YieldFarm is ReentrancyGuard, Ownable {
// 1. Transfer all LP tokens back to user
// 2. Reset user info
// 3. Emit EmergencyWithdrawn event
+
+ UserInfo storage user = userInfo[msg.sender];
+ uint256 amount = user.amount;
+ user.amount = 0;
+ user.startTime = 0;
+ user.rewardDebt = 0;
+ user.pendingRewards = 0;
+ totalStaked -= amount;
+
+ lpToken.transfer(msg.sender, amount);
+
+ emit EmergencyWithdrawn(msg.sender, amount);
+
}
/**
@@ -161,6 +235,19 @@ contract YieldFarm is ReentrancyGuard, Ownable {
// Requirements:
// 1. Calculate staking duration
// 2. Return appropriate multiplier based on duration thresholds
+
+ UserInfo storage user = userInfo[_user];
+ uint256 stakingDuration = block.timestamp - user.startTime;
+
+ if (stakingDuration >= BOOST_THRESHOLD_3){
+ return 200;
+ } else if (stakingDuration >= BOOST_THRESHOLD_2){
+ return 150;
+ } else if (stakingDuration >= BOOST_THRESHOLD_1){
+ return 125;
+ } else {
+ return 100;
+ }
}
/**
@@ -172,6 +259,9 @@ contract YieldFarm is ReentrancyGuard, Ownable {
// Requirements:
// 1. Update rewards before changing rate
// 2. Set new reward rate
+
+ updateReward(address(0));
+ rewardRate = _newRate;
}
/**
diff --git a/challenge-2-yield-farm/hardhat.config.ts b/challenge-2-yield-farm/hardhat.config.ts
index 24ee97a..f3cba0b 100644
--- a/challenge-2-yield-farm/hardhat.config.ts
+++ b/challenge-2-yield-farm/hardhat.config.ts
@@ -7,7 +7,7 @@ const config: HardhatUserConfig = {
solidity: {
version: "0.8.20",
settings: {
- optimizer: {
+ optimizer: {
enabled: true,
runs: 1, // Lower optimization runs for simpler bytecode
},
diff --git a/challenge-2-yield-farm/package-lock.json b/challenge-2-yield-farm/package-lock.json
index 4cbdfb0..e224581 100644
--- a/challenge-2-yield-farm/package-lock.json
+++ b/challenge-2-yield-farm/package-lock.json
@@ -9,7 +9,8 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
- "@openzeppelin/contracts": "^5.1.0"
+ "@openzeppelin/contracts": "^5.1.0",
+ "dotenv": "^16.4.7"
},
"devDependencies": {
"@nomicfoundation/hardhat-toolbox": "^5.0.0",
@@ -3383,6 +3384,18 @@
"node": ">=8"
}
},
+ "node_modules/dotenv": {
+ "version": "16.4.7",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
+ "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
"node_modules/dunder-proto": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.0.tgz",
diff --git a/challenge-2-yield-farm/package.json b/challenge-2-yield-farm/package.json
index 5366ac8..99f1f7e 100644
--- a/challenge-2-yield-farm/package.json
+++ b/challenge-2-yield-farm/package.json
@@ -10,13 +10,14 @@
"license": "ISC",
"description": "",
"volta": {
- "node": "22.12.0"
+ "node": "22.14.0"
},
"devDependencies": {
"@nomicfoundation/hardhat-toolbox": "^5.0.0",
"hardhat": "^2.22.17"
},
"dependencies": {
- "@openzeppelin/contracts": "^5.1.0"
+ "@openzeppelin/contracts": "^5.1.0",
+ "dotenv": "^16.4.7"
}
}
diff --git a/challenge-3-frontend/app/page.tsx b/challenge-3-frontend/app/page.tsx
index fb01185..36a9963 100644
--- a/challenge-3-frontend/app/page.tsx
+++ b/challenge-3-frontend/app/page.tsx
@@ -18,16 +18,16 @@ export default function Home() {
Wallet
- Send transaction
+ Vest Tokens
-
- Write contract
+
+ Add Vesting Beneficiaries
- Mint/Redeem LST Bifrost
-
+ Remove Vesting Beneficiaries
+
- */}