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

Add rewards transactions to dapp staking history #106

Merged
merged 13 commits into from
Aug 31, 2023
3 changes: 2 additions & 1 deletion src/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ import { DappsStakingStatsService, IDappsStakingStatsService } from './services/
import { IDappRadarService, DappRadarService } from './services/DappRadarService';
import { GiantSquidService, IGiantSquidService } from './services/GiantSquidService';
import {
BatchCallParser,
BondAndStakeParser,
CallNameMapping,
CallParser,
ICallParser,
NominationTransferParser,
UnbondAndUnstakeParser,
Expand Down Expand Up @@ -125,6 +125,7 @@ container
.bind<ICallParser>(CallNameMapping.withdraw_from_unregistered)
.to(WithdrawFromUnbondedParser)
.inSingletonScope();
container.bind<ICallParser>(CallNameMapping.batch).to(BatchCallParser).inSingletonScope();

// controllers registration
container.bind<IControllerBase>(ContainerTypes.Controller).to(TokenStatsController);
Expand Down
36 changes: 22 additions & 14 deletions src/controllers/DappsStakingController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,13 +268,17 @@ export class DappsStakingController extends ControllerBase implements IControlle
enum: ['7 eras', '30 eras', '90 eras', 'all']
}
*/
res.json(
await this._statsService.getContractStatistics(
req.params.network as NetworkType,
req.params.contractAddress,
req.params.period as PeriodTypeEra,
),
);
try {
res.json(
await this._statsService.getContractStatistics(
req.params.network as NetworkType,
req.params.contractAddress,
req.params.period as PeriodTypeEra,
),
);
} catch (err) {
this.handleError(res, err as Error);
}
},
);

Expand Down Expand Up @@ -302,13 +306,17 @@ export class DappsStakingController extends ControllerBase implements IControlle
}
*/
// this._giantSquidService.getUserCalls(req.params.network as NetworkType, req.params.userAddress, req.params.period as PeriodType);
res.json(
await this._giantSquidService.getUserCalls(
req.params.network as NetworkType,
req.params.userAddress,
req.params.period as PeriodType,
),
);
try {
res.json(
await this._giantSquidService.getUserCalls(
req.params.network as NetworkType,
req.params.userAddress,
req.params.period as PeriodType,
),
);
} catch (err) {
this.handleError(res, err as Error);
}
},
);

Expand Down
1 change: 1 addition & 0 deletions src/services/GiantSquid/CallNameMapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export const CallNameMapping: Map = {
nomination_transfer: 'NominationTransfer',
withdraw_unbonded: 'Withdraw',
withdraw_from_unregistered: 'WithdrawFromUnregistered',
batch: 'Batch',
};
29 changes: 29 additions & 0 deletions src/services/GiantSquid/CallParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,32 @@ export class WithdrawFromUnbondedParser extends CallParser implements ICallParse
return result;
}
}

// Parses Reward events from a batch call and calculates total rewards per batch.
@injectable()
export class BatchCallParser extends CallParser implements ICallParser {
public parse(call: DappStakingCallData): UserEvent {
const EVENT_NAME = 'Reward';
const PALLET_NAME = 'DappsStaking';

let reward = BigInt(0);
for (const event of call.extrinsic.events) {
if (event.eventName === EVENT_NAME && event.palletName === PALLET_NAME) {
reward += BigInt(event.argsStr[4]);
}
}

// If reward is 0 this means that batch call was not claim call and we should not return it.
if (reward === BigInt(0)) {
throw new Error("Batch doesn't contain claim calls");
}

return {
timestamp: new Date(call.timestamp).getTime(),
transaction: EVENT_NAME,
transactionHash: call.extrinsicHash,
transactionSuccess: call.success,
amount: reward.toString(),
};
}
}
11 changes: 11 additions & 0 deletions src/services/GiantSquid/ResponseData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,15 @@ export interface DappStakingCallData {
extrinsicHash: string;
success: boolean;
timestamp: string;
extrinsic: {
events: DappStakingEvent[];
};
}

export interface DappStakingEvent {
argsStr: string[];
eventName: string;
palletName: string;
extrinsicHash: string;
timestamp: string;
}
55 changes: 39 additions & 16 deletions src/services/GiantSquidService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import axios from 'axios';
import { NetworkType } from '../networks';
import { Guard } from '../guard';
import { decodeAddress } from '@polkadot/util-crypto';
import { u8aToHex } from '@polkadot/util';
import { PeriodType, ServiceBase } from './ServiceBase';
import { UserEvent } from '../models/DappStaking';
import { DappStakingCallData, DappStakingCallResponse } from './GiantSquid/ResponseData';
Expand All @@ -25,24 +26,41 @@ export class GiantSquidService extends ServiceBase implements IGiantSquidService
return [];
}

const privateKey = `0x${Buffer.from(decodeAddress(address)).toString('hex')}`;
const publicKey = u8aToHex(decodeAddress(address));
const range = this.getDateRange(period);

const query = `query MyQuery {
calls(where: {
palletName_eq: "DappsStaking",
callerPublicKey_eq: "${privateKey}",
timestamp_gte: "${range.start.toISOString()}",
timestamp_lte: "${range.end.toISOString()}",
callName_not_contains: "claim"
}, orderBy: block_id_DESC) {
callName
argsStr
extrinsicHash
success
timestamp
}
}`;
calls(where: {
callerPublicKey_eq: "${publicKey}",
palletName_eq: "DappsStaking",
timestamp_gte: "${range.start.toISOString()}",
timestamp_lte: "${range.end.toISOString()}",
OR: {
callerPublicKey_eq: "${publicKey}",
callName_contains: "batch",
palletName_eq: "Utility",
timestamp_gte: "${range.start.toISOString()}",
timestamp_lte: "${range.end.toISOString()}",
},
callName_not_contains: "claim"
}, orderBy: block_timestamp_DESC) {
callName
argsStr
palletName
success
timestamp
extrinsicHash
extrinsic {
events {
argsStr
eventName
palletName
extrinsicHash
timestamp
}
}
}
}`;

const result = await axios.post<DappStakingCallResponse>(this.getApiUrl(network), {
operationName: 'MyQuery',
Expand All @@ -62,7 +80,12 @@ export class GiantSquidService extends ServiceBase implements IGiantSquidService
for (const call of calls) {
if (CallNameMapping[call.callName]) {
const parser = container.get<ICallParser>(CallNameMapping[call.callName]);
result.push(parser.parse(call));
try {
result.push(parser.parse(call));
} catch (e) {
console.log(e);
// Nothing special to do here. Batch call parser raised an error because batch the call doesn't contain claim calls.
}
} else {
// Call is not supported. Do nothing. Currently only calls defined in CallNameMapping are supported.
}
Expand Down
Loading