I’ve just finished reconciling the Goodies’ The DAO Token Balance contact that they are planning to use to refund ETCs to The DAO token holders on the Ethereum Classic blockchain.
See:
- github.com/bokkypoobah/TheDAOETCTokenBalance
- How can I verify that my The DAO token balance is correct when the Goodies provide an ETC refund on the Ethereum Classic chain? (Ethereum.StackExchange.com)
- How can I verify that my The DAO token balance is correct when the Goodies provide an ETC refund on the Ethereum Classic chain? (Reddit)
DAOBalanceSnapShot Contract
From dao_balance_snapshot.sol:
// This contract publishes the balances of the DAO at the moment of the hardfork
// After the deployment of the contract the function fill is called many times
// in order to fill the balance of each account.
contract DAOBalanceSnapShot {
uint constant D160 = 0x10000000000000000000000000000000000000000;
mapping (address => uint) public balanceOf;
address public owner;
function DAOBalanceSnapShot() {
owner = msg.sender;
}
uint public totalSupply;
uint public totalAccounts;
bool public sealed;
// The 160 LSB is the address of the balance
// The 96 MSB is the balance of that address.
function fill(uint[] data) {
if ((msg.sender != owner)||(sealed))
throw;
for (uint i=0; i<data.length; i++) {
address a = address( data[i] & (D160-1) );
uint amount = data[i] / D160;
if (balanceOf[a] == 0) { // In case it's filled two times, it only increments once
totalAccounts ++;
balanceOf[a] = amount;
totalSupply += amount;
}
}
}
function seal() {
if ((msg.sender != owner)||(sealed))
throw;
sealed= true;
}
}
WhitehatWithdraw Contract
From whetcwithdraw.sol
.sol:
// The contract that allows DTH to withdraw funds that the white hat
// group has managed to retrieve.
//
// There are 2 ways to use the contract:
// 1. withdraw()
// 2. proxyWithdraw()
//
// For a description of each method, take a look at the docstrings.
//
// License: BSD3
contract DAOBalanceSnapShot {
function balanceOf(address _dth) constant returns(uint);
function totalSupply() constant returns(uint );
}
contract Owned {
/// Prevents methods from perfoming any value transfer
modifier noEther() {if (msg.value > 0) throw; _}
/// Allows only the owner to call a function
modifier onlyOwner { if (msg.sender != owner) throw; _ }
address owner;
function Owned() { owner = msg.sender;}
function changeOwner(address _newOwner) onlyOwner {
owner = _newOwner;
}
function getOwner() noEther constant returns (address) {
return owner;
}
}
contract WhitehatWithdraw is Owned {
uint constant WithdrawType_DIRECT = 1;
uint constant WithdrawType_PROXY = 2;
DAOBalanceSnapShot daoBalance;
mapping (address => uint) paidOut;
mapping (bytes32 => bool) usedSignatures;
uint totalFunds;
uint deployTime;
uint closingTime;
address whg_donation;
address escape;
address remainingBeneficary;
bool sealed;
event Withdraw(address indexed dth, address indexed beneficiary, uint256 amount, uint256 percentageWHG, uint256 withdrawType);
event CertifiedDepositorsChanged(address indexed _depositor, bool _allowed);
event Deposit(uint amount);
event EscapeCalled(uint amount);
event RemainingClaimed(uint amount);
function fill(uint[] data) onlyOwner {
if ((msg.sender != owner)||(sealed))
throw;
for (uint i=0; i< data.length; i+= 2) {
address dth = address(data[i]);
uint amount = uint(data[i+1]);
paidOut[dth] = amount;
totalFunds += amount;
}
}
function seal() {
if ((msg.sender != owner)||(sealed))
throw;
sealed= true;
}
function WhitehatWithdraw(address _whg_donation, address _daoBalanceSnapshotAddress, address _escapeAddress, address _remainingBeneficiary) {
whg_donation = _whg_donation;
daoBalance = DAOBalanceSnapShot(_daoBalanceSnapshotAddress);
escape = _escapeAddress;
remainingBeneficary = _remainingBeneficiary;
totalFunds = msg.value;
deployTime = now;
closingTime = 24 weeks;
}
/// Calculates the remaining funds available for a DTH to withdraw
///
/// @param _dth The address of the DAO Token Holder for whom
/// to get the funds remaining for withdrawal
/// @return The amount of funds remaining for withdrawal
function calculateWithdraw(address _dth) constant noEther returns(uint) {
uint tokens = daoBalance.balanceOf(_dth);
uint acumulatedReward = tokens * totalFunds / daoBalance.totalSupply();
if (acumulatedReward < paidOut[_dth]) {
return 0;
}
return acumulatedReward - paidOut[_dth];
}
/// The core of the withdraw functionality. It is called by all other withdraw functions
///
/// @param _dth The address of the DAO token holder for whom the
/// withdrawal is going to happen
/// @param _beneficiary The address that will receive the _percentage of
/// the funds corresponding to the _dth.
/// @param _percentageWHG The percentage of the funds that will be donated to the
/// White Hat Group. It should be a number ranging from 0
/// to 100. Anything not claimed by the DTH will be going
/// as a donation to the Whitehat Group.
/// @param _withdrawType method used to withdraw (1) Direct (2) Proxy (3) bot (4) owner
function commonWithdraw(address _dth, address _beneficiary, uint _percentageWHG, uint _withdrawType) internal {
if (_percentageWHG > 100) {
throw;
}
uint toPay = calculateWithdraw(_dth);
if (toPay == 0) {
return;
}
if (toPay > this.balance) {
toPay = this.balance;
}
uint portionWhg = toPay * _percentageWHG / 100;
uint portionDth = toPay - portionWhg;
paidOut[_dth] += toPay;
// re-entrancy is not possible due to the use of send() which limits
// the forwarded gas thanks to the gas stipend
if (portionWhg > 0) {
if ( !whg_donation.send(portionWhg) ) {
throw;
}
}
if (portionDth > 0) {
if (!_beneficiary.send(portionDth) ) {
throw;
}
}
Withdraw(_dth, _beneficiary, toPay, _percentageWHG, _withdrawType);
}
/// The simple withdraw function, where the message sender is considered as
/// the DAO token holder whose ratio needs to be retrieved.
function withdraw(address _beneficiary, uint _percentageWHG ) noEther {
commonWithdraw(msg.sender, _beneficiary, _percentageWHG, WithdrawType_DIRECT);
}
/// The proxy withdraw function. Anyone can call this for someone else as long
/// as he includes signed data retrieved by using web3.eth.sign(address, hash).
/// The DAO token holder whose ratio needs to be retrieved is determined by
/// performing ecrecover on the signed data.
///
/// This function will also allow people to use the ETH chain to give an
/// approval for withdrawal in the ETC chain without having to sync the
/// ETC chain. The only requirement is that the account that gives the
/// approval needs to be an end-user account. Multisig wallets can't do that.
function proxyWithdraw(address _beneficiary, uint _percentageWHG, uint8 _v, bytes32 _r, bytes32 _s) noEther {
if (usedSignatures[_r]) {
throw;
}
bytes32 _hash = sha3("Withdraw DAOETC to ", _beneficiary, _percentageWHG);
address _dth = ecrecover(_hash, _v, _r, _s);
usedSignatures[_r] = true;
commonWithdraw(_dth, _beneficiary, _percentageWHG, WithdrawType_PROXY);
}
/// This is the only way to send money to the contract, adding to the total
/// amount of ETH to be refunded.
function deposit() returns (bool) {
totalFunds += msg.value;
Deposit(msg.value);
return true;
}
/// Last Resort call, to allow for a reaction if something bad happens to
/// the contract or if some security issue is uncovered.
function escapeHatch() noEther onlyOwner returns (bool) {
uint total = this.balance;
if (!escape.send(total)) {
throw;
}
EscapeCalled(total);
}
/// Allows the claiming of the remaining funds after a given amount of time
/// Amount is set to 6 months for now but may still change in the future.
function claimRemaining() noEther returns (bool) {
if (now < deployTime + closingTime) {
throw;
}
uint total = this.balance;
if (!remainingBeneficary.send(total)) {
throw;
}
RemainingClaimed(total);
}
/// Allows the option to extend (but not shorten!) the closingTime of the
/// contract to more than 6 months, perhaps even to infinity if that is
/// deemed as the best choice for the DAO Token holders.
function extendClosingTime(uint _additionalSeconds) noEther onlyOwner {
closingTime += _additionalSeconds;
}
function () { //no donations
throw;
}
function getPaidOut(address _account) noEther constant returns (uint) {
return paidOut[_account];
}
function getMyBalance(address _account) noEther constant returns (uint) {
return daoBalance.balanceOf(_account);
}
function getTotalFunds() noEther constant returns (uint) {
return totalFunds;
}
function getWHGDonationAddress() noEther constant returns (address) {
return whg_donation;
}
}