5

What I am trying to do is clear the RAM table completely. This question is not a duplicate since it is asking for a full and a working solution.

The contract that I was executing filling the RAM table (method = transfer) is: https://github.com/EOSIO/eos/blob/master/contracts/eosio.token/eosio.token.cpp

Abi is the same as in the eosio.token directory. RAM table was filled sending a generated token.

What I am trying to accomplish is something that would work like this, repeated as many times as needed to clear the whole RAM tables (accounts and currency_stats):

    for(auto itr = _ramTable.begin(); itr != _ramTable.end() && count!=100;) {
        // delete element and update iterator reference
        itr = _ramTable.erase(itr);
        count++;
    }

The lack of knowledge I have concerns not knowing what to put into hpp file, or how to declare _ramTable. I can cut/paste the snippets of other code, but then I frequently run into errors that usually don't happen with Java's syntax.

Please provide the full code (meaning, cpp, hpp and abi) that works.

5
  • 1
    In your contract, just add the method as another action. No need for extra imports. What is your contract, should be open source anyway (as per constitution)?
    – friedger
    Commented Jul 14, 2018 at 19:26
  • Possible duplicate of How to clear all of the data in my contract table?
    – friedger
    Commented Jul 14, 2018 at 19:28
  • @friedger, I have tried copying and pasting, all other kinds of things, all I am getting is assertion failure with message: cannot pass end iterator to erase Commented Jul 15, 2018 at 4:48
  • Possible duplicate of Delete all multi_index records without iterator?
    – tmm
    Commented Jul 15, 2018 at 9:06
  • 1
    no, you're totally wrong, the ram market is not relative. pls give us your mainnet contract address, I'll help you figure out what happened.
    – Jimmy Guo
    Commented Jul 16, 2018 at 13:11

2 Answers 2

6
+100

UPDATE:

Updated the code to compile with eosio.cdt version 1.6.1.


I finally got it working nicely! Parsing the token symbol in the action parameters was especially complicated.

The other solution posted has a couple of issues that will prevent you from clearing all the RAM properly:

  1. It doesn't delete the stat table, leaving there some info about the created token using up some memory.
  2. You're supposed to hand it an arbitrary amount in the action parameters, which has to match the same exact decimal precision used when creating the token, instead of just specifying the symbol of the token you want to delete.

My final solution is thoroughly tested by creating and issuing various tokens using the standard eosio.token contract, then replacing it by this one, and using the new actions to destroy the database records. I checked every step by inspecting the tables associated to the contract account using

cleos get table <CONTRACT_ACCOUNT> <TOKEN_NAME> stat

and

cleos get table <CONTRACT_ACCOUNT> <AIRDROPPED_ACCOUNT> accounts

and everything was neatly cleared at the end. You will need the list of accounts that you airdropped to, but you won't need the exact amounts that they are currently holding.

The code goes like this:

token_ram_recovery.cpp

#include <eosio/asset.hpp>
#include <eosio/eosio.hpp>

#include <string>

using namespace eosio;
using std::string;

class[[eosio::contract]] token : public contract
{
private:
  struct [[eosio::table]] account
  {
    asset balance;

    uint64_t primary_key() const { return balance.symbol.code().raw(); }
  };

  struct [[eosio::table]] currency_stats
  {
    asset supply;
    asset max_supply;
    name issuer;

    uint64_t primary_key() const { return supply.symbol.code().raw(); }
  };

  typedef eosio::multi_index<name("accounts"), account> accounts;
  typedef eosio::multi_index<name("stat"), currency_stats> stats;

public:
  using contract::contract;

  [[eosio::action]] void destroytoken(string symbol) {
    require_auth(_self);

    symbol_code sym(symbol);
    stats stats_table(_self, sym.raw());
    auto existing = stats_table.find(sym.raw());
    check(existing != stats_table.end(), "Token with symbol does not exist");

    stats_table.erase(existing);
  };

  [[eosio::action]] void destroyacc(string symbol, name account) {
    require_auth(_self);

    symbol_code sym(symbol);
    accounts accounts_table(_self, account.value);
    const auto &row = accounts_table.get(sym.raw(), "No balance object found for provided account and symbol");
    accounts_table.erase(row);
  };
};

EOSIO_DISPATCH(token, (destroytoken)(destroyacc))

The code is well annotated so you can generate the .abi automatically when compiling the code using eosio-cpp -abigen -contract token token_ram_recovery.cpp -o token_ram_recovery.wasm. Then just deploy this new contract to the same account that had the previous one, and proceed with the deletion of the token and the accounts:

Destroy the token from the stat table:

cleos push action <CONTRACT_ACCOUNT> destroytoken '["<TOKEN_SYMBOL>"]' -p <CONTRACT_ACCOUNT>@active

Destroy each account that holds any of the token:

cleos push action <CONTRACT_ACCOUNT> destroyacc '["<TOKEN_SYMBOL>", "<AIRDROPPED_ACCOUNT>"]' -p <CONTRACT_ACCOUNT>@active

Let me know if you need any more assistance. I hope everything goes well!

8
  • 1
    Andres, AWESOME... I will be testing this in a few hours. Cheers mate, and thank you for contacting me directly. Commented Jul 17, 2018 at 20:10
  • 2
    Nice, this is way better than the previous posted solution! Cheers!
    – tmm
    Commented Jul 17, 2018 at 21:46
  • so any token that was already airdropped can be updated at any point by the creator and delete everyone else's tokens?
    – confused00
    Commented Jul 19, 2018 at 9:18
  • @confused00 yes, the contract account can update its contract whenever it wants and modify the data however it wants. Sadly, this is actually very centralized, as it puts all the power on the contract account's hands, and for a truly decentralized dapp administration you would need to make that account an "organization", managed by the community itself and not a central developer. Commented Jul 19, 2018 at 14:17
  • yeah, that sucks. it'd be nice if there was an option to deploy an immutable contract. doesn't seem too hard to implement on the existing code base and it would help a lot with trust issues for some contracts that require it more than they require bug fixes
    – confused00
    Commented Jul 19, 2018 at 14:23
1

Maybe no simple way, because it may cause transaction timeout, you must delete the record one by one, and control the delete number. I think you could have one action on chain, and an execution batch or script offline to finish this work.

6
  • The only thing I got are the addresses the contracts were executed to send the tokens to. Would it be possible to go through the list, one by one, with a batch process ? Allocating enough CPU and bandwidth is not an issue. Commented Jul 14, 2018 at 14:02
  • 1
    yes you can, but you must limit the numbers, e.g. 100 records an action, the cpu will not be saved so much.
    – Jimmy Guo
    Commented Jul 14, 2018 at 14:24
  • I am fine having 1 action per execution too, although I don't know whether I would be occupying RAM for that. I cannot find the source code here: github.com/EOSIO/eos/tree/master/contracts what should I be looking at to find a solution ? Commented Jul 14, 2018 at 16:32
  • your link question is the answer.
    – Jimmy Guo
    Commented Jul 15, 2018 at 6:46
  • I implemented this kind of serialized deletion in this question. eosio.stackexchange.com/questions/1214/…
    – tmm
    Commented Jul 15, 2018 at 9:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.