Skip to content

Commit 7435fae

Browse files
authored
Merge pull request #734 from Goodnessukaigwe/fix/654-backend-no-gas-estimation-for-soroban-contract-calls
[654] [Backend] — No Gas Estimation for Soroban Contract Calls
2 parents 67b2669 + 92eaa1c commit 7435fae

1 file changed

Lines changed: 61 additions & 0 deletions

File tree

backend/src/modules/blockchain/stellar.service.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,64 @@ export class StellarService implements OnModuleInit {
203203
}
204204
}
205205

206+
/**
207+
* Estimate fees for a Soroban contract call using simulation
208+
*/
209+
async estimateSorobanFees(
210+
contractId: string,
211+
functionName: string,
212+
args: any[] = [],
213+
): Promise<{ resourceFee: string; baseFee: string; totalFee: string }> {
214+
try {
215+
return await this.rpcClient.executeWithRetry(async (client) => {
216+
const rpcServer = client as rpc.Server;
217+
const sourceAccount = new Account(Keypair.random().publicKey(), '0');
218+
const contract = new Contract(contractId);
219+
220+
const transaction = new TransactionBuilder(sourceAccount, {
221+
fee: BASE_FEE,
222+
networkPassphrase: this.getNetworkPassphrase(),
223+
})
224+
.addOperation(
225+
contract.call(
226+
functionName,
227+
...args.map((arg) => nativeToScVal(arg as never)),
228+
),
229+
)
230+
.setTimeout(30)
231+
.build();
232+
233+
const simulation = await rpcServer.simulateTransaction(transaction);
234+
if (rpc.Api.isSimulationError(simulation)) {
235+
throw new Error(`Soroban simulation failed: ${simulation.error}`);
236+
}
237+
238+
return this.calculateFees(transaction, simulation);
239+
}, 'rpc');
240+
} catch (error) {
241+
this.logger.error(
242+
`Failed to estimate fees for ${contractId}.${functionName}: ${(error as Error).message}`,
243+
error,
244+
);
245+
throw error;
246+
}
247+
}
248+
249+
private calculateFees(
250+
transaction: any,
251+
simulation: any,
252+
): { resourceFee: string; baseFee: string; totalFee: string } {
253+
const resourceFee = BigInt(simulation.minResourceFee ?? 0);
254+
const baseFee = BigInt(transaction.operations.length) * BigInt(BASE_FEE);
255+
const totalFee = baseFee + resourceFee;
256+
257+
return {
258+
resourceFee: resourceFee.toString(),
259+
baseFee: baseFee.toString(),
260+
totalFee: totalFee.toString(),
261+
};
262+
}
263+
206264
async invokeContractWrite(
207265
contractId: string,
208266
functionName: string,
@@ -236,6 +294,9 @@ export class StellarService implements OnModuleInit {
236294
throw new Error(`Soroban simulation failed: ${simulation.error}`);
237295
}
238296

297+
const fees = this.calculateFees(transaction, simulation);
298+
transaction.fee = fees.totalFee;
299+
239300
transaction = rpc.assembleTransaction(transaction, simulation).build();
240301
transaction.sign(sourceKeypair);
241302

0 commit comments

Comments
 (0)