The Anchor Server is the SDK that helps developers build and operate anchor services. It provides the server-side infrastructure to run an anchor that other users can discover and interact with.
Purpose
The Anchor Server SDK provides:
HTTP server infrastructure with built-in request routing
Request validation and authentication
Signature verification for client requests
Metadata publishing for service discovery
Queue management for async operations
Error handling with standardized error responses
Who Uses It
Anchor Servers are operated by:
Financial institutions providing fiat on/off ramps
Liquidity providers offering FX services
Bridge operators enabling cross-chain transfers
KYC providers offering identity verification
Payment processors facilitating settlements
Advantages of the SDK
Building an anchor from scratch requires handling many complex concerns:
Secure authentication and signature verification
Service metadata formatting and publishing
Request/response validation
Asynchronous operation management
Error handling and status codes
The Anchor Server SDK handles all of this, letting you focus on your business logic. It provides:
Standardized APIs: All anchors implement the same interfaces, making integration easier for clients
Security: Built-in signature verification and authentication
Reliability: Queue management and retry logic for async operations
Discoverability: Automatic metadata publishing for the Resolver
Compliance: Structured patterns for KYC and regulatory requirements
#! /usr/bin/env ts-node
/*
* Description: Example of using the Keeta Anchor FX HTTP Server.
* This example starts a very basic FX Server that provides the necessary endpoints
* The KeetaNetFXAnchorHTTPServer class includes logic for handling the swap blocks and publishing
* The user is responsible for providing the configuration including a method for getConversionRateAndFee
*/
import * as KeetaAnchor from '@keetanetwork/anchor';
import { KeetaNetFXAnchorHTTPServer } from '@keetanetwork/anchor/services/fx/server.js';
const Account = KeetaAnchor.KeetaNet.lib.Account;
const DEBUG = false;
const logger = DEBUG ? { logger: console } : {};
const network = 'test';
async function main() {
/**
* Generate a random seed to create a `liqudityProvider` ccount which holds the FX Anchor liquidity
* The liquidy provider should be granted access to the token pairs it will be managing
* Funds should be added separately
*/
const seed = Account.generateRandomSeed({ asString: true });
const liquidityProvider = Account.fromSeed(seed, 0);
console.log(`Seed: ${seed}`);
console.log(`Liquidity FX Provider: ${liquidityProvider.publicKeyString.get()}`);
await using liquidityProviderUserClient = KeetaAnchor.KeetaNet.UserClient.fromNetwork(network, liquidityProvider);
// Setup FX Anchor HTTP Server
const fxServer = new KeetaNetFXAnchorHTTPServer({
...logger,
account: liquidityProvider,
quoteSigner: liquidityProvider,
client: liquidityProviderUserClient,
fx: {
/**
* Example function to calculate the rate
* This is purely an example and should not be used in a production scenario
* Decimals for both tokens involved in the swap should be consider
* As well as any other factors that should affect the conversion rate
* like external pricing, constant product formulae etc
*/
getConversionRateAndFee: async function(request) {
// Determine how much precision to use in the conversion
const rateDecimals = 8;
/**
* Rate to use for the conversion. This is purely an example
* Real usage should manage the rate either through recent trades or external sources
* Consideration should also be given on how to handle the rate related to the number of decimals each token has
*/
let rate = 0.88;
// Affinity could be 'from' or 'to' and can change which direction the rate should be calculated
if (request.affinity === 'to') {
rate = 1 / rate;
}
/**
* Convert the request amount to bigint
* Multiple the rate by the number of decimals for the token so we can do bigint math
* This should look at the actual decimals for the tokens in the request
*/
const scaledDecimalRate = Math.round((rate * (10 ** rateDecimals)));
const convertedAmount = (BigInt(request.amount) * BigInt(scaledDecimalRate)) / BigInt((10 ** rateDecimals));
return({
account: liquidityProvider,
convertedAmount: convertedAmount,
cost: {
amount: 0n,
token: liquidityProviderUserClient.baseToken
}
});
}
}
});
// Start the HTTP Server - getEstimate, getQuote, createExchange and getExchangeStatus endpoints are already defined in the KeetaNetFxAnchorHTTPServer class
await fxServer.start();
const fxServerURL = fxServer.url;
console.log(`FX Server Start at ${fxServerURL}`);
console.log('Use Ctrl+C to Stop the Server');
// Run the shutdown logic
await new Promise<void>((resolve) => {
process.on('SIGINT', async () => {
console.log('\nReceived Ctrl+C (SIGINT)');
console.log('Shutting down server...');
await fxServer.stop();
resolve();
});
});
}
main().then(function() {
process.exit(0);
}, function(err: unknown) {
console.error(err);
process.exit(1);
});