Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Contracts for fetching a value from another chain with an async callback #35

Open
michaelkaplan13 opened this issue Sep 22, 2023 · 1 comment
Labels
enhancement New feature or request

Comments

@michaelkaplan13
Copy link
Collaborator

Context and scope
One possible use of Teleporter that could prove very helpful for dApp developers is the ability to query the state of a contract on another chain. The data being queried could be the current value of an oracle, the current state of an account, a random value from a VRF contract, etc.

The query would be sent to the other chain as a Teleporter message with a specific request ID. When the message is delivered to the destination, the specified data would be looked up on chain, and then submitted in a subsequent Teleporter message sent back to the origin chain. The delivery of that second message would invoke a callback providing the value to be used by the original requesting contract.

In order to make this pattern as easy as possible, create contracts that demonstrate this flow on top of Teleporter. Ideally, the interface can be generic enough such that dApps could inherit a CrossChainRequester or CrossChainProvider contract that handles most of the logic.

Open questions

  • Is it possible to make the interface generic of the type of the value being queried?
  • What's the best pattern for registering callbacks? Could either store the callback for a given request ID on the source chain contract, or pass it through the messages.
  • How to set the relayer incentive fees for each message? To start, a working demo that doesn't use any relayer fees is sufficient to demonstrate the use case.
@michaelkaplan13 michaelkaplan13 added the enhancement New feature or request label Sep 22, 2023
@martineckardt
Copy link
Contributor

I was thinking about how to tackle this best and couldn't get an approach out of my head: Javascript has the Promise API. The approach is simple. I perform an action and provide two callbacks:

  • Then: logic to be executed when asynchronous operation was successful
  • Catch: logic to be executed when async operation failed

I believe this pattern could make developing with Teleporter in some cases more straight forward. Let's say I want to perform a simple bridging operation. The users send the tokens to the bridge. The bridge attempts to bridge the token. On success nothing happens, but on failure the user receives their funds back.

In Javascript the logic for the handling the success and failure case are passed in as functions. I did not find a straight forward way to do this in solidity, so I worked with contract interfaces. You can find a very rough draft (code is not deployable or testable) of how this pattern could be implemented below.

Full Code

abstract contract TeleporterPromiseSender is ITeleporterReceiver {
    function thenCallback(bytes32 messageId, bytes memory result) external virtual;
    function catchCallback(bytes32 messageId, bytes memory error) external virtual;

    function receiveTeleporterMessage(bytes32 messageId, bytes calldata message) external override {
        // Unpack message and call thenCallback or catchCallback
        (TeleporterPromiseOutcome outcome, bytes memory data) = abi.decode(message, (TeleporterPromiseOutcome, bytes));
        if (outcome == TeleporterPromiseOutcome.Success) {
            thenCallback(messageId, data);
        } else {
            catchCallback(messageId, data);
        }
    }
}

contract TeleporterPromiseReceiver {
    ITeleporterMessenger public messenger;
    ITeleporterPromiseProcessor public processor;

    function receiveTeleporterMessage(bytes32 sourceBlockchainID, address originSenderAddress, bytes calldata message)
        external
    {
        try processor.processMessage(sourceBlockchainID, originSenderAddress, message) returns (bytes result) {
            // Successful processing
            this._sendCallback(TeleporterPromiseOutcome.Success, result);
        } catch (bytes memory error) {
            // Failed processing
            this._sendCallback(TeleporterPromiseOutcome.Fail, error);
        }
    }

    function _sendCallback(TeleporterPromiseOutcome outcome, bytes memory resultOrError) internal {
        // Pack outcome and resultOrError
        // ...

        // Send feedback to the sender
        TeleporterMessageInput memory input;
        // Fill in input parameters for sending the callback
        // ...

        messenger.sendCrossChainMessage(input);
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
Status: Backlog 🗄️
Status: 📋 Backlog
Development

No branches or pull requests

2 participants