Interaction smart contract - smartcontracts

I am approaching blockchain and smart contracts for the first time and I have trouble understanding some concepts.
I have to design a decision making architecture based on smart contracts. I have to manage hotel reservations based on the weather. The weather information is provided by the oracle. After the oracle has collected the weather data what happens? Is there an oracle smart contract that communicates with my smart contract?

If you want to use Ethereum, the easiest way is to call the oracle some view-method from your smart contract and get the required data. An example is shown below.
pragma solidity >=0.5.8 <0.6.0;
contract Booking
{
Weather WeatherAddr ;
constructor() public
{
}
function AnyFunction(bytes32 place_) public
{
int256 Conditions ;
int256 Temperature ;
(Conditions, Temperature)=WeatherAddr.GetWeather(place_) ;
// ...
}
}
contract Weather
{
struct PlaceWeather
{
int256 Temperature ;
int256 Conditions ;
}
mapping (bytes32 => PlaceWeather) Places ;
constructor() public
{
}
function GetWeather(bytes32 place_) public view returns (int256, int256 retVal)
{
return(Places[place_].Conditions, Places[place_].Temperature) ;
}
}

Here I will schematically describe a variant with the transmission of an event to a oracle. The corresponding smart contracts are listed at the end.
The sequence of actions is as follows:
Deploy the contract Weather on the network and get its address.This must be done from the account owned by the Oracle node.
Enter the address of the contract Weather from (1) into the original text of the Booking contract into the WeatherAddr variable, compile Booking and deploy it to the network
When calling the method BookRequest of the contract Booking the following is executed:
saving the context of the request in the Requests mapping
calling the method GetWeather of the contract Weather with the transfer of the request ID and the location code, as well as the funds received from the transaction
Simultaneously with (3), when the GetWeather method of the Weather contract is executed, the RequestWeather event is initiated, in which send the request data and the address of the requesting contract Booking
The Oracle node captures the RequestWeather event. For this, the eth_getLogs or eth_getFilterLogs RPC API methods (or their analogs from the web3 interface) can be used. To avoid forks, I use eth_getLogs 5-6 blocks behind the last block.
The Oracle node extracts location data from the event data and determines the required weather information.
The Oracle node forms a transaction to the Booking contract address passed in the event data by calling the CallBack method and passing the request ID and weather data there.
When executing the CallBack method of the Booking contract, the following is performed:
restoration of the request context by its identifier from the Requests mapping
processing of the request in accordance with the received weather data
Periodically, the Oracle node forms a transaction for the Weather contract by calling the SendMoneyOwner method to transfer funds received from Booking contracts to its own account.
pragma solidity >=0.5.8 <0.6.0;
contract Booking
{
Weather WeatherAddr=0x00 ; // Address of contract Weather
struct Request
{
bytes32 Location ;
bytes32 Attr1 ;
int256 Attr2 ;
}
mapping (bytes32 => Request) Requests ;
constructor() public
{
}
function BookRequest(bytes32 id_, bytes32 location_, bytes32 attr1, int256 attr2) public payable
{
bool result ;
Requests[id_]=Request({ Location: location_,
Attr1: attr1,
Attr2: attr2 }) ;
(result,)=address(WeatherAddr).call.gas(0x300000).value(msg.value)(abi.encodeWithSignature("GetWetaher(bytes32,bytes32)", id_, location_)) ;
if(result==false) require(false) ;
}
function CallBack(bytes32 id_, int256 tempirature_) public
{
// 1. Restore request context from Requests[id_]
// 2. Process request for booking
}
}
contract Weather
{
address Owner ;
uint256 Value ;
event RequestWeather(address booking, bytes32 id, bytes32 location) ;
constructor() public
{
Owner=tx.origin ;
}
function GetWeather(bytes32 id_, bytes32 location_) public payable
{
Value+=msg.value ;
emit RequestWeather(msg.sender, id_, location_) ;
}
function SendMoneyOwner() public
{
bool result ;
(result,)=Owner.call.gas(0x30000).value(Value)("") ;
if(result==false) require(false) ;
Value=0 ;
}
}

Related

How to create a time-based upkeep directly from my contract rather than use the GUI?

I want to create a time-based upkeep directly from my contract. I was able to register and fund the upkeep but for some reason the function is not getting executed automatically.
Here's the code
`
// Goerli network
address public cronFactoryAddress = 0x1af3cE8de065774B0EC08942FC5779930d1A9622;
address public keeperRegistrar = 0x57A4a13b35d25EE78e084168aBaC5ad360252467;
constructor(){
cronFactory = ICronFactory(cronFactoryAddress);
}
function createUpkeep(string memory _cronString) public{
address _target = address(this);
bytes memory functionToCall = bytes(abi.encodeWithSignature("sendSalary(string)", _cronString));
bytes memory job = cronFactory.encodeCronJob(_target, functionToCall, _cronString);
uint256 maxJobs = cronFactory.s_maxJobs();
address delegateAddress = cronFactory.cronDelegateAddress();
address newCronUpkeep = address(new CronUpkeep(msg.sender, delegateAddress, maxJobs, job));
allUpkeeps.push(newCronUpkeep);
}
function fundUpkeep(uint256 _linkAmount, address _upkeepAddress) public{
bytes4 reg = bytes4(keccak256("register(string,bytes,address,uint32,address,bytes,bytes,uint96,address)"));
bytes memory _data = abi.encode(
"TestV2",
"",
_upkeepAddress,
uint32(500000),
address(this),
"",
"",
_linkAmount,
address(this)
);
bytes memory combinedData = abi.encodePacked(reg, _data);
LinkContract.transferAndCall(keeperRegistrar, _linkAmount, combinedData);
}
sendSalary is the function in my contract that I want to be executed at regular intervals.
cronFactory is the cron factory contract.
cronUpkeep is the cronUpkeep.sol contract from the chainlink github repo.
To create these functions, I created a time-based upkeep manually and used the transaction logs to find what all function are being called and implemented the same here.
But, Once I execute both these functions nothing happens, however, I am able to find the upkeep registered on chainlink's website . And also it shows the trigger as custom trigger on upkeep page on chainlink:
chanlink upkeep
Please let me know how I can solve this? Any help would be appreciated. Thanks in advance
Contracts cannot execute themselves. Function needs to be called. While contract (function) is not called, contract is sleeping, because every time it makes operations, they should be payed (aka gas), so there is no way to throw an allways-active-timer inside of the contract (infinite gas). It means that you have to make calls manually or use automation services like ChainLink, Openzepplin Defender etc.
You can make a requirement by time-passed with
uint256 private lastTimeStamp;
uint256 private interval;
constructor() {
lastTimeStamp = block.timestamp;
interval = 7 days;
}
function isTimePassed() public view returns (bool timePassed) {
timePassed = ((block.timestamp - lastTimeStamp) > /*7 days */ interval);
return timePassed;
}
function smth() public {
(bool timePassed) = isTimePassed();
...
}
Something like this.

How to call a function from smart contract A on a token ID received from smart contract B?

I have two ERC721 smart contracts A and B. I have successfully minted a token ID on contract A and transferred it to contract B address (to transfer it to a contract address and not a wallet address I used IERC721Receiver). From here, is there a way for contract's B functions, which take a token ID as argument, to be called on the token ID received from A that now belongs to B?
For example, if Contract A was:
contract ContractA is ERC721 {
...
function mint(address _to, uint256 _mintAmount) public payable {
for (uint256 i = 1; i <= _mintAmount; i++) {
_safeMint(_to, supply + i);
}
}
}
and contract B was:
contract ContractB is ERC721 {
...
function exampleFunction(uint256 tokenId) public payable {
// Do something with tokenId
}
}
How can I call exampleFunction(6) on ContractB if token ID #6 was transferred from ContractA to ContractB (and not minted on ContractB)?
It just seems to me like there is no way to use methods from ERC721 contracts on token IDs that are not minted from the same contract where the methods are implemented.
Anything helps, so thank you in advance!
I am able to see that ContractB owns the token transferred to it by ContractA, but only on ContractA's ownerOf() method. I cannot do anything with that token on ContractB's methods, even though it now belongs to it.
You can call your exampleFunction() from your onERC721Received() implementation.
However you will not be able to do anything with the token as it will not have been transferred to you yet. The onERC721Received is purely to check that the contract supports receiving ERC721 tokens.

Chainlink- Issue when fetching an API in form of `bytes32`

I've been learning the chainlink API and trying to modify the example from the Chainlin's documentation to get a byets32 value from an API. The original code of the example works well, but since the API I was hitting is returning a byets32 foem, the Chainlink job need to be configured to handle this type of return. The node is given here with Kovan testnet. Here is my code
pragma solidity ^0.8.7;
import "#chainlink/contracts/src/v0.8/ChainlinkClient.sol";
/**
* Request testnet LINK and ETH here: https://faucets.chain.link/
* Find information on LINK Token Contracts and get the latest ETH and LINK faucets here: https://docs.chain.link/docs/link-token-contracts/
*/
/**
* THIS IS AN EXAMPLE CONTRACT WHICH USES HARDCODED VALUES FOR CLARITY.
* PLEASE DO NOT USE THIS CODE IN PRODUCTION.
*/
contract APIConsumer is ChainlinkClient {
using Chainlink for Chainlink.Request;
//Result of the api
bytes32 public martket;
address private oracle;
bytes32 private jobId;
uint256 private fee;
/**
* Get -> bytes32
* Network: Kovan
* Oracle: 0xc57B33452b4F7BB189bB5AfaE9cc4aBa1f7a4FD8 (Chainlink Devrel
* Node)
* Job ID: 7401f318127148a894c00c292e486ffd
* Fee: 0.1 LINK
*/
constructor() {
setPublicChainlinkToken();
// Get -> bytes32 node taken from documentation
oracle = 0xc57B33452b4F7BB189bB5AfaE9cc4aBa1f7a4FD8;
jobId = "7401f318127148a894c00c292e486ffd";
fee = 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 requestVolumeData() 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://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD");
// Set the path to find the desired data in the API response, where the response format is:
// {"RAW":
// {"ETH":
// {"USD":
// {
// "MARKET": "CCCAGG",
// }
// }
// }
// }
//Get the MARKET field of API
request.add("path", "RAW,ETH,USD,MARKET"); // Chainlink nodes 1.0.0 and later support this format
// Sends the request
return sendChainlinkRequestTo(oracle, request, fee);
}
/**
* Receive the response in the form of bytes32
*/
function fulfill(bytes32 _requestId, bytes32 _market) public recordChainlinkFulfillment(_requestId)
{
martket = _market;
}
// function withdrawLink() external {} - Implement a withdraw function to avoid locking your LINK in the contract
}
The value of market should be a byets32 reprsenting "CCCAGG" as shown in the API. But what I got was just 0x0...00 all the time, which means market has not been modified yet. I've checked this various ways and found out that the fulfill function never get rans. Then same thing happens when I changed the jobId and oracle to handle get-> int256, get -> bool (of course I did change the return type of the variable such that it's consistent with the returning form of API). I also noticed that only the job get -> uint256 works well (the example from documentation also used this job). Does anyon know why? Was my code wrong or the problem came from the node/job? Since I was able the get the example right, I don't think the problem cam from my wallet.
Any help would be appreciated!
You can test with a GET>STRING JOB
Go to this link: https://docs.chain.link/docs/any-api-testnet-oracles/
Take the Oracle contract address relevant to your network. For instance: 0xCC79157eb46F5624204f47AB42b3906cAA40eaB7 for Goerli
Use a Get>String job: 7d80a6386ef543a3abb52817f6707e3b
Make sure that your callback function expects to receive a string. (see a full example here: https://docs.chain.link/docs/api-array-response/)

Burning Deployed ERC Tokens In an NFT Mint Function - Compiles, but Transaction Fails

I am very new to Solidity, and have recently been working on trying to learn the ropes. For reference, I have been using code from this video (https://www.youtube.com/watch?v=tBMk1iZa85Y) as a primer after having gone through the basic crypto zombies tutorial series.
I have been attempting to adapt the Solidity contract code presented in this video (which I had functioning just fine!) to require a Burn of a specified amount of an ERC-20 token before minting an NFT as an exercise for myself. I thought I had what should be a valid implementation which compiled in Remix, and then deployed to Rinkeby. I call the allowAccess function in Remix after deploying to Rinkeby, and that succeeds. But, when I call the mint function with the two parameters, I get: "gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending? execution reverted."
If I still send the transaction, metamask yields "Transaction xx failed! Transaction encountered an error.".
I'm positive it has to do with "require(paymentToken.transfer(burnwallet, amounttopay),"transfer Failed");", though I'm not sure what's wrong. Below is my entire contract code. I'm currently just interacting with the Chainlink contract on Rinkeby as my example, since they have a convenient token faucet.
pragma solidity ^0.8.0;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Counters.sol";
contract myNFTwithBurn is ERC721, Ownable {
address externalTokenAddress = 0x01BE23585060835E02B77ef475b0Cc51aA1e0709; //Token Type to burn on minting
uint256 amounttopay = 5; //number of these tokens to burn
IERC20 paymentToken = IERC20(externalTokenAddress); //my code: create an interface of the external token
address burnwallet = 0x000000000000000000000000000000000000dEaD; //burn wallet
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
using Strings for uint256;
// Optional mapping for token URIs
mapping (uint256 => string) private _tokenURIs;
// Base URI
string private _baseURIextended;
constructor() ERC721("NFTsWithBurn","NWB") {
}
function setBaseURI(string memory baseURI_) external onlyOwner() {
_baseURIextended = baseURI_;
}
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
_tokenURIs[tokenId] = _tokenURI;
}
function _baseURI() internal view virtual override returns (string memory) {
return _baseURIextended;
}
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
string memory _tokenURI = _tokenURIs[tokenId];
string memory base = _baseURI();
// If there is no base URI, return the token URI.
if (bytes(base).length == 0) {
return _tokenURI;
}
// If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
if (bytes(_tokenURI).length > 0) {
return string(abi.encodePacked(base, _tokenURI));
}
// If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
return string(abi.encodePacked(base, tokenId.toString()));
}
function allowAccess() public
{
paymentToken.approve(address(this), 5000000); //This is my attempt to allow the contract access to the user's external tokens, in this case Chainlink (paymentToken)
}
function mintItem(address to, string memory tokenURI)
public
onlyOwner
returns (uint256)
{
require(paymentToken.transfer(burnwallet, amounttopay),"transfer Failed"); //Try to transfer 5 chainlink to the burn wallet
_tokenIds.increment();
uint256 id = _tokenIds.current();
_mint(to, id);
_setTokenURI(id, tokenURI);
return id;
}
}
If anybody can at least point me to what I'm doing completely wrong in the code that I've added, please do! TIA!
I'm not sure why are you trying to burn link in order to mint and nft but first check if the link code does not have a require that check if the destination address is the burn address if it has then burn the link is not possible and you should use any other erc20 maybe your own erc20, also your contract probably does not have any link and if you want to transfer the link from the user you should do this in the contract paymentToken.transferFrom(msg.sender,destinationAddress,amount) and if the user previously approve your contract you will able to send the tokens, and i suppose that the purpose of the allowAccess function is to make the user approve the contract to move the tokens that will never work, the approve function let's anyone that call it approve any address to move an amount of tokens, the thing is that to know who is approving to let other to move the tokens the function use msg.sender to explain how this work take a look at this example
let's say that your contract is the contract A and the link contract is the contract B
now a user call allowAccess in the contract A, so here the msg.sender is the user because they call the function
now internally this function call approve on contract B, here the contract A is the msg.sender, because the contract is who call the function
so what allowAccess is really doing is making the contract approving itself to move their own tokens that I assume it doesn't have

send ERC-20 tokens with solidity to user

as you know, ERC-20 network has many tokens like USDT, SHIB, LINK, . . .
I want to create a website , when user need to buy USDT token I should send the USDT token in his wallet or use need to send USDT to another wallet on the blockchain and I want do all of these things into the blockchain, and the user could see the detail of his USDT transaction :
USDT Transactions
now I have a big question about transfer tokens in ERC-20 network.
i write this codes in remix and solidity :
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
import 'https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol';
contract DEX {
struct Token {
string token;
address contractAddress;
}
mapping(string => Token) public tokens;
string[] public tokenLis;
address public admin;
constructor() {
admin = msg.sender;
}
function addToken(string memory token, address contractAddress) external {
tokens[token] = Token(ticker,contractAddress);
tokenLis.push(ticker);
}
function getTokenAddress(string memory token) external view returns(address moemory) {
return tokens[token].contractAddress;
}
function sendToken(string memory token, address from , uint256 amount)
external
{
IERC20(tokens[token].contractAddress).transferFrom(
from,
address(this),
amount
);
}
}
I want to add dynamically tokens to my website and smart contract, for this write this codes :
struct Token {
string token;
address contractAddress;
}
mapping(string => Token) public tokens;
function addToken(string memory token, address contractAddress) external {
tokens[token] = Token(ticker,contractAddress);
tokenLis.push(ticker);
}
I called addToken with this info :
Token : USDT
contractAddress : 0xdac17f958d2ee523a2206206994597c13d831ec7 ( USDT mainnet contract address )
it's work and add success USDT in tokens .
now I want to send some USDT to the user with this function ( imported the openzepelin IERC20) :
function sendToken(string memory ticker , address from , uint256 amount)
external
{
IERC20(tokens[ticker].contractAddress).transferFrom(
from,
address(this),
amount
);
}
now when I want to transfer amount from remix address one to remixaddress to into the USDT contract address it show me this error :
What is the problem? how can I solve this error?
Your contract is on a different network (Remix VM emulator) than the USDT contract (Ethereum mainnet), so they cannot interact. You can fork the mainnet and connect to the forked network from your Remix IDE, see this answer for more details.
Then you're probably going to run into another issue within the sendToken() function (since you don't cover it in your question, I'm assuming you haven't performed this step). In order to successfully invoke transferFrom(), the function caller (in this case your contract address) needs to be approved by the token owner (in this case the from variable) to spend their tokens. You can find more info in the ERC-20 specification to the approve() function.
Summary: The from address needs to invoke the approve() method on the token contract directly (not through your or any other contract), passing it your contract address as the first param, so that your contract is able to pull their tokens.