How to airdrop NFTs to specific NFT owners? - solidity

I want to understand how can we give free ERC721 or ERC20 tokens to specific NFT owner addresses. For example Bored Ape yacht club created an ERC20 coin with a pre-defined amount which can be claimed only from the owners of the BAYC NFTs. I tried to find out an answer in their smart contracts, but I couldn't find their ERC20 coin contract therefore I can't figure out how the restrict the distribution of coins.
In my project I want to create 2 ERC721 smart contracts and all owners of NFTs from the first contract should be able to mint for free NFTs from the second smart contract. If you are an owner of an NFT from smart contract 1 you can claim free NFT from smart contract 2. Can you provide me with some resources or ideas where I can learn how to achieve that

You can, in the second smart contract, check whether the caller of the mint function is a token holder of the first smart contract or not.
function mint() external {
require(IERC721(_firstToken).balanceOf(msg.sender) > 0, 'should be a holder of the first token');
_mint();
}
You can import ERC721 interface from openzeppelin library, or just cut&paste from EIP-721.

There must be some restrictions on how many nftTwo tokens can be minted per single nftOne token. Otherwise, you will be exploited and users will be able to mint an unlimited amount of nftTwo tokens.
IERC721 public nftOne;
uint public nftTwoMaxMintCount;
mapping(uint => uint) public nftTwoMints;
function mintNftTwo(uint nftOneTokenId) external {
// Only the owner of nftOne token can execute mint
require(msg.sender == nftOne.ownerOf(nftOneTokenId), "not the owner of nftOne token");
// The number of allowed nftTwo token mints is limited by nftTwoMaxMintCount
require(nftTwoMints[nftOneTokenId] <= nftTwoMaxMintCount, "nftTwo token mints overflow");
// Increment the number of minted nftTwo tokens per nftOne token id
nftTwoMints[nftOneTokenId] += 1;
// Execute mint
_mintNftTwo();
}
Please check OpenZeppelin's implementation of the ERC721 and read their docs for more details.

Related

how can i change the owner of the ecr20 token aka msg.sender to a new owner if anything happened to the owner(inactivity for a period of time)

I want to make a function that changes the ownership of the ecr20 token incase the true owner is killed or anything related. how can we test his inactivity to check if we should transfer ownership? and is it possible to split the ownership in equity between like 10 people
One small thing to point out is msg.sender does not necessarily refer to the owner of the erc20 token, but it refers to the address that calls the smart contract function. So the functionality you are looking for you need to override the erc20 token contract. One thing you can do is maintain a mapping to track when the last time an owner did something the erc20 token:
mapping (address=>uint256) lastUpdated;
With every function call, you can update the lastUpdated variable.
lastUpdated = block.timestamp;
Whether or not you want to update it for view functions is up to you. Now you can create an extra function on the ERC20 token that enables anyone to transfer the token to a new owner if there is a certain period of inactivity.
function removeTokenFromInactiveAccount(address inactiveUser, address transferTo) public {
require(block.timestamp > lastUpdated[inactiveUser]+inactivePeriod, "Inactive period threshold not reached");
transferFrom(inactiveUser, transferTo, balanceOf(inactiveUser));
}
But there is a problem here, the contract has to send the token on behalf of the inactive user. This is not possible if the inactive user does not increase the allowance for the contract. So a workaround would be to for every erc20 owner the allowance for the contract address could be set to a large number or update the allowance every time the balance increases. Here is a sample of the first approach:
_allowed[owner][this(address)] = // <large number>
The above example assumes Openzepplin ERC20 implementation. Hope this helps.
how can we test his inactivity to check if we should transfer ownership
In a Solidity contract, you can only check activity directly related to this contract. Your code does not have access to read other transactions of the owner or any other user to check whether they are active elsewhere. In the example below, the owner needs to invoke the ping() function at least once a year - otherwise the claimOwnership() unlocks for anyone else to claim the ownership.
pragma solidity ^0.8;
import "#openzeppelin/contracts/access/Ownable.sol";
contract MyContract is Ownable {
uint256 lastPingTimestamp;
// can be executed only by the owner
function ping() public onlyOwner {
lastPingTimestamp = block.timestamp;
}
// can be executed by anyone
function claimOwnership() public {
// reverts if at least a year hasn't passed since the last owner ping
require(lastPingTimestamp < block.timestamp - 356 days);
// make the user invoking this function the new owner
_transferOwnership(msg.sender);
lastPingTimestamp = block.timestamp;
}
}
Note that claimOwnership() in this example is simplified and not resistant against frontrunning. In real-life scenarion, you might want to add some commit logic or a list of preauthorized addresses that are able to claim ownership.
is it possible to split the ownership in equity between like 10 people
OpenZeppelin published the AccessControl library that allows you to give authorized persmissions to several addresses. Here's an example:
pragma solidity ^0.8;
import "#openzeppelin/contracts/access/AccessControl.sol";
contract MyContract is AccessControl {
constructor(address[] memory admins) {
for (uint i; i < admins.length; i++) {
_setupRole(DEFAULT_ADMIN_ROLE, admins[i]);
}
}
}

transfer ERC20 on behalf of token owner after a payment of ether occurs

I have the following scenario:
I have a contract to generate a limited amount of erc20 tokens. User A buys X amount of this token. Then user A wants to sell some of his tokens, for X amount of ether, then user B wants to buy tokens of user A, how can I automatically transfer tokens from user A to user B when the transfer of ether from user B to user A has been successful be transfer?
Do I need a second contract to handle this kind of operation?
You may have two options,
Play around with Escrow SmartContracts (Like Openzeppelin) and modify it as per your need.
Use ERC20 Approve(spender,amount) on User B, to get allowance for your contract to spend on behalf of B. (spender : Your contract address)
Run a web3 script to listen on events to catch the corresponding transaction of sending ether from B->A ,
once the ether is transferred, you can place a function call to smart contract to spend the token from B to A
In your ERC20 Contract implementation, you can have a mapping of address -> eth, where the eth represents the selling price each user has set up.
Now, when a user "sells" their token, they can allow the same contract to transfer X amount of tokens from their balance which they want to sell.
And to sell, you can create a payable function that receives ether, and via parameter a seller address. This function will dynamically ( using the address received on the mapping we created at the beginning ) transfer X tokens to the buyer, using that seller's token price and the amount of ether received.
For example: If 1TKs = 0.5ETH, and the buyer sends 2eth, the contract will transfer 4TKS.
Recomendation.
Usually, the token contract and the seller/buyer functions are done in different contracts for security reasons, for example having the actual token, and the token shop separated on different contracts.
Disclaimer.
This is the simplest implementation I could think of, but it will accomplish your requirements.

Could ChainLink facilitate getting the current Ask/Bid price from DEX?

Could ChainLink facilitate getting the current Ask/Bid price from DEX
like Binance and PancakeSwap?
"bidPrice" and "askPrice" on Binance
https://github.com/binance/binance-spot-api-docs/blob/master/rest-api.md#new-order--trade
"price" on PancakeSwap
https://github.com/pancakeswap/pancake-info-api/blob/develop/v2-documentation.md
Could you show an example of how to do this?
Thank you!
If the data is accessible via an API then you can use Chainlink Any-API calls to bring it into your smart contract
To do so, you need to know 3 things
The API endpoint that contains the data, and the inputs required
The outputs that the API returns, including their types (integer, string etc), as well as the path in the resulting JSON that contains the data you want
A Chainlink oracle on the network you're contract is on that has a compatible job that you can use (whether one you run yourself or someone elses)
Once you have these things, you can use the example consumer contract in the docs linked above, and then change the values to suit. ie here is an example contract that will make an API call to PancakeSwap to get the price of PancakeSwap token on BSC testnet:
1 - API address and inputs. In this case, according to your linked docs, the URL of the API call is https://api.pancakeswap.info/api/v2/tokens/0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82. The only input required is the token address in the URL
2 - We want the price, which is an integer and in the 'price' JSON element. We will multiply the price by 10**8 when we bring it on-chain because Solidity can't handle decimals
3 - Because this is a simple API call, we can use a community run CL node that takes a HTTP GET request, parses the JSON to find an element we specify, then multiplies the result and converts it to the type we want before returning it on-chain. Taking a look at the BSC testnet jobs on market.link, I found a suitable one here (GET, multiples result, returns a uint). From here we take the job ID and the oracle address, and note the cost in LINK required to use it
Now that we have all these details, we can modify the standard API consumer contract and put them all in, as follows. Changes I made include updating variables to reflect price instead of volume, also i changed the variables for job, oracle contract and fee, and i changed the setPublicChainlinkToken() method in the contructor to setChainlinkToken, specifically passing in the address of the LINK token on BSC testnet
pragma solidity ^0.8.7;
import "#chainlink/contracts/src/v0.8/ChainlinkClient.sol";
contract APIConsumer is ChainlinkClient {
using Chainlink for Chainlink.Request;
uint256 public price;
address private oracle;
bytes32 private jobId;
uint256 private fee;
constructor() {
setChainlinkToken(0x84b9B910527Ad5C03A9Ca831909E21e236EA7b06);
oracle = 0x19f7f3bF88CB208B0C422CC2b8E2bd23ee461DD1;
jobId = "9b32442f55d74362b26c608c6e9bb80c";
fee = 0.0001 * 10 ** 18; // (Varies by network and job)
}
function requestPriceData() public returns (bytes32 requestId)
{
Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
request.add("get", "https://api.pancakeswap.info/api/v2/tokens/0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82");
request.add("path", "price");
// 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 fulfill(bytes32 _requestId, uint256 _price) public recordChainlinkFulfillment(_requestId)
{
price = _price;
}
}
Once you compile and deploy, you then need to fund the contract with enough link to perform the request. You can get some testnet BSC LINK from the faucet, then transfer enough from your wallet to the deployed contract (in this case 0.001 LINK)
Once that's done you can execute the requestPriceData function, wait 30 secs then check the price getter function to see if you have a result. If you don't have a result after a while (1 min), it could mean the BSC node isn't up still. You can either run your own node, or use another network like Ethereum Kovan or Polygon Mumbai, which has many more active jobs

Reason provided by the contract: "ERC20: transfer amount exceeds allowance"

I'm new to solidity and I wanted to develop a subscription contract where a user can subscribe to a merchants plan and pay. But I'm unable to subscribe function and transfer the token to merchant.
I'm using open zeppelin ERC20 standard token to transfer.
IERC20 token = IERC20(plans[planId].token);
token.transferFrom(
payable(msg.sender),
payable(plan.merchant),
plan.amount
);
I don't know why but this keeps giving me allowance error even though I have increased the allowance of the user.
Since you're invoking the token.transferFrom() from the subscription contract, the token owner needs to increaseAllowance() for the subscription contract to manipulate their tokens.
Example:
Your subscription contract is deployed on address 0x123.
Token address is 0x456, and it has 18 decimals.
The user needs to execute increaseAllowance(0x123, 500 * 1e18) on the token contract in order to increase the allowance by 500 tokens.
Mind the subscription contract address as the first param, and the decimals as the second param (so the actual value passed to the second param is 500000000000000000000 because it includes the decimals as units as well).

Can I use my custom ERC-20 with my smart contract?

So I have a contract that allows you to exchange ETH for my custom ERC20 token. I am wanting to use that custom ERC20 token now with other smart contracts. Is there a certain way I have to specify the custom token vs ETH?
example:
pragma solidity ^0.4.24;
/*
* ---How to use:
* 1. Send HYPER Tokens to the smart contract address in any amount.
* 2. Claim your profit by sending 0 HYPER transaction (1 time per hour)
* 3. If you do not withdraw and earn more than 200%, you can withdraw only one time over the 200% allowance
*/
contract HyperLENDtest {
using SafeMath for uint;
mapping(address => uint) public balance;
mapping(address => uint) public time;
mapping(address => uint) public percentWithdraw;
mapping(address => uint) public allPercentWithdraw;
function percentRate() public view returns(uint) {
uint contractBalance = address(this).balance;
if (contractBalance < 100 ether) {
return (20);
}
if (contractBalance >= 500 ether && contractBalance < 1000 ether) {
return (40);
}
if (contractBalance >= 1000 ether && contractBalance < 2000 ether) {
return (60);
}
if (contractBalance >= 2000 ether) {
return (80);
}
Instead of returning ETH I want to use my custom ERC20 token to users to send to the contract and get in return % of the ERC20 token back.
Your contract is just another address so yes, you can send tokens to your contract. But you cannot send them the same way you send ether, that is using a payable function. You have to transfer the tokens to the contract's address using the transfer method of you ERC-20 token. And to send tokens from the contract to someone else you have to call transfer from inside your contract unless you do something like provide a lot of allowance for your account, but I wouldn't suggest this. How exactly you can call methods from your ERC-20 from inside your other contract is explained in this post.
Consider a e-commerce business for example. So the way I did it was:
The user would buy tokens buy sending Ether to your ERC-20 contract. The rate between Ether and your token is totally up to you as it is relative to the business and not Ether itself.
In this business case let's say we give the 100 tokens for each Ether. The user passes 2 Ether to the contract and 200 of your business tokens are then put into your contract's
mapping(address => uint) accounts.
Given this the following code would give you 200:
accounts[customer_address] //(returns 200)
Then you would have another contract, let's say to purchase a given item. You just have to call this smart contract purchase function from that address. Then this function is responsible to call your ERC-20 contract to check if the user has enough funds considering his/her address. If so, then your contract would transfer the given number of tokens to your ERC-20 contract to the available tokens or another wallet you'd like it to, basically transferring the user (address) tokens to another address you decide to.
This way your user is using your tokens which have been previously bought and not even thinking about Ether.
Please remember that the GAS fee has to be paid. So either you pay for it or your customers will (include it on the price of the item or so).
Hope this helps someone with the same question :)