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

feat: 🎸 Add PolyxTransaction entity #113

Merged
merged 18 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
fb961ef
feat: 🎸 Add PolyxTransaction entity
prashantasdeveloper Jan 10, 2023
e10b2bb
chore: 🤖 Add type to PolyxTransaction entity
prashantasdeveloper Jan 17, 2023
c63ea02
Merge branch 'alpha' of github.com:PolymeshAssociation/polymesh-subqu…
prashantasdeveloper Apr 11, 2023
cbe827a
chore: 🤖 Populate primaryAccount key for genesis block DIDs
prashantasdeveloper Apr 13, 2023
9bdf610
chore: 🤖 Handle edge cases for duplication of entries
prashantasdeveloper Apr 13, 2023
4e19a31
refactor: 💡 Remove unnecessary logs
prashantasdeveloper Apr 13, 2023
be8c4ae
chore: 🤖 Handle `BalanceSet` event
prashantasdeveloper Apr 17, 2023
0c4d00a
Merge branch 'alpha' of github.com:PolymeshAssociation/polymesh-subqu…
prashantasdeveloper Apr 18, 2023
2c1d47e
feat: 🎸 Add accounts for genesis block DIDs
prashantasdeveloper Apr 18, 2023
ec565d8
chore: 🤖 Add migrations
prashantasdeveloper Apr 19, 2023
32b98b1
chore: 🤖 add entity creation statement
prashantasdeveloper Apr 19, 2023
71a05ab
chore: 🤖 handle data migration with block handlers
prashantasdeveloper Apr 20, 2023
1bf956c
refactor: 💡 remove code duplication
prashantasdeveloper Apr 20, 2023
cb2dc4b
Merge branch 'alpha' of github.com:PolymeshAssociation/polymesh-subqu…
prashantasdeveloper Apr 20, 2023
8479067
chore: 🤖 add `eventIdx` attribute
prashantasdeveloper May 2, 2023
06e9345
Merge branch 'alpha' of github.com:PolymeshAssociation/polymesh-subqu…
prashantasdeveloper Jul 25, 2023
fc80625
chore: 🤖 Update migration file name and handle 6.0.0 changes
prashantasdeveloper Jul 25, 2023
79d2f76
refactor: 💡 change switch to if
prashantasdeveloper Jul 25, 2023
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
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"fundings",
"Polkadot",
"Polymesh",
"Polyx",
"signedbyAddress",
"snapshotted",
"subql",
Expand Down
109 changes: 83 additions & 26 deletions db/genesisMigrations.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,105 @@
import { ApiPromise, WsProvider } from '@polkadot/api';
import { Option } from '@polkadot/types-codec';
import { Codec } from '@polkadot/types-codec/types';
import { env } from 'process';
import { getAccountId, systematicIssuers } from '../src/mappings/consts';

type DidWithAccount = { did: string; accountId: string };
// Insert for genesis block id
const genesisBlock = `INSERT INTO "public"."blocks" ("id", "block_id", "parent_id", "hash", "parent_hash", "state_root", "extrinsics_root", "count_extrinsics", "count_extrinsics_unsigned", "count_extrinsics_signed", "count_extrinsics_error", "count_extrinsics_success", "count_events", "datetime", "spec_version_id", "created_at", "updated_at") VALUES
('0', 0, 0, '${env.NETWORK_CHAIN_ID}', '', '', '', 0, 0, 0, 0, 0, 0, now(), '3000', now(), now()) ON CONFLICT(id) DO NOTHING;`;

const getAccounts = ({ did, accountId }: DidWithAccount): string[] => {
const permissionsInsert = `INSERT INTO "public"."permissions" ("id", "assets", "portfolios", "transactions", "transaction_groups", "created_block_id", "updated_block_id", "datetime", "created_at", "updated_at") VALUES
('${accountId}', null, null, null, '[]', '0', '0', now(), now(), now()) ON CONFLICT(id) DO NOTHING;`;
const accountInsert = `INSERT INTO "public"."accounts"("id", "identity_id", "permissions_id", "event_id", "address", "created_block_id", "updated_block_id", "datetime", "created_at", "updated_at") VALUES
('${accountId}', '${did}', '${accountId}', 'DidCreated', '${accountId}', '0', '0', now(), now(), now()) ON CONFLICT(id) DO NOTHING;`;

return [permissionsInsert, accountInsert];
};

/**
* Get `identities` and `portfolios` inserts for a specific DID
*/
const getInserts = ({ did, accountId }: DidWithAccount): string[] => {
const insertQueries = [
`INSERT INTO "public"."identities" ("id", "did", "primary_account", "secondary_keys_frozen", "event_id", "created_block_id", "updated_block_id", "datetime", "created_at", "updated_at") VALUES
('${did}', '${did}', '${accountId}', 'f', 'DidCreated', 0, 0, now(), now(), now()) ON CONFLICT(id) DO UPDATE SET
"primary_account" = excluded.primary_account;`,
`INSERT INTO "public"."portfolios" ("id", "identity_id", "number", "name", "custodian_id", "event_idx", "created_block_id", "updated_block_id", "created_at", "updated_at") VALUES
('${did}/0', '${did}', 0, NULL, NULL, 1, '0', '0', now(), now()) ON CONFLICT(id) DO NOTHING;`,
];

if (accountId) {
insertQueries.push(...getAccounts({ did, accountId }));
}

return insertQueries;
};

/*
* This function returns SQL statements to be inserted before processing of any blocks.
*
* For all the `Systematic Issuers` and GC identities, we need to insert a row in `identities` and its corresponding default portfolio entry in `portfolios` table.
*/
export const genesisMigrationQueries = (): string[] => {
const genesisBlock = `INSERT INTO "public"."blocks" ("id", "block_id", "parent_id", "hash", "parent_hash", "state_root", "extrinsics_root", "count_extrinsics", "count_extrinsics_unsigned", "count_extrinsics_signed", "count_extrinsics_error", "count_extrinsics_success", "count_events", "datetime", "spec_version_id", "created_at", "updated_at") VALUES
('0', 0, 0, '${env.NETWORK_CHAIN_ID}', '', '', '', 0, 0, 0, 0, 0, 0, now(), '3000', now(), now()) ON CONFLICT(id) DO NOTHING;`;
export const genesisMigrationQueries = async (): Promise<string[]> => {
const wsProvider = new WsProvider(env.NETWORK_ENDPOINT);
const api = await ApiPromise.create({ provider: wsProvider });
// get the chain information to extract SS58Format
const chainInfo = await api.registry.getChainProperties();

// List of all the systematic issuers can be found [here](https://github.com/PolymeshAssociation/Polymesh/blob/d45fd1a161990310242f21230ac8a1a5d15498eb/pallets/common/src/constants.rs#L23)
const systematicIssuers = [
'0x73797374656d3a676f7665726e616e63655f636f6d6d69747465650000000000', // Governance Committee
'0x73797374656d3a637573746f6d65725f6475655f64696c6967656e6365000000', // CDD Providers
'0x73797374656d3a74726561737572795f6d6f64756c655f646964000000000000', // Treasury
'0x73797374656d3a626c6f636b5f7265776172645f726573657276655f64696400', // Block Reward Reserve
'0x73797374656d3a736574746c656d656e745f6d6f64756c655f64696400000000', // Settlement Module
'0x73797374656d3a706f6c796d6174685f636c61737369635f6d69670000000000', // Classic Migration
'0x73797374656d3a666961745f7469636b6572735f7265736572766174696f6e00', // FIAT Tickers Reservation
'0x73797374656d3a726577617264735f6d6f64756c655f64696400000000000000', // Rewards
];
const ss58Format = chainInfo?.ss58Format.unwrapOrDefault().toNumber();

// There are special Identities specified in the chain's genesis block that need to be included in the DB.
const gcIdentities = Array(33)
const gcDids = Array(33)
.fill('')
.map((_, index) => {
const twoDigitNumber = index.toString(16).padStart(2, '0');
return `0x${twoDigitNumber}`.padEnd(66, '0');
});
const rawGcAccountIds = await api.query.identity.didRecords.multi(gcDids);

/**
* Get `identities` and `portfolios` inserts for a specific DID
*/
const getInserts = (did: string): string[] => [
`INSERT INTO "public"."identities" ("id", "did", "primary_account", "secondary_keys_frozen", "event_id", "created_block_id", "updated_block_id", "datetime", "created_at", "updated_at") VALUES
('${did}', '${did}', 'primaryAccount', 'f', 'DidCreated', 0, 0, now(), now(), now()) ON CONFLICT(id) DO NOTHING;`,
const gcIdentities = rawGcAccountIds.map((account, index) => {
const rawAccount = account as Option<Codec>;
return {
did: gcDids[index],
accountId: rawAccount.unwrapOrDefault().toJSON()?.['primaryKey'],
};
});

`INSERT INTO "public"."portfolios" ("id", "identity_id", "number", "name", "custodian_id", "event_idx", "created_block_id", "updated_block_id", "created_at", "updated_at") VALUES
('${did}/0', '${did}', 0, NULL, NULL, 1, '0', '0', now(), now()) ON CONFLICT(id) DO NOTHING;`,
];
const accountPromises = await Promise.all(
gcIdentities
.filter(({ accountId }) => accountId !== null)
.map(({ did }) => api.query.identity.didKeys.entries(did))
);

const otherAccounts = accountPromises.reduce((result: string[], accountEntry) => {
const accounts: string[] = accountEntry
.map(
([
{
args: [rawDid, rawAccountId],
},
]) =>
getAccounts({
did: rawDid.toString(),
accountId: rawAccountId.toString(),
})
)
.flat();
return [...result, ...accounts];
}, []);

const identityAndPortfolioInserts = [...systematicIssuers, ...gcIdentities]
const systematicIssuerIdentities = Object.values(systematicIssuers).map(({ did, accountId }) => ({
did,
accountId: getAccountId(accountId, ss58Format),
}));

const identityAndPortfolioInserts = [...systematicIssuerIdentities, ...gcIdentities]
.map(getInserts)
.flat();

return [genesisBlock, ...identityAndPortfolioInserts];
await api.disconnect();

return [genesisBlock, ...identityAndPortfolioInserts, ...otherAccounts];
};
47 changes: 47 additions & 0 deletions db/migrations/3_polyx_transactions_entity.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
alter table migrations add column if not exists executed boolean;
update migrations set executed = false where executed is null;
alter table migrations alter column executed set not null;

DO $$
BEGIN
IF NOT EXISTS (select 1 from pg_type where typname = 'public_enum_5df0f1d22c') then
create type public_enum_5df0f1d22c AS ENUM ('Free', 'Reserved', 'Bonded', 'Unbonded', 'Locked');
END IF;
END
$$;

create table if not exists polyx_transactions
(
id text not null PRIMARY KEY,
identity_id text,
"address" text,
to_id text,
to_address text,
amount numeric not null,
"type" public_enum_5df0f1d22c not null,
module_id public_enum_7a0b4cc03e,
call_id public_enum_0bf3c7d4ef,
event_id public_enum_8f5a39c8ee,
memo text,
extrinsic_id text,
"datetime" timestamp without time zone not null,
created_block_id text not null,
updated_block_id text not null,
created_at timestamp with time zone not null,
updated_at timestamp with time zone not null
);

alter table polyx_transactions
drop constraint if exists "polyx_transactions_extrinsic_id_fkey",
drop constraint if exists "polyx_transactions_created_block_id_fkey",
drop constraint if exists "polyx_transactions_updated_block_id_fkey";

alter table polyx_transactions
add constraint "polyx_transactions_extrinsic_id_fkey" foreign key (extrinsic_id) references extrinsics(id) ON UPDATE CASCADE ON DELETE SET NULL,
add constraint "polyx_transactions_created_block_id_fkey" foreign key (created_block_id) references blocks(id) on update cascade,
add constraint "polyx_transactions_updated_block_id_fkey" foreign key (updated_block_id) references blocks(id) on update cascade;

create index if not exists "polyx_transactions_pkey" on polyx_transactions using btree (id);
create index if not exists "polyx_transactions_created_block_id" on polyx_transactions using hash (created_block_id);
create index if not exists "polyx_transactions_extrinsic_id" on polyx_transactions using hash (extrinsic_id);
create index if not exists "polyx_transactions_updated_block_id" on polyx_transactions using hash (updated_block_id);
4 changes: 2 additions & 2 deletions db/schemaMigrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ const getOldMigrationQueries = (): string[] => {
const migrationInsert = (
migrationNumber: number,
id?: string
) => `INSERT INTO "public"."migrations" ("id", "number", "version", "created_at", "updated_at")
VALUES ('${id || migrationNumber}', ${migrationNumber}, '${latestVersion}', now(), now())
) => `INSERT INTO "public"."migrations" ("id", "number", "version", "executed", "created_at", "updated_at")
VALUES ('${id || migrationNumber}', ${migrationNumber}, '${latestVersion}', false, now(), now())
ON CONFLICT(id) DO UPDATE SET "updated_at" = now();`;

export const schemaMigrations = async (connection?: Connection): Promise<void> => {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"@types/bn.js": "^4.11.6",
"@types/jest": "^27.0.1",
"@types/js-yaml": "^4.0.3",
"@types/node": "^18.15.11",
"@typescript-eslint/eslint-plugin": "^4.29.2",
"@typescript-eslint/parser": "^4.29.2",
"babel-jest": "^27.0.6",
Expand Down
34 changes: 34 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2081,4 +2081,38 @@ type Migration @entity {
id: ID!
number: Int!
version: String!
executed: Boolean! @index
}

"""
Represents possible all possible balance types
"""
enum BalanceTypeEnum {
Free
Reserved
Bonded
Unbonded
Locked
}

"""
Represents transactions involving POLYX
"""
type PolyxTransaction @entity {
id: ID!
identityId: String
address: String
toId: String
toAddress: String
amount: BigInt!
type: BalanceTypeEnum!
moduleId: ModuleIdEnum
callId: CallIdEnum
eventId: EventIdEnum
memo: String
extrinsic: Extrinsic
datetime: Date!
eventIdx: Int!
createdBlock: Block!
updatedBlock: Block!
}
3 changes: 2 additions & 1 deletion scripts/run-sql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ const main = async (): Promise<void> => {
await postgres.query(readFileSync('../db/compat.sql', 'utf-8'));
console.log('Applied initial SQL');

await postgres.query(genesisMigrationQueries().join('\n'));
const migrationQueries = await genesisMigrationQueries();
await postgres.query(migrationQueries.join('\n'));
console.log('Applied genesis migration SQL');

await updateSQVersion(postgres);
Expand Down
41 changes: 41 additions & 0 deletions src/mappings/consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { encodeAddress } from '@polkadot/keyring';
import { stringToHex } from '@polkadot/util';

export const getAccountId = (value: string, ss58Format?: number): string =>
encodeAddress(stringToHex(`modl${value}`).padEnd(66, '0'), ss58Format);

// List of all the systematic issuers can be found [here](https://github.com/PolymeshAssociation/Polymesh/blob/d45fd1a161990310242f21230ac8a1a5d15498eb/pallets/common/src/constants.rs#L23)
export const systematicIssuers = {
governanceCommittee: {
did: '0x73797374656d3a676f7665726e616e63655f636f6d6d69747465650000000000', // Governance Committee
accountId: 'pm/govcm',
},
cddProvider: {
did: '0x73797374656d3a637573746f6d65725f6475655f64696c6967656e6365000000',
accountId: 'pm/cusdd',
},
treasury: {
did: '0x73797374656d3a74726561737572795f6d6f64756c655f646964000000000000',
accountId: 'pm/trsry',
},
blockRewardReserve: {
did: '0x73797374656d3a626c6f636b5f7265776172645f726573657276655f64696400',
accountId: 'pm/blrwr',
},
settlementModule: {
did: '0x73797374656d3a736574746c656d656e745f6d6f64756c655f64696400000000',
accountId: 'pm/setmn',
},
classicMigration: {
did: '0x73797374656d3a706f6c796d6174685f636c61737369635f6d69670000000000',
accountId: 'pm/ehmig',
},
fiatTickersReservation: {
did: '0x73797374656d3a666961745f7469636b6572735f7265736572766174696f6e00',
accountId: 'pm/ftres',
},
rewards: {
did: '0x73797374656d3a726577617264735f6d6f64756c655f64696400000000000000',
accountId: 'pm/rewrd',
},
};
Loading
Loading