From what I can see, it seems trivially easy to set message sender in a view function to whatever address you want for external calls through ethers.js via the 'connect' function.
For example, if you have a contract like this:
contract Test {
address public owner;
string private secret;
modifier onlyOwner() {
require(msg.sender==owner,"onlyOwner");
_;
}
constructor() {
owner = msg.sender;
secret="Abracadabra";
}
function setSecret(string memory newSecret) public onlyOwner {
secret = newSecret;
}
function getSecret() public view onlyOwner returns(string memory) {
return secret;
}
}
Even if you aren't the owner account, you could run
let owner = await con.owner();
let secret = con.connect(owner).getSecret()
And get no complaints from ethers. I know this would fail if you tried to run setSecret but when there no transaction involved it looks like you don't need a real signer, just a contract address.
Is there some other to check if the msg.sender is actually an account, not just a string of the account address?
Sure it can be, it is not a 'hack', when you call view function you can overwrite msg.sender
And anyway 'secret' variable (even private) can be read by some tools, as any data on blockchain (even if you will not provide read function), it is not a security measure to make variable private..
Related
I'm new to Solidity but I can't find much information about my problem.
For example, I want to make different contracts for different functionalities (I see them as classes)
For example
Main contract
// SPDX-License-Identifier: None
pragma solidity >=0.8.6;
import "./AuthContract.sol";
contract Contract {
string public message;
constructor() {
message = "test";
}
function getMessage() public view returns(string memory) {
return message;
}
}
and second contract
contract Auth {
struct UserDetail {
address addr;
string name;
string password;
string CNIC;
bool isUserLoggedIn;
}
mapping(address => UserDetail) user;
// user registration function
function register(
address _address,
string memory _name,
string memory _password,
string memory _cnic
) public returns (bool) {
require(user[_address].addr != msg.sender);
user[_address].addr = _address;
user[_address].name = _name;
user[_address].password = _password;
user[_address].CNIC = _cnic;
user[_address].isUserLoggedIn = false;
return true;
}
// user login function
function login(address _address, string memory _password)
public
returns (bool)
{
if (
keccak256(abi.encodePacked(user[_address].password)) ==
keccak256(abi.encodePacked(_password))
) {
user[_address].isUserLoggedIn = true;
return user[_address].isUserLoggedIn;
} else {
return false;
}
}
// check the user logged In or not
function checkIsUserLogged(address _address) public view returns (bool) {
return (user[_address].isUserLoggedIn);
}
// logout the user
function logout(address _address) public {
user[_address].isUserLoggedIn = false;
}
}
How could I use the functionalities from that contract in the main contract?
Is such a thing possible in the blockchain?
I am quite new here also but i can solve your problem, if not solve i can lead you to a good path .
so firstly you said you want to create multiple contract for different functionalities, this is good but keep in my you are going to exhaust a lot of gas.
so the answer to your problem is easy you can just read it and implement it.
if you want to use a contract in the Another contract (main in your case) you can do it in two ways(according to my knowledge there might be other).
using new keyword
using address of your previously deployed contract
we will be using the First case as i suppose you havenot deployed the second contract yet
In Order to do it you can use
Auth myObj=new Auth();
This will create a new instance of the contract Auth in your main contract and now you can use the Auth contract's function in your Main contract.you can create a function copy the above line and you can use the Functions using dot operator.
myObj.register(_address,_name,moreandmore);
I believe this will solve your problem if not you can ask it.
Thank You!
I want to create a payable token
which includes a function transferAndCall(TokenReceiver to, uint256 amount, bytes4 selector).
By calling this function, you can transfer tokens to the TokenReceiver smart contract address,
and then call onTransferReceived(address from,uint tokensPaid, bytes4 selector) on the receiver,
which in turn invokes a function specified in thebytes4 selector on the receiver.
Note that this is similar to/ inspired by ERC1363.
Here is a simplified version of my receivable token:
import "#openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MeowToken is ERC20 {
constructor() ERC20("MeowToken", "MEO") {
ERC20._mint(msg.sender, 10_000_000);
}
function transferAndCall(
TokenReceiver to,
uint256 amount,
bytes4 selector
) external {
ERC20.transfer(address(to), amount);
to.onTransferReceived(msg.sender, amount, selector);
}
}
And this is a token receiver:
contract TokenReceiver {
address acceptedToken;
event PurchaseMade(address from, uint tokensPaid);
modifier acceptedTokenOnly () {
require(msg.sender == address(acceptedToken), "Should be called only via the accepted token");
_;
}
constructor(address _acceptedToken) {
acceptedToken = _acceptedToken;
}
function onTransferReceived(
address from,
uint tokensPaid,
bytes4 selector
) public acceptedTokenOnly {
(bool success,) = address(this).call(abi.encodeWithSelector(selector, from, tokensPaid));
require(success, "Function call failed");
}
function purchase(address from, uint tokensPaid) public acceptedTokenOnly {
emit PurchaseMade(from, tokensPaid);
}
}
I want to make sure that public functions on the receiver are only called via the payable token.
For this reason I added acceptedTokenOnly modifier to both of them.
However after adding the modifier my test began to fail:
it('Transfer Tokens and call Purchase', async () => {
const tokenAmount = 100;
const tx = meowToken.transferAndCall(
tokenReceiver.address,
tokenAmount,
tokenReceiver.interface.getSighash('purchase'),
);
await expect(tx)
.to.emit(tokenReceiver, 'PurchaseMade')
.withArgs(deployer.address, tokenAmount);
});
1) Transfer and call
Transfer Tokens and call Purchase:
Error: VM Exception while processing transaction: reverted with reason string 'Function call failed'
Why does this happen?
How to make sure the receiver's functions are invoked only by the accepted token?
For reference, I am developing and testing smart contracts in Hardhat and deploying on RSK.
When you're doing this:
(bool success,) = address(this).call(abi.encodeWithSelector(selector, from, tokensPaid));
you're making an external call, meaning that msg.sender will become address(this).
Now the modifier acceptedTokenOnly during function purchase will fail since msg.sender isn't the token anymore.
Suggested changing the function to this:
function purchase(address from, uint tokensPaid) public {
require(msg.sender == address(this), "wrong sender");
emit PurchaseMade(from, tokensPaid);
}
The problem is, you are using low level call method, here:
(bool success,) = address(this).call(abi.encodeWithSelector(selector, from, tokensPaid));
This changes the value of msg.sender inside onTransferReceived from the accepted token to the receiver itself.
Here is one way to achieve what you want:
Replace call with delegatecall.
This will solve your problem instantly.
Unlike call, the delegatecall will invoke your function on behalf of the caller smart contract:
function onTransferReceived(
address from,
uint tokensPaid,
bytes4 selector
) public acceptedTokenOnly {
(bool success,) = address(this).delegatecall(abi.encodeWithSelector(selector, from, tokensPaid));
require(success, "Function call failed");
}
Apart from switching from call to delegatecall, as mentioned in #Juan's answer, there is a more "manual" approach:
Do not use call altogether, and instead invoke the functions by name.
This can be accomplished using an if ... else control structure that compares the selector parameter with the intended function selector (purchase):
function onTransferReceived(
address from,
uint tokensPaid,
bytes4 selector
) public acceptedTokenOnly {
if (selector == this.purchase.selector) {
purchase(from, tokensPaid);
} else {
revert("Call of an unknown function");
}
}
While this is more tedious to do, it might be preferable from a security point of view.
For example, if you wish to white-list the functions that you allow to be called through
this mechanism.
Note that the approach using call/ delegatecall exposes a potential vulnerability
for arbitrary (and possibly unintended) function execution.
function withdraw() public payable {
msg.sender.transfer(address(this).balance);
}
I wrote this code and I got "ERROR send and transfer are only available for objects of type address payable , not address.".
Only the payable address type has the transfer method. msg.sender is the address type so you need to cast it to be payable:
payable(msg.sender).transfer(address(this).balance);
From the docs:
The address type comes in two flavours, which are largely identical:
address: Holds a 20 byte value (size of an Ethereum address).
address payable: Same as address, but with the additional members > transfer and send.
You need to cast it to an address payable type to use the send and transfer methods. See https://docs.soliditylang.org/en/v0.8.11/types.html#address for more details.
address payable private owner;
then set the owner in constructor as msg.sender:
constructor() {
setOwner(msg.sender);
}
this is setOwner:
function setOwner(address newOwner) private {
owner = payable(newOwner);
}
this is withdraw function:
function withdraw() external onlyOwner {
(bool success,)=owner.call{value:address(this).balance}("");
// if it is not success, throw error
require(success,"Transfer failed!");
}
Make sure only owner can call this so write a modifier:
modifier onlyOwner() {
if (msg.sender != getContractOwner()) {
revert OnlyOwner();
}
_;
}
revert OnlyOwner is sending custom message with newer versions of solidity:
/// Only owner has an access!
error OnlyOwner();
I have deployed smart contract using remix IDE, launched with Injected Web3 on Ropsten test network. I could call BuyTokens function within solidity IDE successfully, but when tried to buy tokens with metamask from other address transaction get reverted. I can see the difference between those operations on ropsten.etherscan explorer - the difference is in Input Data field.
Metamask transaction has value 0x and transaction via remix is:
Function: buyTokens() ***
MethodID: 0xd0febe4c
Code:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract Token {
// Track how many tokens are owned by each address.
mapping (address => uint256) public balanceOf;
// Modify this section
string public name = "DemoCoin";
string public symbol = "DC";
uint8 public decimals = 8;
uint256 public totalSupply = 1000000000 * (uint256(10) ** decimals);
address public owner;
//uint scaler = 10e18; // == 1 ETH in wei
//uint public coinPrice = 20; //initial price => 20 cents
event Transfer(address indexed from, address indexed to, uint256 value);
constructor() {
// Initially assign all tokens to the contract's creator.
owner = msg.sender;
balanceOf[msg.sender] = totalSupply;
emit Transfer(address(0), msg.sender, totalSupply);
}
// Might be executed automaticlly
// https://blog.chronologic.network/schedule-your-transaction-now-using-mycrypto-and-myetherwallet-17b48166b412
// function changeCoinPrice() public {
// uint newCoinPrice;
// require(msg.sender == address(0));
// coinPrice = newCoinPrice;
// }
function buyTokens() public payable {
// msg.value in wei so 1ETH = 10e18
// lets set 0.20 cents for 1 token
uint paidAmount;
require(balanceOf[msg.sender] >= paidAmount);
require(balanceOf[owner] >= value);
uint tokens;
tokens = value/10e14;
balanceOf[owner] -= tokens;
balanceOf[msg.sender] += tokens;
emit Transfer(owner, msg.sender, tokens);
}
function msgSenderBalancce() public view returns (uint) {
return balanceOf[msg.sender];
}
function withDrawEth() public view {
require(msg.sender == owner);
}
}
Why these methods are called diffrently? And how to add method id in metamask? Or am I missing something and this should be handled in other way?
MetaMask has a very basic UI. It only allows transfers of ETH and standardized tokens, but it doesn't show any buttons to call other contract functions. It also doesn't allow creating any custom buttons in their UI.
You'll need to set the data field of the transaction to 0xd0febe4c (which effectively executes the buyTokens() function).
But - they also don't allow specifying the data field value manually in the UI, so you'll need to preset it using the Ethereum provider API.
Your web app connects to the user's MetaMask acccount. It opens a MetaMask window and the user needs to manually confirm the connect.
The web app sends a request to MetaMask specifying the transaction with data field value.
The user confirms the transaction (which now includes the data field value 0xd0febe4c) in their MetaMask UI.
I have a smart contract that I’m trying to make, it pays out the winners of my League of Legends tournament. However I’m running into an issue. I need to make an API call to get the winner of the match, I have a simple URL that I’ve make.
"example-winner.com/winner"
And it returns simple JSON with the address of the winner:
{"winner":"0xa7D0......."}
However, I’m not sure how to make the API call to the outside function. I know I need to use some sort of oracle technology.
Any thoughts? Below is my code:
pragma solidity ^0.4.24;
contract LeagueWinners{
address public manager;
address[] public players;
uint256 MINIMUM = 1000000000000000;
constructor() public{
manager = msg.sender;
}
function enter() public payable{
assert(msg.value > MINIMUM);
players.push(msg.sender);
}
function getWinner() public{
assert(msg.sender == manager);
// TODO
// Get the winner from the API call
result = 0; // the result of the API call
players[result].transfer(address(this).balance);
// returns an adress object
// all units of transfer are in wei
players = new address[](0);
// this empties the dynamic array
}
}
You can use Chainlink as your Oracle.
As many have mentioned, you will need an oracle to get your API call. Something that is important to note, your contract is actually asking an oracle to make your API call for you, and not making the API call itself. This is because the blockchain is deterministic. For more information see this thread.
To answer your question, you can use the decentralized oracle service Chainlink.
You'd add a function:
function getWinner()
public
onlyOwner
{
Chainlink.Request memory req = buildChainlinkRequest(JOB, address(this), this.fulfill.selector);
req.add("get", "example-winner.com/winner");
req.add("path", "winner");
sendChainlinkRequestTo(ORACLE, req, ORACLE_PAYMENT);
}
For the purpose of the following exmaple, we are going to pretend you want to return a uint256 instead of an address. You can return a bytes32 and then convert it to an address, but for simplicity let's say the API returns the index of the winner. You'll have to find a node and jobId that can make a http.get request and return a uint256 object. You can find nodes and jobs from market.link. Each testnet (Ropsten, Mainnet, Kovan, etc) has different node addresses, so make sure you pick the right ones.
For this demo, we are going to use LinkPool's ropsten node
address ORACLE=0x83F00b902cbf06E316C95F51cbEeD9D2572a349a;
bytes32 JOB= "c179a8180e034cf5a341488406c32827";
Ideally, you'd choose a number of nodes to run your job, to make it trustless and decentralized. You can read here for more information on precoordinators and aggregating data. disclosure I am the author of that blog
Your full contract would look like:
pragma solidity ^0.6.0;
import "github.com/smartcontractkit/chainlink/evm-contracts/src/v0.6/ChainlinkClient.sol";
contract GetData is ChainlinkClient {
uint256 indexOfWinner;
address public manager;
address payable[] public players;
uint256 MINIMUM = 1000000000000000;
// The address of an oracle
address ORACLE=0x83F00b902cbf06E316C95F51cbEeD9D2572a349a;
//bytes32 JOB= "93fedd3377a54d8dac6b4ceadd78ac34";
bytes32 JOB= "c179a8180e034cf5a341488406c32827";
uint256 ORACLE_PAYMENT = 1 * LINK;
constructor() public {
setPublicChainlinkToken();
manager = msg.sender;
}
function getWinnerAddress()
public
onlyOwner
{
Chainlink.Request memory req = buildChainlinkRequest(JOB, address(this), this.fulfill.selector);
req.add("get", "example-winner.com/winner");
req.add("path", "winner");
sendChainlinkRequestTo(ORACLE, req, ORACLE_PAYMENT);
}
// When the URL finishes, the response is routed to this function
function fulfill(bytes32 _requestId, uint256 _index)
public
recordChainlinkFulfillment(_requestId)
{
indexOfWinner = _index;
assert(msg.sender == manager);
players[indexOfWinner].transfer(address(this).balance);
players = new address payable[](0);
}
function enter() public payable{
assert(msg.value > MINIMUM);
players.push(msg.sender);
}
modifier onlyOwner() {
require(msg.sender == manager);
_;
}
// Allows the owner to withdraw their LINK on this contract
function withdrawLink() external onlyOwner() {
LinkTokenInterface _link = LinkTokenInterface(chainlinkTokenAddress());
require(_link.transfer(msg.sender, _link.balanceOf(address(this))), "Unable to transfer");
}
}
This would do about everything you need.
If you can't adjust the API to return a uint, you can return a bytes32 and then convert it to an address or a string.
function bytes32ToStr(bytes32 _bytes32) public pure returns (string memory) {
bytes memory bytesArray = new bytes(32);
for (uint256 i; i < 32; i++) {
bytesArray[i] = _bytes32[i];
}
return string(bytesArray);
}
You cannot. The vm does not have any I/O outside of the blockchain itself. Instead you will need to tell your smart contract who the winner is and then the smart contract can just read the value of that variable.
This design pattern is also known as the "oracle". Google "Ethereum oracle" for more info.
Basically your web server can call your smart contract. Your smart contract cannot call your web server. If you need your smart contract to access a 3rd party service then your web server will need to make the request then forward the result to solidity by calling a function in your smart contract.
You didn't properly explain what you are trying to do. Are you having trouble with the solidity code? or rather with your server? Here is an edited version. See if it helps.
pragma solidity ^0.4.24;
contract LeagueWinners{
address public manager;
//address[] public players;
uint256 MINIMUM = 1000000000000000;
constructor() public{
manager = msg.sender;
}
struct Player {
address playerAddress;
uint score;
}
Player[] public players;
// i prefer passing arguments this way
function enter(uint value) public payable{
assert(msg.value > MINIMUM);
players.push(Player(msg.sender, value));
}
//call this to get the address of winner
function winningPlayer() public view
returns (address winner)
{
uint winningScore = 0;
for (uint p = 0; p < players.length; p++) {
if (players[p].score > winningScore) {
winningScore = players[p].score;
winner = players[p].playerAddress;
}
}
}
// call this to transfer fund
function getWinner() public{
require(msg.sender == manager, "Only a manager is allowed to perform this operation");
// TODO
address winner = winningPlayer();
// Get the winner from the API call
//uint result = 0; // the result of the API call
winner.transfer(address(this).balance);
// returns an adress object
// all units of transfer are in wei
delete players;
// this empties the dynamic array
}
}
At least that is what I understand by your question.