Skip to content

Commit

Permalink
feat: implement ethereum event subscriber
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyudong3 committed Jun 26, 2019
1 parent 723dfbb commit a202c49
Show file tree
Hide file tree
Showing 19 changed files with 320 additions and 30 deletions.
5 changes: 4 additions & 1 deletion server/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ import { GhostModule } from './port/module/ghost.module';
import * as path from 'path';
import {UserModule} from './port/module/user.module';
import {DatabaseModule} from './port/module/database.module';
import {EventSubscriberModule} from './port/adapter/service/blockchain/ethereum/event/event.subscriber.module';

@Module({
imports: [
ConfigModule.load(path.resolve(__dirname, 'config', '**/!(*.d).{ts,js}')),
GhostModule,
UserModule,
DatabaseModule,
EventSubscriberModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
export class AppModule {
}
7 changes: 7 additions & 0 deletions server/src/config/auction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default {
abi: [{
name: 'auction',
type: 'function',
}],
address: 'ax1234',
};
7 changes: 7 additions & 0 deletions server/src/config/ghost.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default {
abi: [{
name: 'ghost',
type: 'function',
}],
address: 'ax1234',
};
5 changes: 5 additions & 0 deletions server/src/config/subscriber.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
startBlock: 5853011,
endBlock: 'latest',
network: 'testNet',
};

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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 {
}
Original file line number Diff line number Diff line change
@@ -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>(EventSubscriber);
});
});
});
Original file line number Diff line number Diff line change
@@ -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<void> {
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);
}
}
Original file line number Diff line number Diff line change
@@ -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<number> {
return await this.web3.eth.getBlockNumber();
}

async parseData(datas: EventData[]): Promise<void> {
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;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default {
startBlock: 5853011,
endBlock: 'latest',
};
Original file line number Diff line number Diff line change
@@ -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>(ContractFactory);
web3 = new Web3(new Web3.providers.WebsocketProvider('ws://localhost:8545'));
});

describe('#getContract()', () => {
it('should return specific Contract', async () => {
});
});
});
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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 {
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
abi : [{
test: 'test',
}],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
abi: [{
test: 'test',
}],
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export default {
url: 'localhost:8545',
url: 'ws://localhost:8545',
type: 'socket',
abi : [{
test: 'test',
}],
};

0 comments on commit a202c49

Please sign in to comment.