web3 sending data and tokens - ethereum

Actually from our backend we are sending 2 transactions to the Ethereum Blockchain, first Tx we sent tokens, and second Tx we send Data as names, age, passport etc.
Can we send everything in only 1 Transaction? Data and Tokens?

Yes. You can send ETH with any smart contract transaction call.
If you are wanting to send some token other than ETH in one transaction, write a contract that handles this logic
contract OneTx {
mapping(address => string) names;
function constructor() {
ERC20 erc20 = ERC20("0x...")
}
function doStuff(address from, address to, uint amount, string name) {
erc20.transferFrom(from, to, amount);
names[from] = name;
}
}
Keep in mind you need the contract to be approved to move funds on behalf of from

Related

Solidity smart contract audit

I have a solidity code to audit like this
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/token/ERC20/IERC20.sol";
// Allow to split the balance through complex rules
interface Split{
function getAddressAndAmountToSplit() view external returns(address, uint);
}
// MyBank contract
// This contract allows anyone to store any ERC20 tokens
contract MyBank {
// (token => user => amount)
mapping (address => mapping(address => uint)) public userBalance;
// (address => Split contract)
mapping (address => Split) splits;
// Deposit ERC20 tokens to the contracts
// The user must approve the bank before calling addToBalance
function addToBalance(IERC20 token, uint amount) external {
token.transferFrom(msg.sender, address(this), amount);
userBalance[address(token)][msg.sender] += amount;
}
// Withdraw part of the balance
function withdrawBalance(IERC20 token) external {
token.transfer(msg.sender, userBalance[address(token)][msg.sender]);
userBalance[address(token)][msg.sender] = 0;
}
// Allow to register a split contract
function registerSplit(Split split) external {
splits[msg.sender] = split;
}
// Split the balance into two accounts
// The usage of a Split contract allows to create complex split strategies
function splitBalance(IERC20 token) external {
Split split = splits[msg.sender];
require(split != Split(address(0x0)));
uint balance = userBalance[address(token)][msg.sender];
(address dest, uint amount) = Split(split).getAddressAndAmountToSplit();
userBalance[address(token)][dest] = amount;
userBalance[address(token)][msg.sender] = balance - amount;
}
}
What I found.
function withdrawBalance(IERC20 token) external possible reentrancy attack, because we check balance in the end
function splitBalance(IERC20 token) external - vulnerable business logic, because if amount is greater than balance we get negative value and possible integer overflow
If you have any idea of possible vulnerabilities of code above, please feel free to provide any further assistance
Probably a bit late but if anyone reads this for why I think these aren’t vulnerabilities. First of all, there are no reentrancy attacks possible here, due to the fact that transfer function only forwards 2300 gas, which is quite not enough to execute something meaningful.
Secondly, its possible to add a require check if balance is bigger than the amount. But since contract is using later than or equal to 0.8.0 compiler versions, if amount is indeed bigger than balance, it will automatically revert due to underflow.
I think as a smart contract auditor, you should know these better.

How to transfer an ERC721 token

I'm trying to transfer an ERC721 token, but I'm getting the error ERC721: transfer caller is not owner nor approved for the transferToken method.
Main.sol
import "./ERC721.sol";
import "./Counters.sol";
contract Main is ERC721 {
using Counters for Counters.Counter;
Counters.Counter internal _tokenIds;
address payable internal admin;
constructor() ERC721("MyToken", "TOKEN") {
admin = payable(msg.sender);
}
}
Auction.sol
import "./Main.sol";
contract Auction is Main {
struct AuctionInfo {
uint256 tokenId;
address highestBidder;
uint highestBid;
}
mapping(string => AuctionInfo) private _auctionInfo;
function createAuction(string memory id) public {
_tokenIds.increment();
uint256 newTokenId = _tokenIds.current();
_mint(msg.sender, newTokenId);
_auctionInfo[id].tokenId = newTokenId;
}
function transferToken(string memory id) public {
require(msg.sender == _auctionInfo[id].highestBidder, "You are not the highest bidder");
safeTransferFrom(address(this), _auctionInfo[id].highestBidder, _auctionInfo[id].tokenId);
}
// other methods...
}
The minting contract is this and the owner of the token is the msg.sender of the minting method if I'm not mistaken. Am I to use the approve (or setApprovalForAll) for this each time before transferring? I've tried this, payable(this), and address(this) for the safeTransferFrom method, but none seem to be working.
For example, I tried the following, but get the same revert message:
approve(address(this), _auctionInfo[id].tokenId);
this.safeTransferFrom(address(this), _auctionInfo[id].highestBidder, _auctionInfo[id].tokenId);
The main principle behind any Blockchain is that nobody on the blockchain network should be trusted, and still the transactions should happen fool proof, with no possibility of any cheating being done (barring of course of some hacking).
If you invoke the approve method from the Auction contract, then the msg.sender for the approve function in the ERC721 token contract is your auction contract address. So, in other words, your Auction Contract is trying to approve itself to sell someone else's NFTs, which is not very trustworthy.
What should really happen is that owner of the NFT should invoke the approve method of the ERC721 contract - i.e. the transaction that you send for the approve function call, should be signed by the NFT owner wallet address. This way, the msg.sender for the approve function in the ERC721 contract will be the owner of the NFT. As per the ERC721 standards, the owner of the NFT can approve anyone they want, to sell their NFT(s), as the no-trust in the network is still maintained (At least I should be able to trust myself). The approve method should be invoked from within your DAPP, before the transferToken function is invoked from the DAPP.
Hope that explains why you are unable to transfer your ERC721 tokens.
Because of the internal visibility of the ERC721._approve() function, you can effectively perform the approval for the user.
Then you'll be able to execute the safeTransferFrom(tokenOwner, receiver, tokenId) from your contract, because your contract address is approved to operate this specific token even though it belongs to the tokenOwner.
This snippet mints the token, assigning the ownership to the msg.sender. But then it also calls the _approve() function that doesn't contain any validations and simply assigns the approval of the token to the Auction address.
pragma solidity ^0.8;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol";
contract Auction is ERC721 {
constructor() ERC721("CollectionName", "Symbol") {}
function createAuction() public {
uint256 newTokenId = 1;
_mint(msg.sender, newTokenId);
_approve(address(this), newTokenId);
}
}
You can see from the screenshot that the owner is 0x5B... (the user address) and that the token is approved for 0xd9... (the contract address).
Note: The _approve() function is internal - it can be called from the ERC721 contract and contracts deriving from it (in your case Main and Auction), but it can't be called from external contracts or end user addresses.

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).

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.

Send all Tokens and Eth to a Address automatically

I have few child wallets and a parent wallet. Whenever these child wallets get incoming ERC20 tokens or ETH. I want to send them automatically to the parent wallet.
I know I can do below to send the entire eth balance.
beneficiary.transfer(this.balance);
How can I do it for erc20 tokens.
Thanks in advance.
require(token.transfer(beneficiary, token.balanceOf(this)));
where token is the ERC20 token you're transferring. E.g.:
interface IERC20Token {
function transfer(address, uint256) external returns (bool);
function balanceOf(address) external returns (uint256);
}
contract Foo {
IERC20Token token = IERC20Token(0x123abc...);
function doit(address beneficiary) public {
require(token.transfer(beneficiary, token.balanceOf(this)));
}
}