I want to build an NFT that I can paid with an ERC-20 token to mint it. I'm using currently the Mumbai testnet on polygon, and I'm using the Dummy ERC20 token to test it out.
This is currently my constructor:
ERC20 token;
constructor() ERC721("Token", "TKN") {
token = ERC20(0xfe4F5145f6e09952a5ba9e956ED0C25e3Fa4c7F1);
}
And this is my mint function:
function mint() public returns (uint256) {
uint256 tokenId = _tokenIds.current();
require(tokenId <= MAX_TOKEN_ID);
token.approve(address(this), NFT_PRICE);
token.transfer(address(this), NFT_PRICE);
_mint(msg.sender, tokenId);
_setTokenURI(tokenId, TOKEN_URI);
_tokenIds.increment();
return tokenId;
}
If I remove those 2 lines the code works fine, it mints the NFT:
token.approve(address(this), NFT_PRICE);
token.transfer(address(this), NFT_PRICE);
But as soon as I add it, the code breaks, it gives me the following gas estimation error:
The transaction execution will likely fail. Do you want to force sending?
Internal JSON-RPC error. { "code": 3, "message": "execution reverted: ERC20: transfer amount exceeds balance", "data": "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002645524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e63650000000000000000000000000000000000000000000000000000" }
As a troubleshooting step I've also added this inside my mint function to make sure I'm calling from my own wallet:
sender = msg.sender;
And created this function:
function tokenBalance(address addr) public view returns (uint256) {
return token.balanceOf(addr);
}
And if I grab the token balance of the sender address it gives me the value:
0: uint256: 2000000000000000000
It's because the logic is wrong. That approve function you call inside your mint function is useless: the spender needs to call the approve function from the the contract of your dummy ERC20.
And then you can call the transferFrom(msg.sender, address(this), NFT_PRICE) from your mint function.
Related
I'm trying to build an external adapter for a chainlink node to import API information. On the chainlink node and API, everything seems like it worked, however when I try to call the stored value from the smart contract, it's always 0 despite the logs indicating that it ran successfully.
type = "directrequest"
schemaVersion = 1
name = "Mimi-Fund-EA"
externalJobID = "834d2179-321d-49ac-bf63-140635e3a606"
forwardingAllowed = false
maxTaskDuration = "0s"
contractAddress = "0xAf644831B57E5625ac64cDa68248b810bE4D4D01"
minContractPaymentLinkJuels = "0"
observationSource = """
decode_log [type=ethabidecodelog
abi="OracleRequest(bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data)"
data="$(jobRun.logData)"
topics="$(jobRun.logTopics)"]
decode_cbor [type=cborparse data="$(decode_log.data)"]
fetch [type=bridge name="mimifund" requestData="{\\"id\\": $(jobSpec.externalJobID), \\"data\\": { \\"year\\": $(decode_cbor.year), \\"discount_rate\\": $(decode_cbor.discount_rate)}}"]
parse [type=jsonparse path="data,result" data="$(fetch)"]
ds_multiply [type="multiply" times=1000000000000000000]
encode_data [type=ethabiencode abi="(uint256 value)" data="{ \\"value\\": $(ds_multiply) }"]
encode_tx [type=ethabiencode
abi="fulfillOracleRequest(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes32 data)"
data="{\\"requestId\\": $(decode_log.requestId), \\"payment\\": $(decode_log.payment), \\"callbackAddress\\": $(decode_log.callbackAddr), \\"callbackFunctionId\\": $(decode_log.callbackFunctionId), \\"expiration\\": $(decode_log.cancelExpiration), \\"data\\": $(encode_data)}"
]
submit_tx [type=ethtx to="0xAf644831B57E5625ac64cDa68248b810bE4D4D01" data="$(encode_tx)"]
decode_log -> decode_cbor -> fetch -> parse -> ds_multiply-> encode_data -> encode_tx -> submit_tx
"""
These are the run logs from the Node. Everything compiled just fine and the values look good however, they never update in a smart contract, it's always 0.
This is my smart contract for reference.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "#chainlink/contracts/src/v0.8/ChainlinkClient.sol";
import "#chainlink/contracts/src/v0.8/ConfirmedOwner.sol";
import "#openzeppelin/contracts/utils/Strings.sol";
contract mimifundCO2 is ChainlinkClient, ConfirmedOwner {
using Chainlink for Chainlink.Request;
uint256 public volume;
bytes32 private jobId;
uint256 private fee;
event RequestVolume(bytes32 indexed requestId, uint256 volume);
/**
* #notice Initialize the link token and target oracle
*
* Goerli Testnet details:
* Link Token: 0x326C977E6efc84E512bB9C30f76E30c160eD06FB
* Oracle: 0xCC79157eb46F5624204f47AB42b3906cAA40eaB7 (Chainlink DevRel)
* jobId: ca98366cc7314957b8c012c72f05aeeb
*
*/
constructor() ConfirmedOwner(msg.sender) {
setChainlinkToken(0x326C977E6efc84E512bB9C30f76E30c160eD06FB);
setChainlinkOracle(0xAf644831B57E5625ac64cDa68248b810bE4D4D01);
jobId = "834d2179321d49acbf63140635e3a606";
fee = (1 * LINK_DIVISIBILITY) / 10; // 0,1 * 10**18 (Varies by network and job)
}
/**
* Create a Chainlink request to retrieve API response, find the target
* data, then multiply by 1000000000000000000 (to remove decimal places from data).
*/
function requestCO2PricingData(uint256 _year) public returns (bytes32 requestId) {
Chainlink.Request memory req = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
req.add('year', Strings.toString(_year)); // Chainlink nodes 1.0.0 and later support this format
req.add('discount_rate', '0.0'); // Chainlink nodes 1.0.0 and later support this format
// Sends the request
return sendChainlinkRequest(req, fee);
}
/**
* Receive the response in the form of uint256
*/
function fulfill(bytes32 _requestId, uint256 _volume) public recordChainlinkFulfillment(_requestId) {
emit RequestVolume(_requestId, _volume);
volume = _volume;
}
/**
* Allow withdraw of Link tokens from the contract
*/
function withdrawLink() public onlyOwner {
LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
require(link.transfer(msg.sender, link.balanceOf(address(this))), 'Unable to transfer');
}
}
I feel like there is some update to either my fulfill function or submit_tx that I need to update, but I am out of ideas about what to change.
I've tried changing all the parameters and both the API and chainlink node accurate update and reflect the correct input. The smart contract seems to work perfectly, it's just that calling volume in the code always returns 0 and I've got no clue what the issue is.
According to your description, Chainlink node received your request and run the job successfully, but failed to write the result back to your contract. There might be multiple reasons, but most likely something is wrong when the Chainlink node calls the function fulfillOracleRequest2 in the Operator.sol and fails to write the result back to your contract.
Please check the following:
Check if you fund your Chainlink node. You can check the balance of the chainlink node in the top right of the Chainlink node UI which is usually with port number 6688(eg. http://localhost:6688). Because the Chainlink node changes the state of the blockchain when calling the function in the contract operator, there has to be a minimum balance of ETH remaining in your Chainlink node. The solution to the issue is just to transfer some ETH(not LINK) tokens to your chainlink node address.
Check if you grant the Chainlink node permission to call function fulfillOracleRequest2 in the contract operator. Search your Chainlink node address in the blockchain explorer like etherscan, goerliscan, polygonscan, etc. and if the node has no permission to call function fulfillOracleRequest, error Fail with error 'Not authorized sender' will be thrown. The solution to the issue is to use the function setAuthorizedSenders to grant the node address permission to call the function fulfillOracleRequest2.
I'm fresh on solidity, when I use remix to test my contract, I want to transfer some eth from my account to the smart contract. I have tried this code, but it seems to transfer the eth from the contract but not my account.
function addStaker (uint _stakeAmount) public membership(master, msg.sender) returns(bool) {
if(!members[msg.sender].alreadyExist) {
Member memory newMember = Member(msg.sender, true);
members[msg.sender] = newMember;
bool sent = payable(address(this)).send(_stakeAmount);
require(sent, "invalid balance");
return true;
} else {
return false;
}
}
How could I transfer eth from my account to the smart contract?
A smart contract is not able to pull a contract-specified amount of ETH from an address. The amount needs to always be specified and signed by the sender.
The contract can only validate the sent amount:
function addStaker() public payable {
require(msg.value == 1 ether);
}
How to chose the amount depends on the wallet software you're using. In case of Remix, there's the "Value" input in the "Deploy & Run Transactions" tab, where you specify the amount of ETH sent with the transaction.
If you want to implement a defi contract (since your function name is addStaker) that accepts staked coins from ERC20 tokens, the implementation is different. But if you just want to send money to contract from your metamask account, you have to mark the function payable.
function pay() public payable {
// msg.value is the amount of wei sent with the message to the contract.
// with this you are setting a minimum amount
require(msg.value > .01 ether);
// add your logic
}
I've been learning the Chainlink API and trying to build a simple contract that will make an external call to an API and charge the user based on the result of the request from the Oracle.
For example, "We will charge you $1 if the API results in true and $0.25 if it results in false"
I am running this on the Kovan Testnet, the contract is funded with LINK. The transaction is successful every time I run the "requestCompletedData" function. But the callback/fulfill function never gets ran. I've checked it in various ways.
For reference, it should result completed == true based on the data from the URL.
Here are the Contract Address and Job ID for Chainlink's Kovan test nodes: https://docs.chain.link/docs/decentralized-oracles-ethereum-mainnet/
//
constructor() public {
setPublicChainlinkToken();
oracle = 0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e;
jobId = "6d914edc36e14d6c880c9c55bda5bc04";
fee = 0.1 * 10 * 18; // 0.1 LINK
}
// Make Chainlink request
function requestCompletedData() public returns (bytes32 requestId) {
Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
// URL for request
request.add("get", "https://jsonplaceholder.typicode.com/todos/4");
// Path to the final needed data point in the JSON response
request.add("path", "completed");
return sendChainlinkRequestTo(oracle, request, fee);
}
function fulfill(bytes32 _requestId, bool _completed) public recordChainlinkFulfillment(_requestId) {
validateChainlinkCallback(_requestId);
completed = _completed;
}
Thank you for your help!
Remove the validateChainlinkCallback(_requestId) line in your fulfill() method and it will work.
function fulfill(bytes32 _requestId, bool _completed) public recordChainlinkFulfillment(_requestId) {
completed = _completed;
}
The fulfill() method already has the recordChainlinkFulfillment modifier which runs the same validation as the validateChainlinkCallback(_requestId) method anyway.
Reference: ChainlinkClient source code.
I am playing with Ethereum smart contracts and noticed some strange behavior of some transactions.
Transaction is visible and it states that tokens are sent to an address but when I check balance on Metamask it's not here (Token contract is added to MetaMask). What could be the reason of such contract behavior?
This is example transaction: https://ropsten.etherscan.io/tx/0xf3316c497fd47ee79be57be7b91cb96cc0414003a155d8ff8bc83b3a090594c9
200,000 tokens is sent to https://ropsten.etherscan.io/address/0x9DEF8C013eb590aFf09c8c76F9A74A3055fAA177#tokentxns and it's displayed on ropsten network explorer, but I can't see them or manipulate on MetaMask.
This is part of my smart contract which does transfer, I marked line with comment "invisible" which does not work for me:
function transfer(address to, uint256 value) public returns (bool) {
require(value <= _balances[msg.sender]);
require(to != address(0));
uint256 tokensToBurn = findOnePercent(value);
uint256 tokensToStack = findOnePercent(value);
uint256 tokenstoDeduct = tokensToBurn.add(tokensToStack);
uint256 tokensToTransfer = value.sub(tokenstoDeduct);
_balances[msg.sender] = _balances[msg.sender].sub(value);
_balances[to] = _balances[to].add(tokensToTransfer);
_totalSupply = _totalSupply.sub(tokenstoDeduct);
emit Transfer(msg.sender, to, tokensToTransfer); // visible on metaMask
emit Transfer(msg.sender, address(0), tokensToBurn); // burned
emit Transfer(msg.sender, address(VAULT_ADDRESS), tokensToStack); // invisible on metaMask
return true;
}
This is screenshot of address wallet: 0x9DEF8C013eb590aFf09c8c76F9A74A3055fAA177
Please advise what I could do wrong here.
[meanwhile I submitted ticket to MetaMask, maybe it's a defect on wallet extension]
You need to add the contract address to your Metamask instance.
In the account detail, click on "Add token"
Paste the conctract address to the address field in the "Custom token" tab
Review the data, click "Next", and you you can see the token balance in your wallet.
I am developing smart contract based on openzeppelin-solidity and I want to write an easy Crowdsale contract, only I did is inherit Contract.sol:
// FloatFlowerTokenCrowdsale.sol
pragma solidity 0.4.23;
import "openzeppelin-solidity/contracts/crowdsale/Crowdsale.sol";
contract FloatFlowerTokenCrowdsale is Crowdsale{
constructor(ERC20 _token) public Crowdsale(1000, msg.sender, _token)
{
}
}
Here is my FloatFlowerToken.sol
// FloatFlowerToken.sol
pragma solidity 0.4.23;
import "openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol";
contract FloatFlowerToken is StandardToken {
string public name = "FLOATFLOWER TOKEN";
string public symbol = "FFT";
uint8 public decimals = 18;
constructor() public {
totalSupply_ = 36000000;
balances[msg.sender] = totalSupply_;
}
}
And this is my 2_deploy_contract.js
const FloatFlowerToken = artifacts.require('./FloatFlowerToken.sol');
const FloatFlowerTokenCrowdsale =
artifacts.require('./FloatFlowerTokenCrowdsale.sol');
module.exports = function(deployer, network, accounts) {
return deployer
.then(() => {
return deployer.deploy(FloatFlowerToken);
})
.then(() => {
return deployer.deploy(FloatFlowerTokenCrowdsale, FloatFlowerToken.address);
})
};
After I execute the truffle test and I got the error Error: VM Exception while processing transaction: revert
And here is my test code:
it('one ETH should buy 1000 FLOATFLOWER TOKEN in Crowdsale', function(done) {
FloatFlowerTokenCrowdsale.deployed().then(async function(instance) {
const data = await instance.sendTransaction({from: accounts[7], value: web3.toWei(1, "ether")}, function(error, txhash) {
console.log(error);
});
const tokenAddress = await instance.token.call();
const FloatFlowerToken = FloatFlowerToken.at(tokenAddress);
const tokenAmount = await FloatFlowerToken.balanceOf(accounts[7]);
assert.equal(tokenAmount.toNumber(), 1000000000000000000000, 'The sender didn\'t receive the tokens as crowdsale rate.');
})
})
I don't know how to check the error log and to know which line cause this problem.
You have 2 issues:
First, the units you're working with aren't correct. You've initialized your crowdsale to sell 1000 tokens for every Wei sent. From the documentation in the Zeppelin contract:
#param _rate Number of token units a buyer gets per wei
#param _wallet Address where collected funds will be forwarded to
#param _token Address of the token being sold
You're passing in 1 ether in your transaction, which means you're attempting to buy 1000 * (10^18) token units, but you've only allocated 36000000 total supply. You need to increase your total supply and/or lower your rate.
Second, only token owners can do a transfer unless an approve has been done first. When you deploy the token contract, all of the tokens are owned by msg.sender. However, when someone makes a purchase through your crowdsale contract, the request to do the transfer is coming from the address of the crowdsale contract, not the token owner when your token contract was deployed. The simplest way around this is after deploying your contracts, transfer enough tokens for the crowdsale from the address you used to create the token contract over to the address of the crowdsale contract.