Generate a random number by address (VRF) - chainlink

I would like to know if it is possible to generate a unique number for each address, I tried mapping but it does not work
contract RandomNumberConsumer is VRFConsumerBase {
bytes32 internal keyHash;
uint256 internal fee;
uint256 public randomResult;
mapping(address => uint) public random;
constructor()
VRFConsumerBase(
0x8C7382F9D8f56b33781fE506E897a4F1e2d17255, // VRF Coordinator
0x326C977E6efc84E512bB9C30f76E30c160eD06FB // LINK Token
)
{
keyHash = 0x6e75b569a01ef56d18cab6a8e71e6600d6ce853834d4a5748b720d06f878b3a4;
fee = 0.0001 * 10 ** 18; // 0.0001 LINK (Varies by network)
}
function getRandomNumber() public returns (bytes32 requestId) {
require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK - fill contract with faucet");
return requestRandomness(keyHash, fee);
}
function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
randomResult = randomness;
random[msg.sender] = randomness;
}
}
here is the mapping
mapping(address => uint) public random;
random[msg.sender] = randomness;
Thanks!

You are close with what you have so far. I've updated the contract a bit and included notes below. The main change was a new mapping to store the request id -> address. This lets the fulfillRandomness function update the correct address -> randomNumber mapping.
pragma solidity ^0.8.0;
import "#chainlink/contracts/src/v0.8/VRFConsumerBase.sol";
contract RandomNumberConsumer is VRFConsumerBase {
bytes32 internal keyHash;
uint256 internal fee;
uint256 public randomResult;
// ** New mapping to store requests
mapping(bytes32 => address) public requestIdToAddress;
mapping(address => uint) public random;
constructor()
VRFConsumerBase(
0x8C7382F9D8f56b33781fE506E897a4F1e2d17255, // VRF Coordinator
0x326C977E6efc84E512bB9C30f76E30c160eD06FB // LINK Token
)
{
keyHash = 0x6e75b569a01ef56d18cab6a8e71e6600d6ce853834d4a5748b720d06f878b3a4;
fee = 0.0001 * 10 ** 18; // 0.0001 LINK (Varies by network)
}
function getRandomNumber() public {
require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK - fill contract with faucet");
// ** Store the random request in requestID
bytes32 requestId = requestRandomness(keyHash, fee);
// ** Map requestId to the sender's address
requestIdToAddress[requestId] = msg.sender;
}
function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
// ** Find the sender address based on the request
address _sender = requestIdToAddress[requestId];
// ** Update sender's mapping for randomness
random[_sender] = randomness;
}
}

Related

Solidity Payable function throwing error if it has a loop

I have a contract with the following payable function
Contract Constructor
constructor( address payable _NFT_CONTRACT, address payable _TOKEN_CONTRACT) payable {
NFT_CONTRACT = IERC721(_NFT_CONTRACT);
TOKEN_CONTRACT = IERC20(_TOKEN_CONTRACT);
}
Function Stake:
function stake(uint256[] calldata tokenIds, uint256[] calldata powers) external payable {
for (uint i = 0; i <= tokenIds.length; i++) {
uint256 tokenId = tokenIds[i];
uint256 power = powers[i];
require(NFT_CONTRACT.ownerOf(tokenId) == msg.sender, "You can only stake your own token");
require(vault[tokenId].tokenId == 0, "You can only stake once");
NFT_CONTRACT.safeTransferFrom(msg.sender, address(this), tokenId);
vault[tokenId] = Stake({
tokenId: tokenId,
timestamp: block.timestamp,
owner: msg.sender,
power: power
});
userStacks[msg.sender].push(tokenId);
hasPaid[tokenId] = 0;
}
}
vault, userStacks, and hasPaid are all structs.
struct Stake {
address owner;
uint256 tokenId;
uint256 timestamp;
uint256 power;
}
mapping(uint256 => Stake) public vault;
mapping(address => uint256[]) public userStacks;
mapping(uint256 => uint256) public hasPaid;
The problem:
Function stake works perfectly fine if I remove the for loop, but, throws this error if I have the loop:
revert
The transaction has been reverted to the initial state.
Note: The called function should be payable if you send value and the value you send should be less than your current balance.
Debug the transaction to get more information.
I am using solidity ^0.8.0;
I test with arrays such as [1,2], [4,5], for both params.
Thanks.

Chainlink v0.8 API Large Responses

I am following the Large Responses Chainlink tutorial and am receiving an error when trying to call the requestOracleData() function. Everything works fine when using v0.6, but I need to use v0.8 in order to receive large responses.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "#chainlink/contracts/src/v0.8/ChainlinkClient.sol";
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
/**
* #title MyContract is an example contract which requests data from
* the Chainlink network
* #dev This contract is designed to work on multiple networks, including
* local test networks
*/
contract MyContract is ChainlinkClient, ERC721 {
using Chainlink for Chainlink.Request;
// address constant ORACLE = 0xF405B99ACa8578B9eb989ee2b69D518aaDb90c1F;
// bytes32 constant JOB_ID = bytes32("7c4b968028f74b2eabd7d428f03ba45c");
address constant RINKEBY_ORACLE =
0x3A56aE4a2831C3d3514b5D7Af5578E45eBDb7a40;
bytes32 constant RINKEBY_JOB_ID =
bytes32("187bb80e5ee74a139734cac7475f3c6e");
uint256 constant FEE = 0.1 * 10**18;
bytes32 internal keyHash;
uint256 public fee;
uint256 public tokenCounter;
// bytes public data;
bytes32 public data;
string public image_url;
struct Clip {
string name;
string url;
}
Clip[] public clips;
mapping(bytes32 => string) public requestIdToClipName;
mapping(bytes32 => address) public requestIdToSender;
mapping(bytes32 => string) public requestIdToTokenURI;
event requestedCollectible(bytes32 indexed requestId);
mapping(bytes32 => uint256) public requestToTokenId;
mapping(uint256 => string) public tokenIdToName;
mapping(uint256 => string) public tokenIdToImgUrl;
constructor(address _link) public ERC721("Tests", "TST") {
if (_link == address(0)) {
setPublicChainlinkToken();
} else {
setChainlinkToken(_link);
}
setChainlinkOracle(0x3A56aE4a2831C3d3514b5D7Af5578E45eBDb7a40);
tokenCounter = 0;
}
function getChainlinkToken() public view returns (address) {
return chainlinkTokenAddress();
}
function createRequestTo(
string memory _url,
string memory _path,
string memory clipName
) public returns (bytes32 requestId) {
Chainlink.Request memory req = buildChainlinkRequest(
RINKEBY_JOB_ID,
address(this),
this.fulfill.selector
);
req.add("get", _url);
req.add("path", _path);
requestOracleData(req, FEE);
requestId = sendChainlinkRequestTo(RINKEBY_ORACLE, req, FEE);
requestIdToClipName[requestId] = clipName;
requestIdToSender[requestId] = msg.sender;
emit requestedCollectible(requestId);
return requestId;
}
function fulfill(bytes32 _requestId, bytes32 _data)
public
recordChainlinkFulfillment(_requestId)
{
address nftOwner = requestIdToSender[_requestId];
string memory name = requestIdToClipName[_requestId];
uint256 newItemId = clips.length;
data = _data;
image_url = bytes32ToString(data);
clips.push(Clip(name, image_url));
_safeMint(nftOwner, newItemId);
tokenIdToName[newItemId] = name;
tokenIdToImgUrl[newItemId] = image_url;
}
function getNumberOfClips() public view returns (uint256) {
return clips.length;
}
/**
* #notice Allows the owner to withdraw any LINK balance on the contract
*/
function withdrawLink() public {
LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
require(
link.transfer(msg.sender, link.balanceOf(address(this))),
"Unable to transfer"
);
}
/**
* #notice Call this method if no response is received within 5 minutes
* #param _requestId The ID that was generated for the request to cancel
* #param _payment The payment specified for the request to cancel
* #param _callbackFunctionId The bytes4 callback function ID specified for
* the request to cancel
* #param _expiration The expiration generated for the request to cancel
*/
function cancelRequest(
bytes32 _requestId,
uint256 _payment,
bytes4 _callbackFunctionId,
uint256 _expiration
) public {
cancelChainlinkRequest(
_requestId,
_payment,
_callbackFunctionId,
_expiration
);
}
function bytes32ToString(bytes32 _bytes32)
public
pure
returns (string memory)
{
uint8 i = 0;
while (i < 32 && _bytes32[i] != 0) {
i++;
}
bytes memory bytesArray = new bytes(i);
for (i = 0; i < 32 && _bytes32[i] != 0; i++) {
bytesArray[i] = _bytes32[i];
}
return string(bytesArray);
}
}
Here is the error I am getting:
Running 'scripts/request_data.py::main'...
File "brownie/_cli/run.py", line 49, in main
return_value, frame = run(
File "brownie/project/scripts.py", line 103, in run
return_value = f_locals[method_name](*args, **kwargs)
File "./scripts/request_data.py", line 18, in main
apicall = my_contract.createRequestTo(URL, PATH, "test", {"from": dev})
File "brownie/network/contract.py", line 1693, in __call__
return self.transact(*args)
File "brownie/network/contract.py", line 1566, in transact
return tx["from"].transfer(
File "brownie/network/account.py", line 642, in transfer
receipt, exc = self._make_transaction(
File "brownie/network/account.py", line 725, in _make_transaction
raise VirtualMachineError(e) from None
File "brownie/exceptions.py", line 121, in __init__
raise ValueError(str(exc)) from None
ValueError: Gas estimation failed: 'execution reverted: Must use whitelisted functions'. This transaction will likely revert. If you wish to broadcast, you must set the gas limit manually.
Note: this error doesn't occur when I comment out the requestOracleData() function.
It is due to the fact that the node operator you are using on Rinkeby isn't set up to handle large requests and is most likely using the Oracle.sol code as their oracle contract, which doesn't accept the requestOracleData() method.
You can either switch to Kovan and use the node given in the docs, search for a different but compatible node on market.link, or host your own node on Rinkeby which is compatible with this job.

Problem in fetching data from external API on Matic Mumbai Testnet

This is the documentation of my used API.
The price is still showing 0 as you can see in this image reference.
Here is my code:
pragma solidity ^0.6.0;
import "#chainlink/contracts/src/v0.6/ChainlinkClient.sol";
contract APIConsumer is ChainlinkClient {
event below20(uint _price);
event below30(uint _price);
event below40(uint _price);
event below50(uint _price);
uint256 public Price;
address private oracle;
bytes32 private jobId;
uint256 private fee;
constructor() public {
setChainlinkToken(0x326C977E6efc84E512bB9C30f76E30c160eD06FB);
oracle = 0xb33D8A4e62236eA91F3a8fD7ab15A95B9B7eEc7D;
jobId = "da20aae0e4c843f6949e5cb3f7cfe8c";
fee = 10 ** 16; // 0.01 LINK
}
function requestBTCCNYPrice() public returns (bytes32 requestId)
{
Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
// Set the URL to perform the GET request on
request.add("get", "https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=BTC&to_currency=CNY&apikey=demo");
string[] memory path = new string[](2);
path[0] = "Realtime Currency Exchange Rate";
path[1] = "5. Exchange Rate";
request.addStringArray("path", path);
request.addInt("times", 10000000000);
// Sends the request
return sendChainlinkRequestTo(oracle, request, fee);
}
/**
* Receive the response in the form of uint256
*/
function fulfill(bytes32 _requestId, uint256 _price) public recordChainlinkFulfillment(_requestId)
{
Price = _price;
}
That node is down. Use the following node and JobID in this code:
pragma solidity ^0.6.0;
import "#chainlink/contracts/src/v0.6/ChainlinkClient.sol";
contract APIConsumer is ChainlinkClient {
event below20(uint _price);
event below30(uint _price);
event below40(uint _price);
event below50(uint _price);
uint256 public Price;
address private oracle;
bytes32 private jobId;
uint256 private fee;
constructor() public {
setChainlinkToken(0x326C977E6efc84E512bB9C30f76E30c160eD06FB);
oracle = 0xc8D925525CA8759812d0c299B90247917d4d4b7C;
jobId = "bbf0badad29d49dc887504bacfbb905b";
fee = 10 ** 16; // 0.01 LINK
}
function requestBTCCNYPrice() public returns (bytes32 requestId)
{
Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
// Set the URL to perform the GET request on
request.add("get", "https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=BTC&to_currency=CNY&apikey=demo");
string[] memory path = new string[](2);
path[0] = "Realtime Currency Exchange Rate";
path[1] = "5. Exchange Rate";
request.addStringArray("path", path);
request.addInt("times", 10000000000);
// Sends the request
return sendChainlinkRequestTo(oracle, request, fee);
}
/**
* Receive the response in the form of uint256
*/
function fulfill(bytes32 _requestId, uint256 _price) public recordChainlinkFulfillment(_requestId)
{
Price = _price;
}
}
To check to see if a node is running or not, check out the oracle address in a block explorer. You can see here that the original node you tried to use hasn't posted a transaction in quite a long time.
To find nodes and jobs that are currently working, you should check market.link.

Chainlink VRF - how to check fullfillRandomness result working

I am working on smart contract to generate random cards with some specific rules, I was trying to use Chainlink VRF to generate random number and implemented such contract.
//SPDX-License-Identifier: Unlicense
pragma solidity >=0.7.4;
import "#chainlink/contracts/src/v0.8/VRFConsumerBase.sol";
import "../utils/Context.sol";
import "../utils/ReentrancyGuard.sol";
import "../utils/Address.sol";
import "../NFT/CardNFTAccessControls.sol";
import "../NFT/CardNFT.sol";
/**
* #notice Factory contract for NFT handling payments on mint
*/
contract NFTFactory is Context, ReentrancyGuard, VRFConsumerBase {
// using SafeMath for uint256;
using Address for address payable;
struct CardInfo {
uint256 num;
uint256 kind;
uint256 value;
uint256 qty;
}
struct NFTInfo {
uint256 edition;
uint256 cardInfoId;
}
struct RequestNFTConfig {
uint256 limit;
bool kind;
uint256 id;
}
event NFTCreated(
uint256 id,
uint256 num,
uint256 kind,
uint256 edition
);
event RandomReturned(
bytes32 requestId,
uint256 randomness
);
/// #notice chainlink rng request value mapping
mapping(bytes32 => RequestNFTConfig) private requestIds;
/// #notice edition number id
uint256 private editionId;
/// #notice for switching off factory functionality
bool public isPaused;
/// #notice responsible for enforcing mint and admin role
CardNFTAccessControls accessControls;
/// #notice NFT contract
CardNFT cardNFT;
/// #notice platform fee recipient address which will accept all fees and mint price
address payable platformFeeRecipient;
/// #notice keyhash for chainlink rng generator
bytes32 internal keyHash;
/// #notice fee for chainlink rng generator
uint256 internal rngFee;
/// #notice rng generator result
uint256 public randomResult;
/// #notice deck card information for calculation
mapping(uint256 => CardInfo) deckInfo;
/// #notice nft array
mapping(uint256 => NFTInfo) nfts;
modifier whenNotPaused() {
require(!isPaused, "Function is currently paused");
_;
}
constructor(
CardNFTAccessControls _accessControls,
CardNFT _cardNFT,
address payable _platformFeeRecipient
) VRFConsumerBase(
0xa555fC018435bef5A13C6c6870a9d4C11DEC329C,
0x84b9B910527Ad5C03A9Ca831909E21e236EA7b06
) {
require(address(_accessControls) != address(0), "NFTFactory: Invalid Access Controls");
require(address(_cardNFT) != address(0), "NFTFactory: Invalid NFT");
require(_platformFeeRecipient != address(0), "NFTFactory: Invalid platform Fee recipient");
accessControls = _accessControls;
cardNFT = _cardNFT;
platformFeeRecipient = _platformFeeRecipient;
keyHash = 0xcaf3c3727e033261d383b315559476f48034c13b18f8cafed4d871abe5049186;
rngFee = 0.1 * 10 ** 18; // 0.1 LINK
}
/**
#notice create one NFT
#param _limit min or maximum number of card
#param _kind limit kind with lower or higher
#param _price price of NFT mint
#param _amount amount of NFTs to mint
#param _data additional data to leave
*/
function create(uint256 _limit, bool _kind, uint256 _price, uint256 _amount, bytes memory _data) external payable nonReentrant whenNotPaused {
require(_msgSender().isContract() == false, "Factory.create: No contracts are permitted");
require(_msgSender() != address(0), "Factory.create: sender address is ZERO");
uint256 buyPrice = msg.value;
require(buyPrice >= _price, "Factory.create: payment should be same to original price");
// (bool platformTransferSuccess,) = platformFeeRecipient.call{value : buyPrice}("");
// require(platformTransferSuccess, "Factory.mintPayment: Failed to send platform fee");
address creator = _msgSender();
if(_amount > 1) {
uint256[] memory _ids = cardNFT.batchMint(creator, _amount, _data);
require(_amount == _ids.length, "Factory.create: should batch mint same amount of nfts");
for(uint256 i=0; i<_ids.length; i++) {
getRandomNumber(_ids[i], _limit, _kind);
}
} else {
uint256 id = cardNFT.mint(creator, _data);
getRandomNumber(id, _limit, _kind);
}
}
/**
#notice generate actual NFT info
#param _requestId request id for chainlink rng
#param _randomness random number which return from chainlink org
*/
function generateNFTInfo(bytes32 _requestId, uint256 _randomness) private {
RequestNFTConfig storage config = requestIds[_requestId];
uint256 total = getTotalQtyToNumber(config.limit);
bool _kind = config.kind;
uint256 rngResult = 0;
if(_kind) {
rngResult = _randomness * (200000 - total) + total;
} else {
rngResult = _randomness * total;
}
uint256 cardInfoId = getNearestQtyCard(rngResult);
NFTInfo storage nft = nfts[config.id];
uint256 edition = _getNextEditionID();
_incrementEditionId();
nft.edition = edition;
nft.cardInfoId = cardInfoId;
CardInfo storage cardInfo = deckInfo[cardInfoId];
emit NFTCreated(config.id, cardInfo.num, cardInfo.kind, edition);
}
/**
* Requests randomness
*/
function getRandomNumber(uint256 _id, uint256 _limit, bool _kind) private {
require(LINK.balanceOf(address(this)) >= rngFee, "Not enough LINK - fill contract with faucet");
bytes32 requestId = requestRandomness(keyHash, rngFee);
RequestNFTConfig storage config = requestIds[requestId];
config.id = _id;
config.kind = _kind;
config.limit = _limit;
}
/**
* Callback function used by VRF Coordinator
*/
function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
emit RandomReturned(requestId, randomness);
generateNFTInfo(requestId, randomness);
}
......
}
I tried this one on BSC testnet but no RandomReturned event log and also no updates with generateNFTInfo function.
Also I am not sure how to test unit test on my local without installing chainlink-node on my local.
Can anybody help me on this one?
I thought I ran into the same issue, however, I found that it can take over 2 minutes to generate a random number depending on network congestion. I am testing in the Rinkeby network.

Member "add" not found or not visible after argument-dependent lookup in struct Chainlink.Request memory

I am creating nft smart contract and using chainlinkClient.sol for updation of URI.
chainlinkClient Version: v0.6
Error: TypeError: Member "add" not found or not visible after argument-dependent lookup in struct Chainlink.Request memory.
Any Idea Why am I getting this error?
Code:
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.9.0;
import "chainlink/evm-contracts/src/v0.6/ChainlinkClient.sol";
contract phoneNumber is ChainlinkClient{
uint256 public phone_no;
address private oracle;
bytes32 private jobId;
uint256 private fee;
constructor(uint256 _initial_phone_no){
setPublicChainlinkToken();
oracle = 0x7AFe1118Ea78C1eae84ca8feE5C65Bc76CcF879e;
jobId = "6d1bfe27e7034b1d87b5270556b17277";
fee = 0.1 * 10 ** 18; // 0.1 LINK
phone_no = _initial_phone_no;
}
function requestVolumeData() public returns (bytes32 requestId)
{
Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.update_phone_no.selector);
// Set the URL to perform the GET request on
request.add("get", "http://localhost:3000/images/phone.json");
request.add("path", "phone.new");
// Multiply the result by 1000000000000000000 to remove decimals
int timesAmount = 10**18;
request.addInt("times", timesAmount);
// Sends the request
return sendChainlinkRequestTo(oracle, request, fee);
}
function update_phone_no(bytes32 _requestId, uint256 new_phone_no) public recordChainlinkFulfillment(_requestId)
{
phone_no = new_phone_no;
}
function withdrawLink() external {
LinkTokenInterface linkToken = LinkTokenInterface(chainlinkTokenAddress());
require(linkToken.transfer(msg.sender, linkToken.balanceOf(address(this))), "Unable to transfer");
}
}
Your import statement is missing an "#" symbol. Add it to the beginning like this:
import "#chainlink/contracts/src/v0.6/ChainlinkClient.sol";
Note: Also don't forget to explicitly state your constructor visibility specifier to public or internal as well.

Resources