Hey everyone I am trying to implement the game Chinese Whisper as a smart contract. It works as intended, but there are two issues I have. I will explain the contract idea quickly: So WalletA can send a message to WalletB by setting the whisper in the changeOwner function. I have the isOwner modifier which checks if the user is the owner and allows only that person to read the current whisper that WalletA set before. Below is the contract in its current state:

contract ChineseWhisper {

    address private owner;
    string whisper;
    // event for logging
    event OwnerSet(address indexed oldOwner, address indexed newOwner);
    // modifier to check if caller is owner
    modifier isOwner() {
        require(msg.sender == owner, "Caller is not owner");
     * @dev Set contract deployer as owner and set whisper to default value
    constructor() {
        owner = msg.sender;
        whisper = "Let the game begin";
        emit OwnerSet(address(0), owner);

     * @dev Changes owner and sets sent message as new whisper
     * @param newOwner address of new owner
     * @param message message for setting the whisper
    function changeOwner(address newOwner, string memory message) public isOwner {
        emit OwnerSet(owner, newOwner);
        whisper = message;
        owner = newOwner;

    * @dev Return current whisper
    * @return whisper of last owner
    function getWhisper() public isOwner returns (string memory) {
        return whisper;

     * @dev Return owner address 
     * @return address of owner
    function getOwner() external view returns (address) {
        return owner;

But there are two issues!

The first one is already when switching the owner, the Input (including the whisper) can be seen on etherscan (Here the example just click on input as UTF-8: https://rinkeby.etherscan.io/tx/0x7b5b9114d55bed65eb6ded536973846674cb6d5a46ff09282516451045cb78c3)

The second issue is that when using web3js to query the current whisper, it bypasses the isOwner function

const runGetWhisper = async (contract, walletAdress) => {
    .call({ from: walletAdress })
    .then((res) => {
    .catch((err) => {

In theory, I can just change the "from" value so anybody that knows the currentOwner by calling getOwner() can use that walletAdress in order to query the current value.

Any ideas on how to fix both of the issues?

3 Answers 3


I agree with Meriadoc you can't secret anything in Ethereum.Your First issue If your contract is verified on etherscan then it always decoded in etherscan. your second issue solved by simple if else condition.

Check out the updated getWhisper function.

    function getWhisper() public view returns (string memory) {
        if (msg.sender == owner){
        return whisper;
            return "You are not allowed";

Digital vs. Analog communication?

In "Chinese Whispers" (Americans know this game as "Telephone", btw), a source of error is supposed to primarily be the analog transmission of voice itself. Here there would be no such mechanism. Anyone can see the last thing perfectly--there is no mechanism that equates to 'whispering', I guess--unless your front end intentionally swaps out some percentages of chars, or some equivalent.

But if the point is just that someone can intentionally distort the message and you do want to continue with this idea...

Secrets on the blockchain

you can store secrets on the blockchain, you just have to encrypt them. How would you do this?

  1. The NEXT user posts their public key (let's call it pub_key_next).
  2. The CURRENT user takes pub_key_next and encrypts the starting secret with it.
  3. NEXT user now becomes CURRENT user. He (and no one else) can not only view the current secret message, but decrypt and read it.

When a new user submits their public key and says they want to become the new NEXT user, then the current user can encrypt whatever they want (either the same message they saw, or a variation on it, or something completely different), and submit it encrypted so that only NEXT can view it.

And so on.

The technical part sounds complex, but actually the GUI can handle it pretty well--the tricky part is that you can't really submit the new message until the next user shows up (unless you want to include an off-chain database into the mix).

Basically, the front end should do the encrypting and decrypting, and hide that from the user. The flow would look like:

  1. User submits that they want to be the NEXT in line. (Front end in the background submits their public key to blockchain, possibly generating a public/private key pair on their behalf.)
  2. CURRENT then has to be notified, or check and notice randomly, that there is a NEXT user available. He then submits his message. (Front end in the background encrypts the message he writes with the public key of the NEXT user that's on chain, and stores the encrypted message on-chain.)
  3. NEXT user, who now becomes CURRENT user, is notified that the message is now there, and goes to view it. (Front end in the background decrypts and shows them the message.)
  4. the new CURRENT user waits for a new NEXT user, and the cycle repeats.

Your last issue (isOwner bypass)

When you adopt this new setup, the issue becomes irrelevant. They will be able to look at the encrypted whisper if they want, it will be garbled nonsense.

  • 1
    That's quite clever, great response.
    – Meriadoc
    Commented Mar 17, 2022 at 12:56

It is not possible to keep anything secret on the blockchain--etherscan can always access any storage slots in your contract. The only way to make it truly secret would be to use some kind of encryption algorithm on the message that only the message recipient knows.

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.