Agent guide: gswap-sdk
This page is for an AI agent (or a developer working with one) that writes code against
@gala-chain/gswap-sdk, the client of the GSwap DEX. Every recipe compiles against the package's
built types (the docs build fails otherwise), and every invariant names the test or golden that
enforces it.
Which package for which job
| Job | Package |
|---|---|
| Quote, swap, manage liquidity, read pools, positions and market data on GSwap | @gala-chain/gswap-sdk (this package) |
| Signers, wallet providers, plain token transfers and balance reads | @gala-chain/gala-core-sdk (the peer dependency of this package) |
| Buy and sell a bonding-curve token before it graduates, launch a token | @gala-chain/launchpad-sdk |
| The same abilities as MCP tools | @gala-chain/gala-mcp-server |
Install with npm install @gala-chain/gswap-sdk@next and the core peer next to it (range >=1.0.0-rc.7 <2).
Recipes
Create the client
import { GSwap } from '@gala-chain/gswap-sdk';
import { PrivateKeySigner, getEthereumAddressFromPrivateKey } from '@gala-chain/gala-core-sdk';
const privateKey = process.env.GALACHAIN_PRIVATE_KEY!;
const gSwap = new GSwap({
env: 'stage',
signer: new PrivateKeySigner(privateKey),
walletAddress: getEthereumAddressFromPrivateKey(privateKey),
});
console.log(gSwap.pools !== undefined);
Quote a swap
A quote is a read: it signs nothing and spends nothing.
import { GSwap } from '@gala-chain/gswap-sdk';
const gSwap = new GSwap({ env: 'stage' });
const quote = await gSwap.quoting.quoteExactInput('GALA|Unit|none|none', 'GUSDC|Unit|none|none', '100');
console.log(quote.feeTier, quote.amountOut);
Swap and confirm
A swap is queued. The acknowledgement means the chain accepted the request, not that it executed:
confirm() reads the applied trade or throws the chain's rejection.
import { GSwap } from '@gala-chain/gswap-sdk';
import { PrivateKeySigner, getEthereumAddressFromPrivateKey } from '@gala-chain/gala-core-sdk';
const privateKey = process.env.GALACHAIN_PRIVATE_KEY!;
const gSwap = new GSwap({
env: 'stage',
signer: new PrivateKeySigner(privateKey),
walletAddress: getEthereumAddressFromPrivateKey(privateKey),
});
const quote = await gSwap.quoting.quoteExactInput('GALA|Unit|none|none', 'GUSDC|Unit|none|none', '100');
const submitted = await gSwap.swaps.swap('GALA|Unit|none|none', 'GUSDC|Unit|none|none', quote.feeTier, {
exactIn: '100',
amountOutMinimum: quote.amountOut,
});
console.log(submitted.mode, submitted.uniqueKey);
const applied = await submitted.confirm();
console.log(applied.transactionId, applied.result);
Add liquidity at a tick range
import { GSwap } from '@gala-chain/gswap-sdk';
import { PrivateKeySigner, getEthereumAddressFromPrivateKey } from '@gala-chain/gala-core-sdk';
const privateKey = process.env.GALACHAIN_PRIVATE_KEY!;
const gSwap = new GSwap({
env: 'stage',
signer: new PrivateKeySigner(privateKey),
walletAddress: getEthereumAddressFromPrivateKey(privateKey),
});
const added = await gSwap.positions.addLiquidityByTicks({
token0: 'GALA',
token1: 'GUSDC',
fee: 3000,
tickLower: -6000,
tickUpper: 6000,
amount: '100',
amountIsToken0: true,
});
await added.confirm();
Recover from an unknown submission
import { GSwap } from '@gala-chain/gswap-sdk';
import { PrivateKeySigner, getEthereumAddressFromPrivateKey, isGalaError } from '@gala-chain/gala-core-sdk';
const privateKey = process.env.GALACHAIN_PRIVATE_KEY!;
const gSwap = new GSwap({
env: 'stage',
signer: new PrivateKeySigner(privateKey),
walletAddress: getEthereumAddressFromPrivateKey(privateKey),
});
try {
await gSwap.swaps.swap('GALA|Unit|none|none', 'GUSDC|Unit|none|none', 3000, { exactIn: '100', amountOutMinimum: '1' });
} catch (error) {
if (isGalaError(error) && error.code === 'SUBMISSION_OUTCOME_UNKNOWN') {
// The write may have been queued: confirm by its uniqueKey, never submit a second swap blindly.
console.log('check before retrying:', error.uniqueKey);
} else {
throw error;
}
}
Invariants
- The SDK signs through core's signers, and its DTO builders produce exactly the DTOs (key order
included) of the pinned golden builder cases. Enforced by
packages/gswap-sdk/tests/builder-parity.test.ts("DTO builder parity with the golden builders") and the goldenpackages/gala-core-sdk/tests/fixtures/frontend-signing-golden.json("builders.0"). - A swap is queued: the acknowledgement is not execution.
confirm()reads the applied trade by itsuniqueKeywithout resubmitting, and surfaces a chain rejection. Enforced bypackages/gswap-sdk/tests/swaps.test.ts("confirms a swap by uniqueKey from apply-results without resubmitting") andpackages/gswap-sdk/tests/swaps.test.ts("surfaces the chain rejection when confirming a rejected swap by uniqueKey"). - Slippage bounds travel as the decimal strings you gave (non-negative), and a swap with a
non-positive quantity is refused. Enforced by
packages/gswap-sdk/tests/swaps.test.ts("preserves non-negative slippage bounds as decimal strings and appends the signer result") andpackages/gswap-sdk/tests/swaps.test.ts("builds exact-output slippage and rejects non-positive quantities"). - Amounts are decimal strings: a JavaScript number is refused, naming the field. Enforced by
packages/gswap-sdk/tests/validation.test.ts("should reject a JavaScript number, naming the field"). - A user reference is
eth|<EIP-55 checksummed 40 hex digits>or aclient|alias: every Ethereum spelling is converted, a wrong mixed-case checksum is refused. Enforced bypackages/gswap-sdk/tests/validation.test.ts("sends every spelling of an Ethereum account as eth|") and packages/gswap-sdk/tests/validation.test.ts("refuses a wrong-checksum mixed-case account and anything else, naming the field"). eth|0x<hex>is not an address form and is REFUSED with core'sINVALID_ETH_USER_REF_PREFIX, naming the right spelling. Enforced bypackages/gswap-sdk/tests/validation.test.ts("refuses eth|0xas core's INVALID_ETH_USER_REF_PREFIX (not an address form), naming the right one"). - A class key carries no symbol: it is parsed from the right and a symbol is looked up in the
registry, never derived from a key. Enforced by
packages/gswap-sdk/tests/symbol-key-independence.test.ts("class keys carry no symbol"). - A write POST that gets no answer (a timeout, or a connection that failed) is
SUBMISSION_OUTCOME_UNKNOWNwith its reason and theuniqueKey; a failed read keepsTRANSPORT_ERROR. Enforced bypackages/gswap-sdk/tests/submission_outcome_unknown.test.ts("a write POST that gets no answer is SUBMISSION_OUTCOME_UNKNOWN, with the reason") andpackages/gswap-sdk/tests/submission_outcome_unknown.test.ts("reads keep TRANSPORT_ERROR: only a state-changing submit is an unknown outcome"). - Every attempt mints a fresh
uniqueKey; a custom prefix that would make the key unreadable by apply-results is refused. Enforced bypackages/gswap-sdk/tests/builder-parity.test.ts("mints a fresh uniqueKey per attempt, with the default prefix") andpackages/gswap-sdk/tests/builder-parity.test.ts("rejects a uniqueKeyPrefix that would make the key unreadable by apply-results").
Error codes
Branch on error.code; a GSwapSDKError also carries core's codes. The full lists are at
/errors.json (every SDK) and in the error handling page of this package.
| Code | Meaning | Fix |
|---|---|---|
SUBMISSION_OUTCOME_UNKNOWN | The write POST got no answer (timeout or lost connection). | Confirm by error.uniqueKey before any retry. |
CONFIRMATION_TIMEOUT | confirm() did not see the apply result in time. | Call confirm() again with the same uniqueKey; do not resubmit. |
CHAIN_REJECTED | The chain applied the request and rejected it. | Read error.body; fix the input and submit a new swap. |
INSUFFICIENT_LIQUIDITY | The pool cannot fill the amount. | Quote a smaller amount or another fee tier. |
NO_POOL_AVAILABLE | No pool exists for the pair. | Check the pair and fee tier with gSwap.pools. |
NO_SIGNER | A write was attempted on a client built without a signer. | Pass signer and walletAddress to the constructor. |
AMOUNT_IS_JS_NUMBER | A JavaScript number was given as an amount. | Pass the amount as a decimal string. |
TICKS_NOT_ALIGNED | A tick is not divisible by the fee tier's spacing. | Align the ticks, or use addLiquidityByPrice. |
RATE_LIMITED | The backend asked the client to slow down. | Wait error.retryAfterMs and retry the read. |
UPDATE_REQUIRED | The backend refuses this client version (HTTP 426). | Upgrade the SDK. |
INVALID_ETH_USER_REF_PREFIX | An address was written eth|0x<hex>. | Write eth|<EIP-55 digits> or the bare 0x<hex>. |