Skip to content
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@cityofzion/neon-dappkit-types",
"comment": "streamline stack item and notification types in Neo3EventListener",
"type": "major"
}
],
"packageName": "@cityofzion/neon-dappkit-types"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@cityofzion/neon-dappkit",
"comment": "update to use latest type changes",
"type": "major"
}
],
"packageName": "@cityofzion/neon-dappkit"
}
26 changes: 7 additions & 19 deletions packages/neon-dappkit-types/src/Neo3EventListener.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { InvokeBase, Notification } from './Neo3Invoker'

/**
* An interface that defines the event contract and event name
*/
Expand All @@ -6,38 +8,24 @@ export interface Neo3Event {
eventname: string
}

/**
* An interface that defines the event contract, event name and the state of the event
*/
export interface Neo3EventWithState extends Neo3Event {
state: Neo3StackItem
}

/**
* The event listener callback
*/
export type Neo3EventListenerCallback = (event: Neo3EventWithState) => void
export type Neo3EventListenerCallback = (event: Notification) => void

/**
* An interface that defines the stack item format
* An interface that defines an application execution format
*/
export interface Neo3StackItem {
type: string
value?: string | boolean | number | Neo3StackItem[]
export interface ApplicationExecution extends InvokeBase {
trigger: string
}

/**
* An interface that defines the application log format
*/
export interface Neo3ApplicationLog {
txid: string
executions: {
trigger: string
vmstate: string
gasconsumed: string
stack?: Neo3StackItem[]
notifications: Neo3EventWithState[]
}[]
executions: ApplicationExecution[]
}

/**
Expand Down
29 changes: 22 additions & 7 deletions packages/neon-dappkit-types/src/Neo3Invoker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ export type BuiltTransaction = ContractInvocationMulti & {
export type ArrayResponseArgType = { type: 'Array'; value: RpcResponseStackItem[] }
export type MapResponseArgType = { type: 'Map'; value: { key: RpcResponseStackItem; value: RpcResponseStackItem }[] }
export type ByteStringArgType = { type: 'ByteString'; value: string }
export type InteropInterfaceArgType = { type: 'InteropInterface'; interface: string; id: string }
export type InteropInterfaceArgType = { type: 'InteropInterface'; interface: string; id: string; value?: never }
export type PointerArgType = { type: 'Pointer'; value: string }
export type BufferArgType = { type: 'Buffer'; value: string }
export type StructArgType = { type: 'Struct'; value: RpcResponseStackItem[] }
Expand All @@ -208,12 +208,16 @@ export type RpcResponseStackItem =
| BufferArgType
| StructArgType

/**
* Result from calling invokescript or invokefunction.
*/
export interface InvokeResult<T extends RpcResponseStackItem = RpcResponseStackItem> {
/** The script that is sent for execution on the blockchain as a base64 string. */
script: string
export interface Notification {
/** hash of the smart contract that emitted the notification */
contract: string
/** name of the notification */
eventname: string
/** the emitted notification contents */
state: RpcResponseStackItem
}

export interface InvokeBase<T extends RpcResponseStackItem = RpcResponseStackItem> {
/** State of VM on exit. HALT means a successful exit while FAULT means exit with error. */
state: 'HALT' | 'FAULT'
/** Amount of gas consumed up to the point of stopping in the VM. If state is FAULT, this value is not representative of the amount of gas it will consume if it somehow succeeds on the blockchain.
Expand All @@ -222,7 +226,18 @@ export interface InvokeResult<T extends RpcResponseStackItem = RpcResponseStackI
gasconsumed: string
/** A human-readable string clarifying the exception that occurred. Only available when state is "FAULT". */
exception: string | null
/** Result stack of the VM. */
stack: T[]
/** Notifications emitted by the smart contract. */
notifications: Notification[]
}

/**
* Result from calling invokescript or invokefunction.
*/
export interface InvokeResult extends InvokeBase {
/** The script that is sent for execution on the blockchain as a base64 string. */
script: string
/** A ready to send transaction that wraps the script.
* Only available when signers are provided and the sender's private key is open in the RPC node.
* Formatted in base64-encoding.
Expand Down
49 changes: 39 additions & 10 deletions packages/neon-dappkit/src/NeonEventListener.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import {
ApplicationExecution,
Neo3ApplicationLog,
Neo3Event,
Neo3EventListener,
Neo3EventListenerCallback,
Neo3EventWithState,
Neo3StackItem,
Notification,
RpcResponseStackItem,
TypeChecker,
} from '@cityofzion/neon-dappkit-types'
import { rpc } from '@cityofzion/neon-js'
import type * as NeonTypes from '@cityofzion/neon-core'
Expand Down Expand Up @@ -97,7 +99,8 @@ export class NeonEventListener implements Neo3EventListener {
let error = new Error("Couldn't get application log")
do {
try {
return await this.rpcClient.getApplicationLog(txId)
const log = await this.rpcClient.getApplicationLog(txId)
return this.neonApplogToNeo3ApplicationLog(log)
} catch (e) {
error = e
}
Expand All @@ -108,8 +111,8 @@ export class NeonEventListener implements Neo3EventListener {
}

confirmHalt(txResult: Neo3ApplicationLog) {
if (txResult?.executions[0]?.vmstate !== 'HALT')
throw new Error('Transaction failed. VMState: ' + txResult?.executions[0]?.vmstate)
if (txResult?.executions[0]?.state !== 'HALT')
throw new Error('Transaction failed. VMState: ' + txResult?.executions[0]?.state)
}

confirmStackTrue(txResult: Neo3ApplicationLog) {
Expand All @@ -122,13 +125,14 @@ export class NeonEventListener implements Neo3EventListener {
) {
throw new Error('Transaction failed. No stack found in transaction result')
}
const stack: Neo3StackItem = txResult.executions[0].stack[0]
if (stack.value !== true) {
const stack = txResult.executions[0].stack[0]

if (!TypeChecker.isStackTypeBoolean(stack) || stack.value !== true) {
throw new Error('Transaction failed. Stack value is not true')
}
}

getNotificationState(txResult: Neo3ApplicationLog, eventToCheck: Neo3Event): Neo3EventWithState | undefined {
getNotificationState(txResult: Neo3ApplicationLog, eventToCheck: Neo3Event): Notification | undefined {
return txResult?.executions[0].notifications.find((e) => {
return e.contract === eventToCheck.contract && e.eventname === eventToCheck.eventname
})
Expand Down Expand Up @@ -173,7 +177,8 @@ export class NeonEventListener implements Neo3EventListener {
continue
}

const log = await this.rpcClient.getApplicationLog(transaction.hash)
const neonLog = await this.rpcClient.getApplicationLog(transaction.hash)
const log = this.neonApplogToNeo3ApplicationLog(neonLog)

for (const notification of log.executions[0].notifications) {
const listenersOfContract = this.listeners.get(notification.contract)
Expand All @@ -191,7 +196,7 @@ export class NeonEventListener implements Neo3EventListener {
for (const listener of listenersOfEvent) {
try {
this.options?.debug && console.log('Calling listener')
listener(notification as Neo3EventWithState)
listener(notification)
} catch (e) {
this.options?.debug && console.error(e)
}
Expand All @@ -211,4 +216,28 @@ export class NeonEventListener implements Neo3EventListener {
private wait(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms))
}

private neonApplogToNeo3ApplicationLog(log: NeonTypes.rpc.ApplicationLogJson): Neo3ApplicationLog {
const executions = log.executions.map((execution) => {
return {
trigger: execution.trigger,
state: execution.vmstate,
gasconsumed: execution.gasconsumed,
stack: execution.stack.map((si) => {
return { type: si.type, value: si.value } as RpcResponseStackItem
}),
notifications: execution.notifications.map((notification) => {
return {
contract: notification.contract,
eventname: notification.eventname,
state: { type: notification.state.type, value: notification.state.value },
} as Notification
}),
} as ApplicationExecution
})
return {
txid: log.txid,
executions,
} as Neo3ApplicationLog
}
}
3 changes: 2 additions & 1 deletion packages/neon-dappkit/src/NeonInvoker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ export class NeonInvoker implements Neo3Invoker {
)
if (rpcResult.state === 'FAULT') throw Error(`Execution state is FAULT. Exception: ${rpcResult.exception}`)

return { ...rpcResult, stack: rpcResult.stack as RpcResponseStackItem[] }
// TODO: set actual notifications. Currently not supported in neon-js
return { ...rpcResult, stack: rpcResult.stack as RpcResponseStackItem[], notifications: [] }
}

async invokeFunction(cimOrBt: ContractInvocationMulti | BuiltTransaction): Promise<string> {
Expand Down
30 changes: 16 additions & 14 deletions packages/neon-dappkit/test/NeonEventListener.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { ChildProcess, spawn } from 'child_process'
import { NeonEventListener, NeonInvoker, NeonParser } from '../src/index'
import { NeonEventListener, NeonInvoker, NeonParser } from '../src'
import assert from 'assert'
import {
ContractInvocationMulti,
Neo3ApplicationLog,
Neo3EventListenerCallback,
Neo3EventWithState,
Notification,
TypeChecker,
} from '@cityofzion/neon-dappkit-types'
import { wallet } from '@cityofzion/neon-core'
Expand Down Expand Up @@ -65,8 +65,8 @@ describe('NeonEventListener', function () {
it('adds an eventListener', async () => {
const eventName = 'Transfer'

const eventPromise = new Promise<Neo3EventWithState>((resolve) => {
const callBack = (notification: Neo3EventWithState) => {
const eventPromise = new Promise<Notification>((resolve) => {
const callBack = (notification: Notification) => {
resolve(notification)

eventListener.removeAllEventListenersOfEvent(gasScriptHash, eventName)
Expand Down Expand Up @@ -94,8 +94,8 @@ describe('NeonEventListener', function () {
it('adds eventListeners on the same event', async () => {
const eventName = 'Transfer'

const eventPromise1 = new Promise<Neo3EventWithState>((resolve) => {
const callBack1 = (notification: Neo3EventWithState) => {
const eventPromise1 = new Promise<Notification>((resolve) => {
const callBack1 = (notification: Notification) => {
resolve(notification)

eventListener.removeAllEventListenersOfEvent(gasScriptHash, eventName)
Expand All @@ -104,8 +104,8 @@ describe('NeonEventListener', function () {
eventListener.addEventListener(gasScriptHash, eventName, callBack1)
})

const eventPromise2 = new Promise<Neo3EventWithState>((resolve) => {
const callBack2 = (notification: Neo3EventWithState) => {
const eventPromise2 = new Promise<Notification>((resolve) => {
const callBack2 = (notification: Notification) => {
resolve(notification)

eventListener.removeAllEventListenersOfEvent(gasScriptHash, eventName)
Expand Down Expand Up @@ -166,7 +166,7 @@ describe('NeonEventListener', function () {
it('adds eventListener that callback throws an error', async () => {
const eventName = 'Transfer'

const eventPromise = new Promise<Neo3EventWithState>((resolve, reject) => {
const eventPromise = new Promise<Notification>((resolve, reject) => {
const callBack = () => {
reject('Error')
eventListener.removeAllEventListenersOfEvent(gasScriptHash, eventName)
Expand All @@ -191,7 +191,7 @@ describe('NeonEventListener', function () {
const eventName = 'Transfer'
let called = 0

const callBack: Neo3EventListenerCallback = (notification: Neo3EventWithState) => {
const callBack: Neo3EventListenerCallback = (notification: Notification) => {
assert(notification)
called += 1
}
Expand Down Expand Up @@ -221,7 +221,7 @@ describe('NeonEventListener', function () {
const eventName = 'Transfer'
let called = 0

const callBack: Neo3EventListenerCallback = (notification: Neo3EventWithState) => {
const callBack: Neo3EventListenerCallback = (notification: Notification) => {
assert(notification)
called += 1
}
Expand Down Expand Up @@ -251,7 +251,7 @@ describe('NeonEventListener', function () {
const eventName = 'Transfer'
let called = 0

const callBack: Neo3EventListenerCallback = (notification: Neo3EventWithState) => {
const callBack: Neo3EventListenerCallback = (notification: Notification) => {
assert(notification)
called += 1
}
Expand Down Expand Up @@ -289,7 +289,7 @@ describe('NeonEventListener', function () {
assert(applicationLog.txid === txId, 'Transaction ID should be the same')
assert(applicationLog.executions.length === 1, 'There should be one execution')
assert(applicationLog.executions[0].trigger === 'Application', 'Trigger should be Application')
assert(applicationLog.executions[0].vmstate === 'HALT', 'VMState should be HALT')
assert(applicationLog.executions[0].state === 'HALT', 'VMState should be HALT')
assert(applicationLog.executions[0].gasconsumed !== undefined, 'Gas consumed should be returned')
assert(applicationLog.executions[0].stack.length === 1, 'Stack should be returned')
assert(applicationLog.executions[0].stack[0].type === 'Boolean', 'Stack type should be a boolean')
Expand Down Expand Up @@ -334,9 +334,11 @@ describe('NeonEventListener', function () {
executions: [
{
trigger: 'Application',
vmstate: 'FAULT',
state: 'FAULT',
gasconsumed: '0',
notifications: [],
exception: '',
stack: [],
},
],
}
Expand Down
2 changes: 1 addition & 1 deletion packages/neon-dappkit/test/NeonInvoker.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ChildProcess, spawn, execSync } from 'child_process'
import { ContractInvocationMulti } from '@cityofzion/neon-dappkit-types'
import { NeonEventListener, NeonInvoker, NeonParser, TypeChecker } from '../src/index'
import { NeonEventListener, NeonInvoker, NeonParser, TypeChecker } from '../src'
import assert from 'assert'
import * as path from 'path'
import { tx, u } from '@cityofzion/neon-js'
Expand Down