From 7461b36dc385dd2c5a65cc956e75ed99da27266f Mon Sep 17 00:00:00 2001 From: ccp sondheim Date: Thu, 25 Jun 2026 11:47:22 +0000 Subject: [PATCH 1/5] fix: fuel stream --- biome.jsonc | 2 +- .../providers/SmartObjectProvider.tsx | 6 ++ .../utils/__tests__/inventoryEventBcs.test.ts | 2 +- .../dapp-kit/utils/events/checkpointStream.ts | 21 +++--- packages/libs/dapp-kit/utils/events/consts.ts | 12 ++++ .../dapp-kit/utils/events/fuelEventBcs.ts | 64 +++++++++++++++++++ .../utils/{ => events}/inventoryEventBcs.ts | 12 +--- 7 files changed, 97 insertions(+), 22 deletions(-) create mode 100644 packages/libs/dapp-kit/utils/events/consts.ts create mode 100644 packages/libs/dapp-kit/utils/events/fuelEventBcs.ts rename packages/libs/dapp-kit/utils/{ => events}/inventoryEventBcs.ts (84%) diff --git a/biome.jsonc b/biome.jsonc index d332951..6f9d18c 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.5.0/schema.json", + "$schema": "https://biomejs.dev/schemas/2.5.1/schema.json", "vcs": { "enabled": true, "clientKind": "git", diff --git a/packages/libs/dapp-kit/providers/SmartObjectProvider.tsx b/packages/libs/dapp-kit/providers/SmartObjectProvider.tsx index 8646f38..95c5e18 100644 --- a/packages/libs/dapp-kit/providers/SmartObjectProvider.tsx +++ b/packages/libs/dapp-kit/providers/SmartObjectProvider.tsx @@ -415,6 +415,12 @@ const SmartObjectProvider = ({ children }: { children: ReactNode }) => { fuelEventTypes, ) + console.log( + '[fuel debug] raw events:', + events.map((e) => e.type), + ) + console.log('[fuel debug] fuelEventTarget:', fuelEventTarget) + const relevantInventoryEvents = events.filter((event) => isRelevantAssemblyInventoryEvent(event, inventoryEventTarget), ) diff --git a/packages/libs/dapp-kit/utils/__tests__/inventoryEventBcs.test.ts b/packages/libs/dapp-kit/utils/__tests__/inventoryEventBcs.test.ts index a9edd16..5f13e53 100644 --- a/packages/libs/dapp-kit/utils/__tests__/inventoryEventBcs.test.ts +++ b/packages/libs/dapp-kit/utils/__tests__/inventoryEventBcs.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from 'vitest' import { decodeInventoryEventBcs, inventoryEventBcsToParsedJson, -} from '../inventoryEventBcs' +} from '../events/inventoryEventBcs' function hexToBytes(hex: string): Uint8Array { const bytes = new Uint8Array(hex.length / 2) diff --git a/packages/libs/dapp-kit/utils/events/checkpointStream.ts b/packages/libs/dapp-kit/utils/events/checkpointStream.ts index d83982c..fa0bc49 100644 --- a/packages/libs/dapp-kit/utils/events/checkpointStream.ts +++ b/packages/libs/dapp-kit/utils/events/checkpointStream.ts @@ -1,11 +1,11 @@ import type { SuiEvent } from '@mysten/sui/jsonRpc' - +import { createLogger } from '../logger' +import { isRecord } from '../utils' +import { decodeFuelEventBcs, fuelEventBcsToParsedJson } from './fuelEventBcs' import { decodeInventoryEventBcs, inventoryEventBcsToParsedJson, -} from '../inventoryEventBcs' -import { createLogger } from '../logger' -import { isRecord } from '../utils' +} from './inventoryEventBcs' const CHECKPOINT_STREAM_RECONNECT_MS = 1_000 // Rotate before the public fullnode ~30s stream cutoff. @@ -105,10 +105,10 @@ function protobufStructToJson( ) } -function parseInventoryEventPayloadFromStream(event: { - json?: ProtobufValue - contents?: { value?: Uint8Array } -}): Record | null { +function parseEventPayloadFromStream( + event: { json?: ProtobufValue; contents?: { value?: Uint8Array } }, + eventType: string, +): Record | null { const fromProtobuf = protobufValueToJson(event.json) if (isRecord(fromProtobuf)) return fromProtobuf @@ -116,6 +116,9 @@ function parseInventoryEventPayloadFromStream(event: { if (!bcsBytes) return null try { + if (eventType.endsWith('::fuel::FuelEvent')) { + return fuelEventBcsToParsedJson(decodeFuelEventBcs(bcsBytes)) + } return inventoryEventBcsToParsedJson(decodeInventoryEventBcs(bcsBytes)) } catch { return null @@ -326,7 +329,7 @@ export function extractInventoryEventsFromCheckpoint( const type = getStreamEventType(event) if (!eventTypes.includes(type)) return [] - const parsedJson = parseInventoryEventPayloadFromStream(event) + const parsedJson = parseEventPayloadFromStream(event, type) if (!parsedJson) return [] return [ diff --git a/packages/libs/dapp-kit/utils/events/consts.ts b/packages/libs/dapp-kit/utils/events/consts.ts new file mode 100644 index 0000000..1929215 --- /dev/null +++ b/packages/libs/dapp-kit/utils/events/consts.ts @@ -0,0 +1,12 @@ +import { bcs } from '@mysten/sui/bcs' + +export const BcsObjectId = bcs.fixedArray(32, bcs.u8()).transform({ + input: (value: number[]) => value, + output: (value: number[]) => + `0x${value.map((byte) => byte.toString(16).padStart(2, '0')).join('')}`, +}) + +export const TenantKey = bcs.struct('TenantKey', { + item_id: bcs.u64(), + tenant: bcs.string(), +}) diff --git a/packages/libs/dapp-kit/utils/events/fuelEventBcs.ts b/packages/libs/dapp-kit/utils/events/fuelEventBcs.ts new file mode 100644 index 0000000..0e57681 --- /dev/null +++ b/packages/libs/dapp-kit/utils/events/fuelEventBcs.ts @@ -0,0 +1,64 @@ +import { bcs } from '@mysten/sui/bcs' + +import { BcsObjectId, TenantKey } from './consts' + +const BcsAction = bcs.enum('Action', { + DEPOSITED: null, + WITHDRAWN: null, + BURNING_STARTED: null, + BURNING_STOPPED: null, + BURNING_UPDATED: null, + DELETED: null, +}) + +const FuelMoveEvent = bcs.struct('FuelEvent', { + assembly_id: BcsObjectId, + assembly_key: TenantKey, + type_id: bcs.u64(), + old_quantity: bcs.u64(), + new_quantity: bcs.u64(), + is_burning: bcs.bool(), + action: BcsAction, +}) + +// ---------------------------------------------------------------------------- + +export type DecodedFuelMoveEvent = { + assembly_id: string + assembly_key: { item_id: string; tenant: string } + type_id: string + old_quantity: string + new_quantity: string + is_burning: boolean + action: unknown +} + +export function decodeFuelEventBcs(bytes: Uint8Array): DecodedFuelMoveEvent { + const decoded = FuelMoveEvent.parse(bytes) + return { + assembly_id: decoded.assembly_id, + assembly_key: { + item_id: String(decoded.assembly_key.item_id), + tenant: decoded.assembly_key.tenant, + }, + type_id: String(decoded.type_id), + old_quantity: String(decoded.old_quantity), + new_quantity: String(decoded.new_quantity), + is_burning: Boolean(decoded.is_burning), + action: decoded.action, + } +} + +export function fuelEventBcsToParsedJson( + decoded: DecodedFuelMoveEvent, +): Record { + return { + assembly_id: decoded.assembly_id, + assembly_key: decoded.assembly_key, + type_id: decoded.type_id, + old_quantity: decoded.old_quantity, + new_quantity: decoded.new_quantity, + is_burning: decoded.is_burning, + action: decoded.action, + } +} diff --git a/packages/libs/dapp-kit/utils/inventoryEventBcs.ts b/packages/libs/dapp-kit/utils/events/inventoryEventBcs.ts similarity index 84% rename from packages/libs/dapp-kit/utils/inventoryEventBcs.ts rename to packages/libs/dapp-kit/utils/events/inventoryEventBcs.ts index 910a704..5c23d71 100644 --- a/packages/libs/dapp-kit/utils/inventoryEventBcs.ts +++ b/packages/libs/dapp-kit/utils/events/inventoryEventBcs.ts @@ -1,15 +1,5 @@ import { bcs } from '@mysten/sui/bcs' - -const BcsObjectId = bcs.fixedArray(32, bcs.u8()).transform({ - input: (value: number[]) => value, - output: (value: number[]) => - `0x${value.map((byte) => byte.toString(16).padStart(2, '0')).join('')}`, -}) - -const TenantKey = bcs.struct('TenantKey', { - item_id: bcs.u64(), - tenant: bcs.string(), -}) +import { BcsObjectId, TenantKey } from './consts' const InventoryMoveEvent = bcs.struct('InventoryMoveEvent', { assembly_id: BcsObjectId, From b8f001b5cbcf1fef12b928c3a4f4106089962858 Mon Sep 17 00:00:00 2001 From: ccp sondheim Date: Thu, 25 Jun 2026 12:36:07 +0000 Subject: [PATCH 2/5] chore: update wallet-core version --- bun.lock | 8 ++++---- packages/libs/dapp-kit/package.json | 2 +- .../dapp-kit/utils/__tests__/constants.test.ts | 2 +- packages/libs/dapp-kit/utils/inventory.ts | 14 -------------- 4 files changed, 6 insertions(+), 20 deletions(-) diff --git a/bun.lock b/bun.lock index 10f606a..5cab66e 100644 --- a/bun.lock +++ b/bun.lock @@ -24,7 +24,7 @@ }, "packages/apps/assembly": { "name": "@eveworld/assembly", - "version": "0.3.1", + "version": "0.3.3", "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", @@ -59,9 +59,9 @@ }, "packages/libs/dapp-kit": { "name": "@evefrontier/dapp-kit", - "version": "0.1.10", + "version": "0.1.11", "dependencies": { - "@evefrontier/wallet-core": "0.0.3", + "@evefrontier/wallet-core": "0.0.4", "@mysten/dapp-kit-core": "^1.2.2", "@mysten/dapp-kit-react": "^2.0.1", "@mysten/sui": "^2.14.1", @@ -421,7 +421,7 @@ "@evefrontier/dapp-kit": ["@evefrontier/dapp-kit@workspace:packages/libs/dapp-kit"], - "@evefrontier/wallet-core": ["@evefrontier/wallet-core@0.0.3", "https://npm.pkg.github.com/download/@evefrontier/wallet-core/0.0.3/4e23f76160c6065da570fc46a8b2a7e2cc9c75aa", { "dependencies": { "@mysten/sui": "^2.19.0", "@mysten/wallet-standard": "^0.20.3", "@mysten/webcrypto-signer": "^0.1.2" } }, "sha512-Yv/4//icejFBaAyKbVt9TEiv8G+ga/TxG5YQZEJRIZOD+nWKazlt4F20uCPrGmOsvjIlstOi2Ncs8alQkVygVw=="], + "@evefrontier/wallet-core": ["@evefrontier/wallet-core@0.0.4", "https://npm.pkg.github.com/download/@evefrontier/wallet-core/0.0.4/8e50ab5150c2aea8ecfc210a4cb5f5a95e9416d9", { "dependencies": { "@mysten/sui": "^2.19.0", "@mysten/wallet-standard": "^0.20.3", "@mysten/webcrypto-signer": "^0.1.2" } }, "sha512-1Tr65SMEweGlDCEbAw9T2XJQlE6Jp477QhC8j9l3O4da9Z4HHrR40o00ECJ3czNOvzrLQtouqU8/9LsWtsyrTw=="], "@eveworld/assembly": ["@eveworld/assembly@workspace:packages/apps/assembly"], diff --git a/packages/libs/dapp-kit/package.json b/packages/libs/dapp-kit/package.json index 6e32e27..ddd1491 100644 --- a/packages/libs/dapp-kit/package.json +++ b/packages/libs/dapp-kit/package.json @@ -41,7 +41,7 @@ } }, "dependencies": { - "@evefrontier/wallet-core": "0.0.3", + "@evefrontier/wallet-core": "0.0.4", "@mysten/dapp-kit-core": "^1.2.2", "@mysten/dapp-kit-react": "^2.0.1", "@mysten/sui": "^2.14.1", diff --git a/packages/libs/dapp-kit/utils/__tests__/constants.test.ts b/packages/libs/dapp-kit/utils/__tests__/constants.test.ts index fd6038d..1eafc63 100644 --- a/packages/libs/dapp-kit/utils/__tests__/constants.test.ts +++ b/packages/libs/dapp-kit/utils/__tests__/constants.test.ts @@ -146,7 +146,7 @@ describe('constants', () => { ], [ TenantId.STILLNESS, - '0x2a66a89b5a735738ffa4423ac024d23571326163f324f9051557617319e59d60::EVE::EVE', + '0xac361aa5ceb726bd974f885c9dea9e55dc9bc98fa1f5731c5965a810707bf0b8::EVE::EVE', ], ])('returns the EVE coin type for tenant %s', (tenantId, expected) => { expect(getEveCoinType(tenantId)).toBe(expected) diff --git a/packages/libs/dapp-kit/utils/inventory.ts b/packages/libs/dapp-kit/utils/inventory.ts index 563ffb4..f751b8a 100644 --- a/packages/libs/dapp-kit/utils/inventory.ts +++ b/packages/libs/dapp-kit/utils/inventory.ts @@ -76,20 +76,6 @@ export function clearInventoryTypeVolumeM3Cache() { typeVolumeM3ById.clear() } -export function adjustInventoryUsedCapacity( - usedCapacity: string, - quantity: number, - typeId: number, - operation: 'add' | 'subtract', -): string - -export function adjustInventoryUsedCapacity( - usedCapacity: string | undefined, - quantity: number, - typeId: number, - operation: 'add' | 'subtract', -): string | undefined - export function adjustInventoryUsedCapacity( usedCapacity: string | undefined, quantity: number, From e331e711ec918bc7558a53e09d921b76a0911478 Mon Sep 17 00:00:00 2001 From: ccp sondheim Date: Thu, 25 Jun 2026 12:40:59 +0000 Subject: [PATCH 3/5] chore: bump version --- packages/apps/assembly/CHANGELOG.md | 6 ++++++ packages/apps/assembly/package.json | 2 +- packages/libs/dapp-kit/CHANGELOG.md | 6 ------ packages/libs/dapp-kit/package.json | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/apps/assembly/CHANGELOG.md b/packages/apps/assembly/CHANGELOG.md index 54117ca..f6f1932 100644 --- a/packages/apps/assembly/CHANGELOG.md +++ b/packages/apps/assembly/CHANGELOG.md @@ -1,5 +1,11 @@ # @eveworld/assembly +## 0.3.4 + +### Patch Changes + +- apply fuel optimistic update, bump wallet-core version + ## 0.3.3 ### Patch Changes diff --git a/packages/apps/assembly/package.json b/packages/apps/assembly/package.json index f08c774..d3632b5 100644 --- a/packages/apps/assembly/package.json +++ b/packages/apps/assembly/package.json @@ -1,7 +1,7 @@ { "name": "@eveworld/assembly", "private": true, - "version": "0.3.3", + "version": "0.3.4", "type": "module", "scripts": { "dev": "vite", diff --git a/packages/libs/dapp-kit/CHANGELOG.md b/packages/libs/dapp-kit/CHANGELOG.md index 2387e34..c537067 100644 --- a/packages/libs/dapp-kit/CHANGELOG.md +++ b/packages/libs/dapp-kit/CHANGELOG.md @@ -1,11 +1,5 @@ # @evefrontier/dapp-kit -## 0.1.11 - -### Patch Changes - -- use optimistic updates for inventory - ## 0.1.10 ### Patch Changes diff --git a/packages/libs/dapp-kit/package.json b/packages/libs/dapp-kit/package.json index ddd1491..77b1c60 100644 --- a/packages/libs/dapp-kit/package.json +++ b/packages/libs/dapp-kit/package.json @@ -1,6 +1,6 @@ { "name": "@evefrontier/dapp-kit", - "version": "0.1.11", + "version": "0.1.10", "description": "React SDK for EVE Frontier dApps on Sui", "main": "./dist/index.js", "types": "./dist/index.d.ts", From 5852d288897c0073dcf01230cfba1eea8c9c221e Mon Sep 17 00:00:00 2001 From: ccp sondheim Date: Thu, 25 Jun 2026 12:42:25 +0000 Subject: [PATCH 4/5] fix: build --- .../dapp-kit/utils/events/inventoryEventHandlers.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/libs/dapp-kit/utils/events/inventoryEventHandlers.ts b/packages/libs/dapp-kit/utils/events/inventoryEventHandlers.ts index ae18502..bbbeb56 100644 --- a/packages/libs/dapp-kit/utils/events/inventoryEventHandlers.ts +++ b/packages/libs/dapp-kit/utils/events/inventoryEventHandlers.ts @@ -252,12 +252,13 @@ export function applyInventoryEventToAssembly( ...assembly.storage, mainInventory: { ...assembly.storage.mainInventory, - usedCapacity: adjustInventoryUsedCapacity( - assembly.storage.mainInventory.usedCapacity, - delta.quantity, - delta.typeId, - delta.operation, - ), + usedCapacity: + adjustInventoryUsedCapacity( + assembly.storage.mainInventory.usedCapacity, + delta.quantity, + delta.typeId, + delta.operation, + ) || '', items: sortInventoryItemsByQuantity( mergeInventoryItemsByTypeId(computeNextItems(items, delta)), ), From 2d2bd9845db3d32a8cf7d1bc062dcdf6b1fbceff Mon Sep 17 00:00:00 2001 From: ccp sondheim Date: Thu, 25 Jun 2026 12:52:08 +0000 Subject: [PATCH 5/5] address pr comments --- .../providers/SmartObjectProvider.tsx | 6 -- .../utils/__tests__/fuelEventBcs.test.ts | 60 +++++++++++++++++++ .../events/__tests__/eventRefresh.test.ts | 49 +++++++++++++++ 3 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 packages/libs/dapp-kit/utils/__tests__/fuelEventBcs.test.ts diff --git a/packages/libs/dapp-kit/providers/SmartObjectProvider.tsx b/packages/libs/dapp-kit/providers/SmartObjectProvider.tsx index 95c5e18..8646f38 100644 --- a/packages/libs/dapp-kit/providers/SmartObjectProvider.tsx +++ b/packages/libs/dapp-kit/providers/SmartObjectProvider.tsx @@ -415,12 +415,6 @@ const SmartObjectProvider = ({ children }: { children: ReactNode }) => { fuelEventTypes, ) - console.log( - '[fuel debug] raw events:', - events.map((e) => e.type), - ) - console.log('[fuel debug] fuelEventTarget:', fuelEventTarget) - const relevantInventoryEvents = events.filter((event) => isRelevantAssemblyInventoryEvent(event, inventoryEventTarget), ) diff --git a/packages/libs/dapp-kit/utils/__tests__/fuelEventBcs.test.ts b/packages/libs/dapp-kit/utils/__tests__/fuelEventBcs.test.ts new file mode 100644 index 0000000..44aa286 --- /dev/null +++ b/packages/libs/dapp-kit/utils/__tests__/fuelEventBcs.test.ts @@ -0,0 +1,60 @@ +import { describe, expect, it } from 'vitest' + +import { + decodeFuelEventBcs, + fuelEventBcsToParsedJson, +} from '../events/fuelEventBcs' + +function hexToBytes(hex: string): Uint8Array { + const bytes = new Uint8Array(hex.length / 2) + for (let index = 0; index < bytes.length; index += 1) { + bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16) + } + return bytes +} + +// Manually constructed FuelEvent BCS bytes matching the on-chain Move struct: +// assembly_id: 0x34d08b4e...cc951b (32 bytes) +// assembly_key: { item_id: 1 (u64 LE), tenant: "stillness" } +// type_id: 77810, old_quantity: 10, new_quantity: 5 +// is_burning: false, action: WITHDRAWN (variant index 1) +const FUEL_EVENT_BCS_HEX = + '34d08b4e1afe6a4babcc0642d6a676160df6b777b49214d5c964b4e874cc951b' + + '0100000000000000' + + '09' + + '7374696c6c6e657373' + + 'f22f010000000000' + + '0a00000000000000' + + '0500000000000000' + + '00' + + '01' + +describe('fuelEventBcs', () => { + it('decodes a FuelEvent from BCS bytes', () => { + const decoded = decodeFuelEventBcs(hexToBytes(FUEL_EVENT_BCS_HEX)) + + expect(fuelEventBcsToParsedJson(decoded)).toMatchObject({ + assembly_id: + '0x34d08b4e1afe6a4babcc0642d6a676160df6b777b49214d5c964b4e874cc951b', + assembly_key: { + item_id: '1', + tenant: 'stillness', + }, + type_id: '77810', + old_quantity: '10', + new_quantity: '5', + is_burning: false, + }) + }) + + it('decodes the action variant kind', () => { + const decoded = decodeFuelEventBcs(hexToBytes(FUEL_EVENT_BCS_HEX)) + const json = fuelEventBcsToParsedJson(decoded) + // bcs.enum returns a discriminated union: { $kind: '', : true } + expect((json.action as Record)?.$kind).toBe('WITHDRAWN') + }) + + it('throws on malformed BCS bytes', () => { + expect(() => decodeFuelEventBcs(new Uint8Array([0x00, 0x01]))).toThrow() + }) +}) diff --git a/packages/libs/dapp-kit/utils/events/__tests__/eventRefresh.test.ts b/packages/libs/dapp-kit/utils/events/__tests__/eventRefresh.test.ts index c15b1f8..812b5bf 100644 --- a/packages/libs/dapp-kit/utils/events/__tests__/eventRefresh.test.ts +++ b/packages/libs/dapp-kit/utils/events/__tests__/eventRefresh.test.ts @@ -10,6 +10,7 @@ import { extractInventoryEventsFromCheckpoint, } from '../checkpointStream' import { createEventRefetchScheduler } from '../eventRefresh' +import { getFuelEventType } from '../fuelEventHandlers' import { applyInventoryEventToAssembly, getInventoryEventTarget, @@ -1148,4 +1149,52 @@ describe('event refresh helpers', () => { }, ]) }) + + it('extracts fuel events from gRPC BCS bytes when json is absent', () => { + // Manually constructed FuelEvent BCS: assembly_id 0x34d0...951b, + // assembly_key { item_id: 1, tenant: "stillness" }, type_id: 77810, + // old_quantity: 10, new_quantity: 5, is_burning: false, action: WITHDRAWN + const FUEL_EVENT_BCS_HEX = + '34d08b4e1afe6a4babcc0642d6a676160df6b777b49214d5c964b4e874cc951b' + + '0100000000000000' + + '09' + + '7374696c6c6e657373' + + 'f22f010000000000' + + '0a00000000000000' + + '0500000000000000' + + '00' + + '01' + + const events = extractInventoryEventsFromCheckpoint( + { + transactions: [ + { + digest: 'abc123', + events: { + events: [ + { + eventType: getFuelEventType(PACKAGE_ID), + contents: { + value: hexToBytes(FUEL_EVENT_BCS_HEX), + }, + }, + ], + }, + }, + ], + }, + [getFuelEventType(PACKAGE_ID)], + ) + + expect(events).toHaveLength(1) + expect(events[0].type).toBe(getFuelEventType(PACKAGE_ID)) + expect(events[0].parsedJson).toMatchObject({ + assembly_id: ASSEMBLY_OBJECT_ID, + assembly_key: { item_id: '1', tenant: 'stillness' }, + type_id: '77810', + old_quantity: '10', + new_quantity: '5', + is_burning: false, + }) + }) })