How to do it…

  1. It is not possible to fix the bug by updating the code in the smart contract due to the immutable nature of Ethereum smart contracts. Upgradable smart contracts are a way to overcome this issue for a certain limit. But, it reduces the user's trust in the smart contracts.
  2. There are several design techniques that can be considered while writing smart contracts to minimize the effect of a potential bug.
  3. Let's consider a simple contract that allows the deposit and withdrawal of Ether from a contract. We can try implementing various design techniques to avoid the loss of funds up to a certain limit:
pragma solidity ^0.4.24;

contract VulnerableContract {

function deposit() public {
// Code to accept Ether
}

function withdraw() public {
// Code to transfer Ether
}

}
  1. Include the rate limiting functionality in your contract, which will limit the task performed to a certain time period. For example, the contract will allow a user to withdraw only a certain amount of Ether/tokens per day. Additional withdrawals can be prevented completely or allowed only through elevated or multi-signature approval. This will greatly reduce the loss and give enough time for the user or owner to find a resolution.
  2. Model this contract in the following way to implement the functionality. The modifier verifies the time between the current and last withdrawal:
pragma solidity ^0.4.24;

contract ControlledContract {

// Simplified withdraw tracker
// Can include amount for more precise tracking
mapping(address => uint) lastWithdraw;

// Modifier to limit the rate of withdraw
modifier verifyWithdraw() {
require(lastWithdraw[msg.sender] + 1 days > now);
_;
}

function deposit() public {
// Code to accept Ether
}

function withdraw(uint _value) verifyWithdraw public {
// Code to transfer Ether
require(_value < 1 ether);
lastWithdraw[msg.sender] = now;
}

}
  1. Another approach is to pause or stop contract functionality as soon as a bug is discovered. This prevents the attacker from performing malicious actions on the contract. The example contract can be modified as follows to include the pause functionality:
pragma solidity ^0.4.24;

contract ControlledContract {
bool pause;
address owner;

modifier onlyOwner() {
require(msg.sender == owner);
_;
}

modifier whenNotPaused {
require(!pause);
_;
}

function pauseContract() onlyOwner public {
pause = true;
}

function unPauseContract() onlyOwner public {
pause = true;
}

function deposit() whenNotPaused public {
// some code
}

function withdraw() whenNotPaused public {
// some code
}
}
  1. Consider delaying contract actions as an approach in order to minimize the effect of an attack. Each contract action can either be performed after a certain time period or through an approval. The withdrawal contract can be further modified to include the mentioned method:
pragma solidity ^0.4.24;

contract ControlledContract {

struct WithdrawalReq {
uint value;
uint time;
}

mapping (address => uint) balances;

mapping (address => WithdrawalReq) requests;

uint constant delay = 7 days;

function requestWithdrawal() public {
require(balances[msg.sender] > 0);
uint amountToWithdraw = balances[msg.sender];
balances[msg.sender] = 0;
requests[msg.sender] = WithdrawalReq(
amountToWithdraw,
now
);
}

function withdraw() public {
require(now > requests[msg.sender].time + delay);
uint amountToWithdraw = requests[msg.sender].value;
requests[msg.sender].value = 0;
msg.sender.transfer(amountToWithdraw);
}
}

  1. The decision on which design to choose depends heavily on the targeted user base and the requirements. You can either implement a fail-safe method or all of them. You can also come up with a fail-safe method based on your requirements, but make sure that the code is well-audited and error-free before deploying it to production.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.9.169