Skip to content

Commit

Permalink
Add support for mainnet on web (front-end) (#102)
Browse files Browse the repository at this point in the history
  • Loading branch information
ArturDolzan authored Dec 27, 2024
1 parent 45d2eec commit d061809
Show file tree
Hide file tree
Showing 48 changed files with 830 additions and 111 deletions.
8 changes: 3 additions & 5 deletions apps/web/.env
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
VITE_API_WEBSERVICE_URL=ws://localhost:3000
VITE_TESTNET_API_WEBSERVICE_URL=ws://localhost:3000
VITE_MAINNET_API_WEBSERVICE_URL=ws://localhost:3001
VITE_USE_API_MOCK=false
VITE_GITHUB_URL=https://github.com/BVM-priv/CryptoChords/
VITE_CONTRIBUTORS_URL=https://github.com/BVM-priv/CryptoChords/blob/master/CONTRIBUTING.md
VITE_FEEDBACK_URL=https://github.com/BVM-priv/CryptoChords/issues/new
VITE_DISCORD_URL=https://discord.gg/RyhaPp7NvQ
VITE_X_URL=https://x.com/hemi_xyz
VITE_LOGO_URL=https://x.com/hemi_xyz
VITE_EXPLORER_ETH_URL=https://testnet.explorer.hemi.xyz/tx/\${hash}
VITE_EXPLORER_POP_URL=https://testnet.explorer.hemi.xyz/tx/\${hash}
VITE_EXPLORER_BTC_URL=https://testnet.explorer.hemi.xyz/tx/\${hash}
VITE_EXPLORER_BLOCK_URL=https://testnet.explorer.hemi.xyz/block/\${hash}
VITE_ENABLE_MAINNET=true
6 changes: 4 additions & 2 deletions apps/web/.env.production
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
VITE_API_WEBSERVICE_URL=wss://\${host}/api/testnet
VITE_USE_API_MOCK=false
VITE_TESTNET_API_WEBSERVICE_URL=wss://\${host}/api/testnet
VITE_MAINNET_API_WEBSERVICE_URL=wss://\${host}/api/mainnet
VITE_USE_API_MOCK=false
VITE_ENABLE_MAINNET=false
12 changes: 3 additions & 9 deletions apps/web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ npm run test:cov

The environment variables are defined in the `.env` file. The following variables are used:

- `VITE_API_WEBSERVICE_URL`: The URL of the web service API. If the variable contains the value `${host}` it will be replaced by the host of the web app. Make sure to escape the dollar sign `\${host}` otherwise it will expanded using [dotenv-expand](https://github.com/motdotla/dotenv-expand) as described on [Vite Docs](https://vitejs.dev/guide/env-and-mode#env-files).
- `VITE_TESTNET_API_WEBSERVICE_URL`: The URL of the testnet web service API. If the variable contains the value `${host}` it will be replaced by the host of the web app. Make sure to escape the dollar sign `\${host}` otherwise it will expanded using [dotenv-expand](https://github.com/motdotla/dotenv-expand) as described on [Vite Docs](https://vitejs.dev/guide/env-and-mode#env-files).

- `VITE_MAINNET_API_WEBSERVICE_URL`: The URL of the mainnet web service API. If the variable contains the value `${host}` it will be replaced by the host of the web app. Make sure to escape the dollar sign `\${host}` otherwise it will expanded using [dotenv-expand](https://github.com/motdotla/dotenv-expand) as described on [Vite Docs](https://vitejs.dev/guide/env-and-mode#env-files).

- `VITE_USE_API_MOCK`: A boolean that indicates if the API should be mocked.

Expand All @@ -96,14 +98,6 @@ The environment variables are defined in the `.env` file. The following variable

- `VITE_LOGO_URL`: The URL of the logo. If it is not defined, the logo will point to the root of the web app and will not display the pointer cursor.

- `VITE_EXPLORER_BLOCK_URL`: The URL of the block explorer. It has to contain the string `${hash}` that will be replaced by the block hash. Make sure to escape the dollar sign `\${host}` otherwise it will expanded using [dotenv-expand](https://github.com/motdotla/dotenv-expand) as described on [Vite Docs](https://vitejs.dev/guide/env-and-mode#env-files).

- `VITE_EXPLORER_ETH_URL`: The URL of the ETH transaction explorer. It has to contain the string `${hash}` that will be replaced by the transaction hash. Make sure to escape the dollar sign `\${host}` otherwise it will expanded using [dotenv-expand](https://github.com/motdotla/dotenv-expand) as described on [Vite Docs](https://vitejs.dev/guide/env-and-mode#env-files).

- `VITE_EXPLORER_BTC_URL`: The URL of the BTC transaction explorer. It has to contain the string `${hash}` that will be replaced by the transaction hash. Make sure to escape the dollar sign `\${host}` otherwise it will expanded using [dotenv-expand](https://github.com/motdotla/dotenv-expand) as described on [Vite Docs](https://vitejs.dev/guide/env-and-mode#env-files).

- `VITE_EXPLORER_POP_URL`: The URL of the PoP transaction explorer. It has to contain the string `${hash}` that will be replaced by the transaction hash. Make sure to escape the dollar sign `\${host}` otherwise it will expanded using [dotenv-expand](https://github.com/motdotla/dotenv-expand) as described on [Vite Docs](https://vitejs.dev/guide/env-and-mode#env-files).

## Contribution
If you want to contribute to this project and make it better, your help is very welcome.
You can find more information about how to contribute in the [`CONTRIBUTING.md`](../../CONTRIBUTING.md) file.
Expand Down
2 changes: 1 addition & 1 deletion apps/web/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ server {
add_header Permissions-Policy "geolocation=(), microphone=()";
}

location /api {
location /api/mainnet {
proxy_pass http://cryptochords-api-service:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/application/ObservableService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ implements Observable<ServiceResponseEvent<Request,Response>>{
public async listen(listener: EventSubscription<ServiceResponseEvent<Request, Response>>) {
this.eventBus.subscribe(ServiceResponseEvent.eventKey, listener as EventSubscription<Event>)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const cubeRepositoryMock: CubeRepository = {
delete: vi.fn(),
update: vi.fn(),
list: vi.fn(),
clear: vi.fn()
}

const create: CreateCubeService = new CreateCubeService(cubeRepositoryMock)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export interface CreateTransactionRequest {
txType: string
address: string
network: string
timestamp: number
}
Original file line number Diff line number Diff line change
@@ -1,35 +1,39 @@
import { Address, Timestamp, TxType, TxTypesEnum } from '@cryptochords/shared'
import { Address, NetworkEnum, Timestamp, TxType, TxTypesEnum } from '@cryptochords/shared'
import { Transaction } from '../../../domain/entities/Transaction'
import { InstrumentEnum } from '../../../domain/enum/InstrumentEnum'
import { KeyboardRepository } from '../../../domain/repositories/KeyboardRepository'
import { NetworkRepository } from '../../../domain/repositories/NetworkRepository'
import { TransactionRepository } from '../../../domain/repositories/TransactionRepository'
import { Key } from '../../../domain/valueObjects/Key'
import { ObservableService } from '../../ObservableService'
import { CreateCubeService } from '../CreateCube/CreateCubeService'
import { PressKeyService } from '../PressKey/PressKeyService'
import { ReleaseKeyService } from '../ReleaseKey/ReleaseKeyService'
import { CreateTransactionRequest } from './CreateTransactionDtos'
import { InstrumentEnum } from '../../../domain/enum/InstrumentEnum'

export class CreateTransactionService extends ObservableService<CreateTransactionRequest, void> {
private readonly keyboardRepository: KeyboardRepository
private readonly transactionRepository: TransactionRepository
private readonly createCubeService: CreateCubeService
private readonly pressKeyService: PressKeyService
private readonly releaseKeyService: ReleaseKeyService
private readonly netwtorkRepository: NetworkRepository

constructor(
keyboardRepository: KeyboardRepository,
transactionRepository: TransactionRepository,
createCubeService: CreateCubeService,
pressKeyService: PressKeyService,
releaseKeyService: ReleaseKeyService
releaseKeyService: ReleaseKeyService,
networkRepository: NetworkRepository
) {
super()
this.keyboardRepository = keyboardRepository
this.transactionRepository = transactionRepository
this.createCubeService = createCubeService
this.pressKeyService = pressKeyService
this.releaseKeyService = releaseKeyService
this.netwtorkRepository = networkRepository
}

protected async process(request: CreateTransactionRequest): Promise<void> {
Expand All @@ -45,9 +49,15 @@ export class CreateTransactionService extends ObservableService<CreateTransactio
}

async createTransaction(request: CreateTransactionRequest): Promise<Transaction> {
const network = await this.netwtorkRepository.find(request.network as NetworkEnum)

if (!network)
throw new Error('Network not found')

const transaction = Transaction.create({
txType: TxType.create(request.txType as TxTypesEnum),
address: Address.create(request.address),
network,
timestamp: Timestamp.create(request.timestamp)
})
await this.transactionRepository.create(transaction)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const cubeRepositoryMock: CubeRepository = {
color: { value: 'orange' },
mirrored: false
}]),
clear: vi.fn()
}

const getCubes = new GetCubesService(cubeRepositoryMock)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface GetSelectedNetworkResponseDto {
networkName: string
networkWsUrl: string
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { NetworkRepository } from '../../../domain/repositories/NetworkRepository'
import { ObservableService } from '../../ObservableService'
import { GetSelectedNetworkResponseDto } from './GetSelectedNetworkResponseDto'

export class GetSelectedNetworkService extends ObservableService<void, GetSelectedNetworkResponseDto> {
private readonly netwtorkRepository: NetworkRepository

constructor(
networkRepository: NetworkRepository,
) {
super()
this.netwtorkRepository = networkRepository
}

protected async process(): Promise<GetSelectedNetworkResponseDto> {
const network = await this.netwtorkRepository.getSelected()
return {
networkName: network.name,
networkWsUrl: network.wsUrl,
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { NetworkDto } from './NetworkDto'

export interface ListNetworksResponseDto {
networks: NetworkDto[]
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { NetworkRepository } from '../../../domain/repositories/NetworkRepository'
import { ObservableService } from '../../ObservableService'
import { ListNetworksResponseDto } from './ListNetworksResponseDto'


export class ListNetworksService extends ObservableService<void, ListNetworksResponseDto> {
private readonly networkRepository: NetworkRepository

constructor(
networkRepository: NetworkRepository,
) {
super()
this.networkRepository = networkRepository
}

protected async process(): Promise<ListNetworksResponseDto> {
const [networks, selectedNetwork] = await Promise.all([
this.networkRepository.list(),
this.networkRepository.getSelected()
])

return {
networks: networks.map(network => ({
name: network.name,
wsUrl: network.wsUrl,
selected: network.name === selectedNetwork.name,
}))
}
}
}
6 changes: 6 additions & 0 deletions apps/web/src/application/services/ListNetworks/NetworkDto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

export interface NetworkDto {
name: string
wsUrl: string
selected: boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ interface TransactionDto {
txType: string
address: string
timestamp: number
url: string
}

export interface ListTransactionsResponseDto {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { ListTransactionsResponseDto } from './ListTransactionsDtos'
export class ListTransactionsService extends ObservableService<void, ListTransactionsResponseDto>{
private readonly transactionRepository: TransactionRepository

constructor(transactionRepository: TransactionRepository) {
constructor(
transactionRepository: TransactionRepository,
) {
super()
this.transactionRepository = transactionRepository
}
Expand All @@ -17,7 +19,8 @@ export class ListTransactionsService extends ObservableService<void, ListTransac
transactions: transactions.map(transaction => ({
txType: transaction.txType.value as string,
address: transaction.address.value,
timestamp: transaction.timestamp.value
timestamp: transaction.timestamp.value,
url: transaction.url
}))
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface SwitchNetworkRequestDto {
networkName: string
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface SwitchNetworkResponseDto {
networkName: string,
networkWsUrl: string
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { NetworkEnum } from '@cryptochords/shared'
import { Network } from '../../../domain/entities/Network'
import { CubeRepository } from '../../../domain/repositories/CubeRepository'
import { NetworkRepository } from '../../../domain/repositories/NetworkRepository'
import { TransactionRepository } from '../../../domain/repositories/TransactionRepository'
import { ObservableService } from '../../ObservableService'
import { SwitchNetworkRequestDto } from './SwitchNetworkRequestDto'
import { SwitchNetworkResponseDto } from './SwitchNetworkResponseDto'

export class SwitchNetworkService extends ObservableService<SwitchNetworkRequestDto, SwitchNetworkResponseDto> {
private readonly transactionRepository: TransactionRepository
private readonly networkRepository: NetworkRepository
private readonly cubeRepository: CubeRepository

constructor(
transactionRepository: TransactionRepository,
networkRepository: NetworkRepository,
cubeRepository: CubeRepository
) {
super()
this.transactionRepository = transactionRepository
this.networkRepository = networkRepository
this.cubeRepository = cubeRepository
}

protected async process(request: SwitchNetworkRequestDto): Promise<SwitchNetworkResponseDto> {
const network = await this.validateNetwork(request.networkName)
await Promise.all([
this.transactionRepository.clear(),
this.cubeRepository.clear(),
this.networkRepository.select(network.name)
])

return {
networkName: network.name,
networkWsUrl: network.wsUrl
}
}

private async validateNetwork(networkName: string): Promise<Network> {
if (!networkName) {
throw new Error('Network not found')
}

const network = await this.networkRepository.find(networkName as NetworkEnum)

if (!network) {
throw new Error('Network not found')
}

return network
}
}
39 changes: 39 additions & 0 deletions apps/web/src/domain/entities/Network.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { describe, it, expect } from 'vitest';
import { Network } from './Network';
import { NetworkEnum } from '../../../../../packages/shared/src/domain/enums/NetworkEnum';
import { Uuid } from '../../../../../packages/shared/src/domain/valueObjects/Uuid';

describe('Network', () => {
const mockProps = {
name: NetworkEnum.MAINNET,
explorerUrl: 'https://explorer.mainnet.com',
wsUrl: 'wss://mainnet.ws.com',
};

it('should create a Network instance with default UUID', () => {
const network = Network.create(mockProps);
expect(network).toBeInstanceOf(Network);
expect(network.name).toBe(NetworkEnum.MAINNET);
expect(network.explorerUrl).toBe(mockProps.explorerUrl);
expect(network.wsUrl).toBe(mockProps.wsUrl);
expect(network.transactionUrl).toBe(`${mockProps.explorerUrl}/tx`);
expect(network.blockUrl).toBe(`${mockProps.explorerUrl}/block`);
});

it('should create a Network instance with a provided UUID', () => {
const uuid = Uuid.create();
const network = Network.create(mockProps, uuid);
expect(network).toBeInstanceOf(Network);
expect(network.name).toBe(NetworkEnum.MAINNET);
expect(network.explorerUrl).toBe(mockProps.explorerUrl);
expect(network.wsUrl).toBe(mockProps.wsUrl);
expect(network.transactionUrl).toBe(`${mockProps.explorerUrl}/tx`);
expect(network.blockUrl).toBe(`${mockProps.explorerUrl}/block`);
});

it('should return correct URLs for transactions and blocks', () => {
const network = Network.create(mockProps);
expect(network.transactionUrl).toBe(`${mockProps.explorerUrl}/tx`);
expect(network.blockUrl).toBe(`${mockProps.explorerUrl}/block`);
});
});
Loading

0 comments on commit d061809

Please sign in to comment.