Skip to main content

Agent guide: gala-core-sdk

This page is for an AI agent (or a developer working with one) that writes code against @gala-chain/gala-core-sdk. 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​

JobPackageInstall
Sign a payload, transfer GALA or a token, read a balance, bridge, wrap, connect a wallet@gala-chain/gala-core-sdknpm install @gala-chain/gala-core-sdk@next
Quote, swap and manage liquidity on the GSwap DEX@gala-chain/gswap-sdk (core is its peer)npm install @gala-chain/gswap-sdk@next
Launchpad pools, bonding-curve buy and sell, token creation@gala-chain/launchpad-sdk (core is its peer)npm install @gala-chain/launchpad-sdk@next
Give an MCP client (Claude, Cursor) the same abilities as tools@gala-chain/gala-mcp-servernpm install -g @gala-chain/gala-mcp-server@next

Never install a second copy of core next to a product SDK: two copies split instanceof, the signer identity and the error class. The product SDKs declare core as a peer dependency so one copy is shared.

Recipes​

Sign a payload​

import { PrivateKeySigner } from '@gala-chain/gala-core-sdk';

const signer = new PrivateKeySigner(process.env.GALACHAIN_PRIVATE_KEY!);
// The signer signs exactly the object it is handed: pass the bare DTO of the method.
const signed = await signer.signObject('TransferToken', { quantity: '1', uniqueKey: 'k-1' });
console.log(signed);

Transfer GALA​

import { GALA_CHAIN_ENVIRONMENTS, GatewayWrites, PrivateKeySigner, getEthereumAddressFromPrivateKey } from '@gala-chain/gala-core-sdk';

const privateKey = process.env.GALACHAIN_PRIVATE_KEY!;
const writes = new GatewayWrites({
gatewayBaseUrl: GALA_CHAIN_ENVIRONMENTS.stage.gatewayBaseUrl,
signer: new PrivateKeySigner(privateKey),
walletAddress: getEthereumAddressFromPrivateKey(privateKey),
});

// `to` may be eth|<EIP-55 digits>, 0x<40 hex> or a client| alias; quantity is whole GALA as a string.
const receipt = await writes.transferGala({ to: 'client|5f1b0c0a1b2c3d4e5f6a7b8c', quantity: '1.5' });
console.log(receipt.transactionId, receipt.uniqueKey);

Transfer another token​

import { GALA_CHAIN_ENVIRONMENTS, GatewayWrites, PrivateKeySigner, getEthereumAddressFromPrivateKey } from '@gala-chain/gala-core-sdk';

const privateKey = process.env.GALACHAIN_PRIVATE_KEY!;
const writes = new GatewayWrites({
gatewayBaseUrl: GALA_CHAIN_ENVIRONMENTS.stage.gatewayBaseUrl,
signer: new PrivateKeySigner(privateKey),
walletAddress: getEthereumAddressFromPrivateKey(privateKey),
});

// A token is named by its four-part class key; a bare symbol is never a key.
await writes.transferToken({
to: 'client|5f1b0c0a1b2c3d4e5f6a7b8c',
tokenClassKey: 'GUSDC|Unit|none|none',
quantity: '10',
});

Read a balance​

import { GALA_CHAIN_ENVIRONMENTS, GatewayReads } from '@gala-chain/gala-core-sdk';

const reads = new GatewayReads({ gatewayBaseUrl: GALA_CHAIN_ENVIRONMENTS.stage.gatewayBaseUrl });
const balance = await reads.fetchTokenBalance({
owner: 'client|5f1b0c0a1b2c3d4e5f6a7b8c',
tokenClassKey: 'GALA|Unit|none|none',
});
console.log(balance?.quantity);

Recover from an unknown write outcome​

A write that times out or loses its connection may have been applied. The error carries the write's uniqueKey: check that key before sending anything again, never re-send under a fresh key.

import { GALA_CHAIN_ENVIRONMENTS, GatewayWrites, PrivateKeySigner, getEthereumAddressFromPrivateKey, isGalaError } from '@gala-chain/gala-core-sdk';

const privateKey = process.env.GALACHAIN_PRIVATE_KEY!;
const writes = new GatewayWrites({
gatewayBaseUrl: GALA_CHAIN_ENVIRONMENTS.stage.gatewayBaseUrl,
signer: new PrivateKeySigner(privateKey),
walletAddress: getEthereumAddressFromPrivateKey(privateKey),
});

try {
await writes.transferGala({ to: 'client|5f1b0c0a1b2c3d4e5f6a7b8c', quantity: '1' });
} catch (error) {
if (isGalaError(error, 'GATEWAY_WRITE_OUTCOME_UNKNOWN')) {
console.log('check this write before retrying:', error.uniqueKey, error.params.reason);
} else {
throw error;
}
}

For a swap, a token launch or a buy and sell, use the product SDK: each has its own agent guide (/gswap-sdk/agents.md, /launchpad-sdk/agents.md). The bundler write path belongs to @gala-chain/launchpad-sdk.

Invariants​

  • Sign the bare DTO. The signer is handed exactly the object of the method; the personal-sign path never hashes an embedded prefix field into the message (the "phantom signer" bug), and the signature recovers to the signing wallet. Enforced by packages/gala-core-sdk/tests/chain_dto_signing.test.ts ("never hashes the embedded prefix field into the message (the phantom-signer bug)") and by packages/gala-core-sdk/tests/signing-golden.test.ts ("serializes and natively signs byte-for-byte").
  • A write sends what the signer returned and adds nothing. Enforced by packages/gala-core-sdk/tests/writes.test.ts ("hands the signer the bare DTO for the write method and sends exactly what it returned").
  • A user reference is eth|<EIP-55 checksummed 40 hex digits> or a client| alias. Every Ethereum spelling is converted to the checksummed eth| form; a bare 0x<40 hex> is converted, a wrong mixed-case checksum is refused. Enforced by packages/gala-core-sdk/tests/writes.test.ts ("sends every Ethereum account spelling as eth|, a client| alias as written"), packages/gala-core-sdk/tests/writes.test.ts ("accepts one-case digits and refuses a mixed case with a wrong checksum") and the golden packages/gala-core-sdk/tests/fixtures/writes-signing-golden.json ("vectors.transferGala 0x recipient is sent as checksummed eth|").
  • eth|0x<hex> is not an address form. It is REFUSED with INVALID_ETH_USER_REF_PREFIX before anything is signed or sent, for a write recipient, the writer identity, a lock authority and a read owner. Enforced by packages/gala-core-sdk/tests/writes.test.ts ("refuses eth|0x for each, before anything is signed or sent") and packages/gala-core-sdk/tests/reads.test.ts ("refuses eth|0x owners as INVALID_ETH_USER_REF_PREFIX before anything is sent").
  • Amounts are whole tokens as a decimal string, signed exactly as written. A JS number, an exponent, a sign, hex or a zero quantity is refused with INVALID_QUANTITY. Enforced by packages/gala-core-sdk/tests/writes.test.ts ("signs quantities exactly as written and refuses any other spelling").
  • A token is named by a four-part class key (collection|category|type|additionalKey) or by tokenName through a resolver; a bare symbol is never a class key and none is derived from it. Enforced by packages/gala-core-sdk/tests/writes.test.ts ("names a token by class key (pipe with exactly four parts, $ from the right, object) or by name").
  • A minted uniqueKey is at most 64 characters with no |; a key you supply is 1 to 128 characters of A-Za-z0-9._:- and is refused before signing otherwise. Enforced by packages/gala-core-sdk/tests/writes.test.ts ("mints : at most 64 characters, no "|", a fresh key per write") and packages/gala-core-sdk/tests/writes.test.ts ("keeps a caller key and refuses an invalid one before signing").
  • A write whose outcome is unknown is never retried under a fresh key: the error carries the uniqueKey to check. Enforced by packages/gala-core-sdk/tests/writes.test.ts ("a timeout is GATEWAY_WRITE_OUTCOME_UNKNOWN (timeout) carrying the uniqueKey, never a retryable REQUEST_TIMEOUT").
  • The signing helpers work on a private copy of the key bytes and zero it when the signing action finishes, on success and when it throws; a PrivateKeySigner itself holds its key for its lifetime, so keep it out of logs and tool arguments. Enforced by packages/gala-core-sdk/tests/chain_dto_signing.test.ts ("zeroes its private copy of the key, on success and when the action throws").

Error codes​

Branch on error.code (stable), never on error.message. isGalaError(error, 'CODE') narrows the error and its params.

CodeMeaningFix
INVALID_ETH_USER_REF_PREFIXAn address was written eth|0x<hex>.Write eth|<EIP-55 digits> or the bare 0x<hex>; error.params.expected is the correct spelling.
INVALID_WALLET_ADDRESSA wallet address, recipient or owner is not an accepted form; params.field names it.Use eth|<EIP-55>, a checksummed 0x<hex> or a client| alias.
INVALID_QUANTITYA write quantity is not a plain positive decimal string.Pass whole tokens as a string such as '1.5'.
INVALID_TOKEN_REFERENCEA token is named badly (missing, ambiguous, a bare symbol).Pass the four-part class key, or tokenName with a resolver.
INVALID_UNIQUE_KEYA uniqueKey outside 1 to 128 characters of A-Za-z0-9._:-.Omit it to mint one, or mint with createUniqueKey.
SIGNING_FAILEDThe signer threw before anything was sent (a locked wallet, a declined prompt).Nothing was sent; retry once the wallet is unlocked or approved.
INVALID_PRIVATE_KEYA key is empty, the wrong length, mis-encoded or off the curve; params.reason says which.Supply 32 bytes as 64 hex digits.
GATEWAY_WRITE_OUTCOME_UNKNOWNThe write may have been applied (timeout or lost connection).Check error.uniqueKey on chain before any retry.
GATEWAY_WRITE_REJECTEDGalaChain refused the write (Status other than 1).Read error.body for the chain's reason; fix the input.
REQUEST_TIMEOUTA read exceeded its timeout.Retry the read; reads are safe to repeat.
TRANSPORT_ERRORA read failed before any answer (offline, DNS, CORS).Retry the read after connectivity returns.
CSPRNG_UNAVAILABLENo crypto.getRandomValues (React Native without the polyfill).Import react-native-get-random-values first.