Skip to content

Commit

Permalink
Store Transfers at "transfer" property, not "extrinsic"
Browse files Browse the repository at this point in the history
  • Loading branch information
ef1rspb authored Jul 28, 2021
1 parent 8ce03b0 commit 771b081
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 16 deletions.
2 changes: 1 addition & 1 deletion project.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ network:
dataSources:
- name: main
kind: substrate/Runtime
startBlock: 5748206
startBlock: 6124033

# polkadot test slash bloks: 3570179

Expand Down
1 change: 1 addition & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type Transfer @jsonField {
fee: String!
block: String!
extrinsicId: String
success: Boolean!
}

type Reward @jsonField {
Expand Down
113 changes: 99 additions & 14 deletions src/mappings/HistoryElements.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,107 @@
import { SubstrateExtrinsic } from '@subql/types';
import {HistoryElement} from "../types";
import {exportFeeFromDepositEvent, timestamp} from "./common";
import {SubstrateEvent, SubstrateExtrinsic} from '@subql/types';
import {HistoryElement, Transfer} from "../types";
import {
callFromProxy, callsFromBatch,
exportFeeFromDepositEvent,
extrinsicIdFromBlockAndIdx, isBatch, isProxy,
isTransfer,
timestamp
} from "./common";
import {Balance} from "@polkadot/types/interfaces";
import {CallBase} from "@polkadot/types/types/calls";
import {AnyTuple} from "@polkadot/types/types/codec";

export async function handleHistoryElement(extrinsic: SubstrateExtrinsic): Promise<void> {
const { isSigned } = extrinsic.extrinsic;
if (isSigned) {
const element = new HistoryElement(extrinsic.extrinsic.hash.toString());
element.address = extrinsic.extrinsic.signer.toString()
element.timestamp = timestamp(extrinsic.block)

element.extrinsic = {
hash: extrinsic.extrinsic.hash.toString(),
module: extrinsic.extrinsic.method.section,
call: extrinsic.extrinsic.method.method,
success: extrinsic.success,
fee: exportFeeFromDepositEvent(extrinsic)?.toString()
let failedTransfers = findFailedTransferCalls(extrinsic)
if (failedTransfers != null) {
await saveFailedTransfers(failedTransfers, extrinsic)
} else {
await saveExtrinsic(extrinsic)
}
}
}

async function saveFailedTransfers(transfers: Transfer[], extrinsic: SubstrateExtrinsic): Promise<void> {
let promises = transfers.map(transfer => {
const elementFrom = new HistoryElement(transfer.extrinsicId+`-from`);
elementFrom.address = transfer.from
elementFrom.timestamp = timestamp(extrinsic.block)
elementFrom.transfer = transfer

const elementTo = new HistoryElement(transfer.extrinsicId+`-to`);
elementTo.address = transfer.to
elementTo.timestamp = timestamp(extrinsic.block)
elementTo.transfer = transfer

return [elementTo.save(), elementFrom.save()]
})
await Promise.allSettled(promises)
}

await element.save()
async function saveExtrinsic(extrinsic: SubstrateExtrinsic): Promise<void> {
const element = new HistoryElement(extrinsic.extrinsic.hash.toString());
element.address = extrinsic.extrinsic.signer.toString()
element.timestamp = timestamp(extrinsic.block)
element.extrinsic = {
hash: extrinsic.extrinsic.hash.toString(),
module: extrinsic.extrinsic.method.section,
call: extrinsic.extrinsic.method.method,
success: extrinsic.success,
fee: exportFeeFromDepositEvent(extrinsic)?.toString()
}
await element.save()
}

/// Success Transfer emits Transfer event that is handled at Transfers.ts handleTransfer()
function findFailedTransferCalls(extrinsic: SubstrateExtrinsic): Transfer[] | null {
if (extrinsic.success) {
return null;
}

let transferCallsArgs = determineTransferCallsArgs(extrinsic.extrinsic.method)
if (transferCallsArgs.length == 0) {
return null;
}

let sender = extrinsic.extrinsic.signer
return transferCallsArgs.map(tuple => {
let blockNumber = extrinsic.block.block.header.number.toString()
return {
amount: tuple[1].toString(),
from: sender.toString(),
to: tuple[0],
block: blockNumber,
fee: exportFeeFromDepositEvent(extrinsic).toString(),
extrinsicId: extrinsicIdFromBlockAndIdx(blockNumber, extrinsic.idx.toString()),
success: false
}
})
}

function determineTransferCallsArgs(causeCall: CallBase<AnyTuple>) : [string, number][] {
if (isTransfer(causeCall)) {
return [extractArgsFromTransfer(causeCall)]
} else if (isBatch(causeCall)) {
return callsFromBatch(causeCall)
.map(call => {
return determineTransferCallsArgs(call)
.map((value, index, array) => {
return value
})
})
.flat()
} else if (isProxy(causeCall)) {
let proxyCall = callFromProxy(causeCall)
return determineTransferCallsArgs(proxyCall)
} else {
return []
}
}

function extractArgsFromTransfer(call: CallBase<AnyTuple>): [string, number] {
const [destinationAddress, amount] = call.args

return [destinationAddress.toString(), (amount as Balance).toNumber()]
}
3 changes: 2 additions & 1 deletion src/mappings/Transfers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ async function populateTransfer(element: HistoryElement, event: SubstrateEvent):
to: to.toString(),
block: blockNumber(event),
fee: exportFeeFromDepositEvent(event.extrinsic).toString(),
extrinsicId: extrinsicId(event)
extrinsicId: extrinsicId(event),
success: true
}
await element.save();
}
9 changes: 9 additions & 0 deletions src/mappings/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {EventRecord} from "@polkadot/types/interfaces/system/types"


const batchCalls = ["batch", "batchAll"]
const transferCalls = ["transfer", "transferKeepAlive"]

export function distinct<T>(array: Array<T>): Array<T> {
return [...new Set(array)];
Expand All @@ -21,6 +22,10 @@ export function isProxy(call: CallBase<AnyTuple>) : boolean {
return call.section == "proxy" && call.method == "proxy"
}

export function isTransfer(call: CallBase<AnyTuple>) : boolean {
return call.section == "balances" && transferCalls.includes(call.method)
}

export function callsFromBatch(batchCall: CallBase<AnyTuple>) : CallBase<AnyTuple>[] {
return batchCall.args[0] as Vec<CallBase<AnyTuple>>
}
Expand All @@ -45,6 +50,10 @@ export function blockNumber(event: SubstrateEvent): string {
return event.block.block.header.number.toString()
}

export function extrinsicIdFromBlockAndIdx(blockNumber: string, eventIdx: string) {
return `${blockNumber}-${eventIdx}`
}

export function timestamp(block: SubstrateBlock): string {
return Math.round((block.timestamp.getTime() / 1000)).toString()
}
Expand Down

0 comments on commit 771b081

Please sign in to comment.