Foundry Successfully Added
CI / Foundry project (push) Has been cancelled

This commit is contained in:
2024-08-13 09:45:57 +08:00
parent 5db39d8ed2
commit 8f8b7f880d
7 changed files with 376 additions and 7 deletions
+3 -3
View File
@@ -77,14 +77,14 @@ contract Raffle is VRFConsumerBaseV2Plus, AutomationCompatibleInterface {
/* Function */
constructor(
address vrfCoordinatorV2,
address vrfCoordinatorV2_5,
uint256 subscriptionId,
bytes32 gasLane, // keyHash
uint256 interval,
uint256 entranceFee,
uint32 callbackGasLimit
) VRFConsumerBaseV2Plus(vrfCoordinatorV2) {
i_vrfCoordinator = IVRFCoordinatorV2Plus(vrfCoordinatorV2);
) VRFConsumerBaseV2Plus(vrfCoordinatorV2_5) {
i_vrfCoordinator = IVRFCoordinatorV2Plus(vrfCoordinatorV2_5);
i_gasLane = gasLane;
i_interval = interval;
i_subscriptionId = subscriptionId;
+8 -3
View File
@@ -4,11 +4,16 @@ out = "out"
libs = ["lib"]
remappings = [
"foundry-devops/=lib/foundry-devops/src/",
'@chainlink/contracts/=lib/chainlink-brownie-contracts/contracts/',
'@solmate=lib/solmate/src/',
"@chainlink/=lib/chainlink/contracts/src/v0.8/",
"hardhat/=./contracts/"
"@chainlink/=lib/chainlink/contracts/src/v0.8/"
]
[etherscan]
sepolia = {key = "${ETHERSCAN_API_KEY}"}
[profile.test]
[rpc_endpoints]
sepolia = "${SEPOLIA_RPC_URL}"
# See more config options https://github.com/foundry-rs/foundry/tree/master/config
+1 -1
View File
@@ -101,7 +101,7 @@ module.exports = {
solidity: {
compilers: [
{
version: "0.8.19",
version: "0.8.18",
},
],
},
+51
View File
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script} from "forge-std/Script.sol";
import {HelperConfig} from "./HelperConfig.s.sol";
import {Raffle} from "../contracts/Raffle.sol";
import {AddConsumer, CreateSubscription, FundSubscription} from "./Interactions.s.sol";
contract DeployRaffle is Script {
function run() external returns (Raffle, HelperConfig) {
HelperConfig helperConfig = new HelperConfig(); // This comes with our mocks!
AddConsumer addConsumer = new AddConsumer();
HelperConfig.NetworkConfig memory config = helperConfig.getConfig();
if (config.subscriptionId == 0) {
CreateSubscription createSubscription = new CreateSubscription();
(config.subscriptionId, config.vrfCoordinatorV2_5) = createSubscription
.createSubscription(config.vrfCoordinatorV2_5, config.account);
FundSubscription fundSubscription = new FundSubscription();
fundSubscription.fundSubscription(
config.vrfCoordinatorV2_5,
config.subscriptionId,
config.link,
config.account
);
helperConfig.setConfig(block.chainid, config);
}
vm.startBroadcast(config.account);
Raffle raffle = new Raffle(
config.vrfCoordinatorV2_5,
config.subscriptionId,
config.gasLane,
config.automationUpdateInterval,
config.raffleEntranceFee,
config.callbackGasLimit
);
vm.stopBroadcast();
// We already have a broadcast in here
addConsumer.addConsumer(
address(raffle),
config.vrfCoordinatorV2_5,
config.subscriptionId,
config.account
);
return (raffle, helperConfig);
}
}
+132
View File
@@ -0,0 +1,132 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {LinkToken} from "../test/mocks/LinkToken.sol";
import {Script, console2} from "forge-std/Script.sol";
import {VRFCoordinatorV2_5Mock} from "@chainlink/contracts/src/v0.8/vrf/mocks/VRFCoordinatorV2_5Mock.sol";
abstract contract CodeConstants {
uint96 public MOCK_BASE_FEE = 0.25 ether;
uint96 public MOCK_GAS_PRICE_LINK = 1e9;
// LINK / ETH price
int256 public MOCK_WEI_PER_UINT_LINK = 4e15;
address public FOUNDRY_DEFAULT_SENDER = 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38;
uint256 public constant ETH_SEPOLIA_CHAIN_ID = 11155111;
uint256 public constant ETH_MAINNET_CHAIN_ID = 1;
uint256 public constant LOCAL_CHAIN_ID = 31337;
}
contract HelperConfig is CodeConstants, Script {
/*//////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
error HelperConfig__InvalidChainId();
/*//////////////////////////////////////////////////////////////
TYPES
//////////////////////////////////////////////////////////////*/
struct NetworkConfig {
uint256 subscriptionId;
bytes32 gasLane;
uint256 automationUpdateInterval;
uint256 raffleEntranceFee;
uint32 callbackGasLimit;
address vrfCoordinatorV2_5;
address link;
address account;
}
/*//////////////////////////////////////////////////////////////
STATE VARIABLES
//////////////////////////////////////////////////////////////*/
// Local network state variables
NetworkConfig public localNetworkConfig;
mapping(uint256 chainId => NetworkConfig) public networkConfigs;
/*//////////////////////////////////////////////////////////////
FUNCTIONS
//////////////////////////////////////////////////////////////*/
constructor() {
networkConfigs[ETH_SEPOLIA_CHAIN_ID] = getSepoliaEthConfig();
networkConfigs[ETH_MAINNET_CHAIN_ID] = getMainnetEthConfig();
// Note: We skip doing the local config
}
function getConfig() public returns (NetworkConfig memory) {
return getConfigByChainId(block.chainid);
}
function setConfig(uint256 chainId, NetworkConfig memory networkConfig) public {
networkConfigs[chainId] = networkConfig;
}
function getConfigByChainId(uint256 chainId) public returns (NetworkConfig memory) {
if (networkConfigs[chainId].vrfCoordinatorV2_5 != address(0)) {
return networkConfigs[chainId];
} else if (chainId == LOCAL_CHAIN_ID) {
return getOrCreateAnvilEthConfig();
} else {
revert HelperConfig__InvalidChainId();
}
}
function getMainnetEthConfig() public pure returns (NetworkConfig memory mainnetNetworkConfig) {
mainnetNetworkConfig = NetworkConfig({
subscriptionId: 0, // If left as 0, our scripts will create one!
gasLane: 0x9fe0eebf5e446e3c998ec9bb19951541aee00bb90ea201ae456421a2ded86805,
automationUpdateInterval: 30, // 30 seconds
raffleEntranceFee: 0.01 ether,
callbackGasLimit: 500000, // 500,000 gas
vrfCoordinatorV2_5: 0x271682DEB8C4E0901D1a1550aD2e64D568E69909,
link: 0x514910771AF9Ca656af840dff83E8264EcF986CA,
account: 0x643315C9Be056cDEA171F4e7b2222a4ddaB9F88D
});
}
function getSepoliaEthConfig() public pure returns (NetworkConfig memory sepoliaNetworkConfig) {
sepoliaNetworkConfig = NetworkConfig({
subscriptionId: 0, // If left as 0, our scripts will create one!
gasLane: 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae,
automationUpdateInterval: 30, // 30 seconds
raffleEntranceFee: 0.01 ether,
callbackGasLimit: 500000, // 500,000 gas
vrfCoordinatorV2_5: 0x9DdfaCa8183c41ad55329BdeeD9F6A8d53168B1B,
link: 0x779877A7B0D9E8603169DdbD7836e478b4624789,
account: 0x643315C9Be056cDEA171F4e7b2222a4ddaB9F88D
});
}
function getOrCreateAnvilEthConfig() public returns (NetworkConfig memory) {
// Check to see if we set an active network config
if (localNetworkConfig.vrfCoordinatorV2_5 != address(0)) {
return localNetworkConfig;
}
console2.log(unicode"⚠️ You have deployed a mock conract!");
console2.log("Make sure this was intentional");
vm.startBroadcast();
VRFCoordinatorV2_5Mock vrfCoordinatorV2_5Mock = new VRFCoordinatorV2_5Mock(
MOCK_BASE_FEE,
MOCK_GAS_PRICE_LINK,
MOCK_WEI_PER_UINT_LINK
);
LinkToken link = new LinkToken();
uint256 subscriptionId = vrfCoordinatorV2_5Mock.createSubscription();
vm.stopBroadcast();
localNetworkConfig = NetworkConfig({
subscriptionId: subscriptionId,
gasLane: 0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c, // doesn't really matter
automationUpdateInterval: 30, // 30 seconds
raffleEntranceFee: 0.01 ether,
callbackGasLimit: 500000, // 500,000 gas
vrfCoordinatorV2_5: address(vrfCoordinatorV2_5Mock),
link: address(link),
account: FOUNDRY_DEFAULT_SENDER
});
vm.deal(localNetworkConfig.account, 100 ether);
return localNetworkConfig;
}
}
+122
View File
@@ -0,0 +1,122 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {Script, console} from "forge-std/Script.sol";
import {HelperConfig} from "./HelperConfig.s.sol";
import {Raffle} from "../contracts/Raffle.sol";
// import {DevOpsTools} from "foundry-devops/src/DevOpsTools.sol"; // Deprecated
import {DevOpsTools} from "foundry-devops/DevOpsTools.sol";
import {VRFCoordinatorV2_5Mock} from "@chainlink/contracts/src/v0.8/vrf/mocks/VRFCoordinatorV2_5Mock.sol";
import {LinkToken} from "../test/mocks/LinkToken.sol";
import {CodeConstants} from "./HelperConfig.s.sol";
contract CreateSubscription is Script {
function createSubscriptionUsingConfig() public returns (uint256, address) {
HelperConfig helperConfig = new HelperConfig();
address vrfCoordinatorV2_5 = helperConfig
.getConfigByChainId(block.chainid)
.vrfCoordinatorV2_5;
address account = helperConfig.getConfigByChainId(block.chainid).account;
return createSubscription(vrfCoordinatorV2_5, account);
}
function createSubscription(
address vrfCoordinatorV2_5,
address account
) public returns (uint256, address) {
console.log("Creating subscription on chainId: ", block.chainid);
vm.startBroadcast(account);
uint256 subId = VRFCoordinatorV2_5Mock(vrfCoordinatorV2_5).createSubscription();
vm.stopBroadcast();
console.log("Your subscription Id is: ", subId);
console.log("Please update the subscriptionId in HelperConfig.s.sol");
return (subId, vrfCoordinatorV2_5);
}
function run() external returns (uint256, address) {
return createSubscriptionUsingConfig();
}
}
contract AddConsumer is Script {
function addConsumer(
address contractToAddToVrf,
address vrfCoordinator,
uint256 subId,
address account
) public {
console.log("Adding consumer contract: ", contractToAddToVrf);
console.log("Using vrfCoordinator: ", vrfCoordinator);
console.log("On ChainID: ", block.chainid);
vm.startBroadcast(account);
VRFCoordinatorV2_5Mock(vrfCoordinator).addConsumer(subId, contractToAddToVrf);
vm.stopBroadcast();
}
function addConsumerUsingConfig(address mostRecentlyDeployed) public {
HelperConfig helperConfig = new HelperConfig();
uint256 subId = helperConfig.getConfig().subscriptionId;
address vrfCoordinatorV2_5 = helperConfig.getConfig().vrfCoordinatorV2_5;
address account = helperConfig.getConfig().account;
addConsumer(mostRecentlyDeployed, vrfCoordinatorV2_5, subId, account);
}
function run() external {
address mostRecentlyDeployed = DevOpsTools.get_most_recent_deployment(
"Raffle",
block.chainid
);
addConsumerUsingConfig(mostRecentlyDeployed);
}
}
contract FundSubscription is CodeConstants, Script {
uint96 public constant FUND_AMOUNT = 3 ether;
function fundSubscriptionUsingConfig() public {
HelperConfig helperConfig = new HelperConfig();
uint256 subId = helperConfig.getConfig().subscriptionId;
address vrfCoordinatorV2_5 = helperConfig.getConfig().vrfCoordinatorV2_5;
address link = helperConfig.getConfig().link;
address account = helperConfig.getConfig().account;
if (subId == 0) {
CreateSubscription createSub = new CreateSubscription();
(uint256 updatedSubId, address updatedVRFv2) = createSub.run();
subId = updatedSubId;
vrfCoordinatorV2_5 = updatedVRFv2;
console.log("New SubId Created! ", subId, "VRF Address: ", vrfCoordinatorV2_5);
}
fundSubscription(vrfCoordinatorV2_5, subId, link, account);
}
function fundSubscription(
address vrfCoordinatorV2_5,
uint256 subId,
address link,
address account
) public {
console.log("Funding subscription: ", subId);
console.log("Using vrfCoordinator: ", vrfCoordinatorV2_5);
console.log("On ChainID: ", block.chainid);
if (block.chainid == LOCAL_CHAIN_ID) {
vm.startBroadcast(account);
VRFCoordinatorV2_5Mock(vrfCoordinatorV2_5).fundSubscription(subId, FUND_AMOUNT);
vm.stopBroadcast();
} else {
console.log(LinkToken(link).balanceOf(msg.sender));
console.log(msg.sender);
console.log(LinkToken(link).balanceOf(address(this)));
console.log(address(this));
vm.startBroadcast(account);
LinkToken(link).transferAndCall(vrfCoordinatorV2_5, FUND_AMOUNT, abi.encode(subId));
vm.stopBroadcast();
}
}
function run() external {
fundSubscriptionUsingConfig();
}
}
+59
View File
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: MIT
// @dev This contract has been adapted to fit with foundry
pragma solidity ^0.8.0;
import {ERC20} from "@solmate/tokens/ERC20.sol";
interface ERC677Receiver {
function onTokenTransfer(address _sender, uint256 _value, bytes memory _data) external;
}
contract LinkToken is ERC20 {
uint256 constant INITIAL_SUPPLY = 1000000000000000000000000;
uint8 constant DECIMALS = 18;
constructor() ERC20("LinkToken", "LINK", DECIMALS) {
_mint(msg.sender, INITIAL_SUPPLY);
}
function mint(address to, uint256 value) public {
_mint(to, value);
}
event Transfer(address indexed from, address indexed to, uint256 value, bytes data);
/**
* @dev transfer token to a contract address with additional data if the recipient is a contact.
* @param _to The address to transfer to.
* @param _value The amount to be transferred.
* @param _data The extra data to be passed to the receiving contract.
*/
function transferAndCall(
address _to,
uint256 _value,
bytes memory _data
) public virtual returns (bool success) {
super.transfer(_to, _value);
// emit Transfer(msg.sender, _to, _value, _data);
emit Transfer(msg.sender, _to, _value, _data);
if (isContract(_to)) {
contractFallback(_to, _value, _data);
}
return true;
}
// PRIVATE
function contractFallback(address _to, uint256 _value, bytes memory _data) private {
ERC677Receiver receiver = ERC677Receiver(_to);
receiver.onTokenTransfer(msg.sender, _value, _data);
}
function isContract(address _addr) private view returns (bool hasCode) {
uint256 length;
assembly {
length := extcodesize(_addr)
}
return length > 0;
}
}