How to call API in solidity with ethereum private network - ethereum

I'm trying to call external API in smart contract code with Ethereum private network but didn't get any solution.
I have also gone through chainlink solutions but I didn't get oracle id and job id from markt.link as per latest website update.
Can someone please help me out on how to make HTTP get/post requests in solidity with Ethereum private network?

Solidity is compiled to EVM (Ethereum Virtual Machine) bytecode. To keep all operations deterministic, the code running inside the VM cannot communicate with resources outside of the VM.
Having said that, there are services such as Chainlink, allowing you to communicate with external APIs from Solidity code on public networks. When you invoke a specific Chainlink SDK function passing it an API URL and other params, it emits Solidity event. Their offchain app is listening to this event, queries the URL and sends back a new transaction to your contract, containing the result.
There are no Chainlink nodes on your private network. So on a private network, you can replicate this approach with your own offchain application.
Solidity:
pragma solidity ^0.8;
contract MyContract {
event DataRequested(string url);
function requestData(string memory url) public {
emit DataRequested(url);
}
function receiveData(bytes memory data) public {
// it's recommended to validate `msg.sender`
// and allow for this function to be invoked only from an authorized address
require(msg.sender == address(0x123));
}
}
JS:
const Web3 = require("web3");
const web3 = new Web3(YOUR_NODE_URL);
const myContract = new web3.eth.Contract(ABI, ADDRESS);
// handle the event when it's emitted
myContract.events.DataRequested(async (eventData) => {
// process the URL
const result = queryUrl(eventData.returnValues.url);
// send the data back from the authorized address
// your local `web3` instance or the node need to know the private key of `0x123` address
await myContract.methods.receiveData(result).send({
from: "0x123"
});
});

Related

Issues using a delegateCall (proxy contract with Solidity) and using variables inside the delegate contract

I've written a simple proxy contract with solidity and I've got an issue with the variables inside the delegate contract. When I delegateCall, all my variables are equal to 0, except if there are constant. Is there any reason for that or am I missing something ?
My proxy contract :
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;
contract Proxy {
mapping(string => address) public strategies;
function addStrategy(string memory id, address implementation) external {
strategies[id] = implementation;
}
function removeStrategy(string memory id) external {
delete strategies[id];
}
function displayVar(string memory strategyId) external {
address strategy = strategies[strategyId];
require(strategy != address(0x0), "Strategy not found..");
(bool success, bytes memory data) = strategy.delegatecall(
abi.encodeWithSignature("displayVar()")
);
}
}
The deleguate contract :
pragma solidity ^0.8.3;
import "hardhat/console.sol";
contract Delegate {
mapping(string => address) public strategies;
address public constant CRV = 0xD533a949740bb3306d119CC777fa900bA034cd52;
address public curve = 0x90E00ACe148ca3b23Ac1bC8C240C2a7Dd9c2d7f5;
address public constant cvx = 0xF403C135812408BFbE8713b5A23a04b3D48AAE31;
address public constant CVX = 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B;
function displayVar() external returns (bool) {
console.log(CRV);
console.log(curve);
console.log(cvx);
console.log(CVX);
}
}
the test with HardHat :
import { Contract, ContractFactory } from "ethers";
import { ethers } from "hardhat";
describe("test via proxy", function () {
let Proxy: ContractFactory, proxy: Contract;
let Delegate: ContractFactory, delegate: Contract;
const stratName = "test";
before(async function () {
Proxy = await ethers.getContractFactory("Proxy");
proxy = await Proxy.deploy();
await proxy.deployed();
Delegate = await ethers.getContractFactory("Delegate");
delegate = await Delegate.deploy();
await delegate.deployed();
await proxy.addStrategy(stratName, delegate.address);
});
it("should display", async function () {
const [owner] = await ethers.getSigners();
await proxy.connect(owner).displayVar(stratName);
});
});
And finally the output is :
0xd533a949740bb3306d119cc777fa900ba034cd52
0x0000000000000000000000000000000000000000
0xf403c135812408bfbe8713b5a23a04b3d48aae31
0x4e3fbd56cd56c3e72c1403e103b45db9da5b9d2b
A quick intro:
When you use delegatecall, you are “using” the targeted contract’s code (in your case Delegate) but keeping the storage of the proxy. In other words, the storage of the proxy is completely independent (that is the purpose of the proxy, to be upgradable and / or to save gas on deployment.
With this in mind, your proxy contract can only use the code of delegate, but maintaining its own storage. But, constant and immutable variables do not occupy a storage slot, they are injected in the bytecode at compile time. That is why your proxy also has them. But all the other variables are defaulted to 0 (depending on the type).
The point of using Proxy contract is to keep the state of different implementation versions in the same contract via the delegatecall. Therefore using constructor is not a safe method inside the implementation contract because its state inside the constuctor is set inside the implementation contract when we deploy the contract: Solidity: Why use Initialize function instead of constructor?
Initializing storage variables is not safe either because those state variables are set inside the implementation contract when we deploy them like the constructor.
// this is not safe
// you should move the assignment inside an initializer function
// this is not set inside the proxy
address public curve = 0x90E00ACe148ca3b23Ac1bC8C240C2a7Dd9c2d7f5;
However, if you have constant and immutable variables, those variables will be inside the bytecode of your contract and your proxy needs those variable, it will point to bytecode and get the code. Using constant and immutable is believed to be safe in this case. However, if you set those variables in V1 and then over time you upgraded to V2,V3 etc. If your V1 had selfdestruct function and you killed the contract in the future, V1's bytecode will be deleted and therefore your proxy will no longer have access to those variables.

Smart contract Ethereum - How to recreate same contract after used selfDestruct()

Is it possible to destroy (using selftDestruct function) and generate the same contract at the same time ?
We assume that the first contract is to have 1Eth and then withdraw all eth to another erc20 address, after the complete withdrawal, the first contract calls the selfdestruct function and then the same contract is redeployed, and that in a loop.
Maybe the simply way is to use a conditional function?
Not possible "at the same time", need to do it in separate transactions.
selfdestruct prevents later operations on the contract from being executed, and flags the contract for deletion.
selfdestruct stops the execution in its current scope, and flags the contract for deletion.
However, the EVM doesn't delete its bytecode until the end of processing of the transaction. So you won't be able to deploy new bytecode to the same address until the next tx. But you're still able to perform calls on the to-be-destructed contract.
pragma solidity ^0.8;
contract Destructable {
function destruct() external {
selfdestruct(payable(msg.sender));
}
}
contract Deployer {
function deployAndDestruct() external {
Destructable destructable = new Destructable{salt: keccak256("hello")}();
destructable.destruct();
// The second deployment (within the same transation) causes a revert
// because the bytecode on the desired address is still non-zero
Destructable destructable2 = new Destructable{salt: keccak256("hello")}();
}
}
pragma solidity 0.8.17;
contract Destructable {
uint public number = 1;
function destruct() external {
selfdestruct(payable(msg.sender));
number++; // not executed after selfdestruct
}
}
contract Caller {
Destructable destructable;
constructor(Destructable _destructable) {
destructable = _destructable;
}
function executeSequence() external {
destructable.destruct();
// successfully call after the selfestruct was initiated
uint returnedNumber = destructable.number();
require(returnedNumber == 1);
}
}

What is the use of Uniswap Router Initialization in a Token Contract

I just started on building Tokens using ETH & BSC, this is one statement which I see in many Contracts.
Inside the Constructor method, the Uniswap router is iniliazed probably with the V2 version. What is the use of this?
constructor () public {
_rOwned[_msgSender()] = _rTotal;
IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(0x10ED43C718714eb63d5aA57B78B54704E256024E);
// Create a uniswap pair for this new token
uniswapV2Pair = IUniswapV2Factory(_uniswapV2Router.factory())
.createPair(address(this), _uniswapV2Router.WETH());
// set the rest of the contract variables
uniswapV2Router = _uniswapV2Router;
Why is this initialization required? What is the functionality of this?
Appreciate if someone could help.
Thanks
IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(0x10ED43C718714eb63d5aA57B78B54704E256024E);
This line initializes a pointer to the 0x10ED... address and expects the contract (deployed at the 0x10ED... address) to implement the IUniswapV2Router02 interface.
The interface is defined somewhere with the caller contract source code.
It allows you to execute and call functions defined by the interface instead of building low-level calls. It also allows you to use the returned datatypes instead of parsing the returned binary.
Example:
pragma solidity ^0.8.5;
interface IRemote {
function foo() external view returns (bool);
}
contract MyContract {
IRemote remote;
constructor() {
remote = IRemote(address(0x123));
}
function getFoo() external view returns (bool) {
bool returnedValue = remote.foo();
return returnedValue;
}
}

Solidity function implementing `public onlyOwner` cannot be called even by the owner

I am following along the documentation here: https://docs.alchemyapi.io/alchemy/tutorials/how-to-create-an-nft/how-to-mint-a-nft. And have a smart contract of form:
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
import "#openzeppelin/contracts/utils/Counters.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
contract NFTA is ERC721, Ownable {
using Counters for Counters.Counter;
Counters.Counter public _tokenIds;
mapping (uint256 => string) public _tokenURIs;
mapping(string => uint8) public hashes;
constructor() public ERC721("NFTA", "NFT") {}
function mintNFT(address recipient, string memory tokenURI)
public onlyOwner
returns (uint256)
{
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(recipient, newItemId);
_setTokenURI(newItemId, tokenURI);
return newItemId;
}
/**
* #dev Sets `_tokenURI` as the tokenURI of `tokenId`.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token");
_tokenURIs[tokenId] = _tokenURI;
}
}
When I attempt to estimate the gas cost of minting with this:
const MY_PUBLIC_KEY = '..'
const MY_PRIVATE_KEY = '..'
const ALCHEMY = {
http: '',
websocket:'',
}
const { createAlchemyWeb3 } = require("#alch/alchemy-web3");
const web3 = createAlchemyWeb3(ALCHEMY.http);
const NFTA = require("../artifacts/contracts/OpenSea.sol/NFTA.json");
const address_a = '0x...';
const nft_A = new web3.eth.Contract(NFTA.abi, address_a);
async function mint({ tokenURI, run }){
const nonce = await web3.eth.getTransactionCount(MY_PUBLIC_KEY, 'latest');
const fn = nft_A.methods.mintNFT(MY_PUBLIC_KEY, '')
console.log( 'fn: ', fn.estimateGas() )
}
mint({ tokenURI: '', run: true })
I receive error:
(node:29262) UnhandledPromiseRejectionWarning: Error: Returned error: execution reverted: Ownable: caller is not the owner
Presumably because mintNFT is public onlyOwner. However, when I check Etherscan, the From field is the same as MY_PUBLIC_KEY, and I'm not sure what else can be done to sign the transaction as from MY_PUBLIC_KEY. The easy way to solve this is to remove the onlyOwner from function mintNFT, and everything runs as expected. But suppose we want to keep onlyOwner, how would I sign the transaction beyond what is already written above.
Note I'm using hardHat to compile the contracts and deploying them. That is:
npx hardhat compile
npx hardhat run scripts/deploy.js
=============================================
addendum
The exact code given by alchemy to deploy the mint is:
async function mintNFT(tokenURI) {
const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest'); //get latest nonce
//the transaction
const tx = {
'from': PUBLIC_KEY,
'to': contractAddress,
'nonce': nonce,
'gas': 500000,
'data': nftContract.methods.mintNFT(PUBLIC_KEY, tokenURI).encodeABI()
};
Note in the transaction the from field is PUBLIC_KEY, the same PUBLIC_KEY that deployed the contract, and in this case the nftContract has public onlyOwner specified. This is exactly what I have done. So conceptually who owns this NFT code? On etherscan is it the to address ( the contract address ), or the from address, which is my public key, the address that deployed the contract, and the one that is calling mint, which is now failing with caller is not the owner error.
Search the internet, I see others have encountered this problem here: https://ethereum.stackexchange.com/questions/94114/erc721-testing-transferfrom, for Truffle you can specify the caller with extra field:
await nft.transferFrom(accounts[0], accounts[1], 1, { from: accounts[1] })
Extra parameters is not an option here because I'm using hardhat.
OpenZeppelin's Ownable.sol defines the default owner value as the contract deployer. You can later change it by calling transferOwnership() or renounce the owner (i.e. set to 0x0) by calling renounceOwnership().
The onlyOwner modifier reverts the transaction if it's not sent by the current owner. (see the code)
So you need to call the mintNFT() function from the same address that deployed the contract, because that's the current owner. Or you can change the owner first by calling transferOwnership() (from the current owner address).
Removing the onlyOwner modifier from the mintNFT() function would allow anyone to call the function.
Answering this for anyone else who stumbles across the issue while using the Alchemy tutorial:
In the tutorial, it says to init the contract in your mint method like this:
const contract = require("../artifacts/contracts/MyNFT.sol/MyNFT.json");
const contractAddress = "0x81c587EB0fE773404c42c1d2666b5f557C470eED";
const nftContract = new web3.eth.Contract(contract.abi, contractAddress);
However, if you attempt to call estimateGas() or encodeABI() it will fail with the onlyOwner error.
The solution is to change the third line to:
const nftContract = new web3.eth.Contract(contract.abi, contractAddress, {
from: PUBLIC_KEY
});
This will set a default value of "From" so that when you call estimateGas() on a mint function marked onlyOwner, it will be able to use that from field to see that its the owner calling the estimateGas.
Took forever to figure this out.
I finally figured it out, the contract does not initialize the way I deploy it. So you have to initialize it after deployment.

Calling a public method in smart contract with Web3.js version: '1.0.0-beta.46'

I started first steps into ethereum blockchain with parity on a private network. I was able to configure parity and perform the deployment of smart contract on a dev mode chain on my private network through Parity UI which also is able to call methods of the contract.
The problem that I face is related to calling a function in smart contract using Web3.js. I was able to connect to the chain using the Web.js Library;
Web3 = require('web3')
web3 = new Web3('ws://localhost:8546')
mycontract = web3.eth.Contract([{"constant":false,"inputs":[],"name":"greet","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}],0xEb112542a487941805F7f3a04591F1D6b04D513c)
When I call the method below;
mycontract.methods.greet().call()
It gives me the following output instead of returning the expected string "OK Computer" through a promise object as written in smart contract greet function.
{ [Function: anonymousFunction]
send: { [Function] request: [Function] },
estimateGas: [Function],
encodeABI: [Function] }
Smart Contract code:
pragma solidity ^0.4.22;
//Compiler Version: 0.4.22
contract Greeter {
address owner;
constructor() public {
owner = msg.sender;
}
function greet() public returns(string){
return "OK Computer";
}
}
Every transaction or smart contract method call which involves a change on the blockchain state will return a promise. So you just need to handle the promise accordingly:
mycontract.methods.greet.call().then(function(resp) {
console.log(resp) // This will output "OK Computer"
}
More in web docs