Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save ndirpaya/e2a1dba9d5d5d75aec96f3a4cfb68a78 to your computer and use it in GitHub Desktop.
Save ndirpaya/e2a1dba9d5d5d75aec96f3a4cfb68a78 to your computer and use it in GitHub Desktop.

Revisions

  1. ndirpaya created this gist Feb 25, 2022.
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,195 @@
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;

    import "./interfaces/LinkTokenInterface.sol";

    import "./VRFRequestIDBase.sol";

    /** ****************************************************************************
    * @notice Interface for contracts using VRF randomness
    * *****************************************************************************
    * @dev PURPOSE
    *
    * @dev Reggie the Random Oracle (not his real job) wants to provide randomness
    * @dev to Vera the verifier in such a way that Vera can be sure he's not
    * @dev making his output up to suit himself. Reggie provides Vera a public key
    * @dev to which he knows the secret key. Each time Vera provides a seed to
    * @dev Reggie, he gives back a value which is computed completely
    * @dev deterministically from the seed and the secret key.
    *
    * @dev Reggie provides a proof by which Vera can verify that the output was
    * @dev correctly computed once Reggie tells it to her, but without that proof,
    * @dev the output is indistinguishable to her from a uniform random sample
    * @dev from the output space.
    *
    * @dev The purpose of this contract is to make it easy for unrelated contracts
    * @dev to talk to Vera the verifier about the work Reggie is doing, to provide
    * @dev simple access to a verifiable source of randomness.
    * *****************************************************************************
    * @dev USAGE
    *
    * @dev Calling contracts must inherit from VRFConsumerBase, and can
    * @dev initialize VRFConsumerBase's attributes in their constructor as
    * @dev shown:
    *
    * @dev contract VRFConsumer {
    * @dev constructor(<other arguments>, address _vrfCoordinator, address _link)
    * @dev VRFConsumerBase(_vrfCoordinator, _link) public {
    * @dev <initialization with other arguments goes here>
    * @dev }
    * @dev }
    *
    * @dev The oracle will have given you an ID for the VRF keypair they have
    * @dev committed to (let's call it keyHash), and have told you the minimum LINK
    * @dev price for VRF service. Make sure your contract has sufficient LINK, and
    * @dev call requestRandomness(keyHash, fee, seed), where seed is the input you
    * @dev want to generate randomness from.
    *
    * @dev Once the VRFCoordinator has received and validated the oracle's response
    * @dev to your request, it will call your contract's fulfillRandomness method.
    *
    * @dev The randomness argument to fulfillRandomness is the actual random value
    * @dev generated from your seed.
    *
    * @dev The requestId argument is generated from the keyHash and the seed by
    * @dev makeRequestId(keyHash, seed). If your contract could have concurrent
    * @dev requests open, you can use the requestId to track which seed is
    * @dev associated with which randomness. See VRFRequestIDBase.sol for more
    * @dev details. (See "SECURITY CONSIDERATIONS" for principles to keep in mind,
    * @dev if your contract could have multiple requests in flight simultaneously.)
    *
    * @dev Colliding `requestId`s are cryptographically impossible as long as seeds
    * @dev differ. (Which is critical to making unpredictable randomness! See the
    * @dev next section.)
    *
    * *****************************************************************************
    * @dev SECURITY CONSIDERATIONS
    *
    * @dev A method with the ability to call your fulfillRandomness method directly
    * @dev could spoof a VRF response with any random value, so it's critical that
    * @dev it cannot be directly called by anything other than this base contract
    * @dev (specifically, by the VRFConsumerBase.rawFulfillRandomness method).
    *
    * @dev For your users to trust that your contract's random behavior is free
    * @dev from malicious interference, it's best if you can write it so that all
    * @dev behaviors implied by a VRF response are executed *during* your
    * @dev fulfillRandomness method. If your contract must store the response (or
    * @dev anything derived from it) and use it later, you must ensure that any
    * @dev user-significant behavior which depends on that stored value cannot be
    * @dev manipulated by a subsequent VRF request.
    *
    * @dev Similarly, both miners and the VRF oracle itself have some influence
    * @dev over the order in which VRF responses appear on the blockchain, so if
    * @dev your contract could have multiple VRF requests in flight simultaneously,
    * @dev you must ensure that the order in which the VRF responses arrive cannot
    * @dev be used to manipulate your contract's user-significant behavior.
    *
    * @dev Since the ultimate input to the VRF is mixed with the block hash of the
    * @dev block in which the request is made, user-provided seeds have no impact
    * @dev on its economic security properties. They are only included for API
    * @dev compatability with previous versions of this contract.
    *
    * @dev Since the block hash of the block which contains the requestRandomness
    * @dev call is mixed into the input to the VRF *last*, a sufficiently powerful
    * @dev miner could, in principle, fork the blockchain to evict the block
    * @dev containing the request, forcing the request to be included in a
    * @dev different block with a different hash, and therefore a different input
    * @dev to the VRF. However, such an attack would incur a substantial economic
    * @dev cost. This cost scales with the number of blocks the VRF oracle waits
    * @dev until it calls responds to a request.
    */
    abstract contract VRFConsumerBase is VRFRequestIDBase {
    /**
    * @notice fulfillRandomness handles the VRF response. Your contract must
    * @notice implement it. See "SECURITY CONSIDERATIONS" above for important
    * @notice principles to keep in mind when implementing your fulfillRandomness
    * @notice method.
    *
    * @dev VRFConsumerBase expects its subcontracts to have a method with this
    * @dev signature, and will call it once it has verified the proof
    * @dev associated with the randomness. (It is triggered via a call to
    * @dev rawFulfillRandomness, below.)
    *
    * @param requestId The Id initially returned by requestRandomness
    * @param randomness the VRF output
    */
    function fulfillRandomness(bytes32 requestId, uint256 randomness) internal virtual;

    /**
    * @dev In order to keep backwards compatibility we have kept the user
    * seed field around. We remove the use of it because given that the blockhash
    * enters later, it overrides whatever randomness the used seed provides.
    * Given that it adds no security, and can easily lead to misunderstandings,
    * we have removed it from usage and can now provide a simpler API.
    */
    uint256 private constant USER_SEED_PLACEHOLDER = 0;

    /**
    * @notice requestRandomness initiates a request for VRF output given _seed
    *
    * @dev The fulfillRandomness method receives the output, once it's provided
    * @dev by the Oracle, and verified by the vrfCoordinator.
    *
    * @dev The _keyHash must already be registered with the VRFCoordinator, and
    * @dev the _fee must exceed the fee specified during registration of the
    * @dev _keyHash.
    *
    * @dev The _seed parameter is vestigial, and is kept only for API
    * @dev compatibility with older versions. It can't *hurt* to mix in some of
    * @dev your own randomness, here, but it's not necessary because the VRF
    * @dev oracle will mix the hash of the block containing your request into the
    * @dev VRF seed it ultimately uses.
    *
    * @param _keyHash ID of public key against which randomness is generated
    * @param _fee The amount of LINK to send with the request
    *
    * @return requestId unique ID for this request
    *
    * @dev The returned requestId can be used to distinguish responses to
    * @dev concurrent requests. It is passed as the first argument to
    * @dev fulfillRandomness.
    */
    function requestRandomness(bytes32 _keyHash, uint256 _fee) internal returns (bytes32 requestId) {
    LINK.transferAndCall(vrfCoordinator, _fee, abi.encode(_keyHash, USER_SEED_PLACEHOLDER));
    // This is the seed passed to VRFCoordinator. The oracle will mix this with
    // the hash of the block containing this request to obtain the seed/input
    // which is finally passed to the VRF cryptographic machinery.
    uint256 vRFSeed = makeVRFInputSeed(_keyHash, USER_SEED_PLACEHOLDER, address(this), nonces[_keyHash]);
    // nonces[_keyHash] must stay in sync with
    // VRFCoordinator.nonces[_keyHash][this], which was incremented by the above
    // successful LINK.transferAndCall (in VRFCoordinator.randomnessRequest).
    // This provides protection against the user repeating their input seed,
    // which would result in a predictable/duplicate output, if multiple such
    // requests appeared in the same block.
    nonces[_keyHash] = nonces[_keyHash] + 1;
    return makeRequestId(_keyHash, vRFSeed);
    }

    LinkTokenInterface internal immutable LINK;
    address private immutable vrfCoordinator;

    // Nonces for each VRF key from which randomness has been requested.
    //
    // Must stay in sync with VRFCoordinator[_keyHash][this]
    mapping(bytes32 => uint256) /* keyHash */ /* nonce */
    private nonces;

    /**
    * @param _vrfCoordinator address of VRFCoordinator contract
    * @param _link address of LINK token contract
    *
    * @dev https://docs.chain.link/docs/link-token-contracts
    */
    constructor(address _vrfCoordinator, address _link) {
    vrfCoordinator = _vrfCoordinator;
    LINK = LinkTokenInterface(_link);
    }

    // rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF
    // proof. rawFulfillRandomness then calls fulfillRandomness, after validating
    // the origin of the call
    function rawFulfillRandomness(bytes32 requestId, uint256 randomness) external {
    require(msg.sender == vrfCoordinator, "Only VRFCoordinator can fulfill");
    fulfillRandomness(requestId, randomness);
    }
    }
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,40 @@
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;

    contract VRFRequestIDBase {
    /**
    * @notice returns the seed which is actually input to the VRF coordinator
    *
    * @dev To prevent repetition of VRF output due to repetition of the
    * @dev user-supplied seed, that seed is combined in a hash with the
    * @dev user-specific nonce, and the address of the consuming contract. The
    * @dev risk of repetition is mostly mitigated by inclusion of a blockhash in
    * @dev the final seed, but the nonce does protect against repetition in
    * @dev requests which are included in a single block.
    *
    * @param _userSeed VRF seed input provided by user
    * @param _requester Address of the requesting contract
    * @param _nonce User-specific nonce at the time of the request
    */
    function makeVRFInputSeed(
    bytes32 _keyHash,
    uint256 _userSeed,
    address _requester,
    uint256 _nonce
    ) internal pure returns (uint256) {
    return uint256(keccak256(abi.encode(_keyHash, _userSeed, _requester, _nonce)));
    }

    /**
    * @notice Returns the id for this request
    * @param _keyHash The serviceAgreement ID to be used for this request
    * @param _vRFInputSeed The seed to be passed directly to the VRF
    * @return The id for this request
    *
    * @dev Note that _vRFInputSeed is not the seed passed by the consuming
    * @dev contract, but the one generated by makeVRFInputSeed
    */
    function makeRequestId(bytes32 _keyHash, uint256 _vRFInputSeed) internal pure returns (bytes32) {
    return keccak256(abi.encodePacked(_keyHash, _vRFInputSeed));
    }
    }
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,36 @@
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;

    interface LinkTokenInterface {
    function allowance(address owner, address spender) external view returns (uint256 remaining);

    function approve(address spender, uint256 value) external returns (bool success);

    function balanceOf(address owner) external view returns (uint256 balance);

    function decimals() external view returns (uint8 decimalPlaces);

    function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);

    function increaseApproval(address spender, uint256 subtractedValue) external;

    function name() external view returns (string memory tokenName);

    function symbol() external view returns (string memory tokenSymbol);

    function totalSupply() external view returns (uint256 totalTokensIssued);

    function transfer(address to, uint256 value) external returns (bool success);

    function transferAndCall(
    address to,
    uint256 value,
    bytes calldata data
    ) external returns (bool success);

    function transferFrom(
    address from,
    address to,
    uint256 value
    ) external returns (bool success);
    }
    43 changes: 43 additions & 0 deletions [email protected]
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,43 @@
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)

    pragma solidity ^0.8.0;

    /**
    * @title Counters
    * @author Matt Condon (@shrugs)
    * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
    * of elements in a mapping, issuing ERC721 ids, or counting request ids.
    *
    * Include with `using Counters for Counters.Counter;`
    */
    library Counters {
    struct Counter {
    // This variable should never be directly accessed by users of the library: interactions must be restricted to
    // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
    // this feature: see https://github.com/ethereum/solidity/issues/4637
    uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
    return counter._value;
    }

    function increment(Counter storage counter) internal {
    unchecked {
    counter._value += 1;
    }
    }

    function decrement(Counter storage counter) internal {
    uint256 value = counter._value;
    require(value > 0, "Counter: decrement overflow");
    unchecked {
    counter._value = value - 1;
    }
    }

    function reset(Counter storage counter) internal {
    counter._value = 0;
    }
    }
    227 changes: 227 additions & 0 deletions .deps...npm...@openzeppelin...contracts...utils...math...SafeMath.sol
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,227 @@
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)

    pragma solidity ^0.8.0;

    // CAUTION
    // This version of SafeMath should only be used with Solidity 0.8 or later,
    // because it relies on the compiler's built in overflow checks.

    /**
    * @dev Wrappers over Solidity's arithmetic operations.
    *
    * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
    * now has built in overflow checking.
    */
    library SafeMath {
    /**
    * @dev Returns the addition of two unsigned integers, with an overflow flag.
    *
    * _Available since v3.4._
    */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
    unchecked {
    uint256 c = a + b;
    if (c < a) return (false, 0);
    return (true, c);
    }
    }

    /**
    * @dev Returns the substraction of two unsigned integers, with an overflow flag.
    *
    * _Available since v3.4._
    */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
    unchecked {
    if (b > a) return (false, 0);
    return (true, a - b);
    }
    }

    /**
    * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
    *
    * _Available since v3.4._
    */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
    unchecked {
    // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
    // benefit is lost if 'b' is also tested.
    // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
    if (a == 0) return (true, 0);
    uint256 c = a * b;
    if (c / a != b) return (false, 0);
    return (true, c);
    }
    }

    /**
    * @dev Returns the division of two unsigned integers, with a division by zero flag.
    *
    * _Available since v3.4._
    */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
    unchecked {
    if (b == 0) return (false, 0);
    return (true, a / b);
    }
    }

    /**
    * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
    *
    * _Available since v3.4._
    */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
    unchecked {
    if (b == 0) return (false, 0);
    return (true, a % b);
    }
    }

    /**
    * @dev Returns the addition of two unsigned integers, reverting on
    * overflow.
    *
    * Counterpart to Solidity's `+` operator.
    *
    * Requirements:
    *
    * - Addition cannot overflow.
    */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
    return a + b;
    }

    /**
    * @dev Returns the subtraction of two unsigned integers, reverting on
    * overflow (when the result is negative).
    *
    * Counterpart to Solidity's `-` operator.
    *
    * Requirements:
    *
    * - Subtraction cannot overflow.
    */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    return a - b;
    }

    /**
    * @dev Returns the multiplication of two unsigned integers, reverting on
    * overflow.
    *
    * Counterpart to Solidity's `*` operator.
    *
    * Requirements:
    *
    * - Multiplication cannot overflow.
    */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    return a * b;
    }

    /**
    * @dev Returns the integer division of two unsigned integers, reverting on
    * division by zero. The result is rounded towards zero.
    *
    * Counterpart to Solidity's `/` operator.
    *
    * Requirements:
    *
    * - The divisor cannot be zero.
    */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
    return a / b;
    }

    /**
    * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
    * reverting when dividing by zero.
    *
    * Counterpart to Solidity's `%` operator. This function uses a `revert`
    * opcode (which leaves remaining gas untouched) while Solidity uses an
    * invalid opcode to revert (consuming all remaining gas).
    *
    * Requirements:
    *
    * - The divisor cannot be zero.
    */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
    return a % b;
    }

    /**
    * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
    * overflow (when the result is negative).
    *
    * CAUTION: This function is deprecated because it requires allocating memory for the error
    * message unnecessarily. For custom revert reasons use {trySub}.
    *
    * Counterpart to Solidity's `-` operator.
    *
    * Requirements:
    *
    * - Subtraction cannot overflow.
    */
    function sub(
    uint256 a,
    uint256 b,
    string memory errorMessage
    ) internal pure returns (uint256) {
    unchecked {
    require(b <= a, errorMessage);
    return a - b;
    }
    }

    /**
    * @dev Returns the integer division of two unsigned integers, reverting with custom message on
    * division by zero. The result is rounded towards zero.
    *
    * Counterpart to Solidity's `/` operator. Note: this function uses a
    * `revert` opcode (which leaves remaining gas untouched) while Solidity
    * uses an invalid opcode to revert (consuming all remaining gas).
    *
    * Requirements:
    *
    * - The divisor cannot be zero.
    */
    function div(
    uint256 a,
    uint256 b,
    string memory errorMessage
    ) internal pure returns (uint256) {
    unchecked {
    require(b > 0, errorMessage);
    return a / b;
    }
    }

    /**
    * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
    * reverting with custom message when dividing by zero.
    *
    * CAUTION: This function is deprecated because it requires allocating memory for the error
    * message unnecessarily. For custom revert reasons use {tryMod}.
    *
    * Counterpart to Solidity's `%` operator. This function uses a `revert`
    * opcode (which leaves remaining gas untouched) while Solidity uses an
    * invalid opcode to revert (consuming all remaining gas).
    *
    * Requirements:
    *
    * - The divisor cannot be zero.
    */
    function mod(
    uint256 a,
    uint256 b,
    string memory errorMessage
    ) internal pure returns (uint256) {
    unchecked {
    require(b > 0, errorMessage);
    return a % b;
    }
    }
    }
    40 changes: 40 additions & 0 deletions .deps...remix-tests...remix_accounts.sol
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,40 @@
    // SPDX-License-Identifier: GPL-3.0

    pragma solidity >=0.4.22 <0.9.0;

    library TestsAccounts {
    function getAccount(uint index) pure public returns (address) {
    address[15] memory accounts;
    accounts[0] = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;

    accounts[1] = 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2;

    accounts[2] = 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db;

    accounts[3] = 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB;

    accounts[4] = 0x617F2E2fD72FD9D5503197092aC168c91465E7f2;

    accounts[5] = 0x17F6AD8Ef982297579C203069C1DbfFE4348c372;

    accounts[6] = 0x5c6B0f7Bf3E7ce046039Bd8FABdfD3f9F5021678;

    accounts[7] = 0x03C6FcED478cBbC9a4FAB34eF9f40767739D1Ff7;

    accounts[8] = 0x1aE0EA34a72D944a8C7603FfB3eC30a6669E454C;

    accounts[9] = 0x0A098Eda01Ce92ff4A4CCb7A4fFFb5A43EBC70DC;

    accounts[10] = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c;

    accounts[11] = 0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C;

    accounts[12] = 0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB;

    accounts[13] = 0x583031D1113aD414F02576BD6afaBfb302140225;

    accounts[14] = 0xdD870fA1b7C4700F2BD7f44238821C26f7392148;

    return accounts[index];
    }
    }
    225 changes: 225 additions & 0 deletions .deps...remix-tests...remix_tests.sol
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,225 @@
    // SPDX-License-Identifier: GPL-3.0

    pragma solidity >=0.4.22 <0.9.0;

    library Assert {

    event AssertionEvent(
    bool passed,
    string message,
    string methodName
    );

    event AssertionEventUint(
    bool passed,
    string message,
    string methodName,
    uint256 returned,
    uint256 expected
    );

    event AssertionEventInt(
    bool passed,
    string message,
    string methodName,
    int256 returned,
    int256 expected
    );

    event AssertionEventBool(
    bool passed,
    string message,
    string methodName,
    bool returned,
    bool expected
    );

    event AssertionEventAddress(
    bool passed,
    string message,
    string methodName,
    address returned,
    address expected
    );

    event AssertionEventBytes32(
    bool passed,
    string message,
    string methodName,
    bytes32 returned,
    bytes32 expected
    );

    event AssertionEventString(
    bool passed,
    string message,
    string methodName,
    string returned,
    string expected
    );

    event AssertionEventUintInt(
    bool passed,
    string message,
    string methodName,
    uint256 returned,
    int256 expected
    );

    event AssertionEventIntUint(
    bool passed,
    string message,
    string methodName,
    int256 returned,
    uint256 expected
    );

    function ok(bool a, string memory message) public returns (bool result) {
    result = a;
    emit AssertionEvent(result, message, "ok");
    }

    function equal(uint256 a, uint256 b, string memory message) public returns (bool result) {
    result = (a == b);
    emit AssertionEventUint(result, message, "equal", a, b);
    }

    function equal(int256 a, int256 b, string memory message) public returns (bool result) {
    result = (a == b);
    emit AssertionEventInt(result, message, "equal", a, b);
    }

    function equal(bool a, bool b, string memory message) public returns (bool result) {
    result = (a == b);
    emit AssertionEventBool(result, message, "equal", a, b);
    }

    // TODO: only for certain versions of solc
    //function equal(fixed a, fixed b, string message) public returns (bool result) {
    // result = (a == b);
    // emit AssertionEvent(result, message);
    //}

    // TODO: only for certain versions of solc
    //function equal(ufixed a, ufixed b, string message) public returns (bool result) {
    // result = (a == b);
    // emit AssertionEvent(result, message);
    //}

    function equal(address a, address b, string memory message) public returns (bool result) {
    result = (a == b);
    emit AssertionEventAddress(result, message, "equal", a, b);
    }

    function equal(bytes32 a, bytes32 b, string memory message) public returns (bool result) {
    result = (a == b);
    emit AssertionEventBytes32(result, message, "equal", a, b);
    }

    function equal(string memory a, string memory b, string memory message) public returns (bool result) {
    result = (keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b)));
    emit AssertionEventString(result, message, "equal", a, b);
    }

    function notEqual(uint256 a, uint256 b, string memory message) public returns (bool result) {
    result = (a != b);
    emit AssertionEventUint(result, message, "notEqual", a, b);
    }

    function notEqual(int256 a, int256 b, string memory message) public returns (bool result) {
    result = (a != b);
    emit AssertionEventInt(result, message, "notEqual", a, b);
    }

    function notEqual(bool a, bool b, string memory message) public returns (bool result) {
    result = (a != b);
    emit AssertionEventBool(result, message, "notEqual", a, b);
    }

    // TODO: only for certain versions of solc
    //function notEqual(fixed a, fixed b, string message) public returns (bool result) {
    // result = (a != b);
    // emit AssertionEvent(result, message);
    //}

    // TODO: only for certain versions of solc
    //function notEqual(ufixed a, ufixed b, string message) public returns (bool result) {
    // result = (a != b);
    // emit AssertionEvent(result, message);
    //}

    function notEqual(address a, address b, string memory message) public returns (bool result) {
    result = (a != b);
    emit AssertionEventAddress(result, message, "notEqual", a, b);
    }

    function notEqual(bytes32 a, bytes32 b, string memory message) public returns (bool result) {
    result = (a != b);
    emit AssertionEventBytes32(result, message, "notEqual", a, b);
    }

    function notEqual(string memory a, string memory b, string memory message) public returns (bool result) {
    result = (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b)));
    emit AssertionEventString(result, message, "notEqual", a, b);
    }

    /*----------------- Greater than --------------------*/
    function greaterThan(uint256 a, uint256 b, string memory message) public returns (bool result) {
    result = (a > b);
    emit AssertionEventUint(result, message, "greaterThan", a, b);
    }

    function greaterThan(int256 a, int256 b, string memory message) public returns (bool result) {
    result = (a > b);
    emit AssertionEventInt(result, message, "greaterThan", a, b);
    }
    // TODO: safely compare between uint and int
    function greaterThan(uint256 a, int256 b, string memory message) public returns (bool result) {
    if(b < int(0)) {
    // int is negative uint "a" always greater
    result = true;
    } else {
    result = (a > uint(b));
    }
    emit AssertionEventUintInt(result, message, "greaterThan", a, b);
    }
    function greaterThan(int256 a, uint256 b, string memory message) public returns (bool result) {
    if(a < int(0)) {
    // int is negative uint "b" always greater
    result = false;
    } else {
    result = (uint(a) > b);
    }
    emit AssertionEventIntUint(result, message, "greaterThan", a, b);
    }
    /*----------------- Lesser than --------------------*/
    function lesserThan(uint256 a, uint256 b, string memory message) public returns (bool result) {
    result = (a < b);
    emit AssertionEventUint(result, message, "lesserThan", a, b);
    }

    function lesserThan(int256 a, int256 b, string memory message) public returns (bool result) {
    result = (a < b);
    emit AssertionEventInt(result, message, "lesserThan", a, b);
    }
    // TODO: safely compare between uint and int
    function lesserThan(uint256 a, int256 b, string memory message) public returns (bool result) {
    if(b < int(0)) {
    // int is negative int "b" always lesser
    result = false;
    } else {
    result = (a < uint(b));
    }
    emit AssertionEventUintInt(result, message, "lesserThan", a, b);
    }

    function lesserThan(int256 a, uint256 b, string memory message) public returns (bool result) {
    if(a < int(0)) {
    // int is negative int "a" always lesser
    result = true;
    } else {
    result = (uint(a) < b);
    }
    emit AssertionEventIntUint(result, message, "lesserThan", a, b);
    }
    }
    18 changes: 18 additions & 0 deletions README.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,18 @@
    REMIX EXAMPLE PROJECT

    Remix example project is present when Remix loads for the very first time or there are no files existing in the File Explorer.
    It contains 3 directories:

    1. 'contracts': Holds three contracts with different complexity level, denoted with number prefix in file name.
    2. 'scripts': Holds two scripts to deploy a contract. It is explained below.
    3. 'tests': Contains one test file for 'Ballot' contract with unit tests in Solidity.

    SCRIPTS

    The 'scripts' folder contains example async/await scripts for deploying the 'Storage' contract.
    For the deployment of any other contract, 'contractName' and 'constructorArgs' should be updated (along with other code if required).
    Scripts have full access to the web3.js and ethers.js libraries.

    To run a script, right click on file name in the file explorer and click 'Run'. Remember, Solidity file must already be compiled.

    Output from script will appear in remix terminal.
    14 changes: 14 additions & 0 deletions TODO
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,14 @@
    DONE - Check payWinner function. Doesnt seem to pay correct winner.
    - Add raffle states
    - Transfer and withdrawal only capable in raffle closed state
    DONE - Add safemath and use in every calculation
    - Add winners array
    DONE - Remove answer and timestamp from Tickets struct
    DONE - Refactor Ticket struct to maybe an array??
    - Add security from openzeppelin
    - Remove winning ticketIds from tickets array? Using EnumerableMap for mappings, with remove() function?
    DONE - Fix cooldown
    - Use BNB to pay for raffle entry
    - Add withdraw Balance function, make it shit paranoid secure.
    - Check if player exists in Players arrray before pushing?? find a way to check for uinque value in array
    - require winning address to be valid address not (0x000)
    105 changes: 105 additions & 0 deletions contracts...1_StoryRaffle.sol
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,105 @@
    // SPDX-License-Identifier: MIT

    import "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol";

    pragma solidity ^0.8.11;

    contract StoryRaffle is VRFConsumerBase{
    address public owner;
    address payable[] public players;
    uint public raffleId;
    mapping (uint => address payable) public raffleHistory; // storing winner of each lottery

    bytes32 internal keyHash; // identifies which Chainlink oracle to use
    uint internal fee; // fee to get random number (LINK)

    uint public randomResult;


    constructor()
    VRFConsumerBase(
    0xb3dCcb4Cf7a26f6cf6B120Cf5A73875B7BBc655B, // VRF Coordinator - Rinkeby
    0x01BE23585060835E02B77ef475b0Cc51aA1e0709 // LINK token address
    ) {
    keyHash = 0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311;
    fee = 0.1 * 10 ** 18; // 0.1 LINK

    owner = msg.sender; // owner of the deployer of the contract
    raffleId = 1;
    }

    /**
    * Requests randomness
    */
    function getRandomNumber() public returns (bytes32 requestId) {
    require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK in contract");
    return requestRandomness(keyHash, fee);
    }

    /**
    * Callback function used by VRF Coordinator
    */
    function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
    randomResult = randomness;
    // payWinner();
    }

    function getWinnerByRaffle(uint _lotteryId) public view returns (address payable) {
    return raffleHistory[_lotteryId];
    }

    // get pot balance
    function getBalance() public view returns (uint) {
    return address(this).balance;
    }

    // get list of players
    function getPlayers() public view returns (address payable[] memory) {
    return players;
    }

    // enter Raffle
    function enterRaffle() public payable {
    require(msg.value >= .01 ether);

    // address of player entering the raffle
    players.push(payable(msg.sender));
    }

    // generate a Pseudo random number
    function getPseudoRandomNumber() public returns (uint) {
    randomResult = uint(keccak256(abi.encodePacked(owner, block.timestamp)));
    return randomResult;
    }

    // select winner based on generated random number and transfer balance to their account
    function pickWinner() public onlyOwner {
    // getRandomNumber();
    getPseudoRandomNumber();
    }

    function getWinner() public view returns (address payable) {
    uint index = randomResult % players.length;
    return players[index];
    }

    function payWinner(uint _amount) public {
    uint index = randomResult % players.length;

    // transfer balance of the current smart contract to address of winner
    players[index].transfer(_amount);

    raffleHistory[raffleId] = players[index];
    raffleId++;


    // reset the state of the contract
    players = new address payable[](0);
    }

    // modifier to ensure only the owner can run a function
    modifier onlyOwner() {
    require(msg.sender == owner, "only owner can run this function");
    _;
    }
    }
    51 changes: 51 additions & 0 deletions contracts...2_Owner.sol
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,51 @@
    // SPDX-License-Identifier: GPL-3.0

    pragma solidity >=0.7.0 <0.9.0;

    /**
    * @title Owner
    * @dev Set & change owner
    */
    contract Owner {

    address private owner;

    // event for EVM logging
    event OwnerSet(address indexed oldOwner, address indexed newOwner);

    // modifier to check if caller is owner
    modifier isOwner() {
    // If the first argument of 'require' evaluates to 'false', execution terminates and all
    // changes to the state and to Ether balances are reverted.
    // This used to consume all gas in old EVM versions, but not anymore.
    // It is often a good idea to use 'require' to check if functions are called correctly.
    // As a second argument, you can also provide an explanation about what went wrong.
    require(msg.sender == owner, "Caller is not owner");
    _;
    }

    /**
    * @dev Set contract deployer as owner
    */
    constructor() {
    owner = msg.sender; // 'msg.sender' is sender of current call, contract deployer for a constructor
    emit OwnerSet(address(0), owner);
    }

    /**
    * @dev Change owner
    * @param newOwner address of new owner
    */
    function changeOwner(address newOwner) public isOwner {
    emit OwnerSet(owner, newOwner);
    owner = newOwner;
    }

    /**
    * @dev Return owner address
    * @return address of owner
    */
    function getOwner() external view returns (address) {
    return owner;
    }
    }
    198 changes: 198 additions & 0 deletions contracts...3_Raffle.sol
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,198 @@
    // SPDX-License-Identifier: MIT

    import "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol";
    import "@openzeppelin/contracts/utils/Counters.sol";
    import "@openzeppelin/contracts/utils/math/SafeMath.sol";

    pragma solidity ^0.8.11;

    contract StoryRaffle is VRFConsumerBase{

    using SafeMath for uint;
    address public owner;
    address payable[] public players;
    uint public raffleId;
    mapping (uint => address payable) public raffleHistory; // storing winner of each lottery

    bytes32 internal keyHash; // identifies which Chainlink oracle to use
    uint internal fee; // fee to get random number (LINK)

    enum RaffleState { Open, Closed, Completed }
    RaffleState public state;

    uint public randomResult;

    using Counters for Counters.Counter;
    Counters.Counter private _ticketIds;

    // uint[] public numbers;
    uint public maxEntries;

    // struct Ticket {
    // // address payable player;
    // uint ticketId;
    // string answer;
    // uint timestamp;
    // }

    uint[] public tickets;

    mapping (uint => address) public ticketToPlayer;
    mapping (address => uint) playerTicketCount;

    /*
    * map address to last time they entered the lottery
    */
    mapping(address => uint) public lastEntryTime;

    constructor()
    VRFConsumerBase(
    0xb3dCcb4Cf7a26f6cf6B120Cf5A73875B7BBc655B, // VRF Coordinator - Rinkeby
    0x01BE23585060835E02B77ef475b0Cc51aA1e0709 // LINK token address
    ) {
    keyHash = 0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311;
    fee = 0.1 * 10 ** 18; // 0.1 LINK

    owner = msg.sender; // owner of the deployer of the contract
    raffleId = 1;

    maxEntries = 50000;

    _ticketIds.increment();
    }

    /**
    * Requests randomness
    */
    function getRandomNumber() public returns (bytes32 requestId) {
    require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK in contract");
    return requestRandomness(keyHash, fee);
    }

    /**
    * Callback function used by VRF Coordinator
    */
    function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
    randomResult = randomness;
    // payWinner();
    }

    function getWinnerByRaffle(uint _lotteryId) public view returns (address payable) {
    return raffleHistory[_lotteryId];
    }

    // get pot balance
    function getBalance() public view returns (uint) {
    return address(this).balance;
    }

    // get list of players
    function getPlayers() public view returns (address payable[] memory) {
    return players;
    }

    function getTickets() public view returns (uint[] memory) {
    return tickets;
    }

    // function getTicketIds() public view returns (uint[] memory) {
    // return _ticketIds;
    // }

    // enter Raffle
    function enterRaffle() public payable isState(RaffleState.Open) {

    // cooldown period
    require(
    lastEntryTime[msg.sender] + 5 minutes < block.timestamp,
    "You must wait 5 minutes before entering the raffle again"
    );

    require(tickets.length < maxEntries, "Raffle is full");

    // check if player has already entered
    require(playerTicketCount[msg.sender] < 3 , "You have reached your maximum entries");

    require(msg.value >= .01 ether, "Minimum entry fee not met");

    uint id = _ticketIds.current();

    tickets.push(id);
    ticketToPlayer[id] = msg.sender;

    players.push(payable(msg.sender));

    lastEntryTime[msg.sender] = block.timestamp;

    playerTicketCount[msg.sender]++;
    _ticketIds.increment();
    }

    // generate a Pseudo random number
    function getPseudoRandomNumber() public returns (uint) {
    randomResult = uint(keccak256(abi.encodePacked(owner, block.timestamp)));
    return randomResult;
    }

    function pickWinnerTest() public onlyOwner {
    getPseudoRandomNumber();
    }

    // select winner based on generated random number and transfer balance to their account
    function pickWinner() public onlyOwner {
    getRandomNumber();
    }

    // number of tickets a player has??
    function getPlayerTickets(address _player) public view returns (uint) {
    return playerTicketCount[_player];
    }

    function getWinner() public view returns (address) {
    if (randomResult > 0) {
    uint index = (randomResult % tickets.length)+1;
    // return players[index];
    return ticketToPlayer[index]; // index-1 ????
    }

    }

    function getWinningTicketId() public view returns (uint) {
    if (randomResult > 0) {
    uint index = (randomResult % tickets.length)+1;
    return index;
    }

    }

    function payWinner(uint _amount) public onlyOwner {

    uint index = (randomResult % players.length)+1;

    // transfer balance of the current smart contract to address of winner
    players[index-1].transfer(_amount);
    // getWinner().transfer(_amount);

    raffleHistory[raffleId] = players[index-1];
    raffleId++;


    // reset the state of the contract
    players = new address payable[](0);
    }

    // modifier to ensure only the owner can run a function
    modifier onlyOwner() {
    require(msg.sender == owner, "Only owner can run this function");
    _;
    }

    modifier isState(RaffleState _state) {
    require(state == _state, "Raffle is not in the correct state");
    _;
    }

    function _changeState(RaffleState _state) internal onlyOwner {
    state = _state;
    }
    }
    12,562 changes: 12,562 additions & 0 deletions contracts...artifacts...StoryRaffle.json
    12,562 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
    436 changes: 436 additions & 0 deletions contracts...artifacts...StoryRaffle_metadata.json
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,436 @@
    {
    "compiler": {
    "version": "0.8.11+commit.d7f03943"
    },
    "language": "Solidity",
    "output": {
    "abi": [
    {
    "inputs": [],
    "stateMutability": "nonpayable",
    "type": "constructor"
    },
    {
    "inputs": [],
    "name": "enterRaffle",
    "outputs": [],
    "stateMutability": "payable",
    "type": "function"
    },
    {
    "inputs": [],
    "name": "getBalance",
    "outputs": [
    {
    "internalType": "uint256",
    "name": "",
    "type": "uint256"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [
    {
    "internalType": "address",
    "name": "_player",
    "type": "address"
    }
    ],
    "name": "getPlayerTickets",
    "outputs": [
    {
    "internalType": "uint256",
    "name": "",
    "type": "uint256"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [],
    "name": "getPlayers",
    "outputs": [
    {
    "internalType": "address payable[]",
    "name": "",
    "type": "address[]"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [],
    "name": "getPseudoRandomNumber",
    "outputs": [
    {
    "internalType": "uint256",
    "name": "",
    "type": "uint256"
    }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
    },
    {
    "inputs": [],
    "name": "getRandomNumber",
    "outputs": [
    {
    "internalType": "bytes32",
    "name": "requestId",
    "type": "bytes32"
    }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
    },
    {
    "inputs": [],
    "name": "getTickets",
    "outputs": [
    {
    "internalType": "uint256[]",
    "name": "",
    "type": "uint256[]"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [],
    "name": "getWinner",
    "outputs": [
    {
    "internalType": "address",
    "name": "",
    "type": "address"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [
    {
    "internalType": "uint256",
    "name": "_lotteryId",
    "type": "uint256"
    }
    ],
    "name": "getWinnerByRaffle",
    "outputs": [
    {
    "internalType": "address payable",
    "name": "",
    "type": "address"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [],
    "name": "getWinningTicketId",
    "outputs": [
    {
    "internalType": "uint256",
    "name": "",
    "type": "uint256"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [
    {
    "internalType": "address",
    "name": "",
    "type": "address"
    }
    ],
    "name": "lastEntryTime",
    "outputs": [
    {
    "internalType": "uint256",
    "name": "",
    "type": "uint256"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [],
    "name": "maxEntries",
    "outputs": [
    {
    "internalType": "uint256",
    "name": "",
    "type": "uint256"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [],
    "name": "owner",
    "outputs": [
    {
    "internalType": "address",
    "name": "",
    "type": "address"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [
    {
    "internalType": "uint256",
    "name": "_amount",
    "type": "uint256"
    }
    ],
    "name": "payWinner",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
    },
    {
    "inputs": [],
    "name": "pickWinner",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
    },
    {
    "inputs": [],
    "name": "pickWinnerTest",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
    },
    {
    "inputs": [
    {
    "internalType": "uint256",
    "name": "",
    "type": "uint256"
    }
    ],
    "name": "players",
    "outputs": [
    {
    "internalType": "address payable",
    "name": "",
    "type": "address"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [
    {
    "internalType": "uint256",
    "name": "",
    "type": "uint256"
    }
    ],
    "name": "raffleHistory",
    "outputs": [
    {
    "internalType": "address payable",
    "name": "",
    "type": "address"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [],
    "name": "raffleId",
    "outputs": [
    {
    "internalType": "uint256",
    "name": "",
    "type": "uint256"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [],
    "name": "randomResult",
    "outputs": [
    {
    "internalType": "uint256",
    "name": "",
    "type": "uint256"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [
    {
    "internalType": "bytes32",
    "name": "requestId",
    "type": "bytes32"
    },
    {
    "internalType": "uint256",
    "name": "randomness",
    "type": "uint256"
    }
    ],
    "name": "rawFulfillRandomness",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
    },
    {
    "inputs": [],
    "name": "state",
    "outputs": [
    {
    "internalType": "enum StoryRaffle.RaffleState",
    "name": "",
    "type": "uint8"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [
    {
    "internalType": "uint256",
    "name": "",
    "type": "uint256"
    }
    ],
    "name": "ticketToPlayer",
    "outputs": [
    {
    "internalType": "address",
    "name": "",
    "type": "address"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    },
    {
    "inputs": [
    {
    "internalType": "uint256",
    "name": "",
    "type": "uint256"
    }
    ],
    "name": "tickets",
    "outputs": [
    {
    "internalType": "uint256",
    "name": "",
    "type": "uint256"
    }
    ],
    "stateMutability": "view",
    "type": "function"
    }
    ],
    "devdoc": {
    "kind": "dev",
    "methods": {},
    "version": 1
    },
    "userdoc": {
    "kind": "user",
    "methods": {
    "getRandomNumber()": {
    "notice": "Requests randomness "
    }
    },
    "version": 1
    }
    },
    "settings": {
    "compilationTarget": {
    "contracts/3_Raffle.sol": "StoryRaffle"
    },
    "evmVersion": "london",
    "libraries": {},
    "metadata": {
    "bytecodeHash": "ipfs"
    },
    "optimizer": {
    "enabled": false,
    "runs": 200
    },
    "remappings": []
    },
    "sources": {
    "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol": {
    "keccak256": "0xff9e7d773545a1a5d73106e72bfb429da79c1cc4d893fb62051df801d2e61469",
    "license": "MIT",
    "urls": [
    "bzz-raw://68ff9557dad6da8108073dadcbfe5cd1f45106c2f890443eacd3bf8d59955d4e",
    "dweb:/ipfs/QmSKYkP8wNX7MKYrCroFps62gdxEwwrZwsz4RLc6XzNkxi"
    ]
    },
    "@chainlink/contracts/src/v0.8/VRFRequestIDBase.sol": {
    "keccak256": "0x2bf1168e8fe548fa990e0aeaf89ef90680f80aa31eeaf901f485df60de51d51b",
    "license": "MIT",
    "urls": [
    "bzz-raw://79c72d66deaa4b0f251f3f0817f45ebceb71f5b4f94a86ac0e586de99f5af2b6",
    "dweb:/ipfs/QmcHkApHm5CAynjajcCUYppaKJ9sNWGZTEcSs4tUK8B5K5"
    ]
    },
    "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol": {
    "keccak256": "0xc7d7cd730d36825485ef4107d93c3ff18b9f3a5a00ea3d5988ba9a0bd70b10c5",
    "license": "MIT",
    "urls": [
    "bzz-raw://8cb1064885ecbcd9c3adba779e190cb4a538e5d4d15aeccb67d3376bdffc94bd",
    "dweb:/ipfs/QmcQHK6ewve7tFi4XXK65JthQg4kQzApQikWcURJjGt4iQ"
    ]
    },
    "@openzeppelin/contracts/utils/Counters.sol": {
    "keccak256": "0xf0018c2440fbe238dd3a8732fa8e17a0f9dce84d31451dc8a32f6d62b349c9f1",
    "license": "MIT",
    "urls": [
    "bzz-raw://59e1c62884d55b70f3ae5432b44bb3166ad71ae3acd19c57ab6ddc3c87c325ee",
    "dweb:/ipfs/QmezuXg5GK5oeA4F91EZhozBFekhq5TD966bHPH18cCqhu"
    ]
    },
    "@openzeppelin/contracts/utils/math/SafeMath.sol": {
    "keccak256": "0xa2f576be637946f767aa56601c26d717f48a0aff44f82e46f13807eea1009a21",
    "license": "MIT",
    "urls": [
    "bzz-raw://973868f808e88e21a1a0a01d4839314515ee337ad096286c88e41b74dcc11fc2",
    "dweb:/ipfs/QmfYuZxRfx2J2xdk4EXN7jKg4bUYEMTaYk9BAw9Bqn4o2Y"
    ]
    },
    "contracts/3_Raffle.sol": {
    "keccak256": "0x398d972f976dcab474185e58ff94efde1b21cea6065da921c7112165818afd3b",
    "license": "MIT",
    "urls": [
    "bzz-raw://06b52cd3a1022bb79a6d34d8fd8a717f33dde93403774c93f7c97814423be6f8",
    "dweb:/ipfs/Qmbpgk4wxcX13pah4wrKsbAKUFopHik5FJQMLx5UGz7Hdm"
    ]
    }
    },
    "version": 1
    }
    29 changes: 29 additions & 0 deletions scripts...deploy_ethers.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,29 @@
    // Right click on the script name and hit "Run" to execute
    (async () => {
    try {
    console.log('Running deployWithEthers script...')

    const contractName = 'Storage' // Change this for other contract
    const constructorArgs = [] // Put constructor args (if any) here for your contract

    // Note that the script needs the ABI which is generated from the compilation artifact.
    // Make sure contract is compiled and artifacts are generated
    const artifactsPath = `browser/contracts/artifacts/${contractName}.json` // Change this for different path

    const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath))
    // 'web3Provider' is a remix global variable object
    const signer = (new ethers.providers.Web3Provider(web3Provider)).getSigner()

    let factory = new ethers.ContractFactory(metadata.abi, metadata.data.bytecode.object, signer);

    let contract = await factory.deploy(...constructorArgs);

    console.log('Contract Address: ', contract.address);

    // The contract is NOT deployed yet; we must wait until it is mined
    await contract.deployed()
    console.log('Deployment successful.')
    } catch (e) {
    console.log(e.message)
    }
    })()
    32 changes: 32 additions & 0 deletions scripts...deploy_web3.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,32 @@
    // Right click on the script name and hit "Run" to execute
    (async () => {
    try {
    console.log('Running deployWithWeb3 script...')

    const contractName = 'Storage' // Change this for other contract
    const constructorArgs = [] // Put constructor args (if any) here for your contract

    // Note that the script needs the ABI which is generated from the compilation artifact.
    // Make sure contract is compiled and artifacts are generated
    const artifactsPath = `browser/contracts/artifacts/${contractName}.json` // Change this for different path

    const metadata = JSON.parse(await remix.call('fileManager', 'getFile', artifactsPath))
    const accounts = await web3.eth.getAccounts()

    let contract = new web3.eth.Contract(metadata.abi)

    contract = contract.deploy({
    data: metadata.data.bytecode.object,
    arguments: constructorArgs
    })

    const newContractInstance = await contract.send({
    from: accounts[0],
    gas: 1500000,
    gasPrice: '30000000000'
    })
    console.log('Contract deployed at address: ', newContractInstance.options.address)
    } catch (e) {
    console.log(e.message)
    }
    })()
    26 changes: 26 additions & 0 deletions tests...4_Ballot_test.sol
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,26 @@
    // SPDX-License-Identifier: GPL-3.0

    pragma solidity >=0.7.0 <0.9.0;
    import "remix_tests.sol"; // this import is automatically injected by Remix.
    import "../contracts/3_Ballot.sol";

    contract BallotTest {

    bytes32[] proposalNames;

    Ballot ballotToTest;
    function beforeAll () public {
    proposalNames.push(bytes32("candidate1"));
    ballotToTest = new Ballot(proposalNames);
    }

    function checkWinningProposal () public {
    ballotToTest.vote(0);
    Assert.equal(ballotToTest.winningProposal(), uint(0), "proposal at index 0 should be the winning proposal");
    Assert.equal(ballotToTest.winnerName(), bytes32("candidate1"), "candidate1 should be the winner name");
    }

    function checkWinninProposalWithReturnValue () public view returns (bool) {
    return ballotToTest.winningProposal() == 0;
    }
    }