feat: add GetAccountBalance, PathPaymentStrictSend, InvokeContract to…#55
Conversation
…ols + transactionUtils - Add get_account_balance tool: fetches XLM and all asset trustlines - Add path_payment_strict_send tool: multi-hop DEX routing with slippage protection - Add invoke_soroban_contract tool: generic Soroban contract invocation - Add transactionUtils: fee estimation, retry logic, error classification - Add unit tests for all new tools (Jest) - Update index.ts with new exports and getAllStellarTools() helper
|
| GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
|---|---|---|---|---|---|
| - | - | Generic High Entropy Secret | 22bc283 | tests/tools.test.ts | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secret safely. Learn here the best practices.
- Revoke and rotate this secret.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
There was a problem hiding this comment.
10 issues found across 12 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="package.json">
<violation number="1" location="package.json:44">
P1: Jest tests are added but default/CI test scripts still run only Vitest, so the new Jest test file can be skipped.</violation>
</file>
<file name="index.ts">
<violation number="1" location="index.ts:75">
P2: `getAllStellarTools()` is inconsistent with existing `stellarTools` and returns only a subset despite its "all" contract.</violation>
</file>
<file name="agent.ts">
<violation number="1" location="agent.ts:154">
P1: `bridge()` ignores `AgentClient` network configuration by hardcoding testnet, causing incorrect behavior for mainnet clients.</violation>
</file>
<file name="utils/transactionUtils.ts">
<violation number="1" location="utils/transactionUtils.ts:89">
P1: `estimateFee` does not validate `operationCount`, allowing `0` and producing `recommendedFeeStroops: "NaN"` via division by zero.</violation>
</file>
<file name="tsconfig.json">
<violation number="1" location="tsconfig.json:3">
P2: Primary tsconfig restricts global types to Jest only, which can exclude Node ambient types needed by source code and pollute production typing with test globals.</violation>
</file>
<file name="tools/pathPaymentStrictSend.ts">
<violation number="1" location="tools/pathPaymentStrictSend.ts:97">
P1: Error paths falsely return `success: true` with mock transaction hashes, causing callers to treat failed payments as successful on-chain transactions.</violation>
</file>
<file name="tools/invokeContract.ts">
<violation number="1" location="tools/invokeContract.ts:28">
P1: Numeric argument coercion via `Number` + `Math.round` can silently alter or corrupt i128 values before on-chain submission.</violation>
<violation number="2" location="tools/invokeContract.ts:38">
P1: Argument `type`/`value` mismatch is accepted and coerced, causing silent semantic corruption (e.g., string "false" becomes boolean true).</violation>
<violation number="3" location="tools/invokeContract.ts:72">
P2: `simulateOnly` returns a hardcoded success result instead of running real Soroban simulation/preflight, causing false-positive simulation outcomes.</violation>
<violation number="4" location="tools/invokeContract.ts:74">
P1: Contract invocation reports success unconditionally after a single transaction lookup, which can misreport pending or failed transactions as successful.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| "@vitest/ui": "^4.0.18", | ||
| "c8": "^10.1.3", | ||
| "copyfiles": "^2.4.1", | ||
| "jest": "^30.3.0", |
There was a problem hiding this comment.
P1: Jest tests are added but default/CI test scripts still run only Vitest, so the new Jest test file can be skipped.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At package.json, line 44:
<comment>Jest tests are added but default/CI test scripts still run only Vitest, so the new Jest test file can be skipped.</comment>
<file context>
@@ -35,11 +35,14 @@
"@vitest/ui": "^4.0.18",
"c8": "^10.1.3",
"copyfiles": "^2.4.1",
+ "jest": "^30.3.0",
+ "ts-jest": "^29.4.9",
"vitest": "^4.0.18"
</file context>
| this.network === "mainnet" | ||
| ? "stellar-mainnet" | ||
| : "stellar-testnet", | ||
| fromNetwork: "stellar-testnet" |
There was a problem hiding this comment.
P1: bridge() ignores AgentClient network configuration by hardcoding testnet, causing incorrect behavior for mainnet clients.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At agent.ts, line 154:
<comment>`bridge()` ignores `AgentClient` network configuration by hardcoding testnet, causing incorrect behavior for mainnet clients.</comment>
<file context>
@@ -148,10 +151,7 @@ export class AgentClient {
- this.network === "mainnet"
- ? "stellar-mainnet"
- : "stellar-testnet",
+ fromNetwork: "stellar-testnet"
});
}
</file context>
| fromNetwork: "stellar-testnet" | |
| fromNetwork: | |
| this.network === "mainnet" | |
| ? "stellar-mainnet" | |
| : "stellar-testnet" |
| operationCount, | ||
| networkCongestionLevel: congestionLevel, | ||
| recommendedFeeStroops: String( | ||
| Math.ceil(recommendedFee / operationCount) |
There was a problem hiding this comment.
P1: estimateFee does not validate operationCount, allowing 0 and producing recommendedFeeStroops: "NaN" via division by zero.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At utils/transactionUtils.ts, line 89:
<comment>`estimateFee` does not validate `operationCount`, allowing `0` and producing `recommendedFeeStroops: "NaN"` via division by zero.</comment>
<file context>
@@ -0,0 +1,275 @@
+ operationCount,
+ networkCongestionLevel: congestionLevel,
+ recommendedFeeStroops: String(
+ Math.ceil(recommendedFee / operationCount)
+ ),
+ };
</file context>
| } | ||
|
|
||
| return JSON.stringify({ | ||
| success: true, |
There was a problem hiding this comment.
P1: Error paths falsely return success: true with mock transaction hashes, causing callers to treat failed payments as successful on-chain transactions.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tools/pathPaymentStrictSend.ts, line 97:
<comment>Error paths falsely return `success: true` with mock transaction hashes, causing callers to treat failed payments as successful on-chain transactions.</comment>
<file context>
@@ -0,0 +1,119 @@
+ }
+
+ return JSON.stringify({
+ success: true,
+ transactionHash: result.hash,
+ sent: `${sendAmount} ${sendAsset.code}`,
</file context>
| return StellarSdk.xdr.ScVal.scvString(Buffer.from(String(value))); | ||
|
|
||
| case "number": | ||
| return StellarSdk.nativeToScVal(BigInt(Math.round(Number(value))), { |
There was a problem hiding this comment.
P1: Numeric argument coercion via Number + Math.round can silently alter or corrupt i128 values before on-chain submission.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tools/invokeContract.ts, line 28:
<comment>Numeric argument coercion via `Number` + `Math.round` can silently alter or corrupt i128 values before on-chain submission.</comment>
<file context>
@@ -0,0 +1,175 @@
+ return StellarSdk.xdr.ScVal.scvString(Buffer.from(String(value)));
+
+ case "number":
+ return StellarSdk.nativeToScVal(BigInt(Math.round(Number(value))), {
+ type: "i128",
+ });
</file context>
| ); | ||
|
|
||
| case "boolean": | ||
| return StellarSdk.xdr.ScVal.scvBool(Boolean(value)); |
There was a problem hiding this comment.
P1: Argument type/value mismatch is accepted and coerced, causing silent semantic corruption (e.g., string "false" becomes boolean true).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tools/invokeContract.ts, line 38:
<comment>Argument `type`/`value` mismatch is accepted and coerced, causing silent semantic corruption (e.g., string "false" becomes boolean true).</comment>
<file context>
@@ -0,0 +1,175 @@
+ );
+
+ case "boolean":
+ return StellarSdk.xdr.ScVal.scvBool(Boolean(value));
+
+ case "bytes":
</file context>
| }) => { | ||
| if (simulateOnly === true) { | ||
| return JSON.stringify({ | ||
| success: true, |
There was a problem hiding this comment.
P1: Contract invocation reports success unconditionally after a single transaction lookup, which can misreport pending or failed transactions as successful.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tools/invokeContract.ts, line 74:
<comment>Contract invocation reports success unconditionally after a single transaction lookup, which can misreport pending or failed transactions as successful.</comment>
<file context>
@@ -0,0 +1,175 @@
+ }) => {
+ if (simulateOnly === true) {
+ return JSON.stringify({
+ success: true,
+ mode: "simulation_only",
+ contractId,
</file context>
| export function getAllStellarTools() { | ||
| return [ | ||
| createGetAccountBalanceTool(), | ||
| createPathPaymentStrictSendTool(), | ||
| createInvokeContractTool(), | ||
| ]; | ||
| } No newline at end of file |
There was a problem hiding this comment.
P2: getAllStellarTools() is inconsistent with existing stellarTools and returns only a subset despite its "all" contract.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At index.ts, line 75:
<comment>`getAllStellarTools()` is inconsistent with existing `stellarTools` and returns only a subset despite its "all" contract.</comment>
<file context>
@@ -31,10 +63,19 @@ export type {
+ stellarSendPaymentTool,
];
+
+export function getAllStellarTools() {
+ return [
+ createGetAccountBalanceTool(),
</file context>
| export function getAllStellarTools() { | |
| return [ | |
| createGetAccountBalanceTool(), | |
| createPathPaymentStrictSendTool(), | |
| createInvokeContractTool(), | |
| ]; | |
| } | |
| export function getAllStellarTools() { | |
| return [ | |
| ...stellarTools, | |
| createGetAccountBalanceTool(), | |
| createPathPaymentStrictSendTool(), | |
| createInvokeContractTool(), | |
| ]; | |
| } |
| @@ -1,5 +1,6 @@ | |||
| { | |||
| "compilerOptions": { | |||
| "types": ["jest"], | |||
There was a problem hiding this comment.
P2: Primary tsconfig restricts global types to Jest only, which can exclude Node ambient types needed by source code and pollute production typing with test globals.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tsconfig.json, line 3:
<comment>Primary tsconfig restricts global types to Jest only, which can exclude Node ambient types needed by source code and pollute production typing with test globals.</comment>
<file context>
@@ -1,5 +1,6 @@
{
"compilerOptions": {
+ "types": ["jest"],
/* Visit https://aka.ms/tsconfig to read more about this file */
"outDir": "./dist",
</file context>
| network, | ||
| simulateOnly, | ||
| }) => { | ||
| if (simulateOnly === true) { |
There was a problem hiding this comment.
P2: simulateOnly returns a hardcoded success result instead of running real Soroban simulation/preflight, causing false-positive simulation outcomes.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tools/invokeContract.ts, line 72:
<comment>`simulateOnly` returns a hardcoded success result instead of running real Soroban simulation/preflight, causing false-positive simulation outcomes.</comment>
<file context>
@@ -0,0 +1,175 @@
+ network,
+ simulateOnly,
+ }) => {
+ if (simulateOnly === true) {
+ return JSON.stringify({
+ success: true,
</file context>
feat: Add GetAccountBalance, PathPaymentStrictSend, InvokeContract tools + utilities
Summary
This PR adds 3 new Langchain DynamicStructuredTools and a shared utility module to expand AgentKit's DeFi capabilities on Stellar.
What was changed
New Files
Tool Details
1.
get_account_balanceQueries Horizon for a Stellar account's full balance sheet.
includeZeroBalancesflagWhy it matters: Any DeFi agent needs to verify balances before executing operations. This is a foundational tool every agent workflow will use.
2.
path_payment_strict_sendExecutes a path payment (strict send) through Stellar's SDEX.
strictSendPathsminDestAmountop_too_few_offers,op_no_trust, etc.)Why it matters: Basic swaps only work with a direct orderbook. Path payments enable any-asset-to-any-asset conversions — critical for real DeFi agents.
3.
invoke_soroban_contractInvokes any deployed Soroban smart contract function.
string,number(i128),address,boolean,bytessimulateOnly: truefor read-only/view functionsScValreturn values to native JS typesWhy it matters: Without this tool, every Soroban protocol requires a dedicated implementation. This single tool covers the entire Soroban ecosystem — DeFi, NFTs, DAOs, oracles, and more.
4.
utils/transactionUtils.tsShared utilities used across all tools:
estimateFee(network, operationCount)— Queries Horizon fee stats, classifies network congestion (low/medium/high), returns recommended feewithRetry(fn, options)— Exponential backoff for transient network errors (timeouts, rate limits, ECONNRESET)classifyStellarError(error)— Maps Horizon/Soroban error codes to human-readable messages with remediation suggestionsbuildHorizonServer/buildSorobanServer— Factory functions for consistent server instantiationTests
All tools are covered by unit tests in
tests/tools.test.ts:Run:
npx jest tests/tools.test.tsTechnical Depth
DynamicStructuredToolpattern with Zod schema validation@stellar/stellar-sdkv12 APIs (SorobanRpc.Server,Horizon.Server)anytypes on public interfacesScValtypesEcosystem Impact
invoke_soroban_contractunlocks interaction with every Soroban protocol — StelloFi, StellarPay, Riskon, and any future contractpath_payment_strict_sendenables full DeFi routing, not just direct swapsget_account_balanceis a building block every agent workflow needstransactionUtilsreduces code duplication across future tool additionsTesting
Breaking Changes
None. All additions are additive. Existing exports are untouched.
Summary by cubic
Adds three Stellar tools for balances, path payments, and Soroban contract calls, plus shared transaction utilities and exports. Includes a
getAllStellarTools()helper and Jest tests with safe mock values.New Features
get_account_balance: Returns XLM and all trustline balances with limits and liabilities.path_payment_strict_send: Multi-hop swaps with min-destination slippage protection and optional memo.invoke_soroban_contract: Generic Soroban caller with simulation support and result decoding.estimateFee,withRetry,classifyStellarError,buildHorizonServer,buildSorobanServer,getNetworkPassphrase.getAllStellarTools().ts-jestconfig and unit tests; replaces high-entropy strings with safe mock values.Refactors
AgentClient.bridge:fromNetworknow defaults to"stellar-testnet"; addedTargetChaintype.tools/stellar.ts: Switches to@stellar/stellar-sdkimport.Written for commit b7e96be. Summary will update on new commits.