Skip to content

Commit

Permalink
fix: update subscan to v2 (#627)
Browse files Browse the repository at this point in the history
The SubScan is going to deprecate it's /api/scan/events endpoint on 01.March.2024.

On this PullRequest, this other two endpoints /api/v2/scan/events and /api/scan/event/params replace it.

The API responses are different so some tweaking needed to be done.

Commits:

* feat: subScan.ts from SocialKYC

* fix: update event parsing and value extraction

* fix: update params parsing and value extraction for scanCTypes.ts

* feat: use CType.hashToId() instead of manually converting it

* feat: move parseParams() inside of subScan.ts

* fix: update subscan tests

* fix: update Event mocking

* fix: disable eslint/no-unsafe-return foe one line

* fix: importing structure

* fix: deleted commented testes

* fix: tests a bit more

* feat: make transform() from subScanEventGenerator optional

* fix: tests a bit more

* fix: use new names for parameters on tests

* fix: shorten the import paths
  • Loading branch information
kilted-andres authored Feb 22, 2024
1 parent 3eb8c89 commit e30d9db
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 173 deletions.
20 changes: 15 additions & 5 deletions src/utilities/scanAttestations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { resetDatabase } from '../../testing/resetDatabase';

import { configuration } from './configuration';
import { subScanEventGenerator } from './subScan';
import { type EventParams, scanAttestations } from './scanAttestations';
import { scanAttestations } from './scanAttestations';

vi.mock('./subScan');

Expand Down Expand Up @@ -68,20 +68,30 @@ async function createAttestation(assertionMethod: KiltKeyringPair) {
}

function mockAttestationEvent() {
const mockParams: EventParams = [
const mockParams = [
{
type_name: 'AttesterOf',
value: Utils.Crypto.u8aToHex(
Utils.Crypto.decodeAddress(Did.toChain(did)),
) as unknown as EventParams[0]['value'],
),
},
{ type_name: 'ClaimHashOf', value: attestation.claimHash },
{ type_name: 'CTypeHashOf', value: CType.idToHash(cType.$id) },
{ type_name: 'DelegationNodeIdOf', value: null },
{ type_name: 'CtypeHashOf', value: CType.idToHash(cType.$id) },
{ type_name: 'Option<DelegationNodeIdOf>', value: null },
];
const mockParsedParams = {
AttesterOf: Utils.Crypto.u8aToHex(
Utils.Crypto.decodeAddress(Did.toChain(did)),
),
ClaimHashOf: attestation.claimHash,
CtypeHashOf: CType.idToHash(cType.$id),
'Option<DelegationNodeIdOf>': null,
};

vi.mocked(subScanEventGenerator).mockImplementation(async function* () {
yield {
params: mockParams,
parsedParams: mockParsedParams,
block: 123456,
blockTimestampMs: 160273245600,
extrinsicHash: extrinsic.hash.toHex(),
Expand Down
23 changes: 11 additions & 12 deletions src/utilities/scanAttestations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@ import { Attestation as AttestationModel } from '../models/attestation';
import { subScanEventGenerator } from './subScan';
import { logger } from './logger';

export type EventParams = [
{ type_name: 'AttesterOf'; value: Parameters<typeof Did.fromChain>[0] },
{ type_name: 'ClaimHashOf'; value: HexString },
{ type_name: 'CTypeHashOf'; value: HexString },
{ type_name: 'DelegationNodeIdOf'; value: HexString | null },
];

export async function scanAttestations() {
const latestAttestation = await AttestationModel.findOne({
order: [['createdAt', 'DESC']],
Expand All @@ -28,13 +21,19 @@ export async function scanAttestations() {

for await (const event of eventGenerator) {
const { block, blockTimestampMs, extrinsicHash } = event;
const params = event.params as EventParams;
const params = event.parsedParams;

const createdAt = new Date(blockTimestampMs);

const owner = Did.fromChain(params[0].value);
const claimHash = params[1].value;
const cTypeId = CType.hashToId(params[2].value);
const delegationId = params[3].value;
const owner = Did.fromChain(
params.AttesterOf as Parameters<typeof Did.fromChain>[0],
);
const claimHash = params.ClaimHashOf as HexString;
const cTypeHash = params.CtypeHashOf as HexString;
const cTypeId = CType.hashToId(cTypeHash);
const delegationId = params[
'Option<DelegationNodeIdOf>'
] as HexString | null;

try {
await AttestationModel.upsert({
Expand Down
13 changes: 9 additions & 4 deletions src/utilities/scanCTypes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { createDid } from '../../testing/createDid';
import { createCType } from '../../testing/createCType';
import { resetDatabase } from '../../testing/resetDatabase';

import { type EventParams, scanCTypes } from './scanCTypes';
import { scanCTypes } from './scanCTypes';
import { configuration } from './configuration';
import { subScanEventGenerator } from './subScan';

Expand All @@ -31,13 +31,18 @@ let cType: ICType;
let extrinsic: SubmittableExtrinsic;

function mockCTypeEvent() {
const mockParams: EventParams = [
{ type_name: 'CTypeCreatorOf', value: '0xexamplecreator' },
{ type_name: 'CTypeHashOf', value: CType.idToHash(cType.$id) },
const mockParams = [
{ type_name: 'CtypeCreatorOf', value: '0xexamplecreator' },
{ type_name: 'CtypeHashOf', value: CType.idToHash(cType.$id) },
];
const mockParsedParams = {
CtypeCreatorOf: '0xexamplecreator',
CtypeHashOf: CType.idToHash(cType.$id),
};
vi.mocked(subScanEventGenerator).mockImplementation(async function* () {
yield {
params: mockParams,
parsedParams: mockParsedParams,
block: 123456,
blockTimestampMs: 160273245600,
extrinsicHash: extrinsic.hash.toHex(),
Expand Down
9 changes: 2 additions & 7 deletions src/utilities/scanCTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@ import { CType as CTypeModel } from '../models/ctype';
import { logger } from './logger';
import { subScanEventGenerator } from './subScan';

export type EventParams = [
{ type_name: 'CTypeCreatorOf'; value: HexString },
{ type_name: 'CTypeHashOf'; value: HexString },
];

export async function scanCTypes() {
const latestCType = await CTypeModel.findOne({
order: [['createdAt', 'DESC']],
Expand All @@ -31,8 +26,8 @@ export async function scanCTypes() {

for await (const event of eventGenerator) {
const { blockTimestampMs, extrinsicHash } = event;
const params = event.params as EventParams;
const cTypeHash = params[1].value;
const params = event.parsedParams;
const cTypeHash = params.CtypeHashOf as HexString;

let cTypeDetails: CType.ICTypeDetails;
try {
Expand Down
135 changes: 17 additions & 118 deletions src/utilities/subScan.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { got } from 'got';
import { ConfigService, type connect } from '@kiltprotocol/sdk-js';

import {
type EventsResponseJson,
getEvents,
subScanEventGenerator,
type EventsListJSON,
type EventsParamsJSON,
} from './subScan';
import { configuration } from './configuration';

Expand All @@ -20,10 +21,11 @@ const api = {
} as unknown as Awaited<ReturnType<typeof connect>>;
ConfigService.set({ api });

let postResponse: EventsResponseJson;
let postResponse: EventsListJSON | EventsParamsJSON;
vi.mock('got', () => ({
got: {
post: vi.fn().mockReturnValue({
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
json: () => postResponse,
}),
},
Expand All @@ -34,7 +36,7 @@ beforeEach(() => {
});

const module = 'ctype';
const call = 'CTypeCreated';
const eventId = 'CTypeCreated';

describe('subScan', () => {
describe('getEvents', () => {
Expand All @@ -43,20 +45,21 @@ describe('subScan', () => {

await getEvents({
module,
call,
eventId,
fromBlock: 10,
page: 0,
row: 0,
});

expect(got.post).toHaveBeenCalledWith(
'https://example.api.subscan.io/api/scan/events',
'https://example.api.subscan.io/api/v2/scan/events',
{
headers: { 'X-API-Key': configuration.subscan.secret },
json: {
module,
call,
event_id: eventId,
block_range: '10-100010',
order: 'asc',
page: 0,
row: 0,
finalized: true,
Expand All @@ -70,7 +73,7 @@ describe('subScan', () => {

const cTypeEvents = await getEvents({
module,
call,
eventId,
fromBlock: 10,
page: 0,
row: 0,
Expand All @@ -79,133 +82,29 @@ describe('subScan', () => {
expect(cTypeEvents.count).toBe(0);
expect(cTypeEvents.events).toBeUndefined();
});

it('should return parsed events in reverse order', async () => {
postResponse = {
data: {
count: 2,
events: [
{
params: '[{ "fake": "JSON" }]',
block_num: 123,
block_timestamp: 123_456,
extrinsic_hash: '0xCAFECAFE',
},
{
params: '[{ "JSON": "fake" }]',
block_num: 789,
block_timestamp: 789_123,
extrinsic_hash: '0xFACEFACE',
},
],
},
};

const cTypeEvents = await getEvents({
module,
call,
fromBlock: 10,
page: 0,
row: 0,
});

expect(cTypeEvents.count).toBe(2);
expect(cTypeEvents.events).toEqual([
{
params: [{ JSON: 'fake' }],
block: 789,
blockTimestampMs: 789_123_000,
extrinsicHash: '0xFACEFACE',
},
{
params: [{ fake: 'JSON' }],
block: 123,
blockTimestampMs: 123_456_000,
extrinsicHash: '0xCAFECAFE',
},
]);
});
});

describe('subScanEventGenerator', () => {
it('should iterate through pages in reverse order', async () => {
postResponse = { data: { count: 200, events: [] } };

const eventGenerator = subScanEventGenerator(module, call, 0);
const eventGenerator = subScanEventGenerator(module, eventId, 0);

for await (const event of eventGenerator) {
expect(event).toBeDefined();
}

expect(got.post).toHaveBeenCalledTimes(3);
expect(got.post).toHaveBeenCalledTimes(6);
const { calls } = vi.mocked(got.post).mock;

// get count
expect(calls[0][1]).toMatchObject({ json: { page: 0, row: 1 } });

// get last page
expect(calls[1][1]).toMatchObject({ json: { page: 1, row: 100 } });
expect(calls[2][1]).toMatchObject({ json: { page: 1, row: 100 } });

// get first page
expect(calls[2][1]).toMatchObject({ json: { page: 0, row: 100 } });
});

it('should yield events in reverse order', async () => {
vi.mocked(got.post)
.mockReturnValueOnce({
// @ts-expect-error but the code doesn’t care about the other members
json: () => ({ data: { count: 200 } }),
})
.mockReturnValueOnce({
// @ts-expect-error but the code doesn’t care about the other members
json: () => ({
data: {
count: 200,
events: [
{
block_timestamp: 1,
params: '"JSON"',
extrinsic_hash: '0xCAFECAFE',
},
{
block_timestamp: 0,
params: '"JSON"',
extrinsic_hash: '0xFACEFACE',
},
],
},
}),
})
.mockReturnValueOnce({
// @ts-expect-error but the code doesn’t care about the other members
json: () => ({
data: {
count: 200,
events: [
{
block_timestamp: 3,
params: '"JSON"',
extrinsic_hash: '0xCAFECAFE',
},
{
block_timestamp: 2,
params: '"JSON"',
extrinsic_hash: '0xFACEFACE',
},
],
},
}),
});

const eventGenerator = subScanEventGenerator(module, call, 0);

const events = [];
for await (const event of eventGenerator) {
events.push(event);
}

const timestamps = events.map(({ blockTimestampMs }) => blockTimestampMs);
expect(timestamps).toEqual([0, 1000, 2000, 3000]);
expect(calls[4][1]).toMatchObject({ json: { page: 0, row: 100 } });
});
});
it('should get events in batches if current block is higher than block range', async () => {
Expand All @@ -220,16 +119,16 @@ describe('subScan', () => {

postResponse = { data: { count: 100, events: [] } };

const eventGenerator = subScanEventGenerator(module, call, 0);
const eventGenerator = subScanEventGenerator(module, eventId, 0);

for await (const event of eventGenerator) {
expect(event).toBeDefined();
}

expect(got.post).toHaveBeenCalledTimes(4);
expect(got.post).toHaveBeenCalledTimes(8);
const { calls } = vi.mocked(got.post).mock;

expect(calls[3][1]).toMatchObject({
expect(calls[6][1]).toMatchObject({
json: { block_range: '100000-200000', page: 0, row: 100 },
});
});
Expand Down
Loading

0 comments on commit e30d9db

Please sign in to comment.