TypeScript SDK for interacting with Grainlify Soroban smart contracts on the Stellar network.
Detailed documentation for the SDK is available in the docs/ directory:
npm install @grainlify/contracts-sdk- Type-safe contract interactions
- Comprehensive error handling with typed errors
- Network error detection and reporting
- Input validation
- Support for ProgramEscrow and BountyEscrow lifecycle, claim, refund, batch, and query functions
See sdk/examples/bounty-lock-release.ts for a full walkthrough of the bounty lifecycle.
import { BountyEscrowClient } from '@grainlify/contracts-sdk';
import { Keypair } from '@stellar/stellar-sdk';
const client = new BountyEscrowClient({
contractId: 'YOUR_CONTRACT_ID',
rpcUrl: 'https://soroban-testnet.stellar.org',
networkPassphrase: 'Test SDF Network ; September 2015'
});
const sourceKeypair = Keypair.fromSecret('YOUR_SECRET_KEY');
await client.lockFunds(
'GDEPOSITOR...',
1n, // Bounty ID
10000000n, // Amount
Math.floor(Date.now() / 1000) + 86400, // Deadline
sourceKeypair
);The bounty client wraps the core contract lifecycle:
lockFunds,releaseFunds,partialRelease,refundapproveRefund,setClaimWindow,authorizeClaim,claim,cancelPendingClaimbatchLockFunds,batchReleaseFundsgetEscrowInfo,getPendingClaim,getRefundHistory,getRefundEligibilityqueryEscrowsByStatus,queryEscrowsByAmount,queryEscrowsByDeadline,queryEscrowsByDepositor,queryEscrows,queryExpiringBountiesgetAggregateStats,getEscrowCount,getEscrowIdsByStatus,getFeeConfig,getPauseFlags
State-changing methods require a signing Keypair; read/query methods do not.
await client.setClaimWindow(3600, sourceKeypair);
await client.authorizeClaim(1n, 'GRECIPIENT...', sourceKeypair);
const claim = await client.getPendingClaim(1n);
const recipientKeypair = Keypair.fromSecret('RECIPIENT_SECRET_KEY');
await client.claim(claim.bounty_id, recipientKeypair);
await client.approveRefund(2n, 5000000n, 'GDEPOSITOR...', 'Partial', sourceKeypair);
const eligibility = await client.getRefundEligibility(2n);import { ProgramEscrowClient } from '@grainlify/contracts-sdk';
const client = new ProgramEscrowClient({
contractId: 'YOUR_CONTRACT_ID',
rpcUrl: 'https://soroban-testnet.stellar.org',
networkPassphrase: 'Test SDF Network ; September 2015'
});import { Keypair } from '@stellar/stellar-sdk';
const sourceKeypair = Keypair.fromSecret('YOUR_SECRET_KEY');
try {
const programData = await client.initProgram(
'my-program-id',
'GAUTHORIZED_PAYOUT_KEY...',
'GTOKEN_ADDRESS...',
sourceKeypair
);
console.log('Program initialized:', programData);
} catch (error) {
if (error instanceof ValidationError) {
console.error('Invalid input:', error.message);
} else if (error instanceof ContractError) {
console.error('Contract error:', error.code, error.message);
} else if (error instanceof NetworkError) {
console.error('Network error:', error.statusCode, error.message);
}
}try {
const programData = await client.lockProgramFunds(
10000000n, // Amount in stroops
sourceKeypair
);
console.log('Funds locked. Remaining balance:', programData.remaining_balance);
} catch (error) {
// Handle errors
}const recipients = [
'GRECIPIENT1...',
'GRECIPIENT2...',
'GRECIPIENT3...'
];
const amounts = [
1000000n,
2000000n,
1500000n
];
try {
const programData = await client.batchPayout(
recipients,
amounts,
sourceKeypair
);
console.log('Batch payout completed');
} catch (error) {
// Handle errors
}try {
const programData = await client.getProgramInfo();
console.log('Program ID:', programData.program_id);
console.log('Total funds:', programData.total_funds);
console.log('Remaining balance:', programData.remaining_balance);
} catch (error) {
// Handle errors
}The SDK provides three main error types:
Thrown when input parameters are invalid before making a contract call.
import { ValidationError } from '@grainlify/contracts-sdk';
try {
await client.lockProgramFunds(0n, keypair); // Invalid: amount must be > 0
} catch (error) {
if (error instanceof ValidationError) {
console.error('Field:', error.field);
console.error('Message:', error.message);
}
}Thrown when the smart contract returns an error.
import { ContractError, ContractErrorCode } from '@grainlify/contracts-sdk';
try {
await client.singlePayout(recipient, amount, keypair);
} catch (error) {
if (error instanceof ContractError) {
switch (error.code) {
case ContractErrorCode.NOT_INITIALIZED:
console.error('Program not initialized');
break;
case ContractErrorCode.UNAUTHORIZED:
console.error('Unauthorized access');
break;
case ContractErrorCode.INSUFFICIENT_BALANCE:
console.error('Insufficient balance');
break;
// ... handle other cases
}
}
}Thrown when there are network or transport issues.
import { NetworkError } from '@grainlify/contracts-sdk';
try {
await client.getProgramInfo();
} catch (error) {
if (error instanceof NetworkError) {
console.error('Status code:', error.statusCode);
console.error('Cause:', error.cause);
// Implement retry logic
}
}NOT_INITIALIZED- Program not initializedUNAUTHORIZED- Caller does not have permissionINSUFFICIENT_BALANCE- Insufficient balance for operationINVALID_AMOUNT- Amount must be greater than zeroALREADY_INITIALIZED- Program already initializedEMPTY_BATCH- Cannot process empty batchLENGTH_MISMATCH- Recipients and amounts arrays must matchOVERFLOW- Payout amount overflowGOVERNANCE_VERSION_TOO_LOW- Linked governance contract version is below the program escrow minimumBOUNTY_AMOUNT_BELOW_MINIMUM- Bounty amount is below the configured policy minimumBOUNTY_AMOUNT_ABOVE_MAXIMUM- Bounty amount is above the configured policy maximumBOUNTY_CIRCUIT_BREAKER_OPEN- Bounty escrow circuit breaker is openBOUNTY_GOVERNANCE_VERSION_TOO_LOW- Linked governance contract version is below the bounty escrow minimum
Run the test suite:
npm testRun tests with coverage:
npm run test:coverageThe SDK includes comprehensive error handling tests covering:
- Input validation errors
- Contract error parsing and mapping
- Network and transport errors
- HTTP status code handling
- Error type hierarchy and properties
- Error recovery scenarios
See src/__tests__/error-handling.test.ts and src/__tests__/network-errors.test.ts for detailed test cases.
Build the SDK:
npm run buildRegenerate the SDK API reference:
npm run docsWatch mode for tests:
npm run test:watchMIT