Skip to main content
Launchtube, which served as an experimental service for fee sponsorship and contract invocations, has been instrumental in early-stage deployments and developer experimentation. However, while functional for testing and early use cases, Launchtube does not have the same maturity or scalability as OpenZeppelin’s Relayer, which is why the Stellar Development Foundation has discontinued the Launchtube service and points developers to OpenZeppelin’s managed Stellar Channels service as the replacement. OpenZeppelin Relayer is an open source (AGPL-3.0) framework for submitting Stellar transactions, including Soroban contract invocations. If you run your own instance, you control the signer and you fund the accounts that pay the fees. The signer can be a local keystore, Google Cloud KMS, or Turnkey. Parallel submission and automatic fee bumping come from the Channels plugin, which you can also run yourself. Stellar Channels is the managed service OpenZeppelin operates on top of the Relayer and the Channels plugin. There are no servers, relayers, or channel accounts for you to configure, because OpenZeppelin runs the pool of channel accounts and covers the fees, subject to a fair use policy.
The examples on this page use the managed Channels service, so they authenticate with a Channels API key rather than talking to a relayer you run yourself.

Stellar Channels status

To see the live status of Stellar Channels, visit the Stellar Channels status page.

Smart contract invocation

Let’s create a simple application that submits a transaction, invoking a smart contract function, using Channels. The application calls the Increment smart contract and returns the current counter value. The application is based on the Next.js framework and has server side code and client side code. The @openzeppelin/relayer-plugin-channels SDK makes HTTPS-requests to OpenZeppelin endpoints, which will trigger CORS errors when run client side, but by using the SDK server side, CORS will not be an issue.

Prerequisites

This guide assumes you have deployed the Increment smart contract example code found here. See the Getting Started tutorial section 3 and 4 for more information about building and deploying the Increment contract.

Server Side

In this simple application we only have one function server side, and that’s a function calling a smart contract function, by submitting a transaction through Channels.

1. Initialize the Channels client

First the two necessary SDKs are imported, and the Channels client is initialized. In this tutorial we use testnet, so the Channels endpoint for testnet is used as the base URL. The API key can be generated here.

2. Initialize contract, RPC and account

Next we initialize the resources we need to build the transaction. Both the contractId and sourceId are arguments of the function and will be provided client side.

3. Build transaction

TransactionBuilder takes the user account and network information as parameters, and operations can be added. In this case we want to add the contract invocation by using contract.call(), with the contract function name and arguments as parameters.

4. Simulate transaction and get XDRs

The final steps before we can submit the transaction to Channels is to simulate the transaction, bundle the transaction and simulation, and extract the transaction’s function and auth XDRs. We are extracting the function and auth from the assembled transaction, in XDR format, because that’s what we need to submit to Channels.

5. Build the Channels request and submit it

Now we have completed all necessary steps to submit a transaction to Channels. First the request is built, and then the request is submitted to Channels.
A successful submission will return a response of the following format:

6. Get returned value from contract

The invoked Increment contract function will return the updated, incremented value so let’s get this value so we can show it in the client (frontend). To get the return value we simply poll for the transaction result until we get a response with rpc.pollTransaction(), which will return an object with more details than we need, so only the value is returned.

7. The complete code

The previous six steps contain all the functionality needed for invoking a smart contract function through Channels. This is the complete code for the function that we will call from the client side (frontend):
backend/index.tsx

Client Side

The client side code shows a button on the page in the browser, and when clicked, the server side function will be called with the relevant parameters. When the function returns a value, the value is shown on the page instead of the button. The functionality is very simple, but serves well as an end-to-end example of submitting a transaction with Channels.

1. Call server side function

The client code has a function callContract() that can be invoked from the button click. The function calls the server side function SendContractTransaction() with the sourceId, contractId, contract function name and auth.
The response is stored as the result state.

2. Markup code

The page markup code checks if result contains a value. If not, the button for invoking the callContract function is shown, and if result does contain a value, it’s shown on the page instead. That’s all there is to the client side markup code.
  1. The complete code This is the complete code for the client side (frontend):
page.tsx

Account transfer

Let’s create another simple application, this application submits a transfer transaction, sending XLM tokens from one account to another, using Channels. The application is based on the Next.js framework and has server side code and client side code. The @openzeppelin/relayer-plugin-channels SDK makes HTTPS-requests to OpenZeppelin endpoints, which will trigger CORS errors when run client side, but by using the SDK server side, CORS will not be an issue.

Server Side

In this simple application we only have one function server side, and that’s a function making a transfer of XLM tokens from one account to another, by submitting a transaction through Channels.

  1. Initialize the Channels client

First the two necessary SDKs are imported, and the Channels client is initialized. In this tutorial we use testnet, so the Channels endpoint for testnet is used as the base URL. The API key can be generated here.

2. Initialize RPC and source account

Next we initialize the resources we need to build the transaction. In this example code the source secret key is hardcoded, but this must be handled in a more secure manner in production applications.

3. Build and sign transaction

TransactionBuilder takes the source account and network information as parameters, and operations can be added. In this case we want to add the payment() operation to transfer an amount of XLM tokens (native asset) from the source account to the destination. The transaction is signed with the source keypair.

4. Submit the transaction

Now we have completed all necessary steps to submit the transaction to Channels. The Channels client function submitTransaction() takes the transaction in XDR-format as an argument.
A successful submission will return a response of the following format:

5. Return the transaction hash

As a final step the transaction hash is returned.

6. The complete code

The previous five steps contain all the functionality needed for transferring XLM tokens from the source account to another account through Channels. This is the complete code for the function that we will call from the client side (frontend):
backend/index.tsx

Client Side

The client side code shows a form with input fields for a transfer-to-address, and amount to transfer, on the page in the browser. When the form is filled out and submitted, the server side function will be called with the form values as parameters. When the function returns with the transaction hash, the hash is shown on the page instead of the form. The functionality is very simple, but serves well as an end-to-end example of submitting a token transfer transaction with Channels.

  1. Call server side function

The client code has a function sendXLM() that works as a form action function. The function calls the server side function SendTransaction() with the transfer destination public key and amount as parameters.
The response is stored as the result state.

  1. Markup code

The page markup code checks if result contains a value. If not, the form for submitting a transfer is shown, and if result does contain a value, it’s shown on the page instead. That’s all there is to the client side markup code.

3. The complete code

This is the complete code for the client side (frontend):
page.tsx

OpenZeppelin Relayer documentation

For more information see the OpenZeppelin Relayer documentation here.