From cdb803c0745beefadbfa72be87c3ee69a744d3d0 Mon Sep 17 00:00:00 2001 From: Nicolas Molina Date: Wed, 4 Feb 2026 17:03:55 -0300 Subject: [PATCH 1/2] feat: adding examples for soroban gas abstraction --- examples/relayers/stellar/package.json | 3 +- .../relayers/stellar/src/soroban/.env.example | 19 ++ .../src/soroban/buildSorobanGasAbstraction.ts | 149 ++++++++++ .../src/soroban/quoteSorobanGasAbstraction.ts | 136 +++++++++ .../soroban/sorobanGasAbstractionFullFlow.ts | 279 ++++++++++++++++++ .../relayers/stellar/src/soroban/utils.ts | 137 +++++++++ 6 files changed, 722 insertions(+), 1 deletion(-) create mode 100644 examples/relayers/stellar/src/soroban/.env.example create mode 100644 examples/relayers/stellar/src/soroban/buildSorobanGasAbstraction.ts create mode 100644 examples/relayers/stellar/src/soroban/quoteSorobanGasAbstraction.ts create mode 100644 examples/relayers/stellar/src/soroban/sorobanGasAbstractionFullFlow.ts create mode 100644 examples/relayers/stellar/src/soroban/utils.ts diff --git a/examples/relayers/stellar/package.json b/examples/relayers/stellar/package.json index 8cb444b6..55b9bcf4 100644 --- a/examples/relayers/stellar/package.json +++ b/examples/relayers/stellar/package.json @@ -8,7 +8,8 @@ }, "dependencies": { "@openzeppelin/relayer-sdk": "file:../../..", - "@stellar/stellar-sdk": "^13.0.0" + "@stellar/stellar-sdk": "^13.0.0", + "dotenv": "^16.4.0" }, "devDependencies": { "@types/node": "^20.0.0", diff --git a/examples/relayers/stellar/src/soroban/.env.example b/examples/relayers/stellar/src/soroban/.env.example new file mode 100644 index 00000000..f3622210 --- /dev/null +++ b/examples/relayers/stellar/src/soroban/.env.example @@ -0,0 +1,19 @@ +# Soroban Gas Abstraction Example Configuration +# Copy this file to .env and fill in your values + +# OpenZeppelin Relayer API Configuration +ACCESS_TOKEN=your-api-key-here +BASE_PATH=http://localhost:8080 +RELAYER_ID=stellar-example + +# User Account Configuration +# WARNING: Never commit your actual secret key to version control! +USER_SECRET_KEY=S...your-secret-key-here +USER_ACCOUNT=G...your-public-key-here + +# Contract Configuration (optional - defaults provided) +# CONTRACT_ADDRESS=CC3NDEI265JEKXJN4VHYD55G6VD56RBASU5JFZ3EJFU5ZDNVER3XCYYX +# FUNCTION_NAME=increment + +# Fee Token Configuration (optional - defaults to USDC testnet) +# FEE_TOKEN=CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA diff --git a/examples/relayers/stellar/src/soroban/buildSorobanGasAbstraction.ts b/examples/relayers/stellar/src/soroban/buildSorobanGasAbstraction.ts new file mode 100644 index 00000000..fec4f152 --- /dev/null +++ b/examples/relayers/stellar/src/soroban/buildSorobanGasAbstraction.ts @@ -0,0 +1,149 @@ +import 'dotenv/config'; +import { Configuration, RelayersApi, SponsoredTransactionBuildRequest } from '@openzeppelin/relayer-sdk'; +import { Networks, rpc } from '@stellar/stellar-sdk'; + +import { createSorobanInvocationXdr, addressToScVal } from './utils.js'; + +/** + * Soroban Gas Abstraction - Build Example + * + * This example demonstrates how to build a gas-abstracted Soroban contract invocation + * using the OpenZeppelin Relayer SDK. + * + * The build endpoint prepares a transaction where: + * - The user pays fees in their chosen token (e.g., USDC) instead of XLM + * - The relayer handles the conversion and pays the actual network fees + * - A `user_auth_entry` is returned that must be signed by the user + * + * Flow: + * 1. Build a Soroban contract invocation XDR (client-side) + * 2. Call the build endpoint to get the prepared transaction and auth entry + * + * IMPORTANT: This is provided as a demonstration only. For production use: + * - Use proper error handling and transaction confirmation checks + * - Implement appropriate security measures for API keys and tokens + * - Consider rate limiting and monitoring for production applications + * - Test thoroughly on testnet before using on mainnet + * - Use https connection for production applications + * + * Setup: + * 1. Copy .env.example to .env + * 2. Fill in your configuration values + * + * Usage: + * npx ts-node buildSorobanGasAbstraction.ts + */ + +// Load configuration from environment variables +const accessToken = process.env.ACCESS_TOKEN || ''; +const BASE_PATH = process.env.BASE_PATH || 'http://localhost:8080'; +const RELAYER_ID = process.env.RELAYER_ID || 'stellar-example'; +const USER_ACCOUNT = process.env.USER_ACCOUNT || ''; + +// Contract configuration (with defaults) +const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS || 'CC3NDEI265JEKXJN4VHYD55G6VD56RBASU5JFZ3EJFU5ZDNVER3XCYYX'; +const FUNCTION_NAME = process.env.FUNCTION_NAME || 'increment'; + +// Fee token configuration - USDC on Stellar testnet +const FEE_TOKEN = process.env.FEE_TOKEN || 'CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA'; + +// SDK configuration +const config = new Configuration({ + basePath: BASE_PATH, + accessToken, +}); + +const relayersApi = new RelayersApi(config); + +// Initialize Stellar RPC server via relayer endpoint +const server = new rpc.Server(`${BASE_PATH}/api/v1/relayers/${RELAYER_ID}/rpc`, { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + allowHttp: true, +}); + +async function main() { + try { + console.log('🚀 Soroban Gas Abstraction - Build Example'); + console.log('='.repeat(80)); + + // Validate required configuration + if (!USER_ACCOUNT) { + throw new Error('USER_ACCOUNT is required. Please set it in your .env file.'); + } + + // Step 1: Create Soroban invocation XDR + console.log('\n📦 Step 1: Creating Soroban contract invocation XDR...'); + console.log(` Contract: ${CONTRACT_ADDRESS}`); + console.log(` Function: ${FUNCTION_NAME}`); + console.log(` User: ${USER_ACCOUNT}`); + + const unsignedXdr = await createSorobanInvocationXdr({ + server, + sourceAccount: USER_ACCOUNT, + contractAddress: CONTRACT_ADDRESS, + functionName: FUNCTION_NAME, + args: [addressToScVal(USER_ACCOUNT)], // increment(user_address) + networkPassphrase: Networks.TESTNET, + }); + + console.log('✅ Soroban invocation XDR created'); + + // Step 2: Build gas-abstracted transaction + console.log('\n🔨 Step 2: Building gas-abstracted transaction...'); + console.log(` Relayer: ${RELAYER_ID}`); + console.log(` Fee Token: ${FEE_TOKEN}`); + + const buildRequest: SponsoredTransactionBuildRequest = { + fee_token: FEE_TOKEN, + transaction_xdr: unsignedXdr, + }; + + const buildResponse = await relayersApi.buildSponsoredTransaction(RELAYER_ID, buildRequest); + + if (!buildResponse.data.data) { + throw new Error('Failed to build gas-abstracted transaction: no data in response'); + } + + console.log('✅ Gas-abstracted transaction built successfully'); + console.log('\n📊 Build response:'); + console.log(JSON.stringify(buildResponse.data.data, null, 2)); + + // Extract key information from the response + const buildData = buildResponse.data.data; + + if ('transaction' in buildData && buildData.transaction) { + console.log(`\n📝 Transaction XDR length: ${buildData.transaction.length} characters`); + } + + if ('user_auth_entry' in buildData && buildData.user_auth_entry) { + console.log(` User Auth Entry length: ${(buildData as { user_auth_entry: string }).user_auth_entry.length} characters`); + console.log('\n⚠️ Note: The user_auth_entry must be signed before submitting the transaction.'); + console.log(' Use authorizeEntry() from @stellar/stellar-sdk to sign it.'); + } + + if ('fee_in_token_ui' in buildData && buildData.fee_in_token_ui) { + console.log(`\n💵 Fee: ${buildData.fee_in_token_ui} USDC`); + } + + if ('valid_until' in buildData && buildData.valid_until) { + console.log(` Valid until: ${buildData.valid_until}`); + } + + console.log('\n' + '='.repeat(80)); + console.log('✅ Build completed successfully!'); + console.log('='.repeat(80)); + } catch (error) { + console.error('\n❌ Error building gas-abstracted transaction:', error); + if (error instanceof Error) { + console.error(' Message:', error.message); + if (error.stack) { + console.error(' Stack:', error.stack); + } + } + throw error; + } +} + +main().catch(console.error); diff --git a/examples/relayers/stellar/src/soroban/quoteSorobanGasAbstraction.ts b/examples/relayers/stellar/src/soroban/quoteSorobanGasAbstraction.ts new file mode 100644 index 00000000..c642f360 --- /dev/null +++ b/examples/relayers/stellar/src/soroban/quoteSorobanGasAbstraction.ts @@ -0,0 +1,136 @@ +import 'dotenv/config'; +import { Configuration, RelayersApi, SponsoredTransactionQuoteRequest } from '@openzeppelin/relayer-sdk'; +import { Networks, rpc } from '@stellar/stellar-sdk'; + +import { createSorobanInvocationXdr, addressToScVal } from './utils.js'; + +/** + * Soroban Gas Abstraction - Quote Example + * + * This example demonstrates how to get a fee quote for a Soroban contract invocation + * using the OpenZeppelin Relayer SDK's gas abstraction feature. + * + * The quote endpoint estimates the fee cost in the user's chosen token (e.g., USDC) + * for executing a Soroban smart contract call, allowing users to pay gas fees + * in tokens other than XLM. + * + * Flow: + * 1. Build a Soroban contract invocation XDR (client-side) + * 2. Get a quote for the gas abstraction fee in the chosen token + * + * IMPORTANT: This is provided as a demonstration only. For production use: + * - Use proper error handling and transaction confirmation checks + * - Implement appropriate security measures for API keys and tokens + * - Consider rate limiting and monitoring for production applications + * - Test thoroughly on testnet before using on mainnet + * - Use https connection for production applications + * + * Setup: + * 1. Copy .env.example to .env + * 2. Fill in your configuration values + * + * Usage: + * npx ts-node quoteSorobanGasAbstraction.ts + */ + +// Load configuration from environment variables +const accessToken = process.env.ACCESS_TOKEN || ''; +const BASE_PATH = process.env.BASE_PATH || 'http://localhost:8080'; +const RELAYER_ID = process.env.RELAYER_ID || 'stellar-example'; +const USER_ACCOUNT = process.env.USER_ACCOUNT || ''; + +// Contract configuration (with defaults) +const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS || 'CC3NDEI265JEKXJN4VHYD55G6VD56RBASU5JFZ3EJFU5ZDNVER3XCYYX'; +const FUNCTION_NAME = process.env.FUNCTION_NAME || 'increment'; + +// Fee token configuration - USDC on Stellar testnet +const FEE_TOKEN = process.env.FEE_TOKEN || 'CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA'; + +// SDK configuration +const config = new Configuration({ + basePath: BASE_PATH, + accessToken, +}); + +const relayersApi = new RelayersApi(config); + +// Initialize Stellar RPC server via relayer endpoint +const server = new rpc.Server(`${BASE_PATH}/api/v1/relayers/${RELAYER_ID}/rpc`, { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + allowHttp: true, +}); + +async function main() { + try { + console.log('🚀 Soroban Gas Abstraction - Quote Example'); + console.log('='.repeat(80)); + + // Validate required configuration + if (!USER_ACCOUNT) { + throw new Error('USER_ACCOUNT is required. Please set it in your .env file.'); + } + + // Step 1: Create Soroban invocation XDR + console.log('\n📦 Step 1: Creating Soroban contract invocation XDR...'); + console.log(` Contract: ${CONTRACT_ADDRESS}`); + console.log(` Function: ${FUNCTION_NAME}`); + console.log(` User: ${USER_ACCOUNT}`); + + const unsignedXdr = await createSorobanInvocationXdr({ + server, + sourceAccount: USER_ACCOUNT, + contractAddress: CONTRACT_ADDRESS, + functionName: FUNCTION_NAME, + args: [addressToScVal(USER_ACCOUNT)], // increment(user_address) + networkPassphrase: Networks.TESTNET, + }); + + console.log('✅ Soroban invocation XDR created'); + + // Step 2: Get gas abstraction quote + console.log('\n💰 Step 2: Getting gas abstraction quote...'); + console.log(` Relayer: ${RELAYER_ID}`); + console.log(` Fee Token: ${FEE_TOKEN}`); + + const quoteRequest: SponsoredTransactionQuoteRequest = { + fee_token: FEE_TOKEN, + transaction_xdr: unsignedXdr, + }; + + const quoteResponse = await relayersApi.quoteSponsoredTransaction(RELAYER_ID, quoteRequest); + + if (!quoteResponse.data.data) { + throw new Error('Failed to get gas abstraction quote: no data in response'); + } + + console.log('✅ Gas abstraction quote received'); + console.log('\n📊 Quote details:'); + console.log(JSON.stringify(quoteResponse.data.data, null, 2)); + + // Extract and display key quote information + const quoteData = quoteResponse.data.data; + if ('fee_in_token_ui' in quoteData && quoteData.fee_in_token_ui) { + console.log(`\n💵 Estimated fee: ${quoteData.fee_in_token_ui} USDC`); + } + if ('conversion_rate' in quoteData && quoteData.conversion_rate) { + console.log(` Conversion rate (XLM→USDC): ${quoteData.conversion_rate}`); + } + + console.log('\n' + '='.repeat(80)); + console.log('✅ Quote retrieved successfully!'); + console.log('='.repeat(80)); + } catch (error) { + console.error('\n❌ Error getting gas abstraction quote:', error); + if (error instanceof Error) { + console.error(' Message:', error.message); + if (error.stack) { + console.error(' Stack:', error.stack); + } + } + throw error; + } +} + +main().catch(console.error); diff --git a/examples/relayers/stellar/src/soroban/sorobanGasAbstractionFullFlow.ts b/examples/relayers/stellar/src/soroban/sorobanGasAbstractionFullFlow.ts new file mode 100644 index 00000000..90bd3a6c --- /dev/null +++ b/examples/relayers/stellar/src/soroban/sorobanGasAbstractionFullFlow.ts @@ -0,0 +1,279 @@ +import 'dotenv/config'; +import { + Configuration, + RelayersApi, + SponsoredTransactionQuoteRequest, + SponsoredTransactionBuildRequest, + StellarTransactionRequest, +} from '@openzeppelin/relayer-sdk'; +import { + Networks, + rpc, + Keypair, + authorizeEntry, +} from '@stellar/stellar-sdk'; + +import { createSorobanInvocationXdr, addressToScVal, parseAuthEntry, authEntryToXdr } from './utils.js'; + +/** + * Soroban Gas Abstraction - Full Flow Example + * + * This example demonstrates the complete flow for executing a Soroban smart contract + * call with gas abstraction, where the user pays fees in USDC instead of XLM. + * + * Flow: + * 1. Build a Soroban contract invocation XDR (client-side) + * 2. Get a quote for the gas abstraction fee (optional, for user confirmation) + * 3. Build the gas-abstracted transaction via the relayer + * 4. Sign the user_auth_entry using the user's keypair + * 5. Submit the transaction with the signed auth entry + * + * Architecture: + * - User: Signs the authorization entry (approves the contract call + fee payment) + * - Relayer: Handles fee conversion and pays network fees in XLM + * + * IMPORTANT: This is provided as a demonstration only. For production use: + * - Use proper error handling and transaction confirmation checks + * - Implement appropriate security measures for API keys and tokens + * - NEVER expose secret keys in production code + * - Consider rate limiting and monitoring for production applications + * - Test thoroughly on testnet before using on mainnet + * - Use https connection for production applications + * + * Setup: + * 1. Copy .env.example to .env + * 2. Fill in your configuration values (including USER_SECRET_KEY) + * + * Usage: + * npx ts-node sorobanGasAbstractionFullFlow.ts + */ + +// Load configuration from environment variables +const accessToken = process.env.ACCESS_TOKEN || ''; +const BASE_PATH = process.env.BASE_PATH || 'http://localhost:8080'; +const RELAYER_ID = process.env.RELAYER_ID || 'stellar-example'; + +// User configuration from environment +// WARNING: Never commit your .env file with secret keys to version control! +const USER_SECRET_KEY = process.env.USER_SECRET_KEY || ''; +const USER_ACCOUNT = process.env.USER_ACCOUNT || ''; + +// Contract configuration (with defaults) +const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS || 'CC3NDEI265JEKXJN4VHYD55G6VD56RBASU5JFZ3EJFU5ZDNVER3XCYYX'; +const FUNCTION_NAME = process.env.FUNCTION_NAME || 'increment'; + +// Fee token configuration - USDC on Stellar testnet +const FEE_TOKEN = process.env.FEE_TOKEN || 'CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA'; + +// SDK configuration +const config = new Configuration({ + basePath: BASE_PATH, + accessToken, +}); + +const relayersApi = new RelayersApi(config); + +// Initialize Stellar RPC server via relayer endpoint +const server = new rpc.Server(`${BASE_PATH}/api/v1/relayers/${RELAYER_ID}/rpc`, { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + allowHttp: true, +}); + +/** + * Signs a Soroban authorization entry using the user's keypair + */ +async function signAuthEntry( + authEntryXdr: string, + keypair: Keypair, + networkPassphrase: string +): Promise { + console.log('\n📝 Signing authorization entry...'); + + // Parse the auth entry from XDR + const authEntry = parseAuthEntry(authEntryXdr); + + // Get current ledger sequence for expiration calculation + const latestLedger = await server.getLatestLedger(); + // Set expiration to ~5 minutes from now (assuming ~5 seconds per ledger) + const validUntilLedgerSeq = latestLedger.sequence + 60; + + console.log(` Current ledger: ${latestLedger.sequence}`); + console.log(` Auth valid until ledger: ${validUntilLedgerSeq}`); + + // Sign the auth entry using authorizeEntry from stellar-sdk + const signedAuthEntry = await authorizeEntry( + authEntry, + keypair, + validUntilLedgerSeq, + networkPassphrase + ); + + const signedXdr = authEntryToXdr(signedAuthEntry); + console.log('✅ Authorization entry signed successfully'); + console.log(` Signed auth entry length: ${signedXdr.length} characters`); + + return signedXdr; +} + +/** + * Sends the gas-abstracted transaction + */ +async function sendTransaction( + transactionXdr: string, + signedAuthEntry: string +): Promise { + console.log('\n📤 Sending gas-abstracted transaction...'); + + const transactionRequest: StellarTransactionRequest = { + network: 'stellar-testnet', + transaction_xdr: transactionXdr, + // Note: For Soroban gas abstraction, we send signed_auth_entry instead of using fee_bump + }; + + // Add signed_auth_entry to the request + // The SDK type might not include this field yet, so we extend it + const requestWithAuth = { + ...transactionRequest, + signed_auth_entry: signedAuthEntry, + }; + + const response = await relayersApi.sendTransaction(RELAYER_ID, requestWithAuth as StellarTransactionRequest); + + console.log('✅ Transaction submitted successfully'); + const txData = response.data.data; + if (txData) { + console.log(` Transaction ID: ${txData.id || 'N/A'}`); + console.log(` Status: ${txData.status || 'N/A'}`); + if ('hash' in txData && txData.hash) { + console.log(` Hash: ${txData.hash}`); + } + console.log('\n📊 Transaction details:'); + console.log(JSON.stringify(txData, null, 2)); + } +} + +async function main() { + try { + console.log('🚀 Soroban Gas Abstraction - Full Flow'); + console.log('='.repeat(80)); + + // Validate required configuration + if (!USER_SECRET_KEY) { + throw new Error('USER_SECRET_KEY is required. Please set it in your .env file.'); + } + if (!USER_ACCOUNT) { + throw new Error('USER_ACCOUNT is required. Please set it in your .env file.'); + } + + // Create keypair from secret key + const userKeypair = Keypair.fromSecret(USER_SECRET_KEY); + + // Verify the public key matches + if (userKeypair.publicKey() !== USER_ACCOUNT) { + throw new Error('USER_SECRET_KEY does not match USER_ACCOUNT'); + } + + // Step 1: Create Soroban invocation XDR + console.log('\n📦 Step 1: Creating Soroban contract invocation XDR...'); + console.log(` Contract: ${CONTRACT_ADDRESS}`); + console.log(` Function: ${FUNCTION_NAME}`); + console.log(` User: ${USER_ACCOUNT}`); + + const unsignedXdr = await createSorobanInvocationXdr({ + server, + sourceAccount: USER_ACCOUNT, + contractAddress: CONTRACT_ADDRESS, + functionName: FUNCTION_NAME, + args: [addressToScVal(USER_ACCOUNT)], // increment(user_address) + networkPassphrase: Networks.TESTNET, + }); + + console.log('✅ Soroban invocation XDR created'); + + // Step 2: Get gas abstraction quote (optional, for user confirmation) + console.log('\n💰 Step 2: Getting gas abstraction quote...'); + console.log(` Relayer: ${RELAYER_ID}`); + console.log(` Fee Token: ${FEE_TOKEN}`); + + const quoteRequest: SponsoredTransactionQuoteRequest = { + fee_token: FEE_TOKEN, + transaction_xdr: unsignedXdr, + }; + + const quoteResponse = await relayersApi.quoteSponsoredTransaction(RELAYER_ID, quoteRequest); + + if (!quoteResponse.data.data) { + throw new Error('Failed to get gas abstraction quote'); + } + + console.log('✅ Quote received'); + const quoteData = quoteResponse.data.data; + if ('fee_in_token_ui' in quoteData && quoteData.fee_in_token_ui) { + console.log(` Estimated fee: ${quoteData.fee_in_token_ui} USDC`); + } + + // Step 3: Build gas-abstracted transaction + console.log('\n🔨 Step 3: Building gas-abstracted transaction...'); + + const buildRequest: SponsoredTransactionBuildRequest = { + fee_token: FEE_TOKEN, + transaction_xdr: unsignedXdr, + }; + + const buildResponse = await relayersApi.buildSponsoredTransaction(RELAYER_ID, buildRequest); + + if (!buildResponse.data.data) { + throw new Error('Failed to build gas-abstracted transaction'); + } + + console.log('✅ Transaction built successfully'); + + const buildData = buildResponse.data.data; + + // Extract transaction XDR and user auth entry + if (!('transaction' in buildData) || !buildData.transaction) { + throw new Error('Build response missing transaction XDR'); + } + + if (!('user_auth_entry' in buildData) || !(buildData as { user_auth_entry?: string }).user_auth_entry) { + throw new Error('Build response missing user_auth_entry'); + } + + const transactionXdr = buildData.transaction; + const userAuthEntry = (buildData as { user_auth_entry: string }).user_auth_entry; + + if ('fee_in_token_ui' in buildData && buildData.fee_in_token_ui) { + console.log(` Fee: ${buildData.fee_in_token_ui} USDC`); + } + if ('valid_until' in buildData && buildData.valid_until) { + console.log(` Valid until: ${buildData.valid_until}`); + } + + // Step 4: Sign the authorization entry + const signedAuthEntry = await signAuthEntry( + userAuthEntry, + userKeypair, + Networks.TESTNET + ); + + // Step 5: Send the transaction + await sendTransaction(transactionXdr, signedAuthEntry); + + console.log('\n' + '='.repeat(80)); + console.log('✅ Full flow completed successfully!'); + console.log('='.repeat(80)); + } catch (error) { + console.error('\n❌ Error in gas abstraction flow:', error); + if (error instanceof Error) { + console.error(' Message:', error.message); + if (error.stack) { + console.error(' Stack:', error.stack); + } + } + throw error; + } +} + +main().catch(console.error); diff --git a/examples/relayers/stellar/src/soroban/utils.ts b/examples/relayers/stellar/src/soroban/utils.ts new file mode 100644 index 00000000..86f0e96e --- /dev/null +++ b/examples/relayers/stellar/src/soroban/utils.ts @@ -0,0 +1,137 @@ +import { + Contract, + TransactionBuilder, + Networks, + Address, + BASE_FEE, + rpc, + xdr, +} from '@stellar/stellar-sdk'; + +export interface CreateSorobanInvocationXdrParams { + /** Stellar RPC server instance */ + server: rpc.Server; + /** Source account address (public key) - the user calling the contract */ + sourceAccount: string; + /** Contract address to invoke */ + contractAddress: string; + /** Function name to call on the contract */ + functionName: string; + /** Function arguments as ScVal array */ + args: xdr.ScVal[]; + /** Network passphrase (Networks.TESTNET or Networks.PUBLIC) */ + networkPassphrase: string; + /** Transaction fee in stroops (optional, defaults to BASE_FEE) */ + fee?: string; + /** Transaction timeout in seconds (optional, defaults to 180) */ + timeout?: number; +} + +/** + * Creates an unsigned XDR transaction for a Soroban contract invocation. + * + * This function: + * 1. Creates a Contract instance + * 2. Builds an invokeContract operation + * 3. Simulates the transaction to get proper resource footprint + * 4. Returns the prepared unsigned XDR + * + * @param params - Configuration parameters for the contract invocation + * @returns Promise resolving to the unsigned XDR string (base64 encoded) + * + * @example + * ```typescript + * const server = new rpc.Server('https://soroban-testnet.stellar.org'); + * const xdr = await createSorobanInvocationXdr({ + * server, + * sourceAccount: 'G...', + * contractAddress: 'C...', + * functionName: 'increment', + * args: [new Address('G...').toScVal()], + * networkPassphrase: Networks.TESTNET, + * }); + * ``` + */ +export async function createSorobanInvocationXdr( + params: CreateSorobanInvocationXdrParams +): Promise { + const { + server, + sourceAccount, + contractAddress, + functionName, + args, + networkPassphrase, + fee = BASE_FEE.toString(), + timeout = 180, + } = params; + + console.log('\n🔨 Building Soroban invocation transaction...'); + console.log(` Contract: ${contractAddress}`); + console.log(` Function: ${functionName}`); + console.log(` Source Account: ${sourceAccount}`); + + // Create the contract instance + const contract = new Contract(contractAddress); + + // Fetch the source account to get the current sequence number + const account = await server.getAccount(sourceAccount); + + // Build the transaction with the contract call + const transaction = new TransactionBuilder(account, { + fee, + networkPassphrase, + }) + .addOperation(contract.call(functionName, ...args)) + .setTimeout(timeout) + .build(); + + console.log(' 📡 Simulating transaction to get resource footprint...'); + + // Simulate the transaction to get proper footprint and resource fees + const simulateResponse = await server.simulateTransaction(transaction); + + // Check if simulation was successful + if (rpc.Api.isSimulationError(simulateResponse)) { + throw new Error(`Simulation failed: ${simulateResponse.error}`); + } + + if (!rpc.Api.isSimulationSuccess(simulateResponse)) { + throw new Error('Simulation did not return a successful result'); + } + + // Assemble the transaction with simulation results + const preparedTransaction = rpc.assembleTransaction( + transaction, + simulateResponse + ).build(); + + // Convert to unsigned XDR + const unsignedXdr = preparedTransaction.toXDR(); + + console.log(' ✅ Soroban invocation XDR created successfully'); + console.log(` XDR length: ${unsignedXdr.length} characters`); + + return unsignedXdr; +} + +/** + * Helper to create an Address ScVal from a Stellar public key + */ +export function addressToScVal(address: string): xdr.ScVal { + return new Address(address).toScVal(); +} + +/** + * Helper to parse a Soroban authorization entry from base64 XDR + */ +export function parseAuthEntry(authEntryXdr: string): xdr.SorobanAuthorizationEntry { + return xdr.SorobanAuthorizationEntry.fromXDR(authEntryXdr, 'base64'); +} + +/** + * Helper to convert a signed authorization entry to base64 XDR + */ +export function authEntryToXdr(authEntry: xdr.SorobanAuthorizationEntry): string { + return authEntry.toXDR('base64'); +} From f01d0ca37a89f6aa9eb2c027742163960dff17e2 Mon Sep 17 00:00:00 2001 From: Nicolas Molina Date: Wed, 4 Feb 2026 17:14:51 -0300 Subject: [PATCH 2/2] feat: improvements --- examples/relayers/stellar/package.json | 1 + examples/relayers/stellar/pnpm-lock.yaml | 312 ++++++++++++++++++ .../src/soroban/buildSorobanGasAbstraction.ts | 2 +- .../src/soroban/quoteSorobanGasAbstraction.ts | 2 +- .../soroban/sorobanGasAbstractionFullFlow.ts | 2 +- 5 files changed, 316 insertions(+), 3 deletions(-) diff --git a/examples/relayers/stellar/package.json b/examples/relayers/stellar/package.json index 55b9bcf4..d4776763 100644 --- a/examples/relayers/stellar/package.json +++ b/examples/relayers/stellar/package.json @@ -14,6 +14,7 @@ "devDependencies": { "@types/node": "^20.0.0", "ts-node": "^10.9.2", + "tsx": "^4.7.0", "typescript": "^5.0.0" } } diff --git a/examples/relayers/stellar/pnpm-lock.yaml b/examples/relayers/stellar/pnpm-lock.yaml index b519f844..5e1884ca 100644 --- a/examples/relayers/stellar/pnpm-lock.yaml +++ b/examples/relayers/stellar/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@stellar/stellar-sdk': specifier: ^13.0.0 version: 13.3.0 + dotenv: + specifier: ^16.4.0 + version: 16.6.1 devDependencies: '@types/node': specifier: ^20.0.0 @@ -21,6 +24,9 @@ importers: ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@20.19.10)(typescript@5.9.2) + tsx: + specifier: ^4.7.0 + version: 4.21.0 typescript: specifier: ^5.0.0 version: 5.9.2 @@ -31,6 +37,162 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} + '@esbuild/aix-ppc64@0.27.2': + resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.2': + resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.2': + resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.2': + resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.2': + resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.2': + resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.2': + resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.2': + resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.2': + resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.2': + resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.2': + resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.2': + resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.2': + resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.2': + resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.2': + resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.2': + resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.2': + resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.2': + resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.2': + resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.2': + resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.2': + resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.2': + resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.2': + resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.2': + resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.2': + resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.2': + resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -166,6 +328,10 @@ packages: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -186,6 +352,11 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} + esbuild@0.27.2: + resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} + engines: {node: '>=18'} + hasBin: true + eventsource@2.0.2: resolution: {integrity: sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==} engines: {node: '>=12.0.0'} @@ -210,6 +381,11 @@ packages: resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} engines: {node: '>= 6'} + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} @@ -221,6 +397,9 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} + get-tsconfig@4.13.1: + resolution: {integrity: sha512-EoY1N2xCn44xU6750Sx7OjOIT59FkmstNc3X6y5xpz7D5cBtZRe/3pSlTkDJgqsOk3WwZPkWfonhhUJfttQo3w==} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -290,6 +469,9 @@ packages: resolution: {integrity: sha512-KbXAD5q2+v1GJnkzd8zzbOxchTkStSyJZ9QwoCq3QwEXAaIlG3wDYRZGzVD357jmwaGY7hr5VaoEAL0BkF0Kvg==} engines: {bare: '>=1.10.0'} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -326,6 +508,11 @@ packages: '@swc/wasm': optional: true + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + tweetnacl@1.0.3: resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} @@ -361,6 +548,84 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 + '@esbuild/aix-ppc64@0.27.2': + optional: true + + '@esbuild/android-arm64@0.27.2': + optional: true + + '@esbuild/android-arm@0.27.2': + optional: true + + '@esbuild/android-x64@0.27.2': + optional: true + + '@esbuild/darwin-arm64@0.27.2': + optional: true + + '@esbuild/darwin-x64@0.27.2': + optional: true + + '@esbuild/freebsd-arm64@0.27.2': + optional: true + + '@esbuild/freebsd-x64@0.27.2': + optional: true + + '@esbuild/linux-arm64@0.27.2': + optional: true + + '@esbuild/linux-arm@0.27.2': + optional: true + + '@esbuild/linux-ia32@0.27.2': + optional: true + + '@esbuild/linux-loong64@0.27.2': + optional: true + + '@esbuild/linux-mips64el@0.27.2': + optional: true + + '@esbuild/linux-ppc64@0.27.2': + optional: true + + '@esbuild/linux-riscv64@0.27.2': + optional: true + + '@esbuild/linux-s390x@0.27.2': + optional: true + + '@esbuild/linux-x64@0.27.2': + optional: true + + '@esbuild/netbsd-arm64@0.27.2': + optional: true + + '@esbuild/netbsd-x64@0.27.2': + optional: true + + '@esbuild/openbsd-arm64@0.27.2': + optional: true + + '@esbuild/openbsd-x64@0.27.2': + optional: true + + '@esbuild/openharmony-arm64@0.27.2': + optional: true + + '@esbuild/sunos-x64@0.27.2': + optional: true + + '@esbuild/win32-arm64@0.27.2': + optional: true + + '@esbuild/win32-ia32@0.27.2': + optional: true + + '@esbuild/win32-x64@0.27.2': + optional: true + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.5.5': {} @@ -511,6 +776,8 @@ snapshots: diff@4.0.2: {} + dotenv@16.6.1: {} + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -532,6 +799,35 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 + esbuild@0.27.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.2 + '@esbuild/android-arm': 0.27.2 + '@esbuild/android-arm64': 0.27.2 + '@esbuild/android-x64': 0.27.2 + '@esbuild/darwin-arm64': 0.27.2 + '@esbuild/darwin-x64': 0.27.2 + '@esbuild/freebsd-arm64': 0.27.2 + '@esbuild/freebsd-x64': 0.27.2 + '@esbuild/linux-arm': 0.27.2 + '@esbuild/linux-arm64': 0.27.2 + '@esbuild/linux-ia32': 0.27.2 + '@esbuild/linux-loong64': 0.27.2 + '@esbuild/linux-mips64el': 0.27.2 + '@esbuild/linux-ppc64': 0.27.2 + '@esbuild/linux-riscv64': 0.27.2 + '@esbuild/linux-s390x': 0.27.2 + '@esbuild/linux-x64': 0.27.2 + '@esbuild/netbsd-arm64': 0.27.2 + '@esbuild/netbsd-x64': 0.27.2 + '@esbuild/openbsd-arm64': 0.27.2 + '@esbuild/openbsd-x64': 0.27.2 + '@esbuild/openharmony-arm64': 0.27.2 + '@esbuild/sunos-x64': 0.27.2 + '@esbuild/win32-arm64': 0.27.2 + '@esbuild/win32-ia32': 0.27.2 + '@esbuild/win32-x64': 0.27.2 + eventsource@2.0.2: {} feaxios@0.0.23: @@ -552,6 +848,9 @@ snapshots: hasown: 2.0.2 mime-types: 2.1.35 + fsevents@2.3.3: + optional: true + function-bind@1.1.2: {} get-intrinsic@1.3.0: @@ -572,6 +871,10 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 + get-tsconfig@4.13.1: + dependencies: + resolve-pkg-maps: 1.0.0 + gopd@1.2.0: {} has-property-descriptors@1.0.2: @@ -626,6 +929,8 @@ snapshots: bare-url: 2.2.1 optional: true + resolve-pkg-maps@1.0.0: {} + safe-buffer@5.2.1: {} set-function-length@1.2.2: @@ -674,6 +979,13 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + tsx@4.21.0: + dependencies: + esbuild: 0.27.2 + get-tsconfig: 4.13.1 + optionalDependencies: + fsevents: 2.3.3 + tweetnacl@1.0.3: {} typed-array-buffer@1.0.3: diff --git a/examples/relayers/stellar/src/soroban/buildSorobanGasAbstraction.ts b/examples/relayers/stellar/src/soroban/buildSorobanGasAbstraction.ts index fec4f152..c965c57e 100644 --- a/examples/relayers/stellar/src/soroban/buildSorobanGasAbstraction.ts +++ b/examples/relayers/stellar/src/soroban/buildSorobanGasAbstraction.ts @@ -31,7 +31,7 @@ import { createSorobanInvocationXdr, addressToScVal } from './utils.js'; * 2. Fill in your configuration values * * Usage: - * npx ts-node buildSorobanGasAbstraction.ts + * npx tsx buildSorobanGasAbstraction.ts */ // Load configuration from environment variables diff --git a/examples/relayers/stellar/src/soroban/quoteSorobanGasAbstraction.ts b/examples/relayers/stellar/src/soroban/quoteSorobanGasAbstraction.ts index c642f360..76601ee0 100644 --- a/examples/relayers/stellar/src/soroban/quoteSorobanGasAbstraction.ts +++ b/examples/relayers/stellar/src/soroban/quoteSorobanGasAbstraction.ts @@ -30,7 +30,7 @@ import { createSorobanInvocationXdr, addressToScVal } from './utils.js'; * 2. Fill in your configuration values * * Usage: - * npx ts-node quoteSorobanGasAbstraction.ts + * npx tsx quoteSorobanGasAbstraction.ts */ // Load configuration from environment variables diff --git a/examples/relayers/stellar/src/soroban/sorobanGasAbstractionFullFlow.ts b/examples/relayers/stellar/src/soroban/sorobanGasAbstractionFullFlow.ts index 90bd3a6c..b686bef1 100644 --- a/examples/relayers/stellar/src/soroban/sorobanGasAbstractionFullFlow.ts +++ b/examples/relayers/stellar/src/soroban/sorobanGasAbstractionFullFlow.ts @@ -45,7 +45,7 @@ import { createSorobanInvocationXdr, addressToScVal, parseAuthEntry, authEntryTo * 2. Fill in your configuration values (including USER_SECRET_KEY) * * Usage: - * npx ts-node sorobanGasAbstractionFullFlow.ts + * npx tsx sorobanGasAbstractionFullFlow.ts */ // Load configuration from environment variables