smartcontractkit / full-blockchain-solidity-course-py

Ultimate Solidity, Blockchain, and Smart Contract - Beginner to Expert Full Course | Python Edition
MIT License
10.76k stars 2.9k forks source link

ImportError: cannot import name 'LinkToken' from 'brownie' #1651

Closed MohammadCroc closed 2 years ago

MohammadCroc commented 2 years ago

Hello, I'm in the deploy Lottery part of @PatrickAlphaC 's video and after giving the constructor all the parameters such as fee, keyhash and link token, when I'm trying to run my deploy_lottery.py contract I get this error: ImportError: cannot import name 'LinkToken' from 'brownie' which I think the problem is from the LinkToken.sol solidity version, but i couldn't solve it even when I changed my python version or solidity global version I even checked the directory and replaced the file with the file I downloaded from the github repository. could anyone tell what seems to be the problem?

LinkToken.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.4.11;

import "@chainlink/contracts/src/v0.4/ERC677Token.sol";
import {StandardToken as linkStandardToken} from "@chainlink/contracts/src/v0.4/vendor/StandardToken.sol";

contract LinkToken is linkStandardToken, ERC677Token {
    uint256 public constant totalSupply = 10**27;
    string public constant name = "ChainLink Token";
    uint8 public constant decimals = 18;
    string public constant symbol = "LINK";

    function LinkToken() public {
        balances[msg.sender] = totalSupply;
    }

    /**
     * @dev transfer token to a specified address with additional data if the recipient is a contract.
     * @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 _data
    ) public validRecipient(_to) returns (bool success) {
        return super.transferAndCall(_to, _value, _data);
    }

    /**
     * @dev transfer token to a specified address.
     * @param _to The address to transfer to.
     * @param _value The amount to be transferred.
     */
    function transfer(address _to, uint256 _value)
        public
        validRecipient(_to)
        returns (bool success)
    {
        return super.transfer(_to, _value);
    }

    /**
     * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
     * @param _spender The address which will spend the funds.
     * @param _value The amount of tokens to be spent.
     */
    function approve(address _spender, uint256 _value)
        public
        validRecipient(_spender)
        returns (bool)
    {
        return super.approve(_spender, _value);
    }

    /**
     * @dev Transfer tokens from one address to another
     * @param _from address The address which you want to send tokens from
     * @param _to address The address which you want to transfer to
     * @param _value uint256 the amount of tokens to be transferred
     */
    function transferFrom(
        address _from,
        address _to,
        uint256 _value
    ) public validRecipient(_to) returns (bool) {
        return super.transferFrom(_from, _to, _value);
    }

    // MODIFIERS

    modifier validRecipient(address _recipient) {
        require(_recipient != address(0) && _recipient != address(this));
        _;
    }
}

Footer

helpful_scripts.py

from unicodedata import decimal
from brownie import (
    Contract,
    accounts,
    network,
    config,
    MockV3Aggregator,
    VRFCoordinatorMock,
    LinkToken,
)

FORKED_LOCAL_ENVIRONMENTS = ["mainnet-fork-dev", "mainnet-fork"]
LOCAL_BLOCKCHAIN_INVINRONMENTS = ["development", "ganache-local"]

# INDEX = if pass an index get_account() will use accounts[index]
# ID = if we pass an id get_account() will use accounts.load("account_id")
def get_account(index=None, id=None):
    # accounts[0]
    # accounts.add("env")
    # acconts.load("id")
    if index:
        return accounts[index]
    if id:
        return accounts.load(id)
    if (
        network.show_active() in LOCAL_BLOCKCHAIN_INVINRONMENTS
        or network.show_active() in FORKED_LOCAL_ENVIRONMENTS
    ):
        return accounts[0]
    return accounts.add(config["wallets"])["from_key"]

# In this mapping we are sayng for example if you saw "eth_usd_price_feed"
# you know it's type is MockV3Aggregator if we need to deploy a mock
contract_to_mock = {
    "eth_usd_price_feed": MockV3Aggregator,
    "vrf_coordinator": VRFCoordinatorMock,
    "link_token": LinkToken,
}

def get_contract(contract_name):
    """THis function will grab the contract addresses from the brownie config
    if defined, otherwise, it will deploy mock version of that contract and
    return that mock contract.

        Args:
            contract_name (string)
        Returns:
            brownie.network.contract.ProjectContract: The most recently deployed version of this contract.
    """
    contract_type = contract_to_mock[contract_name]
    # we check if we're on a local blockchain
    if network.show_active() in LOCAL_BLOCKCHAIN_INVINRONMENTS:
        # and we'll check if mocks have been deployed or not
        if len(contract_type) <= 0:
            # MockV3aggregator.length
            deploy_mocks()
        contract = contract_type[-1]
        # MockV3Aggregator[-1]
    else:
        # Adrres:
        contract_address = config["networks"][network.show_active()][contract_name]
        # ABI: With the Contract package we can get the contract from it's ABI and it's Address:
        contract = Contract.from_abi(
            contract_type._name, contract_address, contract_type.abi
        )
        # MockV3Aggregator.abi
    return contract

DECIMALS = 8
INITIAL_VALUE = 200000000000

def deploy_mocks(decimals=DECIMALS, initial_value=INITIAL_VALUE):
    account = get_account()
    MockV3Aggregator.deploy(decimals, initial_value, {"from": account})
    link_token = LinkToken.deploy({"from": account})
    VRFCoordinatorMock.deploy(link_token.address, {"from": account})
    print("Deployed!")

Lottery.sol

//SPDX-License-Identifier: MIT

pragma solidity ^0.6.6;

import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@chainlink/contracts/src/v0.6/VRFConsumerBase.sol";

contract Lottery is VRFConsumerBase, Ownable {
    address payable[] public players;
    address payable public recentWinner;
    uint256 public randomness;
    uint256 public usdEntryFee;
    AggregatorV3Interface internal ethUsdPriceFeed;
    // ENUM is a way to create user-defined types in solidity
    // We use unum to declare the steps of our lottery process in the ORDER they have to pass
    enum LOTTERY_STATE {
        OPEN,
        CLOSED,
        CALCULATING_WINNER
    }
    LOTTERY_STATE public lottery_state;
    uint256 public fee;
    bytes32 public keyhash;

    // OPEN = 0
    //CLOSED = 1
    //CALCULATIN_WINNER = 2

    //We can add a inherited constructor varibales into our constructor:
    constructor(
        address _priceFeedAddress,
        address _vrfCoordinator,
        address _link,
        uint256 _fee,
        bytes32 _keyhash
    ) public VRFConsumerBase(_vrfCoordinator, _link) {
        usdEntryFee = 50 * (10**18);
        ethUsdPriceFeed = AggregatorV3Interface(_priceFeedAddress);
        // When we initialize our contract we want our lottery state to be closed so:
        lottery_state = LOTTERY_STATE.CLOSED;
        // we can also say this: lottery_state = 1;
        fee = _fee;
        keyhash = _keyhash;
    }

    function enter() public payable {
        // We can only enter if somebody starts the lottery
        require(lottery_state == LOTTERY_STATE.OPEN);
        // This needs a require for minimum entry fee 50$
        require(msg.value >= getEntranceFee(), "Not enough ETH!");

        players.push(msg.sender);
    }

    function getEntranceFee() public view returns (uint256) {
        // first we're gonna store whats the minimum fee is
        (, int256 price, , , ) = ethUsdPriceFeed.latestRoundData();
        uint256 adjustedPrice = uint256(price) * 10**10; //18 decimals
        //50 * 1000bignumber / 2000
        uint256 costToEnter = (usdEntryFee * 10**18) / adjustedPrice;
        return costToEnter;
    }

    //we could declare a modifier ourselves for the owner only to start the lottery but instead we import openzeplin library
    function startLottery() public onlyOwner {
        require(
            lottery_state == LOTTERY_STATE.CLOSED,
            "Can't start a new lottery yet!"
        );
        lottery_state = LOTTERY_STATE.OPEN;
    }

    function endLottery() public onlyOwner {
        // !ALERT! THIS IS AN UNACCEPTABLE WAY TO GENERATE RANDOM NUMBERS.
        // uint256(
        //     keccack256(
        //         abi.encodePacked(
        //             nonce, //nonce is predictable (aka, transaction number or transaction count)
        //             msg.sender, //msg.sender is predictable
        //             block.difficulty, //difficulty can actually be manipulated by miners!
        //             block.timestamp // timestamp is predictable
        //         ) // So basically the random is not actually random!
        //     )
        // ) % players.length;
        lottery_state = LOTTERY_STATE.CALCULATING_WINNER;
        bytes32 requestId = requestRandomness(keyhash, fee); //this will return a bytes32 called request ID
    }

    // intrenal: because we dont want any one outside of our contract to be able to call thiss function to manipulate it.
    // override: this function is in VRFConsumerBase contract but it doesnt do anything and it has beed made for us
    // to override it and give it parameters

    //??? HOW DO WE GET _RANDOMNESS AND WHERE FROM (i know from vrf but how does it come from vrf contract to our lottery contract) ???
    function fulfillRandomness(bytes32 _requestId, uint256 _randomness)
        internal
        override
    {
        require(
            lottery_state == LOTTERY_STATE.CALCULATING_WINNER,
            "You are not ther yet!"
        );
        require(_randomness > 0, "random-not-found");
        //???
        uint256 indexOfWinner = _randomness % players.length;
        recentWinner = players[indexOfWinner];
        //paying the winner
        recentWinner.transfer(address(this).balance);
        // reset the lottery
        players = new address payable[](0);
        lottery_state = LOTTERY_STATE.CLOSED;
        // keeping track of the random number
        randomness = _randomness;
    }
}

deploy_lottery.py

from brownie import Lottery, network, config
from scripts.helpful_scripts import get_account, get_contract

def deploy_lottery():
    account = get_account()
    # Lottery.deloy(get_contract("eth_usd_price_feed")) : thsi will return the contract but we only need the address
    lottery = Lottery.deloy(
        get_contract("eth_usd_price_feed").address,
        get_contract("vrf_coordinator").address,
        get_contract("link_token").address,
        config["network"][network.show_active()]["fee"],
        config["network"][network.show_active()]["keyhash"],
        {"from": account},
        publish_source=config["networks"][network.show_active()].get("verify", False),
    )
    # here False means if there is no verify set it to False
    print("Deployed Lottery!")

def main():
    deploy_lottery()
`
### brownie-config.yaml
`dependencies:
  - smartcontractkit/chainlink-brownie-contracts@1.1.0
  - OpenZeppelin/openzeppelin-contracts@3.4.0
compiler:
  solc:
    remappings:
      - "@chainlink=smartcontractkit/chainlink-brownie-contracts@1.1.0"
      - "@openzeppelin=OpenZeppelin/openzeppelin-contracts@3.4.0"

networks:
  default: development
  development:
    keyhash: "0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311"
    fee: 100000000000000000
  rinkeby:
    vrf_coordinator: "0xb3dCcb4Cf7a26f6cf6B120Cf5A73875B7BBc655B"
    eth_usd_price_feed: "0x8A753747A1Fa494EC906cE90E9f37563A8AF630e"
    link_token: "0x01BE23585060835E02B77ef475b0Cc51aA1e0709"
    keyhash: "0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311"
    fee: 100000000000000000
    verify: True

  mainnet-fork:
    eth_usd_price_feed: "0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419"
wallets:
  from_key: ${PRIVATE_KEY}

Screenshot (83)