How to sign a transaction send by contract - ethereum

I am trying to understand the basics behind signing an ethereum transaction.
I come across Gnosis's MultiSigWallet :
https://github.com/gnosis/MultiSigWallet/blob/master/contracts/MultiSigWallet.sol
In which, a modifier onlyWallet requires a transaction must be sent from the contract itself, but the transaction is signed by one of the owners of the contracts:
modifier onlyWallet() {
require(msg.sender == address(this));
_;
}
for functions like:
function replaceOwner(address owner, address newOwner)
public
onlyWallet
ownerExists(owner)
ownerDoesNotExist(newOwner)
{...}
I have successfully deployed the contracts on my testnet, and tried its functionalities using their dapp https://wallet.gnosis.pm/#/transactions
However, I cannot understand how a transaction is signed and sent to meet the onlyWallet requirement, since signing a transaction using metamask for example will cause the msg.sender to be my own wallet address.
If possible, an example of this ethereumjs-tx's functions would be much appreciated.
like:
const privateKey = Buffer.from('<private key 1>','hex');
const txParams = {
nonce: web3.utils.toHex(11),
gasPrice: web3.utils.toHex(1000000000),
gasLimit: web3.utils.toHex(300000),
to: '<contract address>',
value: web3.utils.toHex(web3.utils.toWei("1",'ether')),
data: '0x00',
chainId: 1
};
let tx = new EthTx(txParams);
tx.sign(tx.serialize().toString('hex');
web3.eth.sendSignedTransaction(`0x${tx.serialize().toString('hex')}`,
(error, data) => {
if(!error) {
console.log(data);
}else console.log(error);
}
);
Thank you very much

Adding and removing an owner follows the same rules as confirming any transactions from the MultiSigWallet. Assume the wallet was deployed with 3 EOA addresses as owners and requiring 2 confirmations for execution. For any regular transaction, say donate funds from the wallet to a FundRaisingContract, one of the owners would need to first call submitTransaction() passing in the address of FundRaisingContract, the amount of ether, and the hash for the donate function. After submission, you still need 1 of the other owners to confirmTransaction (the submitter is automatically confirmed). Once completed, now executeTransaction can be successfully run which will execute the call to donate() from the MultiSigWallet address.
That last part is what you're looking for to answer your question. To add an owner, repeat the example but use the MultiSigWallet address and the hash of the addOwner method. When one of the other owners confirms the transaction, addOwner will be called and it will pass the onlyWallet modifier check.

Related

How to interact with the deployed ERC20 token with another smart-contract?

I have created a basic ERC20 token by implementing OpenZeppelin as follow in ERC20.sol file:
pragma solidity ^0.6.4;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/token/ERC20/ERC20.sol";
contract Token is ERC20 {
constructor(string memory _name, string memory _symbol)
public
ERC20(_name, _symbol)
{
_mint(msg.sender, 10000000000000000000000000000);
}
}
Then implement another contract Contract.sol as follow:
import "./ERC20.sol";
pragma solidity ^0.6.4;
contract SimpleBank{
Token tokenContract;
constructor(Token _tokenContract) public {
tokenContract = _tokenContract;
}
function deposit(uint amt) public returns (bool) {
require(amt != 0 , "deposit amount cannot be zero");
tokenContract.transfer(address(this),amt);
return true;
}
}
As, I have deployed both contract from the address 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2 so, it holds 10000000000000000000000000000 tokens.
But when I call deposit function from same address I got the following error:
transact to SimpleBank.deposit errored: VM error: revert. revert The
transaction has been reverted to the initial state. Reason provided by
the contract: "ERC20: transfer amount exceeds balance". Debug the
transaction to get more information.
So, what is the proper way to interact with the deployed ERC20 token so that the deploy function works.
The user address 0xAb8483... sends a transaction executing SimpleBank's function deposit(), which makes 0xAb8483... the value of msg.sender in SimpleBank.
But then SimpleBank sends an internal transaction executing Token's function transfer(). Which makes SimpleBank address (not the 0xAb8483...) the value of msg.sender in Token.
So the snippet tokenContract.transfer(address(this),amt); within SimpleBank is trying to send SimpleBank's tokens. Not the user's (0xAb8483...) tokens.
This transfer of tokens (from point 2) reverts, because SimpleBank doesn't own any tokens. Which makes the top-level transaction (from point 1) revert as well.
If you want SimpleBank to be able to transfer 0xAb8483...'s tokens, 0xAb8483... needs to approve() the tokens first to be spent by SimpleBank. Directly from their address, so that they are msg.sender in the Token contract.
Only then SimpleBank can execute transferFrom(0xAb8483..., address(this), amt) (from, to, amount).
TLDR: Your contract can't spend tokens that it doesn't own, unless the owner has manually approved your contract to spend them.
If it could spend someone else's tokens without approval, it would be very easy to steal from people who can't/don't verify your source code (by spending their USDT, WETH and other widely-used tokens).

How to prevent a token transfer in smart contract?

Is there any way to prevent token transfer from address A to address B in a smart contract? Let's say it needs some sort of approval from smart contract owner. Assuming it is not ERC-20 token.
There are many ways to implement such a restriction in your code logic.
For example, you could create a mapping with addresses that only the smart contract owner can add entries to. (Using an onlyOwner modifier). Then implement a require function that only permits the transfer if the address is in the mapping.
mapping (address=>bool) addr_mapping;
function transferToken(address sender, address receiver) public{
require(addr_mapping[sender] == true, "Sender not in mapping");
require(addr_mapping[receiver] == true, "Receiver not in mapping");
...
}
function addToMapping(address addr) onlyOwner {
...
}
PS. Not sure if the boolean comparison is possible, but you can create your own flag.

Re-entrancy not reproduceable

Short description
I was playing with the re-entrancy and mis-use of tx.origin example from solidity readthedocs.
This example shows how a user wallet can be tricked into having all of the calling account's funds transferred to an attacker's account.
After changing the invocation of transfer to call.value to allow for sending more gas (so the custom fall-back function could be invoked for the re-entrancy attack), I observed behaviour that I do not understand (see below).
The relevant code
User wallet:
pragma solidity >=0.5.0 <0.7.0;
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
contract TxUserWallet {
address owner;
constructor() public payable {
owner = msg.sender;
}
function() external payable {}
function transferTo(address payable dest, uint amount) public payable {
//require(tx.origin == owner, "tx.origin not owner");
dest.call.value(amount)("");
}
}
Attacker's wallet:
pragma solidity >=0.5.0 <0.7.0;
interface TxUserWallet {
function transferTo(address payable dest, uint amount) external;
}
contract TxAttackWallet {
address payable owner;
constructor() public payable {
owner = msg.sender;
}
function() external payable {
TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance);
}
}
Observations I don't understand
I compiled and deployed the contracts in Remix.
When I call the function TxUserWallet.transferTo (from the owner's account, with enough gas, value and balance) using as parameters (1) the attacker's wallet address and (2) some value val (smaller than the msg.value), then I notice that an amount of val is transferred, whereas I would expect the total sender account balance to be transferred ... ?
When I comment out the body of the fall-back function of the attack wallet, compile, deploy and then repeat the steps from point 1 above, then Remix reports success on the transaction, but nothing is transferred, whereas in this case I would expect val to be transferred ... ?
My question
How to understand the above observations?
Transactions:
Deploying attack wallet from account 1:
[vm]
from:0xca3...a733c
to:TxAttackWallet.(constructor)
value:0 wei
data:0x608...b0032
logs:0
hash:0x37b...32f64
status 0x1 Transaction mined and execution succeed
transaction hash 0x37bfe3f84e1b164b4a3fc711fadda2ed287071e07477ecf82a9a437f90e32f64
contract address 0x22e37c29ad8303c6b58d3cea5a3f86160278af01
from 0xca35b7d915458ef540ade6068dfe2f44e8fa733c
to TxAttackWallet.(constructor)
gas 3000000 gas
transaction cost 150927 gas
execution cost 74747 gas
hash 0x37bfe3f84e1b164b4a3fc711fadda2ed287071e07477ecf82a9a437f90e32f64
input 0x608...b0032
decoded input {}
decoded output -
logs []
value 0 wei
Deploying user wallet from account 2 (which currently has a balance of over 100 ether):
[vm]
from:0x147...c160c
to:TxUserWallet.(constructor)
value:0 wei
data:0x608...b0032
logs:0
hash:0x5c1...18439
status 0x1 Transaction mined and execution succeed
transaction hash 0x5c183894bc0f00f420b8c19f86f51fb91dc3b288729cd34f4ee9a0932aa18439
contract address 0x1439818dd11823c45fff01af0cd6c50934e27ac0
from 0x14723a09acff6d2a60dcdf7aa4aff308fddc160c
to TxUserWallet.(constructor)
gas 3000000 gas
transaction cost 148247 gas
execution cost 72747 gas
hash 0x5c183894bc0f00f420b8c19f86f51fb91dc3b288729cd34f4ee9a0932aa18439
input 0x608...b0032
decoded input {}
decoded output -
logs []
value 0 wei
Calling TxUserWallet.transferTo from account 2 (owner) with the address of attack wallet:
[vm]
from:0x147...c160c
to:TxUserWallet.transferTo(address,uint256) 0x143...27ac0
value:1000000000000000000 wei
data:0x2cc...03039
logs:0
hash:0xcfc...476b8
status 0x1 Transaction mined and execution succeed
transaction hash 0xcfc442c88207d20c0b365548e5bdc6bf7b868d2991486246875d8ca11fe476b8
from 0x14723a09acff6d2a60dcdf7aa4aff308fddc160c
to TxUserWallet.transferTo(address,uint256) 0x1439818dd11823c45fff01af0cd6c50934e27ac0
gas 3000000 gas
transaction cost 40659 gas
execution cost 17723 gas
hash 0xcfc442c88207d20c0b365548e5bdc6bf7b868d2991486246875d8ca11fe476b8
input 0x2cc...03039
decoded input {
"address dest": "0x22e37c29Ad8303c6b58D3Cea5A3f86160278af01",
"uint256 amount": {
"_hex": "0x3039"
}
}
decoded output {}
logs []
value 1000000000000000000 wei
Now account 2 has 1 ether less in stead of being completely robbed empty.
It's not the owner's balance that you trying to transfer it's the contract balance. Look at the msg.sender.balance this is the contract balance, because the contract is the one who sent this transaction. It works right now because you sending etc to the contract in your transaction's value. So the contract balance becomes equal to the value of your transaction. And then you sending the entire balance of the contract to your account 1.

web3 calls Solidity contract function with extra parameter?

In the http://truffleframework.com/tutorials/pet-shop example, there is the following contract, and the function adopt(uint petId) has only one parameter.
contract Adoption {
address[16] public adopters;
function adopt(uint petId) public returns (uint) {
require(petId >= 0 && petId <= 15);
adopters[petId] = msg.sender;
return petId;
}
function getAdopters() public returns (address[16]) {
return adopters;
}
}
However, in the javascript code app.js, the handleAdopt function call the contract function using the following code.
App.contracts.Adoption.deployed().then(function(instance) {
adoptionInstance = instance;
return adoptionInstance.adopt(petId, {from: account});
})
The function is called with the extra object {from: account}. Why? And is this parameter discarded in the solidity code?
BTW, there is an undefined global variable web3? Is the value be assigned by the MetaMask extension?
That is the transactionObject which describes general information about all transaction calls (gas limit, price, amount of ether to send, etc.). The JS code you posted is using the web3 library. That's not the direct call to the contract API. The web3 library converts it to an RPC. The transactionObject comes after all of the contract parameters. There is another parameter that comes after which is the callback with the results of the contract call (see here).
These are all of the options for the transactionobject described from the docs:
from: String - The address for the sending account. Uses the web3.eth.defaultAccount property, if not specified.
to: String - (optional) The destination address of the message, left undefined for a contract-creation transaction.
value: Number|String|BigNumber - (optional) The value transferred for the transaction in Wei, also the endowment if it's a contract-creation transaction.
gas: Number|String|BigNumber - (optional, default: To-Be-Determined) The amount of gas to use for the transaction (unused gas is refunded).
gasPrice: Number|String|BigNumber - (optional, default: To-Be-Determined) The price of gas for this transaction in wei, defaults to the mean network gas price.
data: String - (optional) Either a byte string containing the associated data of the message, or in the case of a contract-creation transaction, the initialisation code.
nonce: Number - (optional) Integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce.

How to detect if an Ethereum address is an ERC20 token contract?

If I only get an Ethereum address from the input, is there a way to find out whether it matches the ERC20 token standard?
ERC165 tackles this problem, but, unfortunately, most ERC20 implementations don't support it (as of Nov 2018, at least OpenZeppelin doesn't). This means that you could try calling the supportsInterface function, but it would revert anyway and you'd rather complicate things.
Nevertheless, here's how it's defined in ERC721:
bytes4 private constant _InterfaceId_ERC721 = 0x80ac58cd;
/*
* 0x80ac58cd ===
* bytes4(keccak256('balanceOf(address)')) ^
* bytes4(keccak256('ownerOf(uint256)')) ^
* bytes4(keccak256('approve(address,uint256)')) ^
* bytes4(keccak256('getApproved(uint256)')) ^
* bytes4(keccak256('setApprovalForAll(address,bool)')) ^
* bytes4(keccak256('isApprovedForAll(address,address)')) ^
* bytes4(keccak256('transferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
*/
Although it's not guaranteed for all implementations to define the interface id, there's a higher chance it will work in the case of ERC721, given the fact the community agreed on applying ERC165 right from the get-go. If the return value of the query below is true, then it means you have a compliant contract, otherwise just revert the transaction.
// you can call this in your contracts
IERC721(contractAddress).supportsInterface(0x80ac58cd)
Also, a useful resource for manually checking the bytes4 of a given method is 4byte.directory
If you are asking about off-chain, so use these functions:
getContract(url, smartContractAddress){
const Web3Eth = require('web3-eth');
const abi_ = this.getABI();
const web3Eth = new Web3Eth(Web3Eth.givenProvider || url);
return new web3Eth.Contract(abi_, smartContractAddress);
}
async getERCtype(contract){
const is721 = await contract.methods.supportsInterface('0x80ac58cd').call();
if(is721){
return "ERC721";
}
const is1155 = await contract.methods.supportsInterface('0xd9b67a26').call();
if(is1155){
return "ERC1155";
}
return undefined;
}
getABI(){
return [
{"constant":true,"inputs": [
{"internalType":"bytes4","name": "","type": "bytes4"}],
"name": "supportsInterface",
"outputs": [{"internalType":"bool","name": "","type": "bool"}],
"payable": false,"stateMutability":"view","type": "function"}
];
}
like this:
const contract = getContract(url, smartContractAddress);
const type = await getERCtype(contract);
console.log(type);
There are many possible ways to achieve this. One possible quick and dirty solution is to check if a ERC20 function exists on the contract address by calling the following:
eth.call({to:contractAddress, data:web3.sha3("balanceOf(address)")})
A non-ERC20 will return a 'null' 0x hex response whereas an ERC20 will give you a 32byte uint, in this case 0 but if you provide an address in the data then it will give you the actual token balance for that address.
This is not a guaranteed way of determining a contract is ERC20 since other contracts may expose the same function, however it is a quick and easy check. You could add additional calls on totalSupply() etc. for more confirmation.
the question states that given an Ethereum address, is this an Ethereum contract address or not?
First thing, you have to decide if this address is a contract address or an externally owned account address. How to find out if an Ethereum address is a contract
If it is a contract address, then you can only get the bytecode that is stored in the blockchain. Now you have to reverse engineer the bytecode to get the contract functions but this is almost impossible. There are some decompilers but they are not perfectly creating the contract code from the bytecode.
ERC165 just checks if the current contract signature supports the given interfaceId or not. Since you have only ethereum address, this will not help in this question.