Skip to content

Commit

Permalink
Final deploy update
Browse files Browse the repository at this point in the history
- Added Avalanche Fuji to HelperConfig
- Added Router and DonId (Chainlink Functions) to NetworkConfig
- Updated DeployRewardManager to use HelperConfig
- Added receive() and fallback()
- Added Natspec
  • Loading branch information
Wakushi committed Dec 3, 2023
1 parent 9a889b9 commit 3f2c3aa
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 19 deletions.
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ ifeq ($(findstring --network sepolia,$(ARGS)),--network sepolia)
NETWORK_ARGS := --rpc-url $(SEPOLIA_RPC_URL) --private-key $(PRIVATE_KEY) --broadcast --verify --etherscan-api-key $(ETHERSCAN_API_KEY) -vvvv
endif

ifeq ($(findstring --network fuji,$(ARGS)),--network fuji)
NETWORK_ARGS := --rpc-url $(FUJI_RPC_URL) --private-key $(PRIVATE_KEY) --broadcast --verifier-url 'https://api.routescan.io/v2/network/testnet/evm/43113/etherscan' --etherscan-api-key "verifyContract" -vvvv
endif

deploy:
@forge script script/DeployDEVMentor.s.sol:DeployDEVMentor $(NETWORK_ARGS)

Expand Down
4 changes: 3 additions & 1 deletion script/DeployDEVMentor.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ contract DeployDEVMentor is Script {
uint32 callbackGasLimit,
address link,
uint256 deployerKey,
address priceFeed
address priceFeed,
,

) = helperConfig.activeNetworkConfig();

if (subscriptionId == 0) {
Expand Down
7 changes: 5 additions & 2 deletions script/DeployRewardManager.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ pragma solidity ^0.8.18;

import {Script} from "forge-std/Script.sol";
import {RewardManager} from "../src/RewardManager.sol";
import {HelperConfig} from "./HelperConfig.s.sol";

contract DeployRewardManager is Script {
function run() external returns (RewardManager) {
string memory baseUri = "https://tan-key-moth-8.mypinata.cloud/ipfs/";
address router = 0xb83E47C2bC239B3bf370bc41e1459A34b41238D0;
bytes32 donId = bytes32("fun-ethereum-sepolia-1");

HelperConfig helperConfig = new HelperConfig();
(, , , , , , , address router, bytes32 donId) = helperConfig
.activeNetworkConfig();

vm.startBroadcast();
RewardManager rewardManager = new RewardManager(baseUri, router, donId);
Expand Down
27 changes: 25 additions & 2 deletions script/HelperConfig.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ contract HelperConfig is Script {
address link;
uint256 deployerKey;
address priceFeed;
address router;
bytes32 donId;
}

uint256 public constant DEFAULT_ANVIL_KEY =
Expand All @@ -27,6 +29,8 @@ contract HelperConfig is Script {
constructor() {
if (block.chainid == 11155111) {
activeNetworkConfig = getSepoliaEthConfig();
} else if (block.chainid == 43113) {
activeNetworkConfig = getFujiAvaxConfig();
} else {
activeNetworkConfig = getOrCreateAnvilEthConfig();
}
Expand All @@ -41,7 +45,24 @@ contract HelperConfig is Script {
callbackGasLimit: 500000,
link: 0x779877A7B0D9E8603169DdbD7836e478b4624789,
deployerKey: vm.envUint("PRIVATE_KEY"),
priceFeed: 0x694AA1769357215DE4FAC081bf1f309aDC325306
priceFeed: 0x694AA1769357215DE4FAC081bf1f309aDC325306,
router: 0xb83E47C2bC239B3bf370bc41e1459A34b41238D0,
donId: bytes32("fun-ethereum-sepolia-1")
});
}

function getFujiAvaxConfig() public view returns (NetworkConfig memory) {
return
NetworkConfig({
vrfCoordinator: 0x2eD832Ba664535e5886b75D64C46EB9a228C2610,
gasLane: 0x354d2f95da55398f44b7cff77da56283d9c6c829a4bdf1bbcaf2ad6a4d081f61,
subscriptionId: 0,
callbackGasLimit: 500000,
link: 0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846,
deployerKey: vm.envUint("PRIVATE_KEY"),
priceFeed: 0x86d67c3D38D2bCeE722E601025C25a575021c6EA,
router: 0xA9d587a00A31A52Ed70D6026794a8FC5E2F5dCb0,
donId: bytes32("fun-avalanche-fuji-1")
});
}

Expand Down Expand Up @@ -73,7 +94,9 @@ contract HelperConfig is Script {
callbackGasLimit: 500000,
link: address(link),
deployerKey: DEFAULT_ANVIL_KEY,
priceFeed: address(mockPriceFeed)
priceFeed: address(mockPriceFeed),
router: 0xb83E47C2bC239B3bf370bc41e1459A34b41238D0,
donId: bytes32("fun-ethereum-sepolia-1")
});
}
}
17 changes: 15 additions & 2 deletions script/Interaction.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,17 @@ import {DevOpsTools} from "foundry-devops/src/DevOpsTools.sol";
contract CreateSubscription is Script {
function createSubscriptionUsingConfig() public returns (uint64) {
HelperConfig helperConfig = new HelperConfig();
(address vrfCoordinator, , , , , uint256 deployerKey, ) = helperConfig
.activeNetworkConfig();
(
address vrfCoordinator,
,
,
,
,
uint256 deployerKey,
,
,

) = helperConfig.activeNetworkConfig();
return createSubscription(vrfCoordinator, deployerKey);
}

Expand Down Expand Up @@ -46,6 +55,8 @@ contract FundSubscription is Script {
,
address link,
uint256 deployerKey,
,
,

) = helperConfig.activeNetworkConfig();
fundSubscription(vrfCoordinator, subId, link, deployerKey);
Expand Down Expand Up @@ -107,6 +118,8 @@ contract AddConsumer is Script {
,
,
uint256 deployerKey,
,
,

) = helperConfig.activeNetworkConfig();
addConsumer(devMentor, vrfCoordinator, subId, deployerKey);
Expand Down
83 changes: 73 additions & 10 deletions src/DEVMentor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ import {SessionRegistry} from "./SessionRegistry.sol";
import {PriceConverter} from "./PriceConverter.sol";
import {Languages} from "./Languages.sol";

/**
* @title DEVMentor - A Mentor Matching Platform for Developers built during Chainlink Constellation Hackathon 2023
* @author Makushi
* @notice This contract is the main entry point for the DEVMentor platform
* @dev Implements Chainlink VRF, Chainlink Automation, Chainlink Data Feeds, and Chainlink Functions.
*/
contract DEVMentor is
SessionRegistry,
VRFConsumerBaseV2,
Expand Down Expand Up @@ -80,6 +86,10 @@ contract DEVMentor is
s_priceFeed = AggregatorV3Interface(config.priceFeed);
}

receive() external payable {}

fallback() external payable {}

////////////////////
// External / Public
////////////////////
Expand Down Expand Up @@ -191,6 +201,20 @@ contract DEVMentor is
s_rewardManager.claimReward(msg.sender, rewardId);
}

/**
* @dev Allows mentors to redeem a reward by triggering an external API call via Chainlink Functions DON.
* This function is called by a mentor who wishes to redeem a reward identified by `_rewardId`.
* The function delegates the redemption process to an external `RewardManager` contract (see FunctionsConsumer.sol _sendMailerRequest())
*
* Upon successful execution, an email containing reward information is sent to the user via Chainlink DON.
*
* @param _rewardId The unique identifier of the reward to be redeemed.
* @param _args Additional arguments required for the redemption process. This could include
* information such as the mentor's email address or other relevant details needed to process the reward.
*
* Requirements:
* - The caller must be a registered mentor.
*/
function redeemReward(
uint256 _rewardId,
string[] calldata _args
Expand All @@ -206,11 +230,6 @@ contract DEVMentor is
_addLanguage(_language);
}

function approveMentor(address _mentor) external onlyOwner {
s_registeredMentors[_mentor].validated = true;
s_mentors.push(_mentor);
}

function addReward(
uint256 price,
uint256 totalSupply,
Expand Down Expand Up @@ -252,14 +271,29 @@ contract DEVMentor is
s_rewardManager.setSecretReference(_secretReference);
}

function adminMintXp(address _to, uint256 _amount) external onlyOwner {
/**
* @notice Ownership control modifier removed for Evaluation Purposes by Testing Teams and Hackathon Judges.
* This function is designed to facilitate the approval of mentors for testing purposes.
* Ownership control should be re-added prior to production deployment (using OpenZeppelin Access Control & Roles)
*/
function adminApproveMentor(address _mentor) external {
s_registeredMentors[_mentor].validated = true;
s_mentors.push(_mentor);
}

/**
* @notice For Evaluation Purposes by Testing Teams and Hackathon Judges - Intended for Removal Prior to Production Deployment
* This function is designed to facilitate the minting of XP tokens for testing purposes.
*/
function testMintXp(address _to, uint256 _amount) external {
s_rewardManager.adminMintXp(_to, _amount);
}

function adminMintMentorToken(
address _to,
uint256 _amount
) external onlyOwner {
/**
* @notice For Evaluation Purposes by Testing Teams and Hackathon Judges - Intended for Removal Prior to Production Deployment
* This function is designed to facilitate the minting of Mentor tokens for testing purposes.
*/
function testMintMentorToken(address _to, uint256 _amount) external {
s_rewardManager.adminMintMentorToken(_to, _amount);
}

Expand All @@ -282,6 +316,10 @@ contract DEVMentor is
}
}

/**
* @dev Opens a session for the mentee with the selected mentor.
* We check if the mentee has enough locked value (min. 5$) to open a session using Chainlink Data Feeds.
*/
function _openSessionWithValueLocked(
uint256 _valueLocked,
address _chosenMentor,
Expand Down Expand Up @@ -323,6 +361,17 @@ contract DEVMentor is
}
}

/**
* @dev Requests a random number from Chainlink VRF to select a random mentor
* from the provided list of matching mentors. The request ID is mapped to the
* mentor selection request for later retrieval in `fulfillRandomWords`.
*
* Emits a `MentorSelectionRequestSent` event upon successful request submission.
*
* @param _matchingMentors An array of addresses representing mentors that match
* the mentee's criteria.
* @param _engagement The duration of the mentorship engagement.
*/
function _getRandomMentor(
address[] calldata _matchingMentors,
uint256 _engagement
Expand All @@ -342,6 +391,17 @@ contract DEVMentor is
emit MentorSelectionRequestSent(msg.sender, requestId);
}

/**
* @dev Callback function used by Chainlink VRF to deliver the random number.
* Selects a random mentor from the list of matching mentors based on the random
* number provided. Initiates the mentorship process by matching the selected
* mentor with the mentee.
*
* This function can only be called by the Chainlink VRF coordinator.
*
* @param requestId The request ID that maps to the original mentor selection request.
* @param randomWords An array containing the random number(s) provided by Chainlink VRF.
*/
function fulfillRandomWords(
uint256 requestId,
uint256[] memory randomWords
Expand Down Expand Up @@ -369,6 +429,9 @@ contract DEVMentor is
return s_sessions[_mentee][s_registeredMentees[_mentee].mentor];
}

/**
* @dev Returns the current price of ETH in USD for dApp frontend usage.
*/
function getEthPrice() external view returns (uint256) {
(, int256 price, , , ) = s_priceFeed.latestRoundData();
return uint256(price);
Expand Down
12 changes: 12 additions & 0 deletions src/FunctionsConsumer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ contract FunctionsConsumer is FunctionsClient, Ownable {
"const { data } = apiResponse;"
"return Functions.encodeString(data.accepted);";

/**
* @notice Invokes a Chainlink Functions service to send an API request for emailing users.
* This function is called when a user burns or redeems their reward NFT to receive
* an official coupon or reduction code.
* @dev > Initializes a FunctionsRequest with JavaScript code and uses a DON-hosted location
* for secrets. Relies on an encrypted secret reference hosted on the DON, ensuring
* the server only accepts requests emitted from this contract. Stores the function ID
* of the last request after sending it.
* @param args An array of arguments to be passed to the API call.
* These include the user's email, the reward ID, and an UUID to help the server
* batch node requests and only send one email per user.
*/
function _sendMailerRequest(string[] calldata args) internal {
FunctionsRequest.Request memory req;
req.initializeRequest(
Expand Down
19 changes: 17 additions & 2 deletions src/SessionRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -63,18 +63,33 @@ contract SessionRegistry is MentorRegistry, MenteeRegistry, Ownable {

constructor() Ownable(msg.sender) {}

function adminUpdateSessionEngagement(
/**
* @notice For Evaluation Purposes by Testing Teams and Hackathon Judges - Intended for Removal Prior to Production Deployment
* This function is specifically designed to modify the engagement status of a session, facilitating the validation of session confirmation mechanisms during the testing phase.
*/
function testUpdateSessionEngagement(
address _mentee,
address _mentor,
uint256 _engagement
) external onlyOwner {
) external {
s_sessions[_mentee][_mentor].engagement = _engagement;
}

function setRewardManager(address _manager) external onlyOwner {
s_rewardManager = IRewardManager(_manager);
}

/**
* @dev Iteratively checks for and fulfills pending mentee requests by matching them with available mentors.
* This function is intended to be called by Chainlink Automation on a daily basis.
*
* The function scans through the list of mentees with pending requests. For each mentee, it attempts
* to find matching mentors based on the mentee's subject, engagement, and language preferences.
* If matching mentors are found, the first available mentor is assigned to the mentee, and the request is
* subsequently removed from the pending list.
*
* If no matching mentors are found for a particular request, the function continues to the next request in the list.
*/
function fulfillPendingRequests() external {
if (s_menteeWithRequest.length > 0) {
for (uint256 i = 0; i < s_menteeWithRequest.length; ++i) {
Expand Down

0 comments on commit 3f2c3aa

Please sign in to comment.