@fairmint/canton-node-sdk
Version:
Canton Node SDK
418 lines • 19.1 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.EnvLoader = void 0;
const dotenv_1 = require("dotenv");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const errors_1 = require("../errors");
// Load environment variables with fallback to parent directory
const currentEnvPath = '.env';
const parentEnvPath = path.join('..', '.env');
// Try to load from current directory first
let result = (0, dotenv_1.config)({ path: currentEnvPath });
// If no .env file found in current directory, try parent directory
if (result.error && fs.existsSync(parentEnvPath)) {
result = (0, dotenv_1.config)({ path: parentEnvPath });
}
/** Singleton class for managing environment variables and configuration */
class EnvLoader {
constructor(options = {}) {
this.env = process.env;
this.options = options;
}
static getInstance(options = {}) {
if (!EnvLoader.instance) {
EnvLoader.instance = new EnvLoader(options);
}
else if (options.currentNetwork || options.currentProvider) {
// Update existing instance with new options
EnvLoader.instance.options = { ...EnvLoader.instance.options, ...options };
}
return EnvLoader.instance;
}
static resetInstance() {
EnvLoader.instance = undefined;
}
/**
* Get configuration for a specific API type from environment variables
*
* @param apiType The API type to get configuration for
* @param options Optional network and provider to use instead of reading from env
* @returns ClientConfig with only the specified API configured
*/
static getConfig(apiType, options) {
const envLoader = EnvLoader.getInstance();
// Determine which values to use - prioritize options over environment
const network = options?.network ?? envLoader.getCurrentNetwork();
// Get provider, with special handling for localnet
let provider = options?.provider;
if (!provider) {
// For localnet, default to 'app-provider' if not specified
if (network === 'localnet') {
provider = 'app-provider';
}
else {
// For other networks, require provider from environment
provider = envLoader.getCurrentProvider();
}
}
if (!provider) {
throw new errors_1.ConfigurationError(`Provider is required for ${apiType}. Either specify it in options or set CANTON_CURRENT_PROVIDER in environment.`);
}
const authUrl = envLoader.getAuthUrl(network, provider);
// Get API-specific configuration using the determined network and provider
const apiConfig = envLoader.loadApiConfig(apiType, network, provider || undefined);
if (!apiConfig) {
const providerStr = provider ? provider.toUpperCase() : 'PROVIDER';
throw new errors_1.ConfigurationError(`Missing required environment variables for ${apiType}. ` +
`Required: CANTON_${network.toUpperCase()}_${providerStr}_${apiType.toUpperCase()}_URI, ` +
`CANTON_${network.toUpperCase()}_${providerStr}_${apiType.toUpperCase()}_CLIENT_ID, ` +
`and either CLIENT_SECRET (for client_credentials) or USERNAME/PASSWORD (for password grant)`);
}
const clientConfig = {
network,
apis: {
[apiType]: apiConfig,
},
};
clientConfig.provider = provider;
clientConfig.authUrl = authUrl;
return clientConfig;
}
/**
* Get a summary of the environment variables being used for a specific configuration This is useful for debugging
* configuration issues
*/
static getConfigSummary(apiType, options) {
const envLoader = EnvLoader.getInstance();
const network = options?.network ?? envLoader.getCurrentNetwork();
const provider = options?.provider ?? envLoader.getCurrentProvider();
const envVars = {};
const missingVars = [];
if (provider) {
const upperNetwork = network.toUpperCase();
const upperProvider = provider.toUpperCase();
const baseKey = `CANTON_${upperNetwork}_${upperProvider}`;
// API-specific variables
envVars[`${baseKey}_${apiType.toUpperCase()}_URI`] = envLoader.env[`${baseKey}_${apiType.toUpperCase()}_URI`];
envVars[`${baseKey}_${apiType.toUpperCase()}_CLIENT_ID`] =
envLoader.env[`${baseKey}_${apiType.toUpperCase()}_CLIENT_ID`];
envVars[`${baseKey}_${apiType.toUpperCase()}_CLIENT_SECRET`] =
envLoader.env[`${baseKey}_${apiType.toUpperCase()}_CLIENT_SECRET`];
envVars[`${baseKey}_${apiType.toUpperCase()}_USERNAME`] =
envLoader.env[`${baseKey}_${apiType.toUpperCase()}_USERNAME`];
envVars[`${baseKey}_${apiType.toUpperCase()}_PASSWORD`] =
envLoader.env[`${baseKey}_${apiType.toUpperCase()}_PASSWORD`];
// Common variables
envVars[`${baseKey}_AUTH_URL`] = envLoader.env[`${baseKey}_AUTH_URL`];
envVars[`${baseKey}_PARTY_ID`] = envLoader.env[`${baseKey}_PARTY_ID`];
envVars[`${baseKey}_USER_ID`] = envLoader.env[`${baseKey}_USER_ID`];
if (!envVars[`${baseKey}_${apiType.toUpperCase()}_URI`]) {
missingVars.push(`${baseKey}_${apiType.toUpperCase()}_URI`);
}
if (!envVars[`${baseKey}_${apiType.toUpperCase()}_CLIENT_ID`]) {
missingVars.push(`${baseKey}_${apiType.toUpperCase()}_CLIENT_ID`);
}
if (!envVars[`${baseKey}_AUTH_URL`]) {
missingVars.push(`${baseKey}_AUTH_URL`);
}
}
// Add template and contract IDs to the summary
const walletTemplateKey = `CANTON_WALLET_TEMPLATE_ID_${network.toUpperCase()}`;
const preapprovalTemplateKey = `CANTON_PREAPPROVAL_TEMPLATE_ID_${network.toUpperCase()}`;
const amuletRulesContractKey = `CANTON_AMULET_RULES_CONTRACT_ID_${network.toUpperCase()}`;
const validatorWalletAppInstallContractKey = `CANTON_VALIDATOR_WALLET_APP_INSTALL_CONTRACT_ID_${network.toUpperCase()}`;
envVars[walletTemplateKey] = envLoader.env[walletTemplateKey];
envVars[preapprovalTemplateKey] = envLoader.env[preapprovalTemplateKey];
envVars[amuletRulesContractKey] = envLoader.env[amuletRulesContractKey];
envVars[validatorWalletAppInstallContractKey] = envLoader.env[validatorWalletAppInstallContractKey];
// Check for missing template and contract variables
if (!envVars[walletTemplateKey]) {
missingVars.push(walletTemplateKey);
}
if (!envVars[preapprovalTemplateKey]) {
missingVars.push(preapprovalTemplateKey);
}
if (!envVars[amuletRulesContractKey]) {
missingVars.push(amuletRulesContractKey);
}
if (!envVars[validatorWalletAppInstallContractKey]) {
missingVars.push(validatorWalletAppInstallContractKey);
}
return {
network,
provider,
envVars,
missingVars,
};
}
getNodeEnv() {
const value = this.env['NODE_ENV'] ?? 'development';
if (!['development', 'production', 'test'].includes(value)) {
throw new errors_1.ConfigurationError(`Invalid NODE_ENV: ${value}. Must be 'development', 'production', or 'test'`);
}
return value;
}
getCurrentNetwork() {
if (this.options.currentNetwork) {
return this.options.currentNetwork;
}
const value = this.env['CANTON_CURRENT_NETWORK']?.toLowerCase();
if (!value || !['devnet', 'testnet', 'mainnet', 'localnet'].includes(value)) {
throw new errors_1.ConfigurationError('Missing or invalid CANTON_CURRENT_NETWORK. Must be "devnet", "testnet", "mainnet", or "localnet"');
}
return value;
}
getCurrentProvider() {
if (this.options.currentProvider) {
return this.options.currentProvider;
}
const value = this.env['CANTON_CURRENT_PROVIDER']?.toLowerCase();
if (!value) {
throw new errors_1.ConfigurationError('Missing or invalid CANTON_CURRENT_PROVIDER');
}
return value;
}
getApiUri(apiType, network, provider) {
const targetNetwork = network ?? this.getCurrentNetwork();
// Special case for APIs that don't require provider-specific configuration
if (apiType === 'LIGHTHOUSE_API') {
const envKey = `CANTON_${targetNetwork.toUpperCase()}_${apiType.toUpperCase()}_URI`;
const uri = this.env[envKey];
return uri;
}
const targetProvider = provider ?? this.getCurrentProvider();
const envKey = `CANTON_${targetNetwork.toUpperCase()}_${targetProvider.toUpperCase()}_${apiType.toUpperCase()}_URI`;
const uri = this.env[envKey];
return uri;
}
getApiClientId(apiType, network, provider) {
const targetNetwork = network ?? this.getCurrentNetwork();
const targetProvider = provider ?? this.getCurrentProvider();
const envKey = `CANTON_${targetNetwork.toUpperCase()}_${targetProvider.toUpperCase()}_${apiType.toUpperCase()}_CLIENT_ID`;
return this.env[envKey];
}
getApiClientSecret(apiType, network, provider) {
const targetNetwork = network ?? this.getCurrentNetwork();
const targetProvider = provider ?? this.getCurrentProvider();
const envKey = `CANTON_${targetNetwork.toUpperCase()}_${targetProvider.toUpperCase()}_${apiType.toUpperCase()}_CLIENT_SECRET`;
return this.env[envKey];
}
getApiUsername(apiType, network, provider) {
const targetNetwork = network ?? this.getCurrentNetwork();
const targetProvider = provider ?? this.getCurrentProvider();
const envKey = `CANTON_${targetNetwork.toUpperCase()}_${targetProvider.toUpperCase()}_${apiType.toUpperCase()}_USERNAME`;
return this.env[envKey];
}
getApiPassword(apiType, network, provider) {
const targetNetwork = network ?? this.getCurrentNetwork();
const targetProvider = provider ?? this.getCurrentProvider();
const envKey = `CANTON_${targetNetwork.toUpperCase()}_${targetProvider.toUpperCase()}_${apiType.toUpperCase()}_PASSWORD`;
return this.env[envKey];
}
getAuthUrl(network, provider) {
const targetNetwork = network ?? this.getCurrentNetwork();
const targetProvider = provider ?? this.getCurrentProvider();
const envKey = `CANTON_${targetNetwork.toUpperCase()}_${targetProvider.toUpperCase()}_AUTH_URL`;
const authUrl = this.env[envKey];
// Provide localnet defaults for cn-quickstart
if (!authUrl && targetNetwork === 'localnet') {
if (targetProvider === 'app-provider') {
return 'http://localhost:8082/realms/AppProvider/protocol/openid-connect/token';
}
if (targetProvider === 'app-user') {
return 'http://localhost:8082/realms/AppUser/protocol/openid-connect/token';
}
}
if (!authUrl) {
throw new errors_1.ConfigurationError(`Missing required environment variable: ${envKey}`);
}
return authUrl;
}
getPartyId(network, provider) {
const targetNetwork = network ?? this.getCurrentNetwork();
const targetProvider = provider ?? this.getCurrentProvider();
const envKey = `CANTON_${targetNetwork.toUpperCase()}_${targetProvider.toUpperCase()}_PARTY_ID`;
const partyId = this.env[envKey];
if (!partyId) {
throw new errors_1.ConfigurationError(`Missing required environment variable: ${envKey}`);
}
return partyId;
}
getUserId(network, provider) {
const targetNetwork = network ?? this.getCurrentNetwork();
const targetProvider = provider ?? this.getCurrentProvider();
const envKey = `CANTON_${targetNetwork.toUpperCase()}_${targetProvider.toUpperCase()}_USER_ID`;
return this.env[envKey];
}
getDatabaseUrl(network) {
const targetNetwork = network ?? this.getCurrentNetwork();
const envKey = `CANTON_${targetNetwork.toUpperCase()}_DATABASE_URL`;
const databaseUrl = this.env[envKey];
if (!databaseUrl) {
throw new errors_1.ConfigurationError(`Missing required environment variable: ${envKey}`);
}
return databaseUrl;
}
getManagedParties(network, provider) {
const targetNetwork = network ?? this.getCurrentNetwork();
const targetProvider = provider ?? this.getCurrentProvider();
const envKey = `CANTON_${targetNetwork.toUpperCase()}_${targetProvider.toUpperCase()}_MANAGED_PARTIES`;
const managedParties = this.env[envKey];
if (!managedParties) {
return [];
}
return managedParties
.split(',')
.map((party) => party.trim())
.filter((party) => party.length > 0);
}
getAmuletRulesContractId(network) {
const targetNetwork = network ?? this.getCurrentNetwork();
const envKey = `CANTON_AMULET_RULES_CONTRACT_ID_${targetNetwork.toUpperCase()}`;
const contractId = this.env[envKey];
if (!contractId) {
throw new errors_1.ConfigurationError(`Missing required environment variable: ${envKey}`);
}
return contractId;
}
getValidatorWalletAppInstallContractId(network) {
const targetNetwork = network ?? this.getCurrentNetwork();
const envKey = `CANTON_VALIDATOR_WALLET_APP_INSTALL_CONTRACT_ID_${targetNetwork.toUpperCase()}`;
const contractId = this.env[envKey];
if (!contractId) {
throw new errors_1.ConfigurationError(`Missing required environment variable: ${envKey}`);
}
return contractId;
}
/**
* Get localnet defaults for cn-quickstart These are the standard OAuth2 credentials and URLs for cn-quickstart with
* OAuth2 enabled
*/
getLocalnetDefaults(apiType, provider) {
if (provider !== 'app-provider' && provider !== 'app-user') {
return undefined;
}
const portPrefix = provider === 'app-provider' ? '3' : '2';
// Default OAuth2 credentials for cn-quickstart
const auth = {
grantType: 'client_credentials',
clientId: `${provider}-validator`,
clientSecret: provider === 'app-provider' ? 'AL8648b9SfdTFImq7FV56Vd0KHifHBuC' : 'k1YVWeC1vjQcqzlqzY98WVxJc6e4IIdZ',
};
// Map API types to their port suffixes
const apiUrlMap = {
VALIDATOR_API: `http://localhost:${portPrefix}903`,
LEDGER_JSON_API: `http://localhost:${portPrefix}975`,
SCAN_API: `http://localhost:4000/api/scan`,
};
const apiUrl = apiUrlMap[apiType];
if (!apiUrl) {
return undefined;
}
return {
apiUrl,
auth,
};
}
loadApiConfig(apiType, network, provider) {
if (!provider) {
return undefined; // Provider must be specified to load this API configuration
}
// For localnet, try defaults first if environment variables are not set
if (network === 'localnet') {
const apiUrl = this.getApiUri(apiType, network, provider);
const clientId = this.getApiClientId(apiType, network, provider);
// If no environment variables are set, use localnet defaults
if (!apiUrl && !clientId) {
return this.getLocalnetDefaults(apiType, provider);
}
}
const apiUrl = this.getApiUri(apiType, network, provider);
const clientId = this.getApiClientId(apiType, network, provider);
const clientSecret = this.getApiClientSecret(apiType, network, provider);
const username = this.getApiUsername(apiType, network, provider);
const password = this.getApiPassword(apiType, network, provider);
const partyId = this.getPartyId(network, provider);
const userId = this.getUserId(network, provider);
if (!apiUrl || !clientId) {
return undefined;
}
// Determine grant type based on available credentials
let grantType;
let auth;
if (clientSecret) {
// Use client_credentials if client secret is available
grantType = 'client_credentials';
auth = {
grantType,
clientId,
clientSecret,
};
}
else if (username && password) {
// Use password grant if username and password are available
grantType = 'password';
auth = {
grantType,
clientId,
username,
password,
};
}
else {
// Fallback to client_credentials without secret (some providers may not require it)
grantType = 'client_credentials';
auth = {
grantType,
clientId,
};
}
const apiConfig = {
apiUrl,
auth,
};
if (partyId) {
apiConfig.partyId = String(partyId);
}
if (userId) {
apiConfig.userId = String(userId);
}
return apiConfig;
}
}
exports.EnvLoader = EnvLoader;
//# sourceMappingURL=EnvLoader.js.map