Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emit events for chain notifications (re-posted after revert) #57

Merged
merged 76 commits into from
May 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
7765a84
Speccing out overall module structure for events.
jnaviask Mar 23, 2020
18455c3
Attempt to fetch and parse blocks from Substrate.
jnaviask Apr 7, 2020
d1b8349
Make interface testable and add first unit test.
jnaviask Apr 7, 2020
a33806b
Adding counter to subscribe test.
jnaviask Apr 7, 2020
2ada578
Moving event handlers into shared.
jnaviask Apr 7, 2020
412b4ac
Move processor types to utils and add test.
jnaviask Apr 7, 2020
3fdacf6
Fix node types import. Add standalone event subscriber.
jnaviask Apr 7, 2020
c9a4540
Wire up chain events to the database.
jnaviask Apr 9, 2020
574024c
Subscription modal and menu UI for chain events.
jnaviask Apr 10, 2020
dd1a21b
Use association between Notification and ChainEvent.
jnaviask Apr 13, 2020
047c95e
Augment ChainEventType with metadata.
jnaviask Apr 13, 2020
d65ec88
Update raw_name of ChainEventType to 'section.method'.
jnaviask Apr 13, 2020
a720ff8
Update header event display format.
jnaviask Apr 13, 2020
bc6c810
Add hacky redraw
raykyri Apr 13, 2020
96a9f0a
Add polling if chain or server goes offline.
jnaviask Apr 14, 2020
9be112b
Always attempt default offline range algorithm.
jnaviask Apr 14, 2020
b1168ee
Fix polling bugs, filter pruned blocks.
jnaviask Apr 14, 2020
babf9fc
Add tentative migration.
jnaviask Apr 15, 2020
eb17c52
Stubbing out filter system.
jnaviask Apr 16, 2020
b3e153b
Initial implementation of filters.
jnaviask Apr 16, 2020
aac702a
Fix bugs to permit full stack notifications.
jnaviask Apr 16, 2020
4bf18c9
Fix processor unit test.
jnaviask Apr 16, 2020
b73d6df
Add kinds for substrate event types.
jnaviask Apr 17, 2020
0bbbe19
Fix function call in header.
jnaviask Apr 17, 2020
6ad1c3c
Remove balance formatter requiring chain from header.
jnaviask Apr 17, 2020
106ef82
Add CWEvent abstraction layer and affectedAddresses to event.
jnaviask Apr 17, 2020
18c7820
Fix affected address notification filter.
jnaviask Apr 17, 2020
7d95316
Add tentative URLs to some chain events.
jnaviask Apr 17, 2020
c602f3e
Write poller unit tests.
jnaviask Apr 20, 2020
7e401b0
Add events README and update types.
jnaviask Apr 20, 2020
322c8f9
Increase test coverage.
jnaviask Apr 20, 2020
2f8d0e5
Revert forgotten local change to package.json
jnaviask Apr 20, 2020
bf14106
Add new chain event types.
jnaviask Apr 20, 2020
46633b1
Add event handler server tests.
jnaviask Apr 21, 2020
8d443d7
Merge branch 'master' into jake.chain-notifications
jnaviask Apr 21, 2020
caa3066
Add test-events command and remove db logging.
jnaviask Apr 21, 2020
5598c58
Switch CircleCI tests to API only.
jnaviask Apr 21, 2020
40b08b8
Add exclude addresses to events. Remove author reliance in emitSubscr…
jnaviask Apr 21, 2020
47d2ee0
Merge branch 'jake.circleci-reconfig' into jake.chain-notifications
jnaviask Apr 21, 2020
90fe336
Add event testing to circleci config.
jnaviask Apr 21, 2020
32ec727
Add titler, remove modal, and update subscriptions page.
jnaviask Apr 21, 2020
d0c1a6a
Update readme for titler and subscription page.
jnaviask Apr 21, 2020
ce9fdbb
Add balance format, debug validator reward inconsistency.
jnaviask Apr 22, 2020
67709b0
Fix down migration.
jnaviask Apr 22, 2020
e289d4b
Merge branch 'master' into jake.chain-notifications
jnaviask Apr 22, 2020
960918c
Update for PR requests.
jnaviask Apr 23, 2020
0c069f2
Add version to events.
jnaviask Apr 23, 2020
9bf48b3
Fix subscription page dropdown ordering.
jnaviask Apr 23, 2020
d5e8b22
Merge branch 'master' into jake.chain-notifications
jnaviask Apr 23, 2020
4cdafb0
Fix merge issues.
jnaviask Apr 23, 2020
d9e44e1
Fix dropdown sort for subscriptions, redux.
jnaviask Apr 23, 2020
e5f91a4
Move version from event_data to chain_event model.
jnaviask Apr 24, 2020
eb38e15
Stub out new chain events.
jnaviask Apr 24, 2020
b0cde06
Implement new events fully.
jnaviask Apr 24, 2020
253d2da
Fix unit tests and related bugs.
jnaviask Apr 24, 2020
9a72d7a
Add processor fail tests.
jnaviask Apr 24, 2020
552c2c1
Update migration for new types.
jnaviask Apr 24, 2020
5d1ac25
Add stubs for enricher tests.
jnaviask Apr 24, 2020
e346ee4
Move usage of chain version to type_parser and off db.
jnaviask Apr 24, 2020
a0b6f08
Add TreasuryRewardMinted event.
jnaviask Apr 24, 2020
c9bd00d
Add TreasuryRewardMintedV2 event.
jnaviask Apr 24, 2020
b713f29
Add full enricher tests.
jnaviask Apr 27, 2020
651c88b
Remove unused version from chain event creation.
jnaviask Apr 27, 2020
d0be7d9
Fix errors from Kusama chain upgrade.
jnaviask Apr 27, 2020
c57e4a3
Merge master into branch and update API.
jnaviask Apr 27, 2020
02f4f59
Swap dispatch queue to new derive.
jnaviask Apr 27, 2020
2b7d21b
Add subscribe all button for chain events.
jnaviask Apr 27, 2020
59d5804
Fixes redirect on subscription page
drewstone Apr 27, 2020
14c359c
Fixes for creation and tx fees
drewstone Apr 27, 2020
5a26978
Fixes differences in client/server edgeware url setups
drewstone Apr 27, 2020
41049f9
Adds flag for event logging
drewstone Apr 27, 2020
cb2355f
Add extrinsic to edgeware event logic + candidacy event.
jnaviask May 1, 2020
edfbcca
Swap candidacy notification link to council page.
jnaviask May 1, 2020
c26621f
Add chain name to notification heading.
jnaviask May 1, 2020
44c0abd
Add candidacy event to migration.
jnaviask May 1, 2020
498d87e
Merge branch 'master' into jake.chain-notifications
jnaviask May 1, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,8 @@ jobs:
- run:
name: run api tests
command: yarn test-api
- run:
name: run event tests
command: yarn test-events
- store_artifacts:
path: coverage
7 changes: 6 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
"lines-between-class-members": 0,
"max-classes-per-file": ["error", 5],
"max-len": ["error", {"code": 120, "tabWidth": 2}],
"dot-notation": 0
"no-trailing-spaces": ["error"],
"no-useless-constructor": 0,
"no-empty-function": 0,
"import/prefer-default-export": 0,
"dot-notation": 0,
"no-lonely-if": 0
}
}
13 changes: 10 additions & 3 deletions client/scripts/controllers/chain/substrate/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,9 +386,16 @@ export class SubstrateAccount extends Account<SubstrateCoin> {
public get balanceTransferFee(): Observable<SubstrateCoin> {
if (this.chainClass === ChainClass.Edgeware) {
// grab const tx fee on edgeware
return this._Chain.api.pipe(
map((api: ApiRx) => this._Chain.coins(api.consts.balances.transferFee as Balance))
);
return from(this._Chain.api.pipe(map((api: ApiRx) => (api.consts.balances.transferFee as Balance)))
.toPromise()
.then((txFee) => {
if (txFee) {
return Promise.resolve(this._Chain.coins(txFee));
} else {
const dummyTxFunc = (api: ApiRx) => api.tx.balances.transfer(this.address, '0');
return this._Chain.computeFees(this.address, dummyTxFunc);
}
}));
} else {
// compute fee on Kusama
const dummyTxFunc = (api: ApiRx) => api.tx.balances.transfer(this.address, '0');
Expand Down
27 changes: 27 additions & 0 deletions client/scripts/models/ChainEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { IChainEventData } from 'events/interfaces';
import ChainEventType from './ChainEventType';

class ChainEvent {
public readonly id: number;
public readonly blockNumber: number;
public readonly data: IChainEventData;
public readonly type: ChainEventType;

constructor(id, blockNumber, data, type) {
this.id = id;
this.blockNumber = blockNumber;
this.data = data;
this.type = type;
}

public static fromJSON(json) {
return new ChainEvent(
json.id,
json.block_number,
json.event_data,
ChainEventType.fromJSON(json.ChainEventType),
);
}
}

export default ChainEvent;
19 changes: 19 additions & 0 deletions client/scripts/models/ChainEventType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { SubstrateEventKind } from 'shared/events/edgeware/types';

class ChainEventType {
public readonly id: string;
public readonly chain: string;
public readonly eventName: SubstrateEventKind;

constructor(id, chain, eventName) {
this.id = id;
this.chain = chain;
this.eventName = eventName;
}

public static fromJSON(json) {
return new ChainEventType(json.id, json.chain, json.event_name);
}
}

export default ChainEventType;
16 changes: 14 additions & 2 deletions client/scripts/models/Notification.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,45 @@
import moment from 'moment-twitter';
import NotificationSubscription from './NotificationSubscription';
import ChainEvent from './ChainEvent';

class Notification {
public readonly id: number;
public readonly data: string;
public readonly createdAt: moment.Moment;
public readonly subscription: NotificationSubscription;
public readonly chainEvent?: ChainEvent;

private _isRead: boolean;
public get isRead(): boolean {
return this._isRead;
}

constructor(id, data, isRead, createdAt, subscription) {
constructor(id, data, isRead, createdAt, subscription, chainEvent?) {
this.id = id;
this.data = data;
this._isRead = isRead;
this.createdAt = moment(createdAt);
this.subscription = subscription;
this.chainEvent = chainEvent;
}

public markRead() {
if (this._isRead) {
throw new Error('notification already read!');
} else {
this._isRead = true;
}
}

public static fromJSON(json, subscription: NotificationSubscription) {
return new Notification(json.id, json.notification_data, json.is_read, json.created_at, subscription);
return new Notification(
json.id,
json.notification_data,
json.is_read,
json.created_at,
subscription,
json.ChainEvent ? ChainEvent.fromJSON(json.ChainEvent) : undefined,
);
}
}

Expand Down
2 changes: 2 additions & 0 deletions client/scripts/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export { default as StorageModule } from './StorageModule';
export { default as ChainObject } from './ChainObject';
export { default as ChainObjectQuery } from './ChainObjectQuery';
export { default as ChainObjectVersion } from './ChainObjectVersion';
export { default as ChainEventType } from './ChainEventType';
export { default as ChainEvent } from './ChainEvent';

export { DepositVote, BinaryVote } from './votes';

Expand Down
87 changes: 55 additions & 32 deletions client/scripts/views/components/header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@ import $ from 'jquery';
import _ from 'lodash';
import moment from 'moment';
import mixpanel from 'mixpanel-browser';
import BN from 'bn.js';

import { initAppState } from 'app';
import app, { ApiStatus } from 'state';
import { ProposalType } from 'identifiers';
import { featherIcon, slugify } from 'helpers';
import { NotificationCategories } from 'types';

import { formatCoin } from 'adapters/currency';
import labelEdgewareEvent from 'events/edgeware/filters/labeler';

import Substrate from 'controllers/chain/substrate/main';
import Cosmos from 'controllers/chain/cosmos/main';
import Edgeware from 'controllers/chain/edgeware/main';
Expand Down Expand Up @@ -610,36 +615,54 @@ const HeaderNotificationRow: m.Component<IHeaderNotificationRow> = {
]);
};

const {
author,
createdAt,
notificationHeader,
notificationBody,
path,
pageJump
} = getNotificationFields(category, JSON.parse(notification.data));

return getHeaderNotificationRow(
author,
createdAt,
notificationHeader,
notificationBody,
path,
pageJump
);

// else if (category === NotificationCategories.NewCommunity) {
// //const { created_at, proposal_id } = JSON.parse(notification.data);
// //const thread = app.threads.store.getByIdentifier(proposal_id);
// const community = app.activeId();

// return getHeaderNotificationRow(
// moment.utc(created_at),
// null,
// `New community created`,
// '',
// `/${community}/`);
// }
if (category === NotificationCategories.ChainEvent) {
if (!notification.chainEvent) {
throw new Error('chain event notification does not have expected data');
}
// TODO: use different labelers depending on chain
const chainId = notification.chainEvent.type.chain;
const chainName = app.config.chains.getById(chainId).name;
const label = labelEdgewareEvent(
notification.chainEvent.blockNumber,
chainId,
notification.chainEvent.data,
);
return m('li.HeaderNotificationRow', {
class: notification.isRead ? '' : 'active',
onclick: async () => {
const notificationArray: Notification[] = [];
notificationArray.push(notification);
app.login.notifications.markAsRead(notificationArray).then(() => m.redraw());
if (!label.linkUrl) return;
await m.route.set(label.linkUrl);
m.redraw.sync();
},
}, [
m('.comment-body', [
m('.comment-body-top', `${label.heading} on ${chainName}`),
m('.comment-body-bottom', `Block ${notification.chainEvent.blockNumber}`),
m('.comment-body-excerpt', label.label),
]),
]);
} else {
const {
author,
createdAt,
notificationHeader,
notificationBody,
path,
pageJump
} = getNotificationFields(category, JSON.parse(notification.data));

return getHeaderNotificationRow(
author,
createdAt,
notificationHeader,
notificationBody,
path,
pageJump
);
}
},
};

Expand Down Expand Up @@ -695,6 +718,7 @@ const NotificationButtons: m.Component<{ notifications }> = {
const { notifications } = vnode.attrs;
return m('.NotificationButtons', [
m('.button', {
class: notifications.length > 0 ? '' : 'disabled',
onclick: (e) => {
e.preventDefault();
if (notifications.length < 1) return;
Expand Down Expand Up @@ -726,9 +750,8 @@ const NotificationMenu : m.Component<{ menusOpen }> = {
class: (unreadCount > 0 || invites.length > 0) ? 'unread-notifications' : '',
}, unreadMessage),
}, [
notifications.length > 0 && m(NotificationButtons, { notifications }),
m(Notifications, { notifications }),
notifications.length > 0
&& m(NotificationButtons, { notifications }),
]);
}
};
Expand Down
21 changes: 12 additions & 9 deletions client/scripts/views/components/settings/send_edg_well.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,19 @@ const getBalanceTransferChecks = (
`Transfer fee: ${formatCoin(txFee)}`
]);
}
if (canTransfer && recipientBalance.eqn(0) && (app.chain as Substrate).chain.creationfee.gtn(0)) {
checks.push([
featherIcon('info', 14, 2, '#444'),
`Account creation fee: ${formatCoin((app.chain as Substrate).chain.creationfee)}`
]);

const creationFee = (app.chain as Substrate).chain.creationfee;
if (canTransfer && recipientBalance.eqn(0) && creationFee) {
if (creationFee.gtn(0)) {
checks.push([
featherIcon('info', 14, 2, '#444'),
`Account creation fee: ${formatCoin((app.chain as Substrate).chain.creationfee)}`
]);
}
}
const resultingBalance = app.chain.chain.coins(recipientBalance.add(recipientBalance.gtn(0) ?
amount.sub(txFee) :
amount.sub(txFee)
.sub((app.chain as Substrate).chain.creationfee)));
const resultingBalance = app.chain.chain.coins(recipientBalance.add(recipientBalance.gtn(0)
? amount.sub(txFee)
: (creationFee) ? amount.sub(txFee).sub(creationFee) : amount.sub(txFee)));
if (recipientBalance.eqn(0) && resultingBalance.lt((app.chain as Substrate).chain.existentialdeposit)) {
checks.push([
featherIcon('slash', 14, 2, '#444'),
Expand Down
Loading