Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
module.exports = class Data1659350771447 {
name = 'Data1659350771447'
module.exports = class Data1665740912657 {
name = 'Data1665740912657'

async up(db) {
await db.query(`CREATE TABLE "transfer" ("id" character varying NOT NULL, "block_number" integer NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "extrinsic_hash" text, "amount" numeric NOT NULL, "fee" numeric, "from_id" character varying, "to_id" character varying, CONSTRAINT "PK_fd9ddbdd49a17afcbe014401295" PRIMARY KEY ("id"))`)
await db.query(`CREATE TABLE "transfer" ("id" character varying NOT NULL, "block_number" integer NOT NULL, "timestamp" TIMESTAMP WITH TIME ZONE NOT NULL, "extrinsic_hash" text, "amount" numeric NOT NULL, "fee" numeric NOT NULL, "from_id" character varying, "to_id" character varying, CONSTRAINT "PK_fd9ddbdd49a17afcbe014401295" PRIMARY KEY ("id"))`)
await db.query(`CREATE INDEX "IDX_d6624eacc30144ea97915fe846" ON "transfer" ("block_number") `)
await db.query(`CREATE INDEX "IDX_70ff8b624c3118ac3a4862d22c" ON "transfer" ("timestamp") `)
await db.query(`CREATE INDEX "IDX_070c555a86b0b41a534a55a659" ON "transfer" ("extrinsic_hash") `)
Expand Down
18 changes: 4 additions & 14 deletions schema.graphql
Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
type Account @entity {
"Account address"
id: ID!
transfersTo: [Transfer!] @derivedFrom(field: "to")
transfersFrom: [Transfer!] @derivedFrom(field: "from")
}

type Transfer @entity {
id: ID!
blockNumber: Int! @index
timestamp: DateTime! @index
extrinsicHash: String @index
from: Account!
to: Account!
amount: BigInt! @index
fee: BigInt! # fee is calculated at the best effort and may be zero for some old extrinsics
from: String!
to: String!
amount: BigInt!
assetId: String!
}
21 changes: 0 additions & 21 deletions src/model/generated/account.model.ts

This file was deleted.

1 change: 0 additions & 1 deletion src/model/generated/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export * from "./account.model"
export * from "./transfer.model"
30 changes: 7 additions & 23 deletions src/model/generated/transfer.model.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_, Index as Index_, ManyToOne as ManyToOne_} from "typeorm"
import {Entity as Entity_, Column as Column_, PrimaryColumn as PrimaryColumn_} from "typeorm"
import * as marshal from "./marshal"
import {Account} from "./account.model"

@Entity_()
export class Transfer {
Expand All @@ -11,30 +10,15 @@ export class Transfer {
@PrimaryColumn_()
id!: string

@Index_()
@Column_("int4", {nullable: false})
blockNumber!: number
@Column_("text", {nullable: false})
from!: string

@Index_()
@Column_("timestamp with time zone", {nullable: false})
timestamp!: Date
@Column_("text", {nullable: false})
to!: string

@Index_()
@Column_("text", {nullable: true})
extrinsicHash!: string | undefined | null

@Index_()
@ManyToOne_(() => Account, {nullable: true})
from!: Account

@Index_()
@ManyToOne_(() => Account, {nullable: true})
to!: Account

@Index_()
@Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false})
amount!: bigint

@Column_("numeric", {transformer: marshal.bigintTransformer, nullable: false})
fee!: bigint
@Column_("text", {nullable: false})
assetId!: string
}
69 changes: 17 additions & 52 deletions src/processor.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
import {lookupArchive} from "@subsquid/archive-registry"
import * as ss58 from "@subsquid/ss58"
import {BatchContext, BatchProcessorItem, SubstrateBatchProcessor} from "@subsquid/substrate-processor"
import {Store, TypeormDatabase} from "@subsquid/typeorm-store"
import {In} from "typeorm"
import {Account, Transfer} from "./model"
import {Transfer} from "./model"
import {BalancesTransferEvent} from "./types/events"


const processor = new SubstrateBatchProcessor()
.setBatchSize(500)
.setDataSource({
// Lookup archive by the network name in the Subsquid registry
archive: lookupArchive("kusama", {release: "FireSquid"})

// Use archive created by archive/docker-compose.yml
// archive: 'http://localhost:8888/graphql'
archive: 'http://localhost:8888/graphql'
})
.addEvent('Balances.Transfer', {
data: {
event: {
args: true,
extrinsic: {
hash: true,
id: true,
from: true,
to: true,
amount: true,
fee: true
}
}
Expand All @@ -36,60 +33,43 @@ type Ctx = BatchContext<Store, Item>
processor.run(new TypeormDatabase(), async ctx => {
let transfersData = getTransfers(ctx)

let accountIds = new Set<string>()
for (let t of transfersData) {
accountIds.add(t.from)
accountIds.add(t.to)
}

let accounts = await ctx.store.findBy(Account, {id: In([...accountIds])}).then(accounts => {
return new Map(accounts.map(a => [a.id, a]))
})

let transfers: Transfer[] = []

for (let t of transfersData) {
let {id, blockNumber, timestamp, extrinsicHash, amount, fee} = t
let { id, amount } = t

let from = getAccount(accounts, t.from)
let to = getAccount(accounts, t.to)
let from = t.from
let to = t.to

transfers.push(new Transfer({
id,
blockNumber,
timestamp,
extrinsicHash,
from,
to,
amount,
fee
assetId
}))
}

await ctx.store.save(Array.from(accounts.values()))
// await ctx.store.save(Array.from(accounts.values()))
await ctx.store.insert(transfers)
})


interface TransferEvent {
id: string
blockNumber: number
timestamp: Date
extrinsicHash?: string
from: string
to: string
amount: bigint
fee?: bigint
amount: string
assetId: string
}


function getTransfers(ctx: Ctx): TransferEvent[] {
let transfers: TransferEvent[] = []
for (let block of ctx.blocks) {
for (let item of block.items) {
if (item.name == "Balances.Transfer") {
let e = new BalancesTransferEvent(ctx, item.event)
let rec: {from: Uint8Array, to: Uint8Array, amount: bigint}
let rec: {from: string, to: string, amount: bigint}
if (e.isV1020) {
let [from, to, amount,] = e.asV1020
rec = {from, to, amount}
Expand All @@ -101,27 +81,12 @@ function getTransfers(ctx: Ctx): TransferEvent[] {
}
transfers.push({
id: item.event.id,
blockNumber: block.header.height,
timestamp: new Date(block.header.timestamp),
extrinsicHash: item.event.extrinsic?.hash,
from: ss58.codec('kusama').encode(rec.from),
to: ss58.codec('kusama').encode(rec.to),
amount: rec.amount,
fee: item.event.extrinsic?.fee || 0n
from: rec.from,
to: rec.to,
amount: rec.amount
})
}
}
}
return transfers
}


function getAccount(m: Map<string, Account>, id: string): Account {
let acc = m.get(id)
if (acc == null) {
acc = new Account()
acc.id = id
m.set(id, acc)
}
return acc
}
9 changes: 3 additions & 6 deletions src/types/events.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import assert from 'assert'
import {Chain, ChainContext, EventContext, Event, Result} from './support'
import * as v1020 from './v1020'
import * as v1050 from './v1050'
import * as v9130 from './v9130'

export class BalancesTransferEvent {
private readonly _chain: Chain
Expand All @@ -27,7 +24,7 @@ export class BalancesTransferEvent {
/**
* Transfer succeeded (from, to, value, fees).
*/
get asV1020(): [v1020.AccountId, v1020.AccountId, v1020.Balance, v1020.Balance] {
get asV1020(): [Uint8Array, Uint8Array, bigint, bigint] {
assert(this.isV1020)
return this._chain.decodeEvent(this.event)
}
Expand All @@ -42,7 +39,7 @@ export class BalancesTransferEvent {
/**
* Transfer succeeded (from, to, value).
*/
get asV1050(): [v1050.AccountId, v1050.AccountId, v1050.Balance] {
get asV1050(): [Uint8Array, Uint8Array, bigint] {
assert(this.isV1050)
return this._chain.decodeEvent(this.event)
}
Expand All @@ -57,7 +54,7 @@ export class BalancesTransferEvent {
/**
* Transfer succeeded.
*/
get asV9130(): {from: v9130.AccountId32, to: v9130.AccountId32, amount: bigint} {
get asV9130(): {from: Uint8Array, to: Uint8Array, amount: bigint} {
assert(this.isV9130)
return this._chain.decodeEvent(this.event)
}
Expand Down
2 changes: 2 additions & 0 deletions src/types/support.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export interface Chain {
getStorageItemTypeHash(prefix: string, name: string): string | undefined
getStorage(blockHash: string, prefix: string, name: string, ...args: any[]): Promise<any>
queryStorage(blockHash: string, prefix: string, name: string, ...args: any[]): Promise<any[]>
getConstantTypeHash(pallet: string, name: string): string | undefined
getConstant(pallet: string, name: string): any
}


Expand Down
5 changes: 0 additions & 5 deletions src/types/v1020.ts

This file was deleted.

5 changes: 0 additions & 5 deletions src/types/v1050.ts

This file was deleted.

3 changes: 0 additions & 3 deletions src/types/v9130.ts

This file was deleted.