How to prevent a token transfer in smart contract? - ethereum

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.

Related

Best plan of attack for digital certificates for proof of completion on EVM

I am looking to explore the option of creating a digital certificate (as in proof) when someone has completed a portion of training, and for this to be issued on an EVM-compatible blockchain using Solidity.
I have prototyped using ERC721 NFTs to encode a "certificate" however, I'd like to prevent recipients from being able to transfer these certificates. To prevent transfer, I attempted to use the Pause.sol functionality from OpenZeppelin, however, this would result in the entire contract being paused, as opposed to a specific tokenId.
Does anyone have any recommendation on an approach? Am I overcomplicating it if I don't want recipients to be able to trade the certificates (i.e. for them to remain static)? Any pointers would be much appreciated!
The simplest and most raw solution is to just set a mapping value.
pragma solidity ^0.8;
contract TrainingResults {
enum Stage {
NONE,
STAGE_1,
STAGE_2,
COMPLETED
}
mapping (address => Stage) public participantStage;
function setParticipantStage(address _graduate, Stage _stage) external {
require(msg.sender == address(0x123), "Not authorized");
participantStage[_graduate] = _stage;
}
}
Or if you want them to be able to see some kind of NFT in their wallet (that supports NFTs), you can modify the ERC-721 contract to disallow transfers.
For example the OpenZeppelin implementation uses a function named _beforeTokenTransfer() (GitHub link) that can be overwritten to disallow transfers altogether.
pragma solidity ^0.8;
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
contract TrainingResults is ERC721 {
constructor() ERC721("TrainingResults", "TR") {}
function _beforeTokenTransfer(address from,address to, uint256 tokenId) override internal {
// Allow only for the admin
// as this function is called on token mint as well
require(msg.sender == address(0x123), "Cannot transfer tokens");
}
}

Calling transfer function using interface for ERC20

I am trying to send ERC20 tokens from a contract to an account .
This is the code that I have
IERC20 testToken = IERC20(tokenAddress);
testToken.transfer(accountAddress,amount);
The function to intitate the trasfer should ideally be called by someone else, lets call him "C"
So
msg.sender != accountAddress
Is there a way I can make the msg.sender for the transfer function to
be the contract itself instead of "C" ?
Also this function somehow works if I i call it from the account
address ,i.e when msg.sender == accountAddress . How does that happen
,in this case the from and to fields should both be the same ,and no
change in balance should happen ?
You'll need the transferFrom function and not transfer. The transfer function automatically uses the msg.sender as a parameter. You will also have to have that address have allowance of the tokens to transfer. The approve method takes care of this. Assuming that is all set, here is the code.
IERC20 tcontract = IERC20(tokenAddress);
require(tcontract.transferFrom(address(this), msg.sender, amount), "Don't have enough balance");

Avoid using solidity's transfer()/send()?

I came across this article dated 2019/9 about avoiding using solidity's transfer()/send(). Here is the reasoning from the article:
It looks like EIP 1884 is headed our way in the Istanbul hard fork. This change increases the gas cost of the SLOAD operation and therefore breaks some existing smart contracts.
Those contracts will break because their fallback functions used to consume less than 2300 gas, and they’ll now consume more. Why is 2300 gas significant? It’s the amount of gas a contract’s fallback function receives if it’s called via Solidity’s transfer() or send() methods. 1
Since its introduction, transfer() has typically been recommended by the security community because it helps guard against reentrancy attacks. This guidance made sense under the assumption that gas costs wouldn’t change, but that assumption turned out to be incorrect. We now recommend that transfer() and send() be avoided.
In remix, there is a warning message about the code below:
(bool success, ) = recipient.call{value:_amount, gas: _gas}("");
Warning:
Low level calls: Use of "call": should be avoided whenever possible. It can lead to unexpected behavior if return value is not handled properly. Please use Direct Calls via specifying the called contract's interface. more
I am not an expert on gas cost over execution of smart contract and security. So I post this article and would appreciate thoughts and comments about it.
First, it's good to know about the fallback function in Solidity:
It has no name, no arguments, no return value, and it can be defined as one per contract, but the most important feature is it is called when a non-existent function is called on the contract such as to send or transfer or call.value()(""). So if you want to send Ether directly to an address which is a contract address, the fallback function of the destination contract will be called.
If the contract's fallback function not marked payable, it will throw an exception if the contract receives plain ether without data.
Now let's look at the reentrancy attack
contract VulnerableContract {
mapping(address => uint) public balances;
function deposit() public payable {
require(msg.value > 1);
balances[msg.sender] += msg.value;
}
function withdraw(uint _amount) public {
require(balances[msg.sender] >= _amount, "Not enough balance!");
msg.sender.call.value(_amount)("");
balances[msg.sender] -= _amount;
}
function getBalance() view public returns(uint) {
return address(this).balance;
}
fallback() payable external {}
}
VuinerableContract has a withdraw function which sends Ether to the calling address. Now the calling address could be a malicious contract such as this :
contract MaliciousContract {
VulnerableContract vulnerableContract = VulnerableContract(0x08970FEd061E7747CD9a38d680A601510CB659FB);
function deposit() public payable {
vulnerableContract.deposit.value(msg.value)();
}
function withdraw() public {
vulnerableContract.withdraw(1 ether);
}
function getBalance() view public returns(uint) {
return address(this).balance;
}
fallback () payable external {
if(address(vulnerableContract).balance > 1 ether) {
vulnerableContract.withdraw(1 ether);
}
}
}
When the malicious contract call the withdraw function, before reducing the balance of the malicious contract, it's fallback function will be called at it can steal more Ether from the vulnerable contract.
So by limiting the gas amount used by the fallback function up to 2300 gas we could prevent this attack. It means we can not put complex and expensive commands in the fallback function anymore.
Look at this for more information: https://swcregistry.io/docs/SWC-107
From Consensys article, they are saying use .call() instead of .transfer() and .send(). Only argument is that all three now sends more gas than 2300. Thus making it possible for reentrancy.
This comes to another conclusion that, regardless of all above, it is important to use checks-effects-interactions pattern to prevent reentracy attack.

How to sign a transaction send by contract

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.

How ERC20 tokens control access to their functions?

Here is my question (basically, I want to know if I am right or mistaken):
First of all, we have standard ERC20:
contract ERC20Interface
{
function transfer(address _to, uint _value) public returns (bool success);
function transferFrom(address _from, address _to, uint _value) public returns (bool success);
function approve(address _spender, uint _value) public returns (bool success);
.....
}
Then, in a CrowdSale, we have a token, derived from it:
contract ICO is Ownable {
...
MyToken public m_token;
...
function ICO() public {
m_token = new MyToken();
...
Then the crowdsale ends. The token with list of all people (addresses) that bought it becomes "free" of crowd sale contract:
function finish() onlyOwner public {
if(goalReached()) {
m_token.transferOwnership(m_addrOwner);
My questions:
1. For this token to be traded on exchanges, I have to somehow provide these exchanges with token's address. Where should I get it?
2. Am I correct? Is it how it supposed to work?
In other words, if a token is a separate contract, passed to ICO contract by address, I can see how exchanges can trade it after the end of crowd sale: they have that same address.
But I see a lot of crowdsales on Etherscan, that create contract internally (like in code snippets above). How do I make it "tradable" after the end of a crowd sale in this case?
Thanks.
Most exchanges will only support your token if they see value in it. You'd have to contact the exchanges and they will evaluate if it's worth it to them based on the demand for your token. Even then, they will most likely ask for payment up front before listing.
For example, Bittrex has their submission process documented here. They offer free listing once you pass their verification process (along with some non-free services). Other exchanges follow a similar process.
The contract address you're looking for that you'd have to provide is returned to your client when you deploy your token contract. If you somehow lose the address in your client, you can find it on etherscan.io by searching for the token name/symbol (assuming your token contract follows the ERC20 spec) or by transaction hash. You can also retrieve the address in code:
function getTokenAddress() constant returns (address) {
return address(m_token);
}
Outside of an exchange, you can also do private transfers of tokens by simply adding the token contract address to a wallet like Mist or Parity and sending tokens between addresses.
Note that all of this is independent of the crowd sale. The crowd sale is a separate contract that is used to raise money and do the initial token distribution.