Update Jun 02 2017
From WARNING: Do NOT Use SafeConditionalHFTransfer! Or Use It Correctly:
The SafeConditionalHFTransfer saved a lot of ethers being moved incorrectly on the wrong chain after The DAO hard fork. So far there has been 20549 txns + 16022 internalTxns passing through the SafeConditionalHFTransfer at 0x1e143b2588705dfea63a17f2032ca123df995ce0. The author contacted me this morning about 67,317.257581981046981598 ETH ~ USD 14,892,596.89 (@ $221.23/ETH) sent incorrectly to the contract.
When using this contract, you have to call the classicTransfer(...)
or transfer(...)
functions to direct your ETH or ETC to the intended chain. If you send ETH (or ETC) DIRECTLY to the contract address, your ETH (or ETC) will not be redirected to the destination address on the destination chain, but will instead be trapped in this contract FOREVER.
As the recent clients on both the ETH and ETC chains have EIP155 Replay Protection built in, you do NOT have to use this SafeConditionalHFTransfer
any more. Just make sure you are using a recent client, with EIP155!
Here are the main clients and the versions implementing EIP155:
This warning has also been placed at the top of the answer to How to conditionally send ethers to another account post-hard-fork to protect yourself from replay attacks.
Update Nov 26 2016 - See @eth's answer to the question above as geth
1.5.3 has implemented replay attack protection.
Summary
Update Aug 25 2016
Ethereum Wallet and Mist Beta 0.8.2 now has replay prevention:
Replay prevention
We added an advanced feature to prevent your transactions on being replayed on other chains, like ethereum classic. This allows you either to prevent that transfer on happening on classic at all, or use that transaction to send the same amount to a different contract, like a newly created account or an exchange. If you want to fully separate all your transactions we recommend you create two new accounts, one for Ethereum proper and the other for Classic, and then move all your funds into them (remember that you need ether to move tokens), making sure that each account has 0 ether on the other chain - doing this once would prevent any future transaction from being replayed. To use this, use the "more options" button on the send page.
This feature also supports splitting tokens, but it's very experimental and will not work on all tokens. Since all of this is done using a contract then first you need to allow that contract to move tokens in your behalf by clicking "Approve token transfer".
As always, these features are experimental and should be tested with small amounts first. Although most transactions are replayed on both chains, some may not for multiple reasons. Also, some exchanges have issues receiving ether from a contract address - if that's your case, contact the exchange.
We've also removed all Fork code from the Mist app, so if you want to use it Ether Classic you'll have to either download Classic Mist directly from their repository or use your own node as the backend for your wallet (both Ethereum Wallet and Mist can connect to any node) as you would do for a private network.
The replay protection contract can be found at 0x1ca4a86bba124426507d1ef67ad271cc5a02820a.
Method 1 - Using geth
And SafeConditionalHFTransfer
Contract
Make sure that you are running geth version 1.4.10 or later. And run your geth commands with the --support-dao-fork option so that you are on the hard-forked blockchain. To transfer using the transfer(...)
or classicTransfer(...)
functions:
user@Kumquat:~$ geth console
// Allow chain to sync
var fromAccount = "{from account}";
var toAccount = "{to account}";
var amount = web3.toWei(1.123, "ether");
personal.unlockAccount(fromAccount, "{password}")
var safeConditionalHFTransferAddress = "0x1e143b2588705dfea63a17f2032ca123df995ce0";
var safeConditionalHFTransferABI = [{"constant":false,"inputs":[{"name":"to","type":"address"}],"name":"transfer","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"}],"name":"classicTransfer","outputs":[],"type":"function"},{"inputs":[],"type":"constructor"}];
var safeConditionalHFTransfer = eth.contract(safeConditionalHFTransferABI).at(safeConditionalHFTransferAddress);
// WARNING - Run the next statement to transfer ETH on the hard-forked chain
// This will only cost some gas on the non-hard-forked ETC Classic chain
// Test with a small amount first
var transfer = safeConditionalHFTransfer.transfer(toAccount, {from: fromAccount, value: amount});
console.log(transfer);
// WARNING - Run the next statement to transfer ETC on the non-hard-forked Classic chain
// This will only cost some gas on the hard-forked ETH chain
// Test with a small amount first
var classicTransfer = safeConditionalHFTransfer.classicTransfer(toAccount, {from: fromAccount, value: amount});
console.log(classicTransfer);
Method 2 - Using geth
And ReplaySafeSplitV2
Contract
Make sure that you are running geth version 1.4.10 or later. And run your geth commands with the --support-dao-fork option so that you are on the hard-forked blockchain. To transfer using the split(...)
function:
user@Kumquat:~$ geth console
// Allow chain to sync
var fromAccount = "{from account}";
var toAccountFork = "{to account on forked chain}";
var toAccountNoFork = "{to account on non-forked chain}";
var amount = web3.toWei(1.123, "ether");
personal.unlockAccount(fromAccount, "{password}")
var replaySafeSplitV2Address = "0xaBbb6bEbFA05aA13e908EaA492Bd7a8343760477";
var replaySafeSplitV2ABI = [{"constant":false,"inputs":[{"name":"targetFork","type":"address"},{"name":"targetNoFork","type":"address"}],"name":"split","outputs":[{"name":"","type":"bool"}],"type":"function"}];
var replaySafeSplitV2 = eth.contract(replaySafeSplitV2ABI).at(replaySafeSplitV2Address);
var transfer = replaySafeSplitV2.split(toAccountFork, toAccountNoFork, {from: fromAccount, value: amount});
console.log(transfer);
Method 3 - Using Ethereum Wallet And SafeConditionalHFTransfer
Contract
Note that the ReplaySafeSplitV2
contract below has more safety features built in.
Make sure that you have downloaded Ethereum Wallet 0.8.1 or later.
The first time you start Ethereum Wallet 0.8.1, select "Yes" to the question "Do you want to activate the chain in which funds linked to the exploit are restored to a contract where they can be withdrawn by The DAO token holders?". You have now made the choice to use the hard-forked Ethereum chain.
In Ethereum Wallet, select the CONTRACTS page in the top menu. Click on WATCH CONTRACT.
- Enter a CONTRACT NAME of
SafeConditionalHFTransfer
- Enter a CONTRACT ADDRESS of
0x1e143b2588705dfea63a17f2032ca123df995ce0
- Enter in JSON INTERFACE the following text
[{"constant":false,"inputs":[{"name":"to","type":"address"}],"name":"transfer","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"}],"name":"classicTransfer","outputs":[],"type":"function"},{"inputs":[],"type":"constructor"}]
- Click OK. Your Watch Contract should look like:
To Transfer Only On The Hard-Forked Chain
This will transfer your ETH to the destination account on the hard-forked chain and will only cost you gas on the non-hard-forked Classic chain.
Select the CONTRACTS page in the top menu. Click on SAFECONDITIONALHFTRANSFER. On the right hand side of the page under WRITE TO CONTRACT, Select function Transfer
. Enter your destination address on the hard-fork chain in the To - address field. In Execute from, select your account. Enter the number of ETH under the Send ETHER field. Click on the EXECUTE button, enter your password and confirm. Here is a screen image:
To Transfer Only On The Non-Hard-Forked Classic Chain
This will transfer your ETH to the destination account on the non-hard-forked Classic chain and will only cost you gas on the hard-forked chain.
Select the CONTRACTS page in the top menu. Click on SAFECONDITIONALHFTRANSFER. On the right hand side of the page under WRITE TO CONTRACT, Select function Classic Transfer
. Enter your destination address on the non-hard-fork chain in the To - address field. In Execute from, select your account. Enter the number of ETH (ETC) under the Send ETHER field. Click on the EXECUTE button, enter your password and confirm. Here is a screen image:
Method 4 - Using Ethereum Wallet And The ReplaySafeSplitV2
Contract
UPDATE 22:27 Sep 4 2016 This is a new safer version of ReplaySafeSplit
as discussed by @chevdor on thedao.slack.com/messages/general in Safer version of the ReplaySafeSplit Smart Contract. This new version has checks that the addresses you specify are not 0x0000...0000 before sending your ethers to the addresses. This section has been updated with the new contract details.
Make sure that you have downloaded Ethereum Wallet 0.8.1 or later.
The first time you start Ethereum Wallet 0.8.1, select "Yes" to the question "Do you want to activate the chain in which funds linked to the exploit are restored to a contract where they can be withdrawn by The DAO token holders?". You have now made the choice to use the hard-forked Ethereum chain.
In Ethereum Wallet, select the CONTRACTS page in the top menu. Click on WATCH CONTRACT.
- Enter a CONTRACT NAME of
ReplaySafeSplitV2
Enter a CONTRACT ADDRESS of 0x8201...
(this old address was on the ETH chain only. If you used this contract, please delete your Watch Contract and recreate the Watch Contract with the new address below.)
- Enter a CONTRACT ADDRESS of
0xaBbb6bEbFA05aA13e908EaA492Bd7a8343760477
- Enter in JSON INTERFACE the following text
[{"constant":false,"inputs":[{"name":"targetFork","type":"address"},{"name":"targetNoFork","type":"address"}],"name":"split","outputs":[{"name":"","type":"bool"}],"type":"function"}]
- Click OK. Your Watch Contract should look like:
To Transfer
This will transfer your ETH to two accounts, the first being the destination account on the hard-forked chain and the second being the destination account on the non-hard-forked Classic chain.
Select the CONTRACTS page in the top menu. Click on REPLAYSAFESPLITV2. On the right hand side of the page under WRITE TO CONTRACT, Select function Split
. Enter your destination address on the hard-fork chain in the Target fork - address field. Enter your destination address on the non-hard-fork Classic chain in the Target no fork - address field. In Execute from, select your account. Enter the number of ethers under the Send ETHER field. Click on the EXECUTE button, enter your password and confirm.
Here is a screen image:
SafeConditionalHFTransfer
Contract
Note that the ReplaySafeSplitV2
contract below has more safety features built in.
Following is the source code for the SafeConditionalHFTransfer contract (suggested by @shoraibit
26/07/2016). This contract does not depend on the WithdrawDAO contract having a balance over 1,000,000 ethers in the future.
contract ClassicCheck {
function isClassic() constant returns (bool isClassic);
}
contract SafeConditionalHFTransfer {
bool classic;
function SafeConditionalHFTransfer() {
classic = ClassicCheck(0x882fb4240f9a11e197923d0507de9a983ed69239).isClassic();
}
function classicTransfer(address to) {
if (!classic)
msg.sender.send(msg.value);
else
to.send(msg.value);
}
function transfer(address to) {
if (classic)
msg.sender.send(msg.value);
else
to.send(msg.value);
}
}
This contract depends on the ClassicCheck contract:
contract ClassicCheck {
bool public classic;
function ClassicCheck() {
if (address(0xbf4ed7b27f1d666546e30d74d50d173d20bca754).balance > 1000000 ether)
classic = false;
else
classic = true;
}
function isClassic() constant returns (bool isClassic) {
return classic;
}
}
When the SafeConditionalHFTransfer
contract was deployed, it used the ClassicCheck
contract to determine whether the code was being deployed to the hard-forked or non-hard-forked chain. And this check was done when the WithdrawDAO had a balance over 1,000,000 . SafeConditionalHFTransfer
should always work as it no longer has to check the balance of the WithdrawDAO balance.
ReplaySafeSplitV2
Contract
UPDATE 22:27 Sep 4 2016 This is a new safer version of ReplaySafeSplit
as discussed by @chevdor on thedao.slack.com/messages/general in Safer version of the ReplaySafeSplit Smart Contract. This new version has checks that the addresses you specify are not 0x0000...0000
before sending your ethers to the addresses.
Following is the source code for the ReplaySafeSplitV2 contract:
contract RequiringFunds {
modifier NeedEth () {
if (msg.value <= 0 ) throw;
_
}
}
contract AmIOnTheFork {
function forked() constant returns(bool);
}
contract ReplaySafeSplit is RequiringFunds {
// address private constant oracleAddress = 0x8128B12cABc6043d94BD3C4d9B9455077Eb18807; // testnet
address private constant oracleAddress = 0x2bd2326c993dfaef84f696526064ff22eba5b362; // mainnet
// Fork oracle to use
AmIOnTheFork amIOnTheFork = AmIOnTheFork(oracleAddress);
// Splits the funds into 2 addresses
function split(address targetFork, address targetNoFork) NeedEth returns(bool) {
// The 2 checks are to ensure that users provide BOTH addresses
// and prevent funds to be sent to 0x0 on one fork or the other.
if (targetFork == 0) throw;
if (targetNoFork == 0) throw;
if (amIOnTheFork.forked() // if we are on the fork
&& targetFork.send(msg.value)) { // send the ETH to the targetFork address
return true;
} else if (!amIOnTheFork.forked() // if we are NOT on the fork
&& targetNoFork.send(msg.value)) { // send the ETH to the targetNoFork address
return true;
}
throw; // don't accept value transfer, otherwise it would be trapped.
}
// Reject value transfers.
function() {
throw;
}
}
Following is the VM code from the non-hard-forked Classic blockchain which matches the verified VM code + source code on the hard-forked chain.
user@PussyWillow:~$ geth -exec 'eth.getCode("0xaBbb6bEbFA05aA13e908EaA492Bd7a8343760477")' attach
"0x6060604052361561001f5760e060020a60003504630f2c93298114610028575b6100005b610002565b6100406004356024356000348190116100e157610002565b60408051918252519081900360200190f35b80547f16c72721000000000000000000000000000000000000000000000000000000006060908152600160a060020a0391909116906316c727219060649060209060048187876161da5a03f11561000257505060405151905080156100d25750604051600160a060020a038416908290349082818181858883f193505050505b1561010f575060015b92915050565b82600160a060020a0316600014156100f857610002565b81600160a060020a03166000141561005257610002565b600060009054906101000a9004600160a060020a0316600160a060020a03166316c727216040518160e060020a0281526004018090506020604051808303816000876161da5a03f11561000257505060405151159050801561018c5750604051600160a060020a038316908290349082818181858883f193505050505b15610023575060016100db56"
This contract depends on the AmIOnTheFork contract:
contract AmIOnTheFork {
bool public forked = false;
address constant darkDAO = 0x304a554a310c7e546dfe434669c62820b7d83490;
// Check the fork condition during creation of the contract.
// This function should be called between block 1920000 and 1921200.
// Approximately between 2016-07-20 12:00:00 UTC and 2016-07-20 17:00:00 UTC.
// After that the status will be locked in.
function update() {
if (block.number >= 1920000 && block.number <= 1921200) {
forked = darkDAO.balance < 3600000 ether;
}
}
function() {
throw;
}
}
Updated 00:57 Aug 20 2016 UTC - using BigNumber
to accurately compute the totalSupply
figures.
Checking Your The DAO Token Balance
From ETC withdraw contract to be reviewed you can check your The DAO token balance at pre-hard-fork block 1,919,999 using geth
synced to the Ethereum Classic chain using the following commands:
geth --fast --oppose-dao-fork console
// Allow the chain to sync
> var daobalancesnapshot_abi = [{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"seal","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"totalAccounts","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"data","type":"uint256[]"}],"name":"fill","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"sealed","outputs":[{"name":"","type":"bool"}],"type":"function"},{"inputs":[],"type":"constructor"}];
undefined
> var daobalancesnapshot = web3.eth.contract(daobalancesnapshot_abi).at("0x180826b05452ce96e157f0708c43381fee64a6b8");
undefined
> daobalancesnapshot.balanceOf(YOURACCOUNTHERE);
123456
Alternatively, you can verify your The DAO token balances at block 1,919,999 in theDAOTokenBalance_20160819_155742UTC_balance.xlsx. You may have to right mouse click -> Save Link As to download the spreadsheet to your computer.
Checking All DAO Token Balances
The script getTheDAOTokenBalance finds all addresses involved in the CreatedToken
and Transfer
The DAO events and queries each of these addresses against The DAO's balanceOf(...)
function, AT block 1,919,999.
The script additionally reconciles this results against the DAOBalanceSnapShot
contract at 0x180826b05452ce96e157f0708c43381fee64a6b8 (with ABI here) that the Goodies intend to use to determine the number of The DAO tokens each address owns at block 1,919,999.
The results are that my script calculate the same number of The DAO tokens for each address compared to the Goodies' DAOBalanceSnapShot
contract and the totals are the same.
There is a summary at github.com/bokkypoobah/TheDAOETCTokenBalance. You can find all the CreatedToken
events in theDAOTokenBalance_20160819_155742UTC_creation.txt, all the Transfer
events in theDAOTokenBalance_20160819_155742UTC_transfer.txt and all balances at block 1,919,999 in theDAOTokenBalance_20160819_155742UTC_balance.txt and theDAOTokenBalance_20160819_155742UTC_balance.xlsx.
My script finds a total of 22,092 non-zero accounts with a total of 1,153,816,598.7024671407837618 DAOs. My script queries the Goodies' DAOBalanceSnapShot
contract and finds the same DAO total. The DAOBalanceSnapShot
contract has a totalSupply
attribute that returns the value 11538165987024671407837618 or 1,153,816,598.7024671407837618 DAOs.The Goodies' DAOBalanceSnapShot
contract accurately represent the balances of DTH's accounts at the pre-hard-fork block 1,919,999.
And checking theDAO's totalSupply()
at pre-hard-fork block 1,919,999 using geth
confirms the total supply figure.
> var theDAOABIFragment = [{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"type":"function"}];
undefined
> var theDAOAddress = "0xbb9bc244d798123fde783fcc1c72d3bb8c189413"
undefined
> var theDAO = web3.eth.contract(theDAOABIFragment).at(theDAOAddress);
undefined
> theDAO.totalSupply(1919999).toString(10);
"11538165987024671407837618"
The Script
To use the following script, you will have to download the Classic blockchain using geth --oppose-dao-fork console
. Note that you cannot use the --fast
parameter as this will exclude the intermediate results required for the debug.traceTransaction(...)
API query required to determine the correct addresses involved in the token creation. The script will take over half a day to execute, depending on the speed of your processor and disk.
The script getTheDAOTokenBalance follows:
#!/bin/sh
# ------------------------------------------------------------------------------
# Retrieve The DAO Creation and Transfer events, collecting addresses. Then
# retrieve the pre-hard-fork balances and the current balances for these
# addresses.
#
# v2 improvements
# * Summing totals as BigNumbers instead of floats
# * Only including non-zero balances at block 1919999 in counter
#
# Usage:
# 1. Download this script to getTheDAOTokenBalance .
# 2. `chmod 700 getTheDAOTokenBalance`
# 3. Run `geth console` in a window.
# 4. Then run this script `./getTheDAOTokenBalance` in a separate window.
#
# Enjoy. (c) BokkyPooBah 2016. The MIT licence.
# ------------------------------------------------------------------------------
DATE=`date -u +%Y%m%d_%H%M%S`
OUTPUT=theDAOTokenBalance_${DATE}UTC_all.txt
CREATION=theDAOTokenBalance_${DATE}UTC_creation.txt
TRANSFER=theDAOTokenBalance_${DATE}UTC_transfer.txt
BALANCE=theDAOTokenBalance_${DATE}UTC_balance.txt
geth attach << EOF | egrep "^Header|^Data|^Stats|^Footer" > $OUTPUT
var theDAOABIFragment = [{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"type":"function"}, {"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"CreatedToken","type":"event"}, {"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"Transfer","type":"event"}];
var theDAOAddress = "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413";
var theDAO = web3.eth.contract(theDAOABIFragment).at(theDAOAddress);
var theDAOTokenBalancePreHardForkABI = [{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"seal","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"totalAccounts","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"data","type":"uint256[]"}],"name":"fill","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"sealed","outputs":[{"name":"","type":"bool"}],"type":"function"},{"inputs":[],"type":"constructor"}];
var theDAOTokenBalancePreHardForkAddress = "0x180826b05452ce96e157f0708c43381fee64a6b8";
var theDAOTokenBalancePreHardForkContract = web3.eth.contract(theDAOTokenBalancePreHardForkABI).at(theDAOTokenBalancePreHardForkAddress);
// The DAO created in block 1428757 Apr-30-2016 01:42:58 AM +UTC
var creationStartBlock = 1428757;
// First The DAO creation event with non-zero extraBalance in block 1520861 May-15-2016 09:00:16 AM +UTC
var creationWithExtraBalanceStartBlock = 1520861;
// Last The DAO creation event in block 1599205 May-28-2016 08:59:47 AM +UTC
var creationEndBlock = 1599205;
// First The DAO transfer event in block 1599207 May-28-2016 09:00:07 AM +UTC, but use block above
var transferStartBlock = creationEndBlock + 1;
// Last The DAO transfer
var transferEndBlock = eth.blockNumber;
// Pre hard-fork block
var preHardForkBlock = 1919999;
// Hard-fork block
var hardForkBlock = 1920000;
// Collect addresses
var addresses = {};
// --- Get creation events ---
var startBlock = creationStartBlock;
var endBlock = creationEndBlock;
// Testing
// startBlock = creationWithExtraBalanceStartBlock - 5;
// endBlock = creationWithExtraBalanceStartBlock + 5;
var theDAOCreatedTokenEvent = theDAO.CreatedToken({}, {fromBlock: startBlock, toBlock: endBlock});
console.log("HeaderCreation\tTokenOwner");
var i = 0;
theDAOCreatedTokenEvent.watch(function(error, result) {
addresses[result.args.to] = 1;
i = i + 1;
console.log("DataCreation\t" + i + "\t" + result.args.to);
});
// --- Get transfer events ---
var startBlock = transferStartBlock;
var endBlock = transferEndBlock;
// Testing
// startBlock = transferStartBlock;
// endBlock = transferStartBlock + 10;
var theDAOTransferEvent = theDAO.Transfer({}, {fromBlock: startBlock, toBlock: endBlock});
console.log("HeaderTransfer\tFrom\tTo");
i = 0;
theDAOTransferEvent.watch(function(error, result) {
addresses[result.args._from] = 1;
addresses[result.args._to] = 1;
i = i + 1;
console.log("DataTransfer\t" + i + "\t" + result.args._from + "\t" + result.args._to);
});
// --- Get balances ---
var currentBlock = transferEndBlock;
console.log("HeaderBalance\tNumber\tAddress\tPreHardForkDAO\tPreHardForkDAOContract\tPreHardForkDAODiff\tCurrentDAO\tChangeDAO\tPreHardForkETH\tCurrentETH\tChangeETH");
var daosPreHardForkTotal = new BigNumber(0);
var daosPreHardForkContractTotal = new BigNumber(0);
var daosCurrentTotal = new BigNumber(0);
var nonZeroAccounts = new BigNumber(0);
i = 0;
for (var address in addresses) {
i = i + 1;
var checkAddress = "0xda0031fab25270778386a2dcac0bb934a288c464";
var daosPreHardFork = theDAO.balanceOf(address, preHardForkBlock);
var daosPreHardForkContract = theDAOTokenBalancePreHardForkContract.balanceOf(address);
var daosPreHardForkDiff = daosPreHardFork - daosPreHardForkContract;
var daosCurrent = theDAO.balanceOf(address, currentBlock);
if (daosPreHardFork != 0 || daosPreHardForkContract != 0) {
nonZeroAccounts++;
}
var daosChange = daosCurrent - daosPreHardFork;
var ethsPreHardFork = eth.getBalance(address, preHardForkBlock);
var ethsCurrent = eth.getBalance(address, currentBlock);
var ethsChange = ethsCurrent - ethsPreHardFork;
daosPreHardForkTotal = daosPreHardForkTotal.plus(daosPreHardFork);
daosPreHardForkContractTotal = daosPreHardForkContractTotal.plus(daosPreHardForkContract);
daosCurrentTotal = daosCurrentTotal.plus(daosCurrent);
console.log("DataBalance\t" + i + "\t" + address + "\t" +
(daosPreHardFork/1e16).toFixed(16) + "\t" + (daosPreHardForkContract/1e16).toFixed(16) + "\t" + (daosPreHardForkDiff/1e16).toFixed(16) + "\t" +
(daosCurrent/1e16).toFixed(16) + "\t" + (daosChange/1e16).toFixed(16) + "\t" +
(ethsPreHardFork/1e18).toFixed(18) + "\t" + (ethsCurrent/1e18).toFixed(18) + "\t" + (ethsChange/1e18).toFixed(18));
}
console.log("Stats\tnonZeroAccounts\t" + nonZeroAccounts);
console.log("Stats\tdaosPreHardForkTotal\t" + daosPreHardForkTotal.toPrecision(30) + "\t" + (daosPreHardForkTotal/1e16).toFixed(16));
console.log("Stats\tdaosPreHardForkContractTotal\t" + daosPreHardForkContractTotal.toPrecision(30) + "\t" + (daosPreHardForkContractTotal/1e16).toFixed(16));
console.log("Stats\tdaosCurrentTotal\t" + daosCurrentTotal.toPrecision(30) + "\t" + (daosCurrentTotal/1e16).toFixed(16));
console.log("Footer\tReport generated at " + (new Date()).toGMTString() + " with latest block number " + transferEndBlock);
EOF
egrep "HeaderCreation|DataCreation|Footer" $OUTPUT > $CREATION
egrep "HeaderTransfer|DataTransfer|Footer" $OUTPUT > $TRANSFER
egrep "HeaderBalance|DataBalance|Footer|Stats" $OUTPUT > $BALANCE
Best Answer
HECO blockchain recently implemented something like this.
HECO is an Ethereum fork modified to implement POSA and other features. As far as I know, nodes are probably all controlled by Huobi (CEX).
Last june, they implemented a hard fork that allowed to blacklist addresses and, similarly to what you want, to only allow a whitelist of addresses to deploy contracts. The latter is not currently enabled, but the code to do it is already there.
You may want to browse the commits of this particular release (1.1.0 Redcoast hardfork) and implement something similar.