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

Feature: Add timestamp field to the Application domain #17

Merged
merged 5 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions db/migrations/1700532563386-Data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = class Data1700532563386 {
name = 'Data1700532563386'

async up(db) {
await db.query(`ALTER TABLE "application" ADD "timestamp" numeric NOT NULL`)
}

async down(db) {
await db.query(`ALTER TABLE "application" DROP COLUMN "timestamp"`)
}
}
1 change: 1 addition & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ type ApplicationFactory @entity @cardinality(value: 5) {
type Application @entity @cardinality(value: 100) {
id: ID!
owner: String
timestamp: BigInt!
factory: ApplicationFactory
inputs: [Input!] @derivedFrom(field: "application")
}
Expand Down
2 changes: 2 additions & 0 deletions src/handlers/ApplicationCreated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ export default class ApplicationCreated implements Handler {
id,
factory,
owner: dappOwner.toLowerCase(),
timestamp: timestamp / 1000n,
});

this.applicationStorage.set(id, app);
ctx.log.info(`${id} (Application) stored`);
}
Expand Down
8 changes: 6 additions & 2 deletions src/handlers/InputAdded.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,17 @@ export default class InputAdded implements Handler {
const timestamp = BigInt(log.block.timestamp);
const event = events.InputAdded.decode(log);
const dappId = event.dapp.toLowerCase();
const timestampInSeconds = timestamp / 1000n;

let application =
this.applicationStorage.get(dappId) ??
(await ctx.store.get(Application, dappId));
if (!application) {
ctx.log.warn(`${dappId} (Application) not found`);
application = new Application({ id: dappId });
application = new Application({
id: dappId,
timestamp: timestampInSeconds,
});
this.applicationStorage.set(dappId, application);
ctx.log.info(`${dappId} (Application) stored`);
}
Expand All @@ -73,7 +77,7 @@ export default class InputAdded implements Handler {
index: Number(event.inboxInputIndex),
msgSender: event.sender.toLowerCase(),
payload: event.input,
timestamp: timestamp / 1000n,
timestamp: timestampInSeconds,
blockNumber: BigInt(log.block.height),
blockHash: log.block.hash,
transactionHash: log.transaction?.hash,
Expand Down
26 changes: 26 additions & 0 deletions tests/handlers/ApplicationCreated.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { beforeEach, describe, expect, test, vi } from 'vitest';
import ApplicationCreated from '../../src/handlers/ApplicationCreated';
import { Application } from '../../src/model';
import { block, ctx, logs } from '../stubs/params';

vi.mock('../../src/model/', async (importOriginal) => {
Expand All @@ -13,10 +14,13 @@ vi.mock('../../src/model/', async (importOriginal) => {
};
});

const ApplicationMock = vi.mocked(Application);

describe('ApplicationCreated', () => {
let applicationCreated: ApplicationCreated;
const mockFactoryStorage = new Map();
const mockApplicationStorage = new Map();

beforeEach(() => {
applicationCreated = new ApplicationCreated(
mockFactoryStorage,
Expand All @@ -26,6 +30,7 @@ describe('ApplicationCreated', () => {
mockApplicationStorage.clear();
vi.clearAllMocks();
});

describe('handle', async () => {
test('call with correct params', async () => {
vi.spyOn(applicationCreated, 'handle');
Expand All @@ -49,5 +54,26 @@ describe('ApplicationCreated', () => {
expect(mockFactoryStorage.has(logs[1].address)).toBe(true);
expect(mockApplicationStorage.has(applicationId)).toBe(true);
});

test('set the timestamp in seconds from the block timestamp', async () => {
const expectedParams = vi.fn();

ApplicationMock.mockImplementationOnce((args) => {
expectedParams(args);
return new Application(args);
});

await applicationCreated.handle(logs[1], block, ctx);
const applicationId = '0x0be010fa7e70d74fa8b6729fe1ae268787298f54';
const timestampInSeconds = BigInt(logs[1].block.timestamp) / 1000n;

expect(expectedParams).toHaveBeenCalledOnce();
expect(expectedParams).toHaveBeenCalledWith({
factory: expect.any(Object),
id: applicationId,
owner: '0x74d093f6911ac080897c3145441103dabb869307',
timestamp: timestampInSeconds,
});
});
});
});
24 changes: 23 additions & 1 deletion tests/handlers/InputAdded.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { dataSlice, getUint } from 'ethers';
import { beforeEach, describe, expect, test, vi } from 'vitest';
import { Contract } from '../../src/abi/ERC20';
import InputAdded from '../../src/handlers/InputAdded';
import { Erc20Deposit, Token } from '../../src/model';
import { Application, Erc20Deposit, Token } from '../../src/model';
import { block, ctx, input, logs } from '../stubs/params';

vi.mock('../../src/abi/ERC20', async (importOriginal) => {
Expand Down Expand Up @@ -30,9 +30,13 @@ vi.mock('../../src/model/', async (importOriginal) => {
Input,
};
});

const ApplicationMock = vi.mocked(Application);

const tokenAddress = dataSlice(input.payload, 1, 21).toLowerCase(); // 20 bytes for address
const from = dataSlice(input.payload, 21, 41).toLowerCase(); // 20 bytes for address
const amount = getUint(dataSlice(input.payload, 41, 73)); // 32 bytes for uint256

describe('InputAdded', () => {
let inputAdded: InputAdded;
let erc20;
Expand Down Expand Up @@ -152,5 +156,23 @@ describe('InputAdded', () => {
await inputAdded.handle(logs[0], block, ctx);
expect(mockDepositStorage.size).toBe(1);
});

test('when creating a non-existing app it should also set the timestamp in seconds', async () => {
const expectedParams = vi.fn();

ApplicationMock.mockImplementationOnce((args) => {
expectedParams(args);
return new Application(args);
});

await inputAdded.handle(logs[0], block, ctx);

const timestamp = BigInt(logs[0].block.timestamp) / 1000n;

expect(expectedParams).toHaveBeenCalledWith({
id: '0x0be010fa7e70d74fa8b6729fe1ae268787298f54',
timestamp,
});
});
});
});
8 changes: 5 additions & 3 deletions tests/stubs/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
CartesiDAppFactoryAddress,
ERC20PortalAddress,
} from '../../src/config';
import { Input } from '../../src/model';
vi.mock('@subsquid/logger', async (importOriginal) => {
const actualMods = await importOriginal;
const Logger = vi.fn();
Expand All @@ -31,21 +32,22 @@ export const input = {
id: '0x60a7048c3136293071605a4eaffef49923e981cc-0',
application: {
id: '0x60a7048c3136293071605a4eaffef49923e981cc',
timestamp: 1696281168n,
owner: null,
factory: null,
inputs: [],
},
index: 1,
msgSender: ERC20PortalAddress,
payload: payload,
timestamp: 1691384268 as unknown as bigint,
blockNumber: 4040941 as unknown as bigint,
timestamp: 1691384268n,
blockNumber: 4040941n,
blockHash:
'0xce6a0d404b4201b3bd4fb8309df0b6a64f6a5d7b71fa89bf2737d4574c58b32f',
erc20Deposit: null,
transactionHash:
'0x6a3d76983453c0f74188bd89e01576c35f9d9b02daecdd49f7171aeb2bd3dc78',
};
} satisfies Input;

export const logs = [
{
Expand Down
5 changes: 3 additions & 2 deletions vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { defineConfig } from 'vitest/config'
import { UserConfig } from 'vitest';
import { defineConfig } from 'vitest/config';

export default defineConfig({
test: {
coverage: {
reporter: ['text', 'lcov'],
exclude: ['src/abi', 'src/model', 'tests/'],
},
} as UserConfig,
})
});