From 694c80193104bb50c35c57c5c01d04c059e551e5 Mon Sep 17 00:00:00 2001 From: kyudong3 Date: Wed, 26 Jun 2019 21:18:43 +0900 Subject: [PATCH 1/2] feat: implement ethereum event subscriber --- server/src/app.module.ts | 5 +- server/src/config/auction.ts | 7 + server/src/config/ghost.ts | 7 + server/src/config/subscriber.ts | 5 + .../blockchain/ethereum/event/event.module.ts | 4 - .../ethereum/event/event.service.spec.ts | 18 --- .../ethereum/event/event.service.ts | 6 - .../ethereum/event/event.subscriber.module.ts | 17 +++ .../ethereum/event/event.subscriber.spec.ts | 24 ++++ .../ethereum/event/event.subscriber.ts | 33 +++++ .../ethereum/event/handler/event.handler.ts | 123 ++++++++++++++++++ .../ethereum/event/test/config/subscriber.ts | 4 + .../web3/contract/contract.factory.spec.ts | 28 ++++ .../web3/contract/contract.factory.ts | 24 ++++ .../ethereum/web3/contract/contract.module.ts | 17 +++ .../ethereum/web3/contract/contracts.ts | 13 ++ .../web3/test/socket/config/auction.ts | 5 + .../ethereum/web3/test/socket/config/ghost.ts | 5 + .../ethereum/web3/test/socket/config/web3.ts | 5 +- 19 files changed, 320 insertions(+), 30 deletions(-) create mode 100644 server/src/config/auction.ts create mode 100644 server/src/config/ghost.ts create mode 100644 server/src/config/subscriber.ts delete mode 100644 server/src/port/adapter/service/blockchain/ethereum/event/event.module.ts delete mode 100644 server/src/port/adapter/service/blockchain/ethereum/event/event.service.spec.ts delete mode 100644 server/src/port/adapter/service/blockchain/ethereum/event/event.service.ts create mode 100644 server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.module.ts create mode 100644 server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.spec.ts create mode 100644 server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.ts create mode 100644 server/src/port/adapter/service/blockchain/ethereum/event/handler/event.handler.ts create mode 100644 server/src/port/adapter/service/blockchain/ethereum/event/test/config/subscriber.ts create mode 100644 server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.factory.spec.ts create mode 100644 server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.factory.ts create mode 100644 server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.module.ts create mode 100644 server/src/port/adapter/service/blockchain/ethereum/web3/contract/contracts.ts create mode 100644 server/src/port/adapter/service/blockchain/ethereum/web3/test/socket/config/auction.ts create mode 100644 server/src/port/adapter/service/blockchain/ethereum/web3/test/socket/config/ghost.ts diff --git a/server/src/app.module.ts b/server/src/app.module.ts index fa4a626..b4a35ce 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -8,6 +8,7 @@ import {UserModule} from './port/module/user.module'; import {ItemModule} from './port/module/item.module'; import {TransactionModule} from './port/module/transaction.module'; import {DatabaseModule} from './port/module/database.module'; +import {EventSubscriberModule} from './port/adapter/service/blockchain/ethereum/event/event.subscriber.module'; @Module({ imports: [ @@ -17,8 +18,10 @@ import {DatabaseModule} from './port/module/database.module'; TransactionModule, UserModule, DatabaseModule, + EventSubscriberModule, ], controllers: [AppController], providers: [AppService], }) -export class AppModule {} +export class AppModule { +} diff --git a/server/src/config/auction.ts b/server/src/config/auction.ts new file mode 100644 index 0000000..a9bd750 --- /dev/null +++ b/server/src/config/auction.ts @@ -0,0 +1,7 @@ +export default { + abi: [{ + name: 'auction', + type: 'function', + }], + address: 'ax1234', +}; diff --git a/server/src/config/ghost.ts b/server/src/config/ghost.ts new file mode 100644 index 0000000..e06b0ba --- /dev/null +++ b/server/src/config/ghost.ts @@ -0,0 +1,7 @@ +export default { + abi: [{ + name: 'ghost', + type: 'function', + }], + address: 'ax1234', +}; diff --git a/server/src/config/subscriber.ts b/server/src/config/subscriber.ts new file mode 100644 index 0000000..7edd576 --- /dev/null +++ b/server/src/config/subscriber.ts @@ -0,0 +1,5 @@ +export default { + startBlock: 5853011, + endBlock: 'latest', + network: 'testNet', +}; diff --git a/server/src/port/adapter/service/blockchain/ethereum/event/event.module.ts b/server/src/port/adapter/service/blockchain/ethereum/event/event.module.ts deleted file mode 100644 index 48f7f64..0000000 --- a/server/src/port/adapter/service/blockchain/ethereum/event/event.module.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Module } from '@nestjs/common'; - -@Module({}) -export class EventModule {} diff --git a/server/src/port/adapter/service/blockchain/ethereum/event/event.service.spec.ts b/server/src/port/adapter/service/blockchain/ethereum/event/event.service.spec.ts deleted file mode 100644 index 78cb816..0000000 --- a/server/src/port/adapter/service/blockchain/ethereum/event/event.service.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { EventService } from './event.service'; - -describe('EventService', () => { - let service: EventService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [EventService], - }).compile(); - - service = module.get(EventService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/server/src/port/adapter/service/blockchain/ethereum/event/event.service.ts b/server/src/port/adapter/service/blockchain/ethereum/event/event.service.ts deleted file mode 100644 index 04be74e..0000000 --- a/server/src/port/adapter/service/blockchain/ethereum/event/event.service.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class EventService { - constructor() {} -} diff --git a/server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.module.ts b/server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.module.ts new file mode 100644 index 0000000..e1ed133 --- /dev/null +++ b/server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.module.ts @@ -0,0 +1,17 @@ +import {Module} from '@nestjs/common'; +import {EventSubscriber} from './event.subscriber'; +import {Web3Module} from '../web3/web3.module'; +import {Web3Config} from '../web3/web3.config'; + +export const eventSubscriber = { + provide: 'EVENT_SUBSCRIBER', + useClass: EventSubscriber, +}; + +@Module({ + providers: [EventSubscriber, Web3Config, eventSubscriber], + imports: [Web3Module], + exports: [eventSubscriber], +}) +export class EventSubscriberModule { +} diff --git a/server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.spec.ts b/server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.spec.ts new file mode 100644 index 0000000..9367b79 --- /dev/null +++ b/server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.spec.ts @@ -0,0 +1,24 @@ +import {Test, TestingModule} from '@nestjs/testing'; +import {EventSubscriber} from './event.subscriber'; +import {Web3Module} from '../web3/web3.module'; +import * as path from 'path'; +import {ConfigModule} from 'nestjs-config'; + +describe('EventSubscriber', () => { + const testUrl = 'ws://localhost:8545'; + let service: EventSubscriber; + + afterAll(() => setTimeout(() => process.exit(), 1000)); + + describe('dependency resolve', () => { + it('should be defined', async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [EventSubscriber], + imports: [ + Web3Module, ConfigModule.load(path.resolve('/test/socket', 'config', '**/!(*.d).{ts,js}')), + ], + }).compile(); + service = module.get(EventSubscriber); + }); + }); +}); diff --git a/server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.ts b/server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.ts new file mode 100644 index 0000000..4a6a0f5 --- /dev/null +++ b/server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.ts @@ -0,0 +1,33 @@ +import {Injectable} from '@nestjs/common'; +import {Contracts} from '../web3/contract/contracts'; +import {EventHandler} from './handler/event.handler'; +import {EventData} from 'web3-eth-contract'; + +@Injectable() +export class EventSubscriber { + constructor(private eventHandler: EventHandler) { + this.contractList = this.eventHandler.getContractList(); + } + + contractList: Contracts[]; + currentBlockNum: number; + + async trackEvents(): Promise { + this.currentBlockNum = await this.eventHandler.getBlockNum(); + for (const index in this.contractList) { + if (this.contractList[index].blockNum !== this.currentBlockNum) { + const datas: EventData[] + = await this.contractList[index].contract.getPastEvents('allEvents', { + fromBlock: this.contractList[index].blockNum, + toBlock: 'latest', + }); + this.contractList[index].blockNum++; + await this.eventHandler.parseData(datas); + } + } + } + + subscribeEvents(): void { + setInterval(() => this.trackEvents(), 1000); + } +} diff --git a/server/src/port/adapter/service/blockchain/ethereum/event/handler/event.handler.ts b/server/src/port/adapter/service/blockchain/ethereum/event/handler/event.handler.ts new file mode 100644 index 0000000..220dc3d --- /dev/null +++ b/server/src/port/adapter/service/blockchain/ethereum/event/handler/event.handler.ts @@ -0,0 +1,123 @@ +import {EventData} from 'web3-eth-contract'; +import {Inject} from '@nestjs/common'; +import Web3 from 'web3'; +import {ContractFactory} from '../../web3/contract/contract.factory'; +import {Contracts} from '../../web3/contract/contracts'; + +export class EventHandler { + constructor(@Inject('WEB3') private web3: Web3, private contracts: ContractFactory) { + this.contractList = contracts.getContracts(); + } + + contractList: Contracts[]; + + getContractList(): Contracts[] { + return this.contractList; + } + + async getBlockNum(): Promise { + return await this.web3.eth.getBlockNumber(); + } + + async parseData(datas: EventData[]): Promise { + let parsedData: { [key: string]: any; }; + + for (const data of datas) { + const hexData = data.raw.data; + switch (data.event) { + case 'AuctionCreated': + parsedData = await this.web3.eth.abi.decodeParameters([{ + type: 'uint256', + name: 'gene', + }, { + type: 'uint256', + name: 'duration', + }, { + type: 'uint256', + name: 'auctionType', + }], hexData); + // auctionCreated + // createAuction(parsedData.gene, parsedData.duration, parsedData.auctionType); + break; + case 'AuctionSuccessful': + parsedData = await this.web3.eth.abi.decodeParameters([{ + type: 'uint256', + name: 'gene', + }, { + type: 'uint256', + name: 'maxPrice', + }, { + type: 'address', + name: 'winner', + }], hexData); + // auctionSuccessed + // updateWinner(parsedData.gene, parsedData.winner, parsedData.maxPrice); + break; + case 'AuctionCancelled': + parsedData = await this.web3.eth.abi.decodeParameters([{ + type: 'uint256', + name: 'gene', + }], hexData); + // auctionCancelled + // cancelAuction(parsedData.gene); + break; + case 'Birth': + parsedData = await this.web3.eth.abi.decodeParameters([{ + type: 'address', + name: 'owner', + }, { + type: 'uint256', + name: 'tokenId', + }, { + type: 'uint256', + name: 'gene', + }], hexData); + // 알 생성 + // createEgg(parsedData.owner, parsedData.gene); + break; + case 'LevelUp': + parsedData = await this.web3.eth.abi.decodeParameters([{ + type: 'address', + name: 'owner', + }, { + type: 'uint256', + name: 'gene', + }, { + type: 'uint256', + name: 'level', + }], hexData); + // level up + // levelUp(parsedData.owner, parsedData.gene, parsedData.level); + break; + case 'Transfer': + parsedData = await this.web3.eth.abi.decodeParameters([{ + type: 'address', + name: 'from', + }, { + type: 'address', + name: 'to', + }, { + type: 'uint256', + name: 'gene', + }], hexData); + // transfer + // transfer(parsedData.from, parsedData.to, parsedData.gene); + break; + case 'Approval': + parsedData = await this.web3.eth.abi.decodeParameters([{ + type: 'address', + name: 'from', + }, { + type: 'address', + name: 'to', + }, { + type: 'uint256', + name: 'tokenId', + }], hexData); + // approval + // ? + break; + } + } + } +} diff --git a/server/src/port/adapter/service/blockchain/ethereum/event/test/config/subscriber.ts b/server/src/port/adapter/service/blockchain/ethereum/event/test/config/subscriber.ts new file mode 100644 index 0000000..8188223 --- /dev/null +++ b/server/src/port/adapter/service/blockchain/ethereum/event/test/config/subscriber.ts @@ -0,0 +1,4 @@ +export default { + startBlock: 5853011, + endBlock: 'latest', +}; diff --git a/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.factory.spec.ts b/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.factory.spec.ts new file mode 100644 index 0000000..df71513 --- /dev/null +++ b/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.factory.spec.ts @@ -0,0 +1,28 @@ +import {Test, TestingModule} from '@nestjs/testing'; +import * as path from 'path'; +import {ConfigModule} from 'nestjs-config'; +import {ContractFactory} from './contract.factory'; +import {Web3Module} from '../web3.module'; +import Web3 from 'web3'; + +describe('ContractFactory', () => { + let contractFactory: ContractFactory; + let web3: Web3; + + afterAll(() => setTimeout(() => process.exit(), 1000)); + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ContractFactory], + imports: [ + Web3Module, ConfigModule.load(path.resolve(__dirname + '/test/socket', 'config', '**/!(*.d).{ts,js}')), + ], + }).compile(); + contractFactory = module.get(ContractFactory); + web3 = new Web3(new Web3.providers.WebsocketProvider('ws://localhost:8545')); + }); + + describe('#getContract()', () => { + it('should return specific Contract', async () => { + }); + }); +}); diff --git a/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.factory.ts b/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.factory.ts new file mode 100644 index 0000000..faf2a1d --- /dev/null +++ b/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.factory.ts @@ -0,0 +1,24 @@ +import {Inject, Injectable} from '@nestjs/common'; +import Web3 from 'web3'; +import {InjectConfig} from 'nestjs-config'; +import {Contracts} from './contracts'; + +@Injectable() +export class ContractFactory { + contracts: Contracts[]; + + constructor(@Inject('WEB3') private web3: Web3, @InjectConfig() private config) { + this.setContracts(); + } + + setContracts(): void { + this.contracts[0] = new Contracts('Auction', this.config.get('subscriber.startBlock'), + new this.web3.eth.Contract(this.config.get('auction.abi'), this.config.get('auction.address'))); + this.contracts[1] = new Contracts('Ghost', this.config.get('subscriber.startBlock'), + new this.web3.eth.Contract(this.config.get('ghost.abi'), this.config.get('ghost.address'))); + } + + getContracts(): Contracts[] { + return this.contracts; + } +} diff --git a/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.module.ts b/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.module.ts new file mode 100644 index 0000000..16226b9 --- /dev/null +++ b/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.module.ts @@ -0,0 +1,17 @@ +import {Module} from '@nestjs/common'; +import {ContractFactory} from './contract.factory'; +import {Web3Config} from '../web3.config'; +import {Web3Module} from '../web3.module'; + +export const contractProvider = { + provide: 'CONTRACT', + useClass: ContractFactory, +}; + +@Module({ + providers: [Web3Config, contractProvider], + exports: [contractProvider], + imports: [Web3Module], +}) +export class ContractFactoryModule { +} diff --git a/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contracts.ts b/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contracts.ts new file mode 100644 index 0000000..96aa060 --- /dev/null +++ b/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contracts.ts @@ -0,0 +1,13 @@ +import {Contract} from 'web3-eth-contract'; + +export class Contracts { + contractName: string; + blockNum: number; + contract: Contract; + + constructor(contractName, blockNum, contract) { + this.contractName = contractName; + this.blockNum = blockNum; + this.contract = contract; + } +} diff --git a/server/src/port/adapter/service/blockchain/ethereum/web3/test/socket/config/auction.ts b/server/src/port/adapter/service/blockchain/ethereum/web3/test/socket/config/auction.ts new file mode 100644 index 0000000..5f9d8f2 --- /dev/null +++ b/server/src/port/adapter/service/blockchain/ethereum/web3/test/socket/config/auction.ts @@ -0,0 +1,5 @@ +export default { + abi : [{ + test: 'test', + }], +}; diff --git a/server/src/port/adapter/service/blockchain/ethereum/web3/test/socket/config/ghost.ts b/server/src/port/adapter/service/blockchain/ethereum/web3/test/socket/config/ghost.ts new file mode 100644 index 0000000..3c7943e --- /dev/null +++ b/server/src/port/adapter/service/blockchain/ethereum/web3/test/socket/config/ghost.ts @@ -0,0 +1,5 @@ +export default { + abi: [{ + test: 'test', + }], +}; diff --git a/server/src/port/adapter/service/blockchain/ethereum/web3/test/socket/config/web3.ts b/server/src/port/adapter/service/blockchain/ethereum/web3/test/socket/config/web3.ts index d83afa3..6afb8f2 100644 --- a/server/src/port/adapter/service/blockchain/ethereum/web3/test/socket/config/web3.ts +++ b/server/src/port/adapter/service/blockchain/ethereum/web3/test/socket/config/web3.ts @@ -1,4 +1,7 @@ export default { - url: 'localhost:8545', + url: 'ws://localhost:8545', type: 'socket', + abi : [{ + test: 'test', + }], }; From b81fde9ea166ebac6af33bf3d755a1c64ed35faf Mon Sep 17 00:00:00 2001 From: kyudong3 Date: Mon, 15 Jul 2019 23:01:00 +0900 Subject: [PATCH 2/2] feat: implement event subscriber --- server/src/app.module.ts | 2 - .../contract/auction.contract.service.spec.ts | 35 +++++ .../contract/auction.contract.service.ts | 42 ++++++ .../contract/ghost.contract.service.spec.ts | 35 +++++ .../contract/ghost.contract.service.ts | 42 ++++++ .../ethereum/event/event.subscriber.module.ts | 17 --- .../ethereum/event/event.subscriber.spec.ts | 24 ---- .../ethereum/event/event.subscriber.ts | 33 ----- .../event/handler/auction.event.handler.ts | 59 +++++++++ .../ethereum/event/handler/event.handler.ts | 122 +----------------- .../event/handler/ghost.event.handler.ts | 50 +++++++ .../web3/contract/contract.factory.spec.ts | 28 ---- .../web3/contract/contract.factory.ts | 24 ---- .../ethereum/web3/contract/contract.module.ts | 17 --- .../ethereum/web3/contract/contracts.ts | 13 -- 15 files changed, 265 insertions(+), 278 deletions(-) create mode 100644 server/src/port/adapter/service/blockchain/ethereum/contract/auction.contract.service.spec.ts create mode 100644 server/src/port/adapter/service/blockchain/ethereum/contract/auction.contract.service.ts create mode 100644 server/src/port/adapter/service/blockchain/ethereum/contract/ghost.contract.service.spec.ts create mode 100644 server/src/port/adapter/service/blockchain/ethereum/contract/ghost.contract.service.ts delete mode 100644 server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.module.ts delete mode 100644 server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.spec.ts delete mode 100644 server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.ts create mode 100644 server/src/port/adapter/service/blockchain/ethereum/event/handler/auction.event.handler.ts create mode 100644 server/src/port/adapter/service/blockchain/ethereum/event/handler/ghost.event.handler.ts delete mode 100644 server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.factory.spec.ts delete mode 100644 server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.factory.ts delete mode 100644 server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.module.ts delete mode 100644 server/src/port/adapter/service/blockchain/ethereum/web3/contract/contracts.ts diff --git a/server/src/app.module.ts b/server/src/app.module.ts index b4a35ce..290e642 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -8,7 +8,6 @@ import {UserModule} from './port/module/user.module'; import {ItemModule} from './port/module/item.module'; import {TransactionModule} from './port/module/transaction.module'; import {DatabaseModule} from './port/module/database.module'; -import {EventSubscriberModule} from './port/adapter/service/blockchain/ethereum/event/event.subscriber.module'; @Module({ imports: [ @@ -18,7 +17,6 @@ import {EventSubscriberModule} from './port/adapter/service/blockchain/ethereum/ TransactionModule, UserModule, DatabaseModule, - EventSubscriberModule, ], controllers: [AppController], providers: [AppService], diff --git a/server/src/port/adapter/service/blockchain/ethereum/contract/auction.contract.service.spec.ts b/server/src/port/adapter/service/blockchain/ethereum/contract/auction.contract.service.spec.ts new file mode 100644 index 0000000..0e501e4 --- /dev/null +++ b/server/src/port/adapter/service/blockchain/ethereum/contract/auction.contract.service.spec.ts @@ -0,0 +1,35 @@ +import {Test} from '@nestjs/testing'; +import {ConfigModule} from 'nestjs-config'; +import * as path from 'path'; +import {Web3Module} from '../web3/web3.module'; +import {instance, mock} from 'ts-mockito'; +import {AuctionHandler} from '../event/handler/auction.event.handler'; +import {AuctionContractService} from './auction.contract.service'; + +describe('AuctionContractService', () => { + let auctionContractService: AuctionContractService; + const mockAuctionEventHandler = mock(AuctionHandler); + + beforeAll( async () => { + const module = await Test.createTestingModule({ + imports: [ + Web3Module, + ConfigModule.load(path.resolve(__dirname, 'config', '**/!(*.d).{ts,js}')), + ], + providers: [ + AuctionContractService, + { + provide: 'AuctionHandler', + useValue: instance(mockAuctionEventHandler), + }, + ], + }).compile(); + auctionContractService = module.get(AuctionContractService); + }); + describe('#watchAuctionEvents()', () => { + it('should check blockNum increase by one', async () => { + await auctionContractService.watchAuctionEvents(); + }); + }); + }, +); diff --git a/server/src/port/adapter/service/blockchain/ethereum/contract/auction.contract.service.ts b/server/src/port/adapter/service/blockchain/ethereum/contract/auction.contract.service.ts new file mode 100644 index 0000000..99d8b59 --- /dev/null +++ b/server/src/port/adapter/service/blockchain/ethereum/contract/auction.contract.service.ts @@ -0,0 +1,42 @@ +import {Contract} from 'web3-eth-contract'; +import Web3 from 'web3'; +import {Inject, Injectable} from '@nestjs/common'; +import {EventData} from 'web3-eth-contract'; +import {InjectConfig} from 'nestjs-config'; +import {AuctionHandler} from '../event/handler/auction.event.handler'; + +@Injectable() +export class AuctionContractService { + private auctionContract: Contract; + private currentNum: number; + + constructor(@Inject('WEB3') private web3: Web3, @InjectConfig() private config, + private auctionEventHandler: AuctionHandler) { + this.createContract(); + } + + private createContract(): void { + this.auctionContract = new this.web3.eth.Contract(this.config.get('auction.abi'), this.config.get('auction.address')); + } + + watchAuctionEvents() { + setInterval(async () => { + const blockNum = await this.web3.eth.getBlockNumber(); + if (blockNum === this.currentNum) { + // Nothing happen + } else { + const eventData: EventData[] + = await this.auctionContract.getPastEvents('allEvents', { + fromBlock: this.currentNum, + toBlock: this.currentNum, + }); + await this.auctionEventHandler.callService(eventData); + this.currentNum++; + } + }, 1000); + } + + public watch() { + this.watchAuctionEvents(); + } +} diff --git a/server/src/port/adapter/service/blockchain/ethereum/contract/ghost.contract.service.spec.ts b/server/src/port/adapter/service/blockchain/ethereum/contract/ghost.contract.service.spec.ts new file mode 100644 index 0000000..9295d14 --- /dev/null +++ b/server/src/port/adapter/service/blockchain/ethereum/contract/ghost.contract.service.spec.ts @@ -0,0 +1,35 @@ +import {Test} from '@nestjs/testing'; +import {ConfigModule} from 'nestjs-config'; +import * as path from 'path'; +import {GhostContractService} from './ghost.contract.service'; +import {Web3Module} from '../web3/web3.module'; +import {GhostHandler} from '../event/handler/ghost.event.handler'; +import {instance, mock} from 'ts-mockito'; + +describe('GhostContractService', () => { + let ghostContractService: GhostContractService; + const mockGhostEventHandler = mock(GhostHandler); + + beforeAll( async () => { + const module = await Test.createTestingModule({ + imports: [ + Web3Module, + ConfigModule.load(path.resolve(__dirname, 'config', '**/!(*.d).{ts,js}')), + ], + providers: [ + GhostContractService, + { + provide: 'GhostHandler', + useValue: instance(mockGhostEventHandler), + }, + ], + }).compile(); + ghostContractService = module.get(GhostContractService); + }); + describe('#watchGhostEvents()', () => { + it('should check blockNum increase by one', async () => { + await ghostContractService.watchGhostEvents(); + }); + }); + }, +); diff --git a/server/src/port/adapter/service/blockchain/ethereum/contract/ghost.contract.service.ts b/server/src/port/adapter/service/blockchain/ethereum/contract/ghost.contract.service.ts new file mode 100644 index 0000000..3d08259 --- /dev/null +++ b/server/src/port/adapter/service/blockchain/ethereum/contract/ghost.contract.service.ts @@ -0,0 +1,42 @@ +import {Contract} from 'web3-eth-contract'; +import Web3 from 'web3'; +import {Inject, Injectable} from '@nestjs/common'; +import {EventData} from 'web3-eth-contract'; +import {InjectConfig} from 'nestjs-config'; +import {GhostHandler} from '../event/handler/ghost.event.handler'; + +@Injectable() +export class GhostContractService { + private ghostContract: Contract; + private currentNum: number; + + constructor(@Inject('WEB3') private web3: Web3, @InjectConfig() private config, + private ghostEventHandler: GhostHandler) { + this.createContract(); + } + + private createContract(): void { + this.ghostContract = new this.web3.eth.Contract(this.config.get('ghost.abi'), this.config.get('ghost.address')); + } + + watchGhostEvents() { + setInterval(async () => { + const blockNum = await this.web3.eth.getBlockNumber(); + if (blockNum === this.currentNum) { + console.log('nothing'); + } else { + const eventData: EventData[] + = await this.ghostContract.getPastEvents('allEvents', { + fromBlock: this.currentNum, + toBlock: this.currentNum, + }); + await this.ghostEventHandler.callService(eventData); + this.currentNum++; + } + }); + } + + public watch() { + this.watchGhostEvents(); + } +} diff --git a/server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.module.ts b/server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.module.ts deleted file mode 100644 index e1ed133..0000000 --- a/server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.module.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {Module} from '@nestjs/common'; -import {EventSubscriber} from './event.subscriber'; -import {Web3Module} from '../web3/web3.module'; -import {Web3Config} from '../web3/web3.config'; - -export const eventSubscriber = { - provide: 'EVENT_SUBSCRIBER', - useClass: EventSubscriber, -}; - -@Module({ - providers: [EventSubscriber, Web3Config, eventSubscriber], - imports: [Web3Module], - exports: [eventSubscriber], -}) -export class EventSubscriberModule { -} diff --git a/server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.spec.ts b/server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.spec.ts deleted file mode 100644 index 9367b79..0000000 --- a/server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {Test, TestingModule} from '@nestjs/testing'; -import {EventSubscriber} from './event.subscriber'; -import {Web3Module} from '../web3/web3.module'; -import * as path from 'path'; -import {ConfigModule} from 'nestjs-config'; - -describe('EventSubscriber', () => { - const testUrl = 'ws://localhost:8545'; - let service: EventSubscriber; - - afterAll(() => setTimeout(() => process.exit(), 1000)); - - describe('dependency resolve', () => { - it('should be defined', async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [EventSubscriber], - imports: [ - Web3Module, ConfigModule.load(path.resolve('/test/socket', 'config', '**/!(*.d).{ts,js}')), - ], - }).compile(); - service = module.get(EventSubscriber); - }); - }); -}); diff --git a/server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.ts b/server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.ts deleted file mode 100644 index 4a6a0f5..0000000 --- a/server/src/port/adapter/service/blockchain/ethereum/event/event.subscriber.ts +++ /dev/null @@ -1,33 +0,0 @@ -import {Injectable} from '@nestjs/common'; -import {Contracts} from '../web3/contract/contracts'; -import {EventHandler} from './handler/event.handler'; -import {EventData} from 'web3-eth-contract'; - -@Injectable() -export class EventSubscriber { - constructor(private eventHandler: EventHandler) { - this.contractList = this.eventHandler.getContractList(); - } - - contractList: Contracts[]; - currentBlockNum: number; - - async trackEvents(): Promise { - this.currentBlockNum = await this.eventHandler.getBlockNum(); - for (const index in this.contractList) { - if (this.contractList[index].blockNum !== this.currentBlockNum) { - const datas: EventData[] - = await this.contractList[index].contract.getPastEvents('allEvents', { - fromBlock: this.contractList[index].blockNum, - toBlock: 'latest', - }); - this.contractList[index].blockNum++; - await this.eventHandler.parseData(datas); - } - } - } - - subscribeEvents(): void { - setInterval(() => this.trackEvents(), 1000); - } -} diff --git a/server/src/port/adapter/service/blockchain/ethereum/event/handler/auction.event.handler.ts b/server/src/port/adapter/service/blockchain/ethereum/event/handler/auction.event.handler.ts new file mode 100644 index 0000000..0786d13 --- /dev/null +++ b/server/src/port/adapter/service/blockchain/ethereum/event/handler/auction.event.handler.ts @@ -0,0 +1,59 @@ +import {Inject, Injectable} from '@nestjs/common'; +import {EventHandler} from './event.handler'; +import {AuctionService} from '../../../../../../../app/auction/auction.service'; +import {EventData} from 'web3-eth-contract'; +import {AuctionDto} from '../../../../../../../app/auction/dto/auction.dto'; + +@Injectable() +export class AuctionHandler implements EventHandler { + constructor(@Inject('AuctionService') private auctionService: AuctionService) { + } + + public async callService(eventData: EventData[]): Promise { + for (const data of eventData) { + if (data.event === 'AuctionCreated') { + await this.createAuction(data); + } + if (data.event === 'AuctionSuccessful') { + await this.updateWinner(data); + } + if (data.event === 'AuctionCancelled') { + await this.cancelAuction(data); + } + // if (data.event === 'AuctionEnded') { + // await this.endAuction(data); + // } + } + } + + async createAuction(event: EventData): Promise { + const { + gene, + seller, + duration, + type, + winner, + bidAmount, + } = event.returnValues; + await this.auctionService.createAuction(new AuctionDto(gene, seller, duration, type, winner, bidAmount)); + } + + async updateWinner(event: EventData): Promise { + const { + gene, + winner, + bidAmount, + } = event.returnValues; + await this.auctionService.updateWinner(gene, winner, bidAmount); + } + + async cancelAuction(event: EventData): Promise { + const { gene } = event.returnValues; + await this.auctionService.cancelAuction(gene); + } + + async endAuction(event: EventData): Promise { + const gene = event.returnValues.gene; + await this.auctionService.endAuction(gene); + } +} diff --git a/server/src/port/adapter/service/blockchain/ethereum/event/handler/event.handler.ts b/server/src/port/adapter/service/blockchain/ethereum/event/handler/event.handler.ts index 220dc3d..4551609 100644 --- a/server/src/port/adapter/service/blockchain/ethereum/event/handler/event.handler.ts +++ b/server/src/port/adapter/service/blockchain/ethereum/event/handler/event.handler.ts @@ -1,123 +1,5 @@ import {EventData} from 'web3-eth-contract'; -import {Inject} from '@nestjs/common'; -import Web3 from 'web3'; -import {ContractFactory} from '../../web3/contract/contract.factory'; -import {Contracts} from '../../web3/contract/contracts'; -export class EventHandler { - constructor(@Inject('WEB3') private web3: Web3, private contracts: ContractFactory) { - this.contractList = contracts.getContracts(); - } - - contractList: Contracts[]; - - getContractList(): Contracts[] { - return this.contractList; - } - - async getBlockNum(): Promise { - return await this.web3.eth.getBlockNumber(); - } - - async parseData(datas: EventData[]): Promise { - let parsedData: { [key: string]: any; }; - - for (const data of datas) { - const hexData = data.raw.data; - switch (data.event) { - case 'AuctionCreated': - parsedData = await this.web3.eth.abi.decodeParameters([{ - type: 'uint256', - name: 'gene', - }, { - type: 'uint256', - name: 'duration', - }, { - type: 'uint256', - name: 'auctionType', - }], hexData); - // auctionCreated - // createAuction(parsedData.gene, parsedData.duration, parsedData.auctionType); - break; - case 'AuctionSuccessful': - parsedData = await this.web3.eth.abi.decodeParameters([{ - type: 'uint256', - name: 'gene', - }, { - type: 'uint256', - name: 'maxPrice', - }, { - type: 'address', - name: 'winner', - }], hexData); - // auctionSuccessed - // updateWinner(parsedData.gene, parsedData.winner, parsedData.maxPrice); - break; - case 'AuctionCancelled': - parsedData = await this.web3.eth.abi.decodeParameters([{ - type: 'uint256', - name: 'gene', - }], hexData); - // auctionCancelled - // cancelAuction(parsedData.gene); - break; - case 'Birth': - parsedData = await this.web3.eth.abi.decodeParameters([{ - type: 'address', - name: 'owner', - }, { - type: 'uint256', - name: 'tokenId', - }, { - type: 'uint256', - name: 'gene', - }], hexData); - // 알 생성 - // createEgg(parsedData.owner, parsedData.gene); - break; - case 'LevelUp': - parsedData = await this.web3.eth.abi.decodeParameters([{ - type: 'address', - name: 'owner', - }, { - type: 'uint256', - name: 'gene', - }, { - type: 'uint256', - name: 'level', - }], hexData); - // level up - // levelUp(parsedData.owner, parsedData.gene, parsedData.level); - break; - case 'Transfer': - parsedData = await this.web3.eth.abi.decodeParameters([{ - type: 'address', - name: 'from', - }, { - type: 'address', - name: 'to', - }, { - type: 'uint256', - name: 'gene', - }], hexData); - // transfer - // transfer(parsedData.from, parsedData.to, parsedData.gene); - break; - case 'Approval': - parsedData = await this.web3.eth.abi.decodeParameters([{ - type: 'address', - name: 'from', - }, { - type: 'address', - name: 'to', - }, { - type: 'uint256', - name: 'tokenId', - }], hexData); - // approval - // ? - break; - } - } - } +export interface EventHandler { + callService(eventData: EventData[]): void; } diff --git a/server/src/port/adapter/service/blockchain/ethereum/event/handler/ghost.event.handler.ts b/server/src/port/adapter/service/blockchain/ethereum/event/handler/ghost.event.handler.ts new file mode 100644 index 0000000..06667a9 --- /dev/null +++ b/server/src/port/adapter/service/blockchain/ethereum/event/handler/ghost.event.handler.ts @@ -0,0 +1,50 @@ +import {Inject, Injectable} from '@nestjs/common'; +import {GhostService} from '../../../../../../../app/ghost/ghost.service'; +import {EventData} from 'web3-eth-contract'; +import {EventHandler} from './event.handler'; +import {GhostDto} from '../../../../../../../app/ghost/dto/ghost.dto'; + +@Injectable() +export class GhostHandler implements EventHandler { + constructor(@Inject('GhostService') private ghostService: GhostService) { + } + + public async callService(eventData: EventData[]): Promise { + for (const data of eventData) { + if (data.event === 'Birth') { + await this.createEgg(data); + } + if (data.event === 'LevelUp') { + await this.levelUp(data); + } + if (data.event === 'Transfer') { + await this.transfer(data); + } + } + } + + async createEgg(event: EventData): Promise { + const { + owner, + gene, + } = event.returnValues; + await this.ghostService.createEgg(new GhostDto(owner, gene)); + } + + async levelUp(event: EventData): Promise { + const { + gene, + level, + } = event.returnValues; + await this.ghostService.levelUp(gene, level); + } + + async transfer(event: EventData): Promise { + const { + from, + to, + gene, + } = event.returnValues; + await this.ghostService.transfer(from, to, gene); + } +} diff --git a/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.factory.spec.ts b/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.factory.spec.ts deleted file mode 100644 index df71513..0000000 --- a/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.factory.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {Test, TestingModule} from '@nestjs/testing'; -import * as path from 'path'; -import {ConfigModule} from 'nestjs-config'; -import {ContractFactory} from './contract.factory'; -import {Web3Module} from '../web3.module'; -import Web3 from 'web3'; - -describe('ContractFactory', () => { - let contractFactory: ContractFactory; - let web3: Web3; - - afterAll(() => setTimeout(() => process.exit(), 1000)); - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ContractFactory], - imports: [ - Web3Module, ConfigModule.load(path.resolve(__dirname + '/test/socket', 'config', '**/!(*.d).{ts,js}')), - ], - }).compile(); - contractFactory = module.get(ContractFactory); - web3 = new Web3(new Web3.providers.WebsocketProvider('ws://localhost:8545')); - }); - - describe('#getContract()', () => { - it('should return specific Contract', async () => { - }); - }); -}); diff --git a/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.factory.ts b/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.factory.ts deleted file mode 100644 index faf2a1d..0000000 --- a/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.factory.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {Inject, Injectable} from '@nestjs/common'; -import Web3 from 'web3'; -import {InjectConfig} from 'nestjs-config'; -import {Contracts} from './contracts'; - -@Injectable() -export class ContractFactory { - contracts: Contracts[]; - - constructor(@Inject('WEB3') private web3: Web3, @InjectConfig() private config) { - this.setContracts(); - } - - setContracts(): void { - this.contracts[0] = new Contracts('Auction', this.config.get('subscriber.startBlock'), - new this.web3.eth.Contract(this.config.get('auction.abi'), this.config.get('auction.address'))); - this.contracts[1] = new Contracts('Ghost', this.config.get('subscriber.startBlock'), - new this.web3.eth.Contract(this.config.get('ghost.abi'), this.config.get('ghost.address'))); - } - - getContracts(): Contracts[] { - return this.contracts; - } -} diff --git a/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.module.ts b/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.module.ts deleted file mode 100644 index 16226b9..0000000 --- a/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contract.module.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {Module} from '@nestjs/common'; -import {ContractFactory} from './contract.factory'; -import {Web3Config} from '../web3.config'; -import {Web3Module} from '../web3.module'; - -export const contractProvider = { - provide: 'CONTRACT', - useClass: ContractFactory, -}; - -@Module({ - providers: [Web3Config, contractProvider], - exports: [contractProvider], - imports: [Web3Module], -}) -export class ContractFactoryModule { -} diff --git a/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contracts.ts b/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contracts.ts deleted file mode 100644 index 96aa060..0000000 --- a/server/src/port/adapter/service/blockchain/ethereum/web3/contract/contracts.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {Contract} from 'web3-eth-contract'; - -export class Contracts { - contractName: string; - blockNum: number; - contract: Contract; - - constructor(contractName, blockNum, contract) { - this.contractName = contractName; - this.blockNum = blockNum; - this.contract = contract; - } -}