Exchange - @energyweb/exchange
Overview
The Exchange package is a NestJS application that provides backend services to manage the Exchange's functionality (account management and buying, selling and transferring Energy Attribute Certificates and bundles through an order book system).
Persistence
The Trade SDK uses PostgreSQL for persistence with TypeORM as a database integration library. The application creates a repository for each entity. Entities are defined in the entity.ts files in each module, and are marked with the @Entity decorator. (You can read more about entities in the TypeORM documentation here).
@Entity({ name: `${DB_TABLE_PREFIX}_account` })
export class Account extends ExtendedBaseEntity {
@ApiProperty({ type: String })
@PrimaryGeneratedColumn('uuid')
id: string;
@ApiProperty({ type: String })
@Column()
@IsString()
userId: string;
@ApiProperty({ type: String })
@Column()
@IsString()
address: string;
}
Repositories are injected into services or command handlers so they are available to use in methods:
import { Account } from './account.entity';
@Injectable()
export class AccountService {
private readonly logger = new Logger(AccountService.name);
private readonly requestQueue = new Subject<string>();
constructor(
@InjectRepository(Account)
private readonly repository: Repository<Account>,
private readonly connection: Connection,
private readonly accountDeployerService: AccountDeployerService
) {
this.requestQueue.pipe(concatMap((id) => this.process(id))).subscribe();
}
You can read more about dependency injection in NestJS here. You can read more about using the repository design pattern with TypeORM in NestJS applications here.
Exchange Architecture
This NestJS applicaton is broken down into NestJS modules that manage Exchange functionalities:
- Account
- Account Balance
- Asset
- Bundle
- Demand
- Matching Engine Service
- Order
- Order Book
- Post-for-sale
- Supply
- Trade
- Transfer
Each module contains code relevant for a specific feature. In general, each NestJS module has:
- A controller that manages requests and responses to the client
- A .entity file that maps an entity to a database repository
- A .service file that provides methods to fetch and transform data
- Data Transfer Object (DTO) file(s) that provide Data Transfer Objects, which are representations of the data that are exposed to the endpoint consumer
- A module class that is used by NestJS to structure the application
The below gives an overview the of the package architecture, however the NestJS documentation provides further detail into the fundamentals of NestJS Architecture and TypeORM integration that may help to understand the elements of this application:
- Custom Providers as Services
- Dependency Injection
- CQRS module
- Modules
- NestJS TypeORM Integration
- TypeORM repository design pattern
Modules
Account
The Account module provides services to manage (find, deploy) Exchange Deposit Accounts.
Each organization has one Exchange Deposit Account, which is a deployed instance of the TokenAccount smart contract. Once a user is a member of the organization, they are linked to the organization's exchange deposit address.
The user id and the address of the Exchange Deposit Account are persisted in the Account repository. You can view the Account entity model here.
const address = await this.accountDeployerService.deployAccount();
await this.repository.save({ userId, address });
Reference Implentation
User Guides on Exchange Deposit Account: - Accounts and User Management: Connecting to the Blockchain
Account Balance
The Account Balance module has two services:
1. Account Balance Service
This service provides a method to register assets to a user:
private assetSources = Array<IAccountableAsset>();
public registerAssetSource(source: IAccountableAsset): void {
const { name } = source.constructor;
this.logger.debug(`Registering asset source: ${name}`);
if (this.assetSources.find((i) => i.constructor.name === name)) {
this.logger.debug('Asset source already registered');
return;
}
this.assetSources.push(source);
}
The remaining methods in the class use the assetSources Array to calculate a user's available and locked assets, and to return their current asset amounts.
2. Account Balance Asset Service
This service returns a users locked and available assets. This class is the base for all of the Accounting Services in the Exchange's modules.
Asset
Assets represent Energy Attribute Certificates that are active (tradeable) on the exchange. Every asset is tied to one specific device and a specific generation time frame. The Asset module provides services to fetch and create assets.
Assets are persisted in the Asset repository. Because each assest represents an on-chain EAC, each asset has a blockchain address. It also has a tokenId that originates from the certificate that was granted by the issuing body. You can view the Asset entity here.
Bundle
Bundles are products that are compiled from a number of different EACs that are offered to buyers as one entity. You can read more about bundles in the glossary here. The Bundle module provides services for:
- CRUD operations for a user’s bundles (creating, retrieving, updating, cancelling)
- Trading (buying/trading) bundles on the exchange
There are two repositories pertaining to bundles:
- Bundle Repository: Persists information related to a bundle (creating, cancelling, updating status of bundles).
- Bundle Trade Repository: Persists information related to bundle trade (buying and selling). Remember that bundles are not sold via the order book on the exchange; they must be purchased directly.
Bundle Accounting Service
The Bundle Accounting Service extends the Account Balance Asset Service. The public methods return the locked and available bundle assets for an owner.
Reference Implentation
User Guides on bundles:
Demand
Demands are automatically recurring bids. By creating a demand, buyers tell the system to automatically create a bid with the same criteria once every defined time period. You can read more about demands in the glossary here.
The Demand module provides services for managing (fetching, creating, updating, pausing, resuming, cancelling) demands.
Reference Implentation
User Guides on demands:
Matching Engine Service
The matching engine service initializes the Matching Engine from the @energyweb/exchange-core module and sends order submissions, queries and cancellations to the Matching Engine.
The Matching Engine is initialized with the price strategy, which is set in the configuration file. There are two price strategies:
- Ask Price Strategy (default): Orders are bought at the defined Ask price
- Order Creation Time Pick Strategy: If the bid price was created first, use the bid price. Otherwise, use the ask price:
Reference Implentation
User Guides on order matching: - Exchange - Market: Trading View
Order
‘Orders’ can be either a sell offer (ask) or a buy offer (bid). Read more about orders in the glossary here. The Order module provides services that manage:
- Creating bids
- Creating asks
- Creating direct buys
- Fetching, cancelling, updating, reactivating orders
Order Accounting Service
The Order Accounting Service extends the Account Balance Asset Service. The public methods retrieve active bids and asks for an owner, and then calculates the current locked and available assets for that owner.
Post for Sale
The Post for Sale module handles posting a device’s certificate hours for sale on the exchange using the Order service (from the Order module). The user must specify the price, the volume, the amount of energy hours and the asset Id. This information is used to create an ask, that is then posted on the exchange .
const ask: CreateAskDTO = {
price: supply.price,
validFrom: new Date(),
volume: amount,
assetId
};
await this.orderService.createAsk(userId, ask);
Supply
The Supply module manages the fetching, creating, updating and removing of a user’s supply. A supply is an automated creation of an “ask” or a sell of certificate hours for a given device. Whenever a device is issued a certificate by the issuing body, this service creates an automatic ask at a designated price per unit, so there is no need to manually create an ask each time a certificate is issued.
There is one supply entity per device, which can either be in a state of ‘active’ or ‘paused’. The price of the supply can be updated.
Reference Implentation
User Guides on Supply: - Exchange - Supply
Trade
The Trade module manages the fetching and persisting of trades, and updating the corresponding orders. A trade occurs when an ask and bid match. You can view a comprehensive overview of trade logic and scenarios here. The trade service uses the NestJS EventBus class to publish events related to emit trade events.
Trade Accounting Service
The Trade Accounting Service extends the Account Balance Asset Service. The public method retrieves all trades for an owner, and then calculates the current available assets for that owner.
Transfer
The Transfer module handles:
- Depositing certificates onto the Exchange. Once deposited onto the exchange, a certificate becomes an Asset.
- Withdrawing certificates from the Exchange (into the user's Blockchain Inbox)
- Claiming (retiring) certificate(s) directly from the Exchange wallet
- Transfering certificate(s) to another Exchange Deposit address
Transfers are persisted in the Transfer repository. Each entity stores the blockchain address of the certificate, and the Transfer Direction, which denotes whether the transfer is a deposit, withdrawal, claim, or transfer. You can view the Transfer entity model here.
Transfer Accounting Service
The Transfer Accounting Service extends the Account Balance Asset Service. The public method retrieves all transfers for an owner, and then calculates the current available assets for that owner based on the transfer directions (withdrawal, claim, send, deposit).