I was involved in a project with a bank wishing to experiment and then implement a tokenization engine. This engine would answer a few concrete use cases :

  1. The bank has a lot of real estate and would like to make it available as an investment vehicle to their clients. Rather than having a unliquid and rather difficult investment process, the real estate should be available in lots, ownership of those lots should be transferable easily.
  2. The bank would like to be able to split inheritance between heirs easily, especially concerning un-liquid asset classes such as art.
  3. The bank would like to open up investment opportunities in new asset classes that were traditionally difficult to get into, specific funds and arts for instance

The bank had nevertheless an interesting requirement : No client from the bank should hold any “cryptocurrency” at any times. Requirement was pushed from the top and no amount of discussion could make them move.

When a client holds token on the Ethereum blockchain after having tokenized an asset and wants to send them from his wallet somewhere, there are fees involved in the transaction. And those fees have to be paid in Ethereum.

So, how to answer the client requirement ?

You can hide the ETH positions in the client wallet in the banking UI and not show it, but the client would still be able to check it on etherscan.

You could holds all the tokens and ETH in a single “bank” wallet and then make the split off chain using a db (such as cryptographically verified database such as google trillian). The client would then not hold any ETH, but he would not hold any tokens neither technically and all transactions would have to go through the bank. Segregated wallets are also a requirement.

Then we thought about ERC-20 and the transferfrom function :

function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
        return true;
}

This function enables a third party to book transfers on behalf of another wallet. The only prerequisite in the standard implementation is that the client wallet should do a one-off technical transaction called “allowance”

function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

Allowance basically gives the “spender” the right to process transfer transaction on behalf of the owner, thus paying the transaction fee on his behalf as well. The client would still have to hold a little amount of ETH for a brief amount of time at the inception of the wallet in order to pay for the fee of the allowance transaction, which is problematic as the rule is “NO” crypto. With ERC-20 there could have been a custom solution here, developing a new customized smart contract in order to bypass the allowance transaction, but then we derive from the standard and other problems might appear in terms of maintenance, updates and general consistency.

source : ERC-20 OpenZeppelin

The last option we explored was ERC-1400, in this standard it is possible to activate a flag at the creation of the smart contract granting “controller” rights to the owner of the contract or any other actor. This flag gives super admin right “sort of” and permits to operate transfers for other wallets. No initial ETH position needed, everything is out of the box.

function setControllers(address[] calldata operators) external onlyOwner {
    _setControllers(operators);
}

This appears to be the best solution for the use case of the bank. No ETH held by the client, no activation function, no deviation from the standard (as long as you trust consensys)..


Brax

Dude in his 30s starting his digital notepad