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;
}
}
forked
, gets an automatically generated accessor method in Solidity.