What?

The kissBTC smart contract lives on the Ethereum blockchain and helps you turn your actual, hard-as-crypto bitcoins into tokens on the Ethereum network - then called kissBTCE. Tokenized in this way, it can then easily be used in Ethereum smart contracts - such as escrow, gambling, financial derivatives, prediction markets, trustless mixers, ponzi schemes and more. At some later date, kissBTCE can be turned back into actual bitcoins again.

How?

The smart contract follows the KISS principle: keep it simple, stupid. Your bitcoins are being sold for Ether and the smart contract issues tokens in return. When redeeming tokens, the smart contracts uses Ether from its reserves to buy bitcoins again.

Is it safe?

It's really not. That's where the "stupid" comes in. The market might move again the smart contract and leave it bankrupt. We take a lesson from the modern financial system here and just hope for the best!

But why?

Safer, better ways of bringing Bitcoin onto the Ethereum network are in the works. Until they are ready, kissBTCE can serve as a testbed to prepare for the real thing.

ETH <-> kissBTCE

If you have Ether, it is easy to get kissBTCE by simply making a payment to the smart contract. Start by adding a new token (Contracts > Watch token in Ethereum Wallet) with the address 0x6777c314B412F0196aCA852632969F63e7971340 . Ethereum Wallet should automatically detect the details. Then send between 0.05 and 10 ETH to 0x6777c314B412F0196aCA852632969F63e7971340 and a few confirmations later you should receive tokens according to the current exchange rate.

To exchange back, just send your tokens back to the address 0x6777c314B412F0196aCA852632969F63e7971340 . Send between 0.00125 and 0.25 kissBTC. Watch your Ether balance to see it increase after a few blocks. Note that you won't get an extra notification for that if you are sending from an account.

The smart contract charges a small fixed fee for the request it needs to make to get the current exchange rate via Oraclize and an extra 1 % to discourage day trading against the contract.

BTC <-> kissBTCE

To go from BTC to kissBTCE and back, we will deploy your own personal Bitcoin adapter smart contract. As above, start by adding a new token (Contracts > Watch token in Ethereum Wallet) with the address 0x6777c314B412F0196aCA852632969F63e7971340 . Next, go to Contracts > Deploy new contract and paste the source code below into the field titled "Solidity contract source code".

// <ORACLIZE_API>
/*
Copyright (c) 2015-2016 Oraclize srl, Thomas Bertani



Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:



The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.



THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

contract OraclizeI {
    address public cbAddress;
    function query(uint _timestamp, string _datasource, string _arg) returns (bytes32 _id);
    function query_withGasLimit(uint _timestamp, string _datasource, string _arg, uint _gaslimit) returns (bytes32 _id);
    function query2(uint _timestamp, string _datasource, string _arg1, string _arg2) returns (bytes32 _id);
    function query2_withGasLimit(uint _timestamp, string _datasource, string _arg1, string _arg2, uint _gaslimit) returns (bytes32 _id);
    function getPrice(string _datasource) returns (uint _dsprice);
    function getPrice(string _datasource, uint gaslimit) returns (uint _dsprice);
    function useCoupon(string _coupon);
    function setProofType(byte _proofType);
}
contract OraclizeAddrResolverI {
    function getAddress() returns (address _addr);
}
contract usingOraclize {
    uint constant day = 60*60*24;
    uint constant week = 60*60*24*7;
    uint constant month = 60*60*24*30;
    byte constant proofType_NONE = 0x00;
    byte constant proofType_TLSNotary = 0x10;
    byte constant proofStorage_IPFS = 0x01;
    uint8 constant networkID_auto = 0;
    uint8 constant networkID_mainnet = 1;
    uint8 constant networkID_testnet = 2;
    uint8 constant networkID_morden = 2;
    uint8 constant networkID_consensys = 161;

    OraclizeAddrResolverI OAR;
    
    OraclizeI oraclize;
    modifier oraclizeAPI {
        address oraclizeAddr = OAR.getAddress();
        if (oraclizeAddr == 0){
            oraclize_setNetwork(networkID_auto);
            oraclizeAddr = OAR.getAddress();
        }
        oraclize = OraclizeI(oraclizeAddr);
        _
    }
    modifier coupon(string code){
        oraclize = OraclizeI(OAR.getAddress());
        oraclize.useCoupon(code);
        _
    }

    function oraclize_setNetwork(uint8 networkID) internal returns(bool){
        if (getCodeSize(0x1d3b2638a7cc9f2cb3d298a3da7a90b67e5506ed)>0){
            OAR = OraclizeAddrResolverI(0x1d3b2638a7cc9f2cb3d298a3da7a90b67e5506ed);
            return true;
        }
        if (getCodeSize(0x9efbea6358bed926b293d2ce63a730d6d98d43dd)>0){
            OAR = OraclizeAddrResolverI(0x9efbea6358bed926b293d2ce63a730d6d98d43dd);
            return true;
        }
        if (getCodeSize(0x20e12a1f859b3feae5fb2a0a32c18f5a65555bbf)>0){
            OAR = OraclizeAddrResolverI(0x20e12a1f859b3feae5fb2a0a32c18f5a65555bbf);
            return true;
        }
        return false;
    }
    
    function oraclize_query(string datasource, string arg) oraclizeAPI internal returns (bytes32 id){
        uint price = oraclize.getPrice(datasource);
        if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price
        return oraclize.query.value(price)(0, datasource, arg);
    }
    function oraclize_query(uint timestamp, string datasource, string arg) oraclizeAPI internal returns (bytes32 id){
        uint price = oraclize.getPrice(datasource);
        if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price
        return oraclize.query.value(price)(timestamp, datasource, arg);
    }
    function oraclize_query(uint timestamp, string datasource, string arg, uint gaslimit) oraclizeAPI internal returns (bytes32 id){
        uint price = oraclize.getPrice(datasource, gaslimit);
        if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price
        return oraclize.query_withGasLimit.value(price)(timestamp, datasource, arg, gaslimit);
    }
    function oraclize_query(string datasource, string arg, uint gaslimit) oraclizeAPI internal returns (bytes32 id){
        uint price = oraclize.getPrice(datasource, gaslimit);
        if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price
        return oraclize.query_withGasLimit.value(price)(0, datasource, arg, gaslimit);
    }
    function oraclize_query(string datasource, string arg1, string arg2) oraclizeAPI internal returns (bytes32 id){
        uint price = oraclize.getPrice(datasource);
        if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price
        return oraclize.query2.value(price)(0, datasource, arg1, arg2);
    }
    function oraclize_query(uint timestamp, string datasource, string arg1, string arg2) oraclizeAPI internal returns (bytes32 id){
        uint price = oraclize.getPrice(datasource);
        if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price
        return oraclize.query2.value(price)(timestamp, datasource, arg1, arg2);
    }
    function oraclize_query(uint timestamp, string datasource, string arg1, string arg2, uint gaslimit) oraclizeAPI internal returns (bytes32 id){
        uint price = oraclize.getPrice(datasource, gaslimit);
        if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price
        return oraclize.query2_withGasLimit.value(price)(timestamp, datasource, arg1, arg2, gaslimit);
    }
    function oraclize_query(string datasource, string arg1, string arg2, uint gaslimit) oraclizeAPI internal returns (bytes32 id){
        uint price = oraclize.getPrice(datasource, gaslimit);
        if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price
        return oraclize.query2_withGasLimit.value(price)(0, datasource, arg1, arg2, gaslimit);
    }
    function oraclize_cbAddress() oraclizeAPI internal returns (address){
        return oraclize.cbAddress();
    }
    function oraclize_setProof(byte proofP) oraclizeAPI internal {
        return oraclize.setProofType(proofP);
    }

    function getCodeSize(address _addr) constant internal returns(uint _size) {
        assembly {
            _size := extcodesize(_addr)
        }
    }


    function parseAddr(string _a) internal returns (address){
        bytes memory tmp = bytes(_a);
        uint160 iaddr = 0;
        uint160 b1;
        uint160 b2;
        for (uint i=2; i<2+2*20; i+=2){
            iaddr *= 256;
            b1 = uint160(tmp[i]);
            b2 = uint160(tmp[i+1]);
            if ((b1 >= 97)&&(b1 <= 102)) b1 -= 87;
            else if ((b1 >= 48)&&(b1 <= 57)) b1 -= 48;
            if ((b2 >= 97)&&(b2 <= 102)) b2 -= 87;
            else if ((b2 >= 48)&&(b2 <= 57)) b2 -= 48;
            iaddr += (b1*16+b2);
        }
        return address(iaddr);
    }


    function strCompare(string _a, string _b) internal returns (int) {
        bytes memory a = bytes(_a);
        bytes memory b = bytes(_b);
        uint minLength = a.length;
        if (b.length < minLength) minLength = b.length;
        for (uint i = 0; i < minLength; i ++)
            if (a[i] < b[i])
                return -1;
            else if (a[i] > b[i])
                return 1;
        if (a.length < b.length)
            return -1;
        else if (a.length > b.length)
            return 1;
        else
            return 0;
   } 

    function indexOf(string _haystack, string _needle) internal returns (int)
    {
        bytes memory h = bytes(_haystack);
        bytes memory n = bytes(_needle);
        if(h.length < 1 || n.length < 1 || (n.length > h.length)) 
            return -1;
        else if(h.length > (2**128 -1))
            return -1;                                  
        else
        {
            uint subindex = 0;
            for (uint i = 0; i < h.length; i ++)
            {
                if (h[i] == n[0])
                {
                    subindex = 1;
                    while(subindex < n.length && (i + subindex) < h.length && h[i + subindex] == n[subindex])
                    {
                        subindex++;
                    }   
                    if(subindex == n.length)
                        return int(i);
                }
            }
            return -1;
        }   
    }

    function strConcat(string _a, string _b, string _c, string _d, string _e) internal returns (string){
        bytes memory _ba = bytes(_a);
        bytes memory _bb = bytes(_b);
        bytes memory _bc = bytes(_c);
        bytes memory _bd = bytes(_d);
        bytes memory _be = bytes(_e);
        string memory abcde = new string(_ba.length + _bb.length + _bc.length + _bd.length + _be.length);
        bytes memory babcde = bytes(abcde);
        uint k = 0;
        for (uint i = 0; i < _ba.length; i++) babcde[k++] = _ba[i];
        for (i = 0; i < _bb.length; i++) babcde[k++] = _bb[i];
        for (i = 0; i < _bc.length; i++) babcde[k++] = _bc[i];
        for (i = 0; i < _bd.length; i++) babcde[k++] = _bd[i];
        for (i = 0; i < _be.length; i++) babcde[k++] = _be[i];
        return string(babcde);
    }
    
    function strConcat(string _a, string _b, string _c, string _d) internal returns (string) {
        return strConcat(_a, _b, _c, _d, "");
    }

    function strConcat(string _a, string _b, string _c) internal returns (string) {
        return strConcat(_a, _b, _c, "", "");
    }

    function strConcat(string _a, string _b) internal returns (string) {
        return strConcat(_a, _b, "", "", "");
    }

    // parseInt
    function parseInt(string _a) internal returns (uint) {
        return parseInt(_a, 0);
    }

    // parseInt(parseFloat*10^_b)
    function parseInt(string _a, uint _b) internal returns (uint) {
        bytes memory bresult = bytes(_a);
        uint mint = 0;
        bool decimals = false;
        for (uint i=0; i<bresult.length; i++){
            if ((bresult[i] >= 48)&&(bresult[i] <= 57)){
                if (decimals){
                   if (_b == 0) break;
                    else _b--;
                }
                mint *= 10;
                mint += uint(bresult[i]) - 48;
            } else if (bresult[i] == 46) decimals = true;
        }
        mint *= 10 ** _b;
        return mint;
    }
    

}
// </ORACLIZE_API>

contract KissBTC {
    function balanceOf(address _owner) constant returns (uint256 balance);
    function approve(address _spender, uint256 _amount) returns (bool success);
    function transfer(address _to, uint256 _amount) returns (bool success);
    function transferFrom(address _from, address _to,
                          uint256 _amount) returns (bool success);
    function buyKissBTCWithCallback(address callback, uint gasLimit);
}

contract BitcoinSender {
    function sendBitcoin(string _address, uint _amount);
}

contract BitcoinAdapter is usingOraclize {
    address constant KISS_BTC =
        0x6777c314B412F0196aCA852632969F63e7971340;
    address constant BITCOIN_SENDER =
        0x71107a8959f1249920CF87dc8C994B9B483D25Ef;
    uint constant MAX_ETH_VALUE = 10 ether;

    address public owner;
    string public bitcoinAddress;

    modifier onlyowner { if (msg.sender == owner) _ }

    function BitcoinAdapter() {
        owner = msg.sender;
        bitcoinAddress = "initializing...";
        requestFreshBitcoinAddress();
    }

    function sendBitcoin(string _address, string _amount) onlyowner {
        uint amount = parseInt(_amount, 8);
        if (!KissBTC(KISS_BTC).transferFrom(owner, this, amount)) throw;
        if (!KissBTC(KISS_BTC).approve(BITCOIN_SENDER, amount)) throw;
        BitcoinSender(BITCOIN_SENDER).sendBitcoin(_address, amount);
    }

    function updateBitcoinAddress() onlyowner oraclizeAPI {
        uint price = oraclize.getPrice("URL");
        if (price > msg.value) throw;
        requestFreshBitcoinAddress();
        msg.sender.send(msg.value - price);
    }

    function requestFreshBitcoinAddress() internal {
        string memory json = strConcat(
            '{"pair": "btc_eth", "withdrawal": "',
            addressToString(this),
            '"}'
        );
        oraclize_query(
            "URL",
            "json(https://shapeshift.io/shift).deposit",
            json
        );
    }

    function () {
        uint value = this.balance;
        if (value > MAX_ETH_VALUE) value = MAX_ETH_VALUE;

        KissBTC(KISS_BTC).buyKissBTCWithCallback
            .value(value)(address(this), 200000);
    }

    function __callback(bytes32 myid, string result) {
        if (msg.sender != oraclize_cbAddress()) throw;
        bitcoinAddress = result;
    }

    function kissBTCCallback(uint id, uint amount) {
        uint totalAmount = KissBTC(KISS_BTC).balanceOf(address(this));
        if (totalAmount > 0) {
            KissBTC(KISS_BTC).transfer(owner, totalAmount);
        }
    }

    function setOwner(address _owner) onlyowner {
        owner = _owner;
    }

    function addressToString(address _address) internal returns (string) {
        uint x = uint(_address);

        // result will have 40 characters
        string memory template = "                                        ";
        bytes memory result = bytes(template);

        for (uint i = 0; i < 40; i++) {
            uint digit = x & 0x0f;
            if (digit < 10) {
                result[39 - i] = byte(48 + digit);  // '0' - '9'
            } else {
                result[39 - i] = byte(87 + digit);  // 'a' - 'f'
            }
            x = x / 16;
        }

        return string(result);
    }
}

It will take a few seconds for the compiler to compile the source code and then you should be able to pick "Bitcoin Adapter" from the choices under "Select contract to deploy" on the right. Then scroll to the bottom of the form, click on "Deploy" and confirm the transaction by entering your password. Once the contract has been deployed, go back to the contracts overview and select the newly created "Bitcoin Adapter" contract. Among the details of the contract you should find a field titled "Bitcoin address" which will list a Bitcoin address that the contract has requested for itself via ShapeShift. Bitcoins sent to that address will be turned into kissBTCE and your Bitcoin adapter smart contract will forward those tokens to you. The kissBTC smart contract only converts amounts from 0.05 ETH to 10 ETH, so when making a Bitcoin payment to the adapter, make sure to send enough bitcoins to end up in this range.

The Bitcoin adapter smart contract can also be used to turn kissBTCE back into BTC. To do that, you will need to give it access to your tokens and then call the function sendBitcoin() with a destination address. To give the adapter full access to your tokens, we need to add the core kissBTC smart contract and call the whitelist() function on it. Go to Contracts > Watch contract and enter "KissBTC" as the contract name, 0x6777c314B412F0196aCA852632969F63e7971340 as the contract address and paste the following into the field titled "JSON interface":

[{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"oraclizeRequests","outputs":[{"name":"","type":"uint256"}],
"type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"type":"function"},
{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_amount","type":"uint256"}],"name":"approve",
"outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_owner","type":"address"}],
"name":"setOwner","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"totalSupply",
"outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"amount","type":"uint256"}],
"name":"inEther","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,
"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_amount","type":"uint256"}],
"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"type":"function"},
{"constant":false,"inputs":[{"name":"oraclizeId","type":"bytes32"},{"name":"result","type":"string"}],
"name":"__callback","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],
"name":"sellKissBTC","outputs":[{"name":"id","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],
"name":"decimals","outputs":[{"name":"","type":"uint8"}],"type":"function"},{"constant":true,"inputs":[],
"name":"exchangeRate","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,
"inputs":[{"name":"_amount","type":"uint256"},{"name":"callback","type":"address"},{"name":"gasLimit","type":"uint256"}],
"name":"sellKissBTCWithCallback","outputs":[{"name":"id","type":"uint256"}],"type":"function"},
{"constant":true,"inputs":[],"name":"standard","outputs":[{"name":"","type":"string"}],"type":"function"},
{"constant":true,"inputs":[],"name":"nextId","outputs":[{"name":"","type":"uint256"}],"type":"function"},
{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],
"type":"function"},{"constant":false,"inputs":[{"name":"callback","type":"address"},{"name":"gasLimit","type":"uint256"}],
"name":"buyKissBTCWithCallback","outputs":[{"name":"id","type":"uint256"}],"type":"function"},
{"constant":false,"inputs":[{"name":"id","type":"uint256"}],"name":"retryOraclizeRequest","outputs":[],"type":"function"},
{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"tasks","outputs":[{"name":"oraclizeId","type":"bytes32"},
{"name":"toKissBTC","type":"bool"},{"name":"sender","type":"address"},{"name":"value","type":"uint256"},
{"name":"callback","type":"address"},{"name":"timestamp","type":"uint256"}],"type":"function"},
{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},
{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"type":"function"},
{"constant":true,"inputs":[{"name":"amount","type":"uint256"}],"name":"inKissBTC","outputs":[{"name":"","type":"uint256"}],
"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"}],"name":"whitelist",
"outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},
{"name":"_amount","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"type":"function"},
{"constant":true,"inputs":[{"name":"amount","type":"uint256"}],"name":"safeToSell",
"outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":true,"inputs":[],"name":"timestamp",
"outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},
{"name":"_amount","type":"uint256"},{"name":"_extraData","type":"bytes"}],"name":"approveAndCall",
"outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":false,"inputs":[],
"name":"buyKissBTC","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},
{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"type":"function"},
{"constant":false,"inputs":[],"name":"donate","outputs":[],"type":"function"},{"constant":false,"inputs":[],
"name":"toldYouItWouldWork","outputs":[],"type":"function"},{"inputs":[],"type":"constructor"},
{"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"},{"anonymous":false,
"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},
{"indexed":false,"name":"_amount","type":"uint256"}],"name":"Approval","type":"event"}]

Go back to the contract page for your Bitcoin adapter and note down its Ethereum address. Then switch to the contract page for the KissBTC contract that you started watching. In the list of functions on the right you should find the option "Whitelist". Select it, then paste the address of your Bitcoin adapter in the field "spender". Make sure that you are invoking the function from the account that holds your tokens, then click on "Execute" and enter your password to confirm the transaction. Your Bitcoin adapter should now be whitelisted to access your tokens. Go back to its contract page and select "Send Bitcoin" from the list of functions. Enter a Bitcoin address in the field "address" and an amount between 0.00125 and 0.25 kissBTC in the field "amount". After executing the function, tokens will be subtracted from your balance and a Bitcoin transaction to the specified address will be created.