Extending FractaLedger
Extending FractaLedger
FractaLedger is designed to be highly extensible. This guide explains how to customize and extend the system to meet your specific requirements.
Overview
FractaLedger has been designed to be published as an npm package while allowing users to extend and customize it without modifying files in the node_modules
directory. This is important because any changes to files in node_modules
would be lost when updating the package.
The extension system follows these key principles:
- Non-invasive: All customizations are made outside the
node_modules
directory - Template-based: CLI tools generate starter templates for your custom implementations
- Configuration-driven: Your custom implementations are connected to the system through configuration
- Modular: You can extend specific parts of the system without affecting others

┌─────────────────────────────────────────────────────────────┐ │ Your Project │ │ │ │ ┌───────────────┐ ┌───────────────┐ ┌──────────────┐ │ │ │ Custom │ │ Custom API │ │ Custom │ │ │ │ Transceivers │ │ Extensions │ │ Chaincode │ │ │ └───────────────┘ └───────────────┘ └──────────────┘ │ │ │ │ │ ┌────────┴─────────┐ │ │ │ Configuration │ │ │ └────────┬─────────┘ │ │ │ │ └──────────────────────────────┼──────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ node_modules │ │ │ │ ┌───────────────┐ ┌───────────────┐ ┌──────────────┐ │ │ │ FractaLedger │ │ Core │ │ Extension │ │ │ │ Core │───▶│ Components │───▶│ System │ │ │ └───────────────┘ └───────────────┘ └──────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘
Extension Process
Here's the step-by-step process to extend FractaLedger:
Install FractaLedger
npm install fractaledger
Initialize a New FractaLedger Project
npx fractaledger-init --dir my-fractaledger-project
This command creates a new project with the necessary directory structure and configuration files.
Navigate to Your Project Directory
cd my-fractaledger-project
Generate Custom Implementations
Use the provided CLI tools to generate templates for your custom implementations:
For Transceivers:
npx fractaledger-generate-transceiver --type bitcoin --output ./my-transceivers
For API Extensions:
npx fractaledger-generate-api-extension --type basic --output ./my-extensions --name my-custom-extension
For Configuration:
npx fractaledger-generate-config --output ./fractaledger.json
Customize the Generated Files
Edit the generated files to implement your specific business logic and requirements.
Update Your Configuration
Update your fractaledger.json
file to point to your custom implementations:
{
"bitcoin": [
{
"name": "btc_wallet_1",
"network": "mainnet",
"walletAddress": "bc1q...",
"secretEnvVar": "BTC_WALLET_1_SECRET",
"transceiver": {
"method": "callback",
"module": "./my-transceivers/bitcoin-transceiver.js",
"config": {
"apiUrl": "https://blockstream.info/api",
"monitoringInterval": 60000
}
}
}
],
"api": {
"port": 3000,
"host": "localhost",
"extensions": [
{
"module": "./my-extensions/my-custom-extension.js"
}
]
}
}
Extension Points
FractaLedger provides several extension points that allow you to customize the system to meet your specific requirements.
Transceivers
Transceivers handle blockchain interactions, including transaction broadcasting and wallet monitoring. They serve as the bridge between FractaLedger and the blockchain networks.
What You Can Customize
- Transaction broadcasting logic
- Wallet address monitoring
- Balance retrieval
- Transaction history retrieval
- UTXO (Unspent Transaction Output) management
- Blockchain API integration
How to Implement
Create a custom transceiver by implementing the UTXOTransceiver
interface:
const { UTXOTransceiver } = require('fractaledger');
class MyCustomTransceiver extends UTXOTransceiver {
constructor(config = {}) {
super(config);
// Initialize your transceiver
this.apiUrl = config.apiUrl || 'https://your-blockchain-api.com';
this.monitoringIntervals = {};
this.processedTransactions = new Set();
}
async broadcastTransaction(txHex, metadata = {}) {
// Implement transaction broadcasting logic
// ...
return { success: true, txid: '...' };
}
async startMonitoring(address, options = {}) {
// Implement wallet monitoring logic
// ...
return { success: true };
}
async stopMonitoring(address) {
// Implement logic to stop monitoring
// ...
return { success: true };
}
async getBalance(address) {
// Implement balance retrieval logic
// ...
return 1.5; // Balance in BTC/LTC/etc.
}
async getTransactionHistory(address, options = {}) {
// Implement transaction history retrieval
// ...
return []; // Array of transactions
}
async getUTXOs(address) {
// Implement UTXO retrieval logic
// ...
return []; // Array of UTXOs
}
}
module.exports = MyCustomTransceiver;
Configuration
In your fractaledger.json
file, reference your custom transceiver:
"bitcoin": [
{
"name": "btc_wallet_1",
"network": "mainnet",
"walletAddress": "bc1q...",
"secretEnvVar": "BTC_WALLET_1_SECRET",
"transceiver": {
"method": "callback",
"module": "./my-transceivers/my-custom-transceiver.js",
"config": {
"apiUrl": "https://your-blockchain-api.com",
"monitoringInterval": 60000
}
}
}
]
API Extensions
API extensions allow you to add custom endpoints to the FractaLedger API server. This is useful for implementing domain-specific functionality or integrating with other systems.
What You Can Customize
- Custom API endpoints
- Domain-specific business logic
- Integration with external systems
- Custom authentication and authorization
- Specialized data processing
How to Implement
Create a custom API extension:
/**
* My Custom API Extension
*
* This extension adds endpoints for a custom feature.
*/
/**
* Register the custom extension with the API server
* @param {Object} app The Express app instance
* @param {Function} authenticateJWT The authentication middleware
* @param {Object} dependencies Additional dependencies (walletManager, fabricClient, etc.)
*/
function registerMyExtension(app, authenticateJWT, dependencies) {
const { walletManager, fabricClient } = dependencies;
/**
* Create a custom feature
* POST /api/custom-feature
*/
app.post('/api/custom-feature', authenticateJWT, async (req, res) => {
try {
const { name, config } = req.body;
if (!name) {
return res.status(400).json({ error: 'Missing required parameters' });
}
// Implement your custom logic here
// ...
res.json({ success: true, name, config });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
/**
* Get custom features
* GET /api/custom-features
*/
app.get('/api/custom-features', authenticateJWT, async (req, res) => {
try {
// Implement your custom logic here
// ...
res.json([]);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
}
module.exports = registerMyExtension;
Configuration
In your fractaledger.json
file, register your API extension:
"api": {
"port": 3000,
"host": "localhost",
"extensions": [
{
"module": "./my-extensions/my-custom-extension.js"
}
]
}
Chaincode (Smart Contracts)
Chaincode is the smart contract code that runs on the Hyperledger Fabric network. It defines the business logic for your FractaLedger application.
What You Can Customize
- Fund distribution rules
- Fee structures
- Withdrawal logic
- Transaction frequency controls
- Custom business logic
How to Implement
Create a custom chaincode based on one of the provided templates:
- Create a directory for your custom chaincode:
mkdir -p src/chaincode/custom/my-custom-chaincode
- Copy files from a template:
cp -r src/chaincode/templates/merchant-fee/* src/chaincode/custom/my-custom-chaincode/
- Customize the chaincode files to implement your business logic.
Example of a custom chaincode implementation:
const { Contract } = require('fabric-contract-api');
class MyCustomContract extends Contract {
async initLedger(ctx) {
// Initialize the ledger
// ...
}
async createInternalWallet(ctx, id, primaryWalletId, description, metadata) {
// Create an internal wallet
// ...
}
async internalTransfer(ctx, fromWalletId, toWalletId, amount, memo) {
// Implement internal transfer logic
// ...
}
async processCustomTransaction(ctx, fromWalletId, toWalletId, amount, customData) {
// Implement your custom transaction logic
// ...
}
}
module.exports = MyCustomContract;
Configuration
In your fractaledger.json
file, reference your custom chaincode:
"hyperledger": {
"chaincodePath": "./src/chaincode/custom/my-custom-chaincode",
"channelName": "fractaledger-channel",
"chaincodeName": "fractaledger-chaincode"
}
Configuration
The configuration file (fractaledger.json
) controls how FractaLedger connects to blockchains and manages wallets.
What You Can Customize
- Blockchain connection details
- Wallet configurations
- Transceiver methods
- Balance reconciliation settings
- Base internal wallet settings
- API server configuration
Example Configuration
Here's a comprehensive example of a fractaledger.json
file:
{
"bitcoin": [
{
"name": "btc_wallet_1",
"network": "mainnet",
"walletAddress": "bc1q...",
"secretEnvVar": "BTC_WALLET_1_SECRET",
"transceiver": {
"method": "callback",
"module": "./my-transceivers/bitcoin-transceiver.js",
"config": {
"apiUrl": "https://blockstream.info/api",
"monitoringInterval": 60000,
"autoMonitor": true
}
}
}
],
"api": {
"port": 3000,
"host": "localhost",
"extensions": [
{
"module": "./my-extensions/my-custom-extension.js"
}
]
},
"baseInternalWallet": {
"namePrefix": "base_wallet_",
"description": "Represents excess funds in the primary on-chain wallet",
"createOnInitialization": true
},
"balanceReconciliation": {
"strategy": "afterTransaction",
"scheduledFrequency": 3600000,
"warningThreshold": 0.00001,
"strictMode": false
}
}
Multiple Blockchain Support
FractaLedger is designed to support multiple UTXO-based blockchains simultaneously. You can create and configure transceivers for different blockchains like Bitcoin, Litecoin, and Dogecoin.
Setting Up Multiple Blockchain Transceivers
Generate transceiver implementations for each blockchain type:
# Generate Bitcoin transceiver
npx fractaledger-generate-transceiver --type bitcoin --output ./my-transceivers
# Generate Litecoin transceiver
npx fractaledger-generate-transceiver --type litecoin --output ./my-transceivers
# Generate Dogecoin transceiver
npx fractaledger-generate-transceiver --type dogecoin --output ./my-transceivers
This will create three separate transceiver files:
./my-transceivers/bitcoin-transceiver.js
./my-transceivers/litecoin-transceiver.js
./my-transceivers/dogecoin-transceiver.js
Customizing Each Transceiver
Each generated file will be pre-configured with blockchain-specific settings:
- Bitcoin transceiver will use
https://blockstream.info/api
as the default API URL - Litecoin transceiver will use
https://ltc.bitaps.com/api/v1/blockchain
- Dogecoin transceiver will use
https://dogechain.info/api/v1
You'll need to customize each transceiver file to implement the specific logic needed for that blockchain, addressing differences in:
- API endpoints and formats
- Transaction structure and serialization
- Address formats and validation
- Fee calculation
- Network-specific features
Configuration for Multiple Blockchains
In your fractaledger.json
configuration file, reference each transceiver in the appropriate blockchain section:
{
"bitcoin": [
{
"name": "btc_wallet_1",
"network": "mainnet",
"walletAddress": "bc1q...",
"secretEnvVar": "BTC_WALLET_1_SECRET",
"transceiver": {
"method": "callback",
"module": "./my-transceivers/bitcoin-transceiver.js",
"config": {
"apiUrl": "https://blockstream.info/api",
"monitoringInterval": 60000,
"autoMonitor": true
}
}
}
],
"litecoin": [
{
"name": "ltc_wallet_1",
"network": "mainnet",
"walletAddress": "ltc1q...",
"secretEnvVar": "LTC_WALLET_1_SECRET",
"transceiver": {
"method": "callback",
"module": "./my-transceivers/litecoin-transceiver.js",
"config": {
"apiUrl": "https://ltc.bitaps.com/api/v1/blockchain",
"monitoringInterval": 60000,
"autoMonitor": true
}
}
}
],
"dogecoin": [
{
"name": "doge_wallet_1",
"network": "mainnet",
"walletAddress": "D...",
"secretEnvVar": "DOGE_WALLET_1_SECRET",
"transceiver": {
"method": "callback",
"module": "./my-transceivers/dogecoin-transceiver.js",
"config": {
"apiUrl": "https://dogechain.info/api/v1",
"monitoringInterval": 60000,
"autoMonitor": true
}
}
}
]
}
How It Works Internally
When FractaLedger starts up, it:
- Reads the configuration file
- For each blockchain section (bitcoin, litecoin, dogecoin), it loads the specified wallets
- For each wallet, it initializes the appropriate transceiver based on the configuration
- The transceiver manager maintains separate instances for each blockchain and wallet
This architecture allows FractaLedger to interact with multiple blockchains simultaneously, each with its own set of wallets and transceivers.
Wallet-Specific Business Logic
FractaLedger uses a specific architecture to handle wallet-specific business logic. Understanding this architecture is important for implementing custom business rules for different wallets.
Chaincode and Wallet Relationship
FractaLedger uses a single Hyperledger Fabric network with one deployed chaincode that handles all the business logic for the entire system. This chaincode is not directly linked to specific wallets on a one-to-one basis. Instead:
- The chaincode contains the logic for all operations (transfers, fee calculations, etc.)
- When operations are performed, the specific wallets involved are passed as parameters to the chaincode functions
This means you don't need to deploy separate chaincodes for each wallet. Rather, you customize a single chaincode that handles all your business logic, and that chaincode processes transactions for all wallets in the system.
API Extensions for Wallet-Specific Logic
To implement wallet-specific business logic, FractaLedger uses API Extensions. These extensions:
- Add custom API endpoints to the system
- Implement business logic that can be specific to certain wallets or wallet types
- Call the appropriate chaincode functions with the right parameters
For example, in the merchant-fee-extension.js, you can see endpoints like:
/api/fee-config
- For setting up fee configurations/api/internal-wallets/:id/fund
- For funding specific internal wallets/api/transactions/merchant
- For processing merchant transactions with specific wallets
Implementing Wallet-Specific Logic
To implement wallet-specific logic, you have two main options:
Option 1: Single Chaincode with Comprehensive Logic
Create a custom chaincode that includes logic for different wallet types, with functions that determine which logic to apply based on wallet metadata or transaction parameters.
// Example of a comprehensive chaincode function
async function processTransaction(ctx, fromWalletId, toWalletId, amount, transactionType) {
if (transactionType === 'merchant') {
// Apply merchant fee logic
// ...
} else if (transactionType === 'payroll') {
// Apply payroll logic
// ...
}
// ...
}
Option 2: Multiple Instances
Run separate FractaLedger instances, each with its own configuration, Hyperledger Fabric network, and deployed chaincode. This approach provides complete separation between different business domains but is more complex to manage.
Recommendation
For most use cases, Option 1 (single instance with comprehensive chaincode) is recommended because:
- It's simpler to manage
- It allows internal transfers between all wallets
- It provides a unified view of all transactions
- It's more resource-efficient
Only use Option 2 if you have strict separation requirements or vastly different business domains that cannot coexist in the same chaincode.
Best Practices
Follow these best practices when extending FractaLedger:
Keep Custom Files Outside node_modules
All customizations should be in your project directory, not in the node_modules
directory. This ensures your customizations won't be lost when updating the package.
Use Relative Paths in Configuration
Your configuration file should point to your custom files using relative paths. This makes your project more portable and easier to deploy.
Follow the Provided Templates
Use the generated templates as a starting point for your customizations. They provide the correct structure and interface implementations.
Test Thoroughly
Make sure your custom implementations work correctly before deploying to production. FractaLedger provides testing utilities to help you test your customizations.
Implement Error Handling
Add proper error handling to your custom implementations. This helps with debugging and ensures your application can recover from failures.
Use Environment Variables for Secrets
Never hardcode sensitive information like API keys or private keys in your code. Use environment variables and reference them in your configuration.
Document Your Extensions
Add comments and documentation to your custom implementations. This helps other developers understand your code and makes maintenance easier.
Use Automatic Monitoring
When implementing custom transceivers, use the autoMonitor
configuration option to automatically start monitoring wallet addresses when the system starts up.
Implement Reconnection Logic
When using real-time connections to blockchain nodes or services, implement robust reconnection logic to handle network interruptions.
Use Balance Reconciliation
Enable the balance reconciliation feature to ensure that your internal wallet balances stay in sync with the actual on-chain balances.