Skip to content

Conversation

@carstenjacobsen
Copy link
Contributor

Adding OpenZeppelin Relayer documentation to the Tools dropdown.

@stellar-jenkins
Copy link

@stellar-jenkins
Copy link

@stellar-jenkins
Copy link

@stellar-jenkins
Copy link

@stellar-jenkins
Copy link

1 similar comment
@stellar-jenkins
Copy link

@stellar-jenkins
Copy link

@stellar-jenkins
Copy link

1 similar comment
@stellar-jenkins
Copy link

@stellar-jenkins
Copy link

@stellar-jenkins
Copy link

@stellar-jenkins
Copy link

Copy link
Contributor

@ElliotFriend ElliotFriend left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lookin' really good!

left a couple small notes, but i think it's very close!


### Prerequisites

This guide assumes you have deployed the Increment smart contract example code found [here](https://github.com/stellar/soroban-examples/tree/v22.0.1/increment). See the [Getting Started](https://developers.stellar.org/docs/build/smart-contracts/getting-started) tutorial section 3 and 4 for more information about building and deploying the Increment contract.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's make the Getting Started link a relative one (the kind that's like ../../path/to/file.mdx)

```js
// Poll for transaction result
let txResponse = await rpc.getTransaction(response.hash!);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think there's something like rpc.pollTransaction() that you could use that takes away the need for the while loop

Co-authored-by: Elliot Voris <elliot@stellar.org>
Copilot AI review requested due to automatic review settings January 15, 2026 19:15
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds comprehensive documentation for the OpenZeppelin Relayer (also known as Stellar Channels Service) to the Stellar developer documentation. The documentation replaces the discontinued Launchtube service and provides developers with a managed infrastructure solution for submitting Stellar Soroban transactions with automatic parallel processing and fee management.

Changes:

  • Added new documentation page with detailed guides for smart contract invocation and account transfers using OpenZeppelin Relayer
  • Integrated OpenZeppelin Relayer into navigation menus, routes, and internationalization files
  • Updated tools overview page to include the new relayer service

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 19 comments.

Show a summary per file
File Description
docs/tools/openzeppelin-relayer.mdx New comprehensive documentation page with code examples for using OpenZeppelin Relayer for contract invocations and token transfers
docs/tools/README.mdx Added OpenZeppelin Relayer section to the tools overview page
config/theme/navbar.ts Added OpenZeppelin Relayer navigation item in the Tools dropdown menu
i18n/en/docusaurus-theme-classic/navbar.json Added internationalization entry for OpenZeppelin Relayer navbar label
static/llms.txt Added OpenZeppelin Relayer link to the developer tools list
routes.txt Added route for the new OpenZeppelin Relayer documentation page

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

<label
htmlFor="amount"
className="block mb-2.5 text-sm font-medium text-heading"
>Amount (XML)</label
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The label incorrectly refers to "XML" when it should be "XLM" (the native Stellar token). XML is a markup language, while XLM is the Stellar Lumens cryptocurrency.

Suggested change
>Amount (XML)</label
>Amount (XLM)</label

Copilot uses AI. Check for mistakes.
<input type="text" id="toAddress" name="toAddress" className="bg-neutral-secondary-medium border border-default-medium text-heading text-sm rounded-base focus:ring-brand focus:border-brand block w-full px-3 py-2.5 shadow-xs placeholder:text-body" placeholder="G.........." required />
</div>
<div className="mb-5">
<label htmlFor="amount" className="block mb-2.5 text-sm font-medium text-heading">Amount (XML)</label>
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The label incorrectly refers to "XML" when it should be "XLM" (the native Stellar token). XML is a markup language, while XLM is the Stellar Lumens cryptocurrency.

Suggested change
<label htmlFor="amount" className="block mb-2.5 text-sm font-medium text-heading">Amount (XML)</label>
<label htmlFor="amount" className="block mb-2.5 text-sm font-medium text-heading">Amount (XLM)</label>

Copilot uses AI. Check for mistakes.
Comment on lines +115 to +120
// Poll for transaction result
let txResponse = await rpc.getTransaction(response.hash!);
while (txResponse.status === "NOT_FOUND") {
await new Promise((r) => setTimeout(r, 2000));
txResponse = await rpc.getTransaction(response.hash!);
}
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The polling loop lacks a timeout mechanism. If the transaction never gets confirmed or fails, this will poll indefinitely. Consider adding a maximum number of retries or a timeout to prevent infinite loops.

Suggested change
// Poll for transaction result
let txResponse = await rpc.getTransaction(response.hash!);
while (txResponse.status === "NOT_FOUND") {
await new Promise((r) => setTimeout(r, 2000));
txResponse = await rpc.getTransaction(response.hash!);
}
// Poll for transaction result with a maximum number of attempts to avoid infinite loops
const maxAttempts = 30; // e.g., ~60 seconds total with 2s delay
let attempts = 0;
let txResponse = await rpc.getTransaction(response.hash!);
while (txResponse.status === "NOT_FOUND" && attempts < maxAttempts) {
await new Promise((r) => setTimeout(r, 2000));
txResponse = await rpc.getTransaction(response.hash!);
attempts++;
}
if (txResponse.status === "NOT_FOUND") {
throw new Error("Transaction could not be confirmed within the expected time.");
}

Copilot uses AI. Check for mistakes.
});

return response.hash;
} catch (error) {
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The catch block simply re-throws the error without adding context or handling. While this may be intentional for demonstration purposes, consider either removing the try-catch entirely or adding meaningful error handling/logging.

Suggested change
} catch (error) {
} catch (error) {
console.error('Failed to submit Stellar transaction via OpenZeppelin Relayer:', error);

Copilot uses AI. Check for mistakes.
Comment on lines +121 to +122
// Return the result
return txResponse.returnValue._value;
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accessing _value (a private/internal property indicated by the underscore prefix) directly may be fragile and could break with SDK updates. Consider using the proper public API method to extract the return value if available.

Suggested change
// Return the result
return txResponse.returnValue._value;
// Return the result using the public SDK helper
return StellarSDK.scValToNative(txResponse.returnValue);

Copilot uses AI. Check for mistakes.
</div>
```
3. The complete code This is the complete code for the client side (frontend):
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This heading should be formatted as a markdown heading (#### 3. The complete code) to maintain consistency with other numbered sections like "#### 1. Call server side function" and "#### 2. Markup code".

Suggested change
3. The complete code This is the complete code for the client side (frontend):
#### 3. The complete code
This is the complete code for the client side (frontend):

Copilot uses AI. Check for mistakes.
const simulation = await rpc.simulateTransaction(tx);
const assembled = StellarSDK.rpc.assembleTransaction(tx, simulation).build();
// Extract function and auth XDRs
const op = await assembled.operations[0];
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The await keyword is unnecessary here since accessing an array element is a synchronous operation. Remove the await keyword as it has no effect and may cause confusion.

Suggested change
const op = await assembled.operations[0];
const op = assembled.operations[0];

Copilot uses AI. Check for mistakes.
Comment on lines +117 to +121
while (txResponse.status === "NOT_FOUND") {
await new Promise((r) => setTimeout(r, 2000));
txResponse = await rpc.getTransaction(response.hash!);
}
// Return the result
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The polling loop only checks for "NOT_FOUND" status but doesn't handle other possible transaction statuses like "FAILED". If the transaction fails, this could result in returning an undefined or incorrect value. Consider adding proper status checking and error handling.

Suggested change
while (txResponse.status === "NOT_FOUND") {
await new Promise((r) => setTimeout(r, 2000));
txResponse = await rpc.getTransaction(response.hash!);
}
// Return the result
let attempts = 0;
const maxAttempts = 30;
while (txResponse.status === "NOT_FOUND" && attempts < maxAttempts) {
await new Promise((r) => setTimeout(r, 2000));
txResponse = await rpc.getTransaction(response.hash!);
attempts += 1;
}
if (txResponse.status !== "SUCCESS") {
throw new Error(`Transaction did not complete successfully. Status: ${txResponse.status}`);
}
// Return the result
if (!txResponse.returnValue || typeof txResponse.returnValue._value === "undefined") {
throw new Error("Transaction completed but no return value was found.");
}

Copilot uses AI. Check for mistakes.
Comment on lines +245 to +251
const args: [] = [];
const response = await SendContractTransaction(
'GAZQUIHE242WV4CK7LLM5ZV4SM6SLV4CEY4LLAKEYD2O000000000000',
'CDAZOG4V2KAPVBBKCAMHUT367AUGYWYEHD652UOJVER5ERNYGSOROBAN',
"increment",
[],
);
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable args is declared but then an empty array is passed directly to SendContractTransaction instead. This makes the args variable declaration redundant. Consider either using the args variable or removing its declaration.

Copilot uses AI. Check for mistakes.
}
```
### OpenZeppelin Relayer documentation
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section is titled "OpenZeppelin Relayer documentation" at the end of a page already about OpenZeppelin Relayer documentation. The title is redundant and should be more specific, such as "Additional Resources" or "Further Reading".

Suggested change
### OpenZeppelin Relayer documentation
### Additional resources

Copilot uses AI. Check for mistakes.
@stellar-jenkins
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants