Skip to content

Instantly share code, notes, and snippets.

@amacdougall
Last active April 11, 2021 16:27
Show Gist options
  • Save amacdougall/37dffb5005d5cab99becb2265d59d581 to your computer and use it in GitHub Desktop.
Save amacdougall/37dffb5005d5cab99becb2265d59d581 to your computer and use it in GitHub Desktop.
troncase.space contract analysis

Summary

This is an analysis of the contract behind https://troncase.space/. This is a contract written in Tron's variant of Solidity.

This contract can receive and pay funds, and stores user accounts and deposit records. It behavior is simple:

The invest function receives a transfer from a user and adds it to the contract balance. Some of the transferred funds are immediately distributed to the contract owners; some are granted to the upline as a referral bonus; and the rest remain in the contract balance.

The withdraw function calculates the current amount available to the user, as a percentage based on the time since each deposit. This percentage can increase based on the user's downline count and the total number of users in the contract. It can also increase slightly if the user does not withdraw, which is intended to disincentivize withdrawals.

The contract has absolutely no source of funds other than what its users deposit into it. It does not make any external financial investments or perform any business function. It has no way of truly earning money. Instead, the only way an individual user can receive more than 100% of their initial deposit is for that money to be taken from other users.

Since the withdrawals happen on a slow time-release basis, this means that it may be possible to acquire new users faster than the balance is depleted by early users; but if the supply of new users dries up, eventually the balance will be exhausted, and newer users will receive less than 100% of their money.

That's the reason for the "hold bonus": if users withdraw more frequently, the contract balance might run out before new deposits come in.

So Who Gets Paid?

The administrators get paid a percentage of every deposit: specifically, 7% as a "marketing fee", 6% as a "project fee", and 2% as an "admin fee". So 15% of your money goes straight to the people who run the contract.

Users who refer other users get paid 7% of the new user's deposit as well, and their uplines also recursively get a progressively smaller fraction.

Early users get paid 1% of their deposit each day, up to 300% of the deposit amount. So after 100 days, they have received their initial deposit; after 300 days, they have received three times that much.

Later users get paid on the same schedule, but since funds are only replenished when new users make deposits, it is possible for later users to get less than they invested.

One more note: if an early user collects their full 300% and then deposits it, they become a late user. They will have to wait another 300 days for another 300% increase, extending the time during which the balance might be depleted by earlier users.

It's worth repeating that this is a closed system. There is no source of money other than deposits from users. It does not "earn" money in any economic sense. It takes money from later users and gives it to earlier users.

Examples

  1. User 1 deposits 100 TRX. 15% of this goes immediately to the contract administrators. The contract balance is now 85 TRX. After 85 days, User 1 may withdraw 85 TRX. The contract balance is now empty.
  2. User 1 deposits 100 TRX. 15% of this goes immediately to the contract administrators. The contract balance is now 85 TRX. After 20 days, User 1 has withdrawn 20 TRX, but Users 2 and 3 make deposits of 100 TRX each, putting it at 235 TRX (again, after the admins get their cut). Now each user withdraws their 1% per day from this point on. Each user gets an additional 78⅓ TRX before the balance is exhausted. User 1 has received 98⅓ TRX in all; Users 2 and 3 have each received 78⅓ TRX.

As you can see, if User 1 is followed by only two other users, they do not get all their money back. And the two new users lose out. If User 1 is followed by three users, User 1 can get their money back, but the three users each get even less. If User 1 is going to get the full 300%, then at least three users later in the sequence will lose money.

I hope I don't need to provide further examples. If we add users 3 through 3000, the math works out about the same: early users get more than they put in, late users get less. The distribution just gets more lopsided as the user base grows.

If referrals are involved, the distribution becomes more lopsided as well: a fraction of the new user's deposit is sent to the uplines, reducing the balance available in the contract. Again, early users receive money from later users.

There is one party in this system which makes money reliably: the administrators.

Code Analysis

I'll be skipping a lot of the code to focus on the vital parts of the implementation. If you want to reassure yourself that there are no other inputs or outputs, just search for the transfer function in the contract code.

Constants

uint[] public REFERRAL_PERCENTS = [700, 300, 150, 100, 50, 50, 50, 40, 30, 20, 10];
uint constant public MARKETING_FEE = 700;
uint constant public PROJECT_FEE = 600;
uint constant public ADMIN_FEE = 200;

REFERRAL_PERCENTS are used to pay out the referral bonuses. The immediate referrer gets 7%; their referrer gets 3%; and so on up the chain. This is usually called an "upline", and in fact it is called upline in the code.

MARKETING_FEE, PROJECT_FEE, and ADMIN_FEE refer to the percentages paid to those accounts with each deposit. You can get each percentage by shifting the decimal four places to the left, so 700 becomes 0.07. Here's the exact code:

uint marketingFee = msgValue.mul(MARKETING_FEE).div(PERCENTS_DIVIDER);

MARKETING_FEE is 700; PERCENTS_DIVIDER is 10,000, and msgValue is the amount deposited. So this line says "the marketing fee is msgValue times 700 divided by 10,000". 700 / 10,000 = 0.07.

Invest

function invest(address referrer) public payable {

This function takes a single argument, the referrer. This is the upline.

User storage user = users[msg.sender];

The sender of the deposit is stored in the users map. The contract maintains an internal record of all depositors.

uint marketingFee = msgValue.mul(MARKETING_FEE).div(PERCENTS_DIVIDER);
uint projectFee = msgValue.mul(PROJECT_FEE).div(PERCENTS_DIVIDER);
uint adminFee = msgValue.mul(ADMIN_FEE).div(PERCENTS_DIVIDER);

marketingAddress.transfer(marketingFee);
projectAddress.transfer(projectFee);
adminAddress.transfer(adminFee);

Immediately calculate the fees and pay them from the contract balance.

if (user.referrer == address(0) && users[referrer].deposits.length > 0 && referrer != msg.sender) {
    user.referrer = referrer;
}

Record the referrer for this user, establishing an upline. If this user ever refers a new user, the user will get 7% of all deposits and the upline will get 3%.

if (user.referrer != address(0)) {
    address upline = user.referrer;
    for (uint i = 0; i < 11; i++) {
        if (upline != address(0)) {
            uint amount = msgValue.mul(REFERRAL_PERCENTS[i]).div(PERCENTS_DIVIDER);

            if (amount > 0) {
                address(uint160(upline)).transfer(amount);
                users[upline].bonus = uint64(uint(users[upline].bonus).add(amount));

                totalRefBonus = totalRefBonus.add(amount);
                emit RefBonus(upline, msg.sender, i, amount);
            }

            users[upline].refs[i]++;
            upline = users[upline].referrer;
        } else break;
    }
}

This loop just ascends the tree, granting a referral bonus to each member of the upline chain: 7% for the immediate referrer, 3% for the referrer's referrer, and so on. See REFERRAL_PERCENTS for the full list of upline referral percentages. The for loop addresses each referral percentage in turn; upline = users[upline].referrer ascends one step in the referrer chain before continuing the iteration.

if (user.deposits.length == 0) {
    user.checkpoint = uint32(block.timestamp);
    emit Newbie(msg.sender);
}

If this is the user's first deposit, set their "checkpoint". This is just a timestamp representing the most recent withdrawal, or the deposit date if no withdrawals have occurred.

user.deposits.push(Deposit(uint64(msgValue), 0, uint32(block.timestamp)));

Add this deposit to the user's deposit list. Each deposit's available withdrawal amount is calculated independently, of course, so that each one only pays 1% per day.

Withdraw

function withdraw() public {
    User storage user = users[msg.sender];

    uint userPercentRate = getUserPercentRate(msg.sender);
    uint communityBonus = getCommunityBonusRate();
    uint leaderbonus = getLeaderBonusRate();

This function has no parameters; it just withdraws the available amount. It begins by setting userPercentRate, which is the "hold bonus" based on the time since the last withdrawal; communityBonus, which is a slight bonus based on the total number of users; and leaderbonus, a slight bonus based on the total number of referrals this user has made. Note that these are not bonuses to the total repayment amount, just bonuses to how quickly it becomes available.

for (uint i = 0; i < user.deposits.length; i++) {

Begin iterating over all deposits this user has made. From this point on, deposits[i] refers to the deposit currently under consideration.

if (uint(user.deposits[i].withdrawn) < uint(user.deposits[i].amount).mul(3)) {

Only executes the following code if the user has withdrawn less than three times the deposit amount.

if (user.deposits[i].start > user.checkpoint) {
    dividends = (uint(user.deposits[i].amount).mul(userPercentRate+communityBonus+leaderbonus).div(PERCENTS_DIVIDER))
        .mul(block.timestamp.sub(uint(user.deposits[i].start)))
        .div(TIME_STEP);
} else {
    dividends = (uint(user.deposits[i].amount).mul(userPercentRate+communityBonus+leaderbonus).div(PERCENTS_DIVIDER))
        .mul(block.timestamp.sub(uint(user.checkpoint)))
        .div(TIME_STEP);
}

Calculate the available amount starting from the most recent withdrawal, or the date of the deposit, whichever is later.

if (uint(user.deposits[i].withdrawn).add(dividends) > uint(user.deposits[i].amount).mul(3)) {
    dividends = (uint(user.deposits[i].amount).mul(3)).sub(uint(user.deposits[i].withdrawn));
}

Cap the available amount at three times the original deposit amount.

user.deposits[i].withdrawn = uint64(uint(user.deposits[i].withdrawn).add(dividends)); /// changing of storage data
totalAmount = totalAmount.add(dividends);

Record this withdrawal for this deposit, and set the total amount to be withdrawn. (Interesting fact, uninitialized uints default to 0 in Solidity. You learn something new every day!)

uint contractBalance = address(this).balance;
if (contractBalance < totalAmount) {
    totalAmount = contractBalance;
}

If this withdrawal is greater than the entire amount remaining in the contract, return only the contract balance. This will happen some day, at which point the admins are free to establish another contract to receive new money. In fact, for all we know, it has already happened many times.

user.checkpoint = uint32(block.timestamp);
msg.sender.transfer(totalAmount);

Update the date of the most recent withdrawal, and transfer the withdrawal amount to the user.

The Rest of the Code

The rest of the code is helper functions and bookkeeping. There is no other code in the contract which sends or receives money.

Conclusion

It is not possible for this contract to create money. It does not make any financial investments; it does not provide a service or product which people pay for; it just moves money around, in a way which is designed to benefit early participants at the expense of later ones. Every 300% reward means that one person gets triple and two people get nothing.

Furthermore, if someone did create a contract which could make money, why would they make it available for public investment? Even if they needed investment, why not go to a bank or a financial firm? A big fund will move heaven and earth to get a 1% advantage. If something really gave 300%, the entire economy of the world would change in the blink of an eye.

I strongly recommend avoiding any financial proposition which promises money without risk, and that goes double if it expects you to refer others. They all work like this. This one simply has the benefit of being written in immutable computer code.

It's ironic that the administrators use the ironclad code as a selling point. It's official! It's set in stone! It's "audited by an independent third party audit firm"! As a programmer, I can look at the code and tell you, with mathematical certainty, that it definitely does what the website says; but what it does is not beneficial.

When evaluating any proposition like this, begin by asking yourself "where does the money come from?" If you don't know, don't put money into it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment