@shipload/sdk 1.0.0-next.4 → 1.0.0-next.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/lib/scan.d.ts +34 -0
  2. package/lib/scan.js +136 -0
  3. package/lib/scan.js.map +1 -0
  4. package/lib/scan.m.js +129 -0
  5. package/lib/scan.m.js.map +1 -0
  6. package/lib/shipload.d.ts +2473 -973
  7. package/lib/shipload.js +11529 -5211
  8. package/lib/shipload.js.map +1 -1
  9. package/lib/shipload.m.js +11338 -5162
  10. package/lib/shipload.m.js.map +1 -1
  11. package/lib/testing.d.ts +970 -0
  12. package/lib/testing.js +4013 -0
  13. package/lib/testing.js.map +1 -0
  14. package/lib/testing.m.js +4007 -0
  15. package/lib/testing.m.js.map +1 -0
  16. package/package.json +20 -2
  17. package/src/capabilities/craftable.ts +51 -0
  18. package/src/capabilities/crafting.test.ts +7 -0
  19. package/src/capabilities/crafting.ts +5 -6
  20. package/src/capabilities/gathering.test.ts +16 -0
  21. package/src/capabilities/gathering.ts +35 -18
  22. package/src/capabilities/index.ts +0 -1
  23. package/src/capabilities/modules.ts +9 -0
  24. package/src/capabilities/storage.ts +16 -1
  25. package/src/contracts/platform.ts +231 -3
  26. package/src/contracts/server.ts +1021 -481
  27. package/src/coordinates/address.ts +88 -0
  28. package/src/coordinates/constants.test.ts +15 -0
  29. package/src/coordinates/constants.ts +23 -0
  30. package/src/coordinates/index.ts +15 -0
  31. package/src/coordinates/memo.test.ts +47 -0
  32. package/src/coordinates/memo.ts +20 -0
  33. package/src/coordinates/permutation.ts +77 -0
  34. package/src/coordinates/regions.ts +48 -0
  35. package/src/coordinates/sectors.ts +115 -0
  36. package/src/data/capabilities.ts +12 -5
  37. package/src/data/capability-formulas.ts +14 -7
  38. package/src/data/catalog.ts +0 -5
  39. package/src/data/colors.ts +14 -47
  40. package/src/data/entities.json +76 -10
  41. package/src/data/item-ids.ts +18 -12
  42. package/src/data/items.json +321 -38
  43. package/src/data/kind-registry.json +109 -0
  44. package/src/data/kind-registry.ts +165 -0
  45. package/src/data/metadata.ts +119 -33
  46. package/src/data/recipes-runtime.ts +3 -23
  47. package/src/data/recipes.json +238 -117
  48. package/src/derivation/build-methods.ts +45 -0
  49. package/src/derivation/capabilities.test.ts +151 -0
  50. package/src/derivation/capabilities.ts +512 -0
  51. package/src/derivation/capability-mappings.ts +9 -12
  52. package/src/derivation/crafting.ts +23 -24
  53. package/src/derivation/index.ts +25 -2
  54. package/src/derivation/recipe-usage.test.ts +78 -0
  55. package/src/derivation/recipe-usage.ts +141 -0
  56. package/src/derivation/reserve-regen.ts +34 -0
  57. package/src/derivation/resources.ts +125 -38
  58. package/src/derivation/rollups.test.ts +55 -0
  59. package/src/derivation/rollups.ts +56 -0
  60. package/src/derivation/stars.test.ts +51 -0
  61. package/src/derivation/stars.ts +15 -0
  62. package/src/derivation/stats.ts +6 -6
  63. package/src/derivation/stratum.ts +17 -20
  64. package/src/derivation/tiers.ts +40 -7
  65. package/src/derivation/wormhole.ts +136 -0
  66. package/src/entities/entity.ts +98 -0
  67. package/src/entities/gamestate.ts +3 -28
  68. package/src/entities/makers.ts +124 -134
  69. package/src/entities/slot-multiplier.ts +43 -0
  70. package/src/errors.ts +12 -16
  71. package/src/format.ts +26 -4
  72. package/src/index-module.ts +267 -47
  73. package/src/managers/actions.ts +528 -95
  74. package/src/managers/base.ts +6 -2
  75. package/src/managers/construction-types.ts +80 -0
  76. package/src/managers/construction.ts +412 -0
  77. package/src/managers/context.ts +20 -1
  78. package/src/managers/coordinates.ts +14 -0
  79. package/src/managers/entities.ts +18 -66
  80. package/src/managers/epochs.ts +40 -0
  81. package/src/managers/index.ts +17 -1
  82. package/src/managers/locations.ts +25 -29
  83. package/src/managers/nft.test.ts +14 -0
  84. package/src/managers/nft.ts +70 -0
  85. package/src/managers/plot.ts +122 -0
  86. package/src/nft/atomicassets.abi.json +1342 -0
  87. package/src/nft/atomicassets.ts +237 -0
  88. package/src/nft/atomicdata.ts +130 -0
  89. package/src/nft/buildImmutableData.ts +338 -0
  90. package/src/nft/description.ts +98 -24
  91. package/src/nft/index.ts +3 -0
  92. package/src/planner/index.ts +127 -0
  93. package/src/planner/planner.test.ts +319 -0
  94. package/src/resolution/describe-module.ts +18 -13
  95. package/src/resolution/display-name.ts +38 -10
  96. package/src/resolution/resolve-item.test.ts +37 -0
  97. package/src/resolution/resolve-item.ts +55 -24
  98. package/src/scan/index.ts +180 -0
  99. package/src/scan/scan-wasm.base64.ts +2 -0
  100. package/src/scheduling/accessor.ts +68 -22
  101. package/src/scheduling/availability.ts +108 -0
  102. package/src/scheduling/cancel.test.ts +348 -0
  103. package/src/scheduling/cancel.ts +209 -0
  104. package/src/scheduling/energy.ts +47 -0
  105. package/src/scheduling/idle-resolve.ts +45 -0
  106. package/src/scheduling/lane-core.ts +128 -0
  107. package/src/scheduling/lanes.test.ts +249 -0
  108. package/src/scheduling/lanes.ts +198 -0
  109. package/src/scheduling/projection.ts +209 -105
  110. package/src/scheduling/schedule.ts +241 -104
  111. package/src/scheduling/task-cargo.ts +46 -0
  112. package/src/shipload.ts +21 -1
  113. package/src/subscriptions/manager.ts +229 -142
  114. package/src/subscriptions/mappers.ts +5 -8
  115. package/src/subscriptions/types.ts +11 -3
  116. package/src/testing/catalog-hash.ts +19 -0
  117. package/src/testing/index.ts +2 -0
  118. package/src/testing/projection-parity.ts +167 -0
  119. package/src/travel/reach.ts +23 -0
  120. package/src/travel/route-planner.ts +196 -0
  121. package/src/travel/travel.ts +200 -112
  122. package/src/types/capabilities.ts +29 -6
  123. package/src/types/entity.ts +3 -3
  124. package/src/types/index.ts +0 -1
  125. package/src/types.ts +28 -13
  126. package/src/utils/cargo.ts +27 -0
  127. package/src/utils/display-name.ts +70 -0
  128. package/src/utils/system.ts +36 -24
  129. package/src/capabilities/loading.ts +0 -8
  130. package/src/entities/container.ts +0 -108
  131. package/src/entities/ship-deploy.ts +0 -259
  132. package/src/entities/ship.ts +0 -204
  133. package/src/entities/warehouse.ts +0 -119
  134. package/src/types/entity-traits.ts +0 -69
@@ -0,0 +1,237 @@
1
+ import {
2
+ ABI,
3
+ type ABIDef,
4
+ Action,
5
+ type APIClient,
6
+ type NameType,
7
+ Name,
8
+ PermissionLevel,
9
+ UInt64,
10
+ } from '@wharfkit/antelope'
11
+ import {deserializeAtomicData, type SchemaField} from './atomicdata'
12
+ import {deserializeAsset, type NFTCargoItem, type NFTModuleSlot} from './deserializers'
13
+ import type {ImmutableEntry} from './buildImmutableData'
14
+ import atomicAssetsAbi from './atomicassets.abi.json'
15
+
16
+ const PLACEHOLDER_AUTH = PermissionLevel.from({
17
+ actor: '............1',
18
+ permission: '............2',
19
+ })
20
+
21
+ export const ATOMICASSETS_ACCOUNT = 'atomicassets'
22
+ export const SHIPLOAD_COLLECTION = 'shipload'
23
+
24
+ export const ATOMICASSETS_ABI = ABI.from(atomicAssetsAbi as ABIDef)
25
+
26
+ const ATOMIC_ATTRIBUTE_VARIANT_NAME =
27
+ 'variant_int8_int16_int32_int64_uint8_uint16_uint32_uint64_float32_float64_string_INT8_VEC_INT16_VEC_INT32_VEC_INT64_VEC_UINT8_VEC_UINT16_VEC_UINT32_VEC_UINT64_VEC_FLOAT_VEC_DOUBLE_VEC_STRING_VEC'
28
+
29
+ const MINTASSET_ABI_DEF: ABIDef = {
30
+ version: 'eosio::abi/1.2',
31
+ types: [
32
+ {new_type_name: 'ATOMIC_ATTRIBUTE', type: ATOMIC_ATTRIBUTE_VARIANT_NAME},
33
+ {new_type_name: 'ATTRIBUTE_MAP', type: 'pair_string_ATOMIC_ATTRIBUTE[]'},
34
+ {new_type_name: 'INT8_VEC', type: 'bytes'},
35
+ {new_type_name: 'INT16_VEC', type: 'int16[]'},
36
+ {new_type_name: 'INT32_VEC', type: 'int32[]'},
37
+ {new_type_name: 'INT64_VEC', type: 'int64[]'},
38
+ {new_type_name: 'UINT8_VEC', type: 'bytes'},
39
+ {new_type_name: 'UINT16_VEC', type: 'uint16[]'},
40
+ {new_type_name: 'UINT32_VEC', type: 'uint32[]'},
41
+ {new_type_name: 'UINT64_VEC', type: 'uint64[]'},
42
+ {new_type_name: 'FLOAT_VEC', type: 'float32[]'},
43
+ {new_type_name: 'DOUBLE_VEC', type: 'float64[]'},
44
+ {new_type_name: 'STRING_VEC', type: 'string[]'},
45
+ ],
46
+ structs: [
47
+ {
48
+ name: 'pair_string_ATOMIC_ATTRIBUTE',
49
+ base: '',
50
+ fields: [
51
+ {name: 'first', type: 'string'},
52
+ {name: 'second', type: 'ATOMIC_ATTRIBUTE'},
53
+ ],
54
+ },
55
+ {
56
+ name: 'mintasset',
57
+ base: '',
58
+ fields: [
59
+ {name: 'authorized_minter', type: 'name'},
60
+ {name: 'collection_name', type: 'name'},
61
+ {name: 'schema_name', type: 'name'},
62
+ {name: 'template_id', type: 'int32'},
63
+ {name: 'new_asset_owner', type: 'name'},
64
+ {name: 'immutable_data', type: 'ATTRIBUTE_MAP'},
65
+ {name: 'mutable_data', type: 'ATTRIBUTE_MAP'},
66
+ {name: 'tokens_to_back', type: 'asset[]'},
67
+ ],
68
+ },
69
+ ],
70
+ actions: [{name: 'mintasset', type: 'mintasset', ricardian_contract: ''}],
71
+ variants: [
72
+ {
73
+ name: ATOMIC_ATTRIBUTE_VARIANT_NAME,
74
+ types: [
75
+ 'int8',
76
+ 'int16',
77
+ 'int32',
78
+ 'int64',
79
+ 'uint8',
80
+ 'uint16',
81
+ 'uint32',
82
+ 'uint64',
83
+ 'float32',
84
+ 'float64',
85
+ 'string',
86
+ 'INT8_VEC',
87
+ 'INT16_VEC',
88
+ 'INT32_VEC',
89
+ 'INT64_VEC',
90
+ 'UINT8_VEC',
91
+ 'UINT16_VEC',
92
+ 'UINT32_VEC',
93
+ 'UINT64_VEC',
94
+ 'FLOAT_VEC',
95
+ 'DOUBLE_VEC',
96
+ 'STRING_VEC',
97
+ ],
98
+ },
99
+ ],
100
+ }
101
+
102
+ export interface MintAssetParams {
103
+ authorizedMinter: NameType
104
+ collectionName: NameType
105
+ schemaName: NameType
106
+ templateId: number
107
+ newAssetOwner: NameType
108
+ immutableData: ImmutableEntry[]
109
+ }
110
+
111
+ const MINTASSET_ABI = ABI.from(MINTASSET_ABI_DEF)
112
+
113
+ export function buildMintAssetAction(params: MintAssetParams): Action {
114
+ return Action.from(
115
+ {
116
+ account: Name.from(ATOMICASSETS_ACCOUNT),
117
+ name: Name.from('mintasset'),
118
+ authorization: [PLACEHOLDER_AUTH],
119
+ data: {
120
+ authorized_minter: Name.from(params.authorizedMinter),
121
+ collection_name: Name.from(params.collectionName),
122
+ schema_name: Name.from(params.schemaName),
123
+ template_id: params.templateId,
124
+ new_asset_owner: Name.from(params.newAssetOwner),
125
+ immutable_data: params.immutableData,
126
+ mutable_data: [],
127
+ tokens_to_back: [],
128
+ },
129
+ },
130
+ MINTASSET_ABI
131
+ )
132
+ }
133
+
134
+ export interface AtomicAssetRow {
135
+ asset_id: string
136
+ collection_name: string
137
+ schema_name: string
138
+ template_id: number
139
+ ram_payer?: string
140
+ backed_tokens?: string[]
141
+ immutable_serialized_data: string | number[]
142
+ mutable_serialized_data?: string | number[]
143
+ }
144
+
145
+ export interface AtomicSchemaRow {
146
+ schema_name: string
147
+ format: SchemaField[]
148
+ }
149
+
150
+ export interface FetchAssetsOptions {
151
+ collection?: NameType
152
+ pageSize?: number
153
+ account?: NameType
154
+ }
155
+
156
+ export async function fetchAtomicAssetsForOwner(
157
+ client: APIClient,
158
+ owner: NameType,
159
+ opts: FetchAssetsOptions = {}
160
+ ): Promise<AtomicAssetRow[]> {
161
+ const collection = opts.collection ? String(Name.from(opts.collection)) : undefined
162
+ const pageSize = opts.pageSize ?? 1000
163
+ const account = Name.from(opts.account ?? ATOMICASSETS_ACCOUNT)
164
+ const out: AtomicAssetRow[] = []
165
+ let lower: UInt64 | undefined
166
+ while (true) {
167
+ const res = await client.v1.chain.get_table_rows({
168
+ code: account,
169
+ scope: String(Name.from(owner)),
170
+ table: Name.from('assets'),
171
+ limit: pageSize,
172
+ lower_bound: lower,
173
+ json: true,
174
+ })
175
+ for (const row of res.rows as AtomicAssetRow[]) {
176
+ if (!collection || row.collection_name === collection) out.push(row)
177
+ }
178
+ if (!res.more) break
179
+ lower = UInt64.from(String(res.next_key))
180
+ }
181
+ return out
182
+ }
183
+
184
+ export async function fetchAtomicSchemas(
185
+ client: APIClient,
186
+ collection: NameType,
187
+ account: NameType = ATOMICASSETS_ACCOUNT
188
+ ): Promise<AtomicSchemaRow[]> {
189
+ const out: AtomicSchemaRow[] = []
190
+ let lower: Name | undefined
191
+ while (true) {
192
+ const res = await client.v1.chain.get_table_rows({
193
+ code: Name.from(account),
194
+ scope: String(Name.from(collection)),
195
+ table: Name.from('schemas'),
196
+ limit: 100,
197
+ lower_bound: lower,
198
+ json: true,
199
+ })
200
+ for (const row of res.rows as AtomicSchemaRow[]) out.push(row)
201
+ if (!res.more) break
202
+ lower = Name.from(String(res.next_key))
203
+ }
204
+ return out
205
+ }
206
+
207
+ export interface DecodedAtomicAsset {
208
+ asset_id: bigint
209
+ schema_name: string
210
+ template_id: number
211
+ item_id: number
212
+ quantity: number
213
+ stats: string
214
+ origin_x: bigint
215
+ origin_y: bigint
216
+ modules?: NFTModuleSlot[]
217
+ }
218
+
219
+ export function decodeAtomicAsset(
220
+ asset: AtomicAssetRow,
221
+ schemaFormat: SchemaField[],
222
+ itemId: number
223
+ ): DecodedAtomicAsset {
224
+ const data = deserializeAtomicData(asset.immutable_serialized_data, schemaFormat)
225
+ const cargo: NFTCargoItem = deserializeAsset(data, itemId)
226
+ return {
227
+ asset_id: BigInt(String(asset.asset_id)),
228
+ schema_name: String(asset.schema_name),
229
+ template_id: Number(asset.template_id),
230
+ item_id: cargo.item_id,
231
+ quantity: cargo.quantity,
232
+ stats: cargo.stats,
233
+ origin_x: BigInt(String(data.origin_x ?? 0)),
234
+ origin_y: BigInt(String(data.origin_y ?? 0)),
235
+ modules: cargo.modules,
236
+ }
237
+ }
@@ -0,0 +1,130 @@
1
+ export interface SchemaField {
2
+ name: string
3
+ type: string
4
+ }
5
+
6
+ export type RawData =
7
+ | Uint8Array
8
+ | string
9
+ | number[]
10
+ | {immutable_serialized_data: Uint8Array | string | number[]}
11
+
12
+ export function deserializeAtomicData(
13
+ data: RawData,
14
+ schema: SchemaField[]
15
+ ): Record<string, unknown> {
16
+ let rawData: Uint8Array | string | number[]
17
+ if (data && typeof data === 'object' && 'immutable_serialized_data' in (data as object)) {
18
+ rawData = (data as {immutable_serialized_data: Uint8Array | string | number[]})
19
+ .immutable_serialized_data
20
+ } else {
21
+ rawData = data as Uint8Array | string | number[]
22
+ }
23
+
24
+ let bytes: Uint8Array
25
+ if (typeof rawData === 'string') {
26
+ const hex = rawData
27
+ bytes = new Uint8Array(hex.length / 2)
28
+ for (let i = 0; i < hex.length; i += 2) {
29
+ bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16)
30
+ }
31
+ } else if (Array.isArray(rawData)) {
32
+ bytes = new Uint8Array(rawData)
33
+ } else {
34
+ bytes = rawData
35
+ }
36
+
37
+ let offset = 0
38
+
39
+ function readVarint(): number {
40
+ let result = 0
41
+ let multiplier = 1
42
+ while (bytes[offset] >= 128) {
43
+ result += (bytes[offset] - 128) * multiplier
44
+ offset++
45
+ multiplier *= 128
46
+ }
47
+ result += bytes[offset] * multiplier
48
+ offset++
49
+ return result
50
+ }
51
+
52
+ function readVarint64(): bigint {
53
+ let result = 0n
54
+ let multiplier = 1n
55
+ while (bytes[offset] >= 128) {
56
+ result += BigInt(bytes[offset] - 128) * multiplier
57
+ offset++
58
+ multiplier *= 128n
59
+ }
60
+ result += BigInt(bytes[offset]) * multiplier
61
+ offset++
62
+ return result
63
+ }
64
+
65
+ function readZigzagInt64(): bigint {
66
+ const unsigned = readVarint64()
67
+ if (unsigned % 2n === 0n) {
68
+ return unsigned / 2n
69
+ } else {
70
+ return -(unsigned / 2n) - 1n
71
+ }
72
+ }
73
+
74
+ function readString(): string {
75
+ const length = readVarint()
76
+ const str = new TextDecoder().decode(bytes.slice(offset, offset + length))
77
+ offset += length
78
+ return str
79
+ }
80
+
81
+ const RESERVED = 4
82
+ const result: Record<string, unknown> = {}
83
+
84
+ while (offset < bytes.length) {
85
+ const fieldIndex = readVarint() - RESERVED
86
+ const field = schema[fieldIndex]
87
+ if (!field) break
88
+
89
+ switch (field.type) {
90
+ case 'uint16':
91
+ result[field.name] = readVarint()
92
+ break
93
+ case 'uint32':
94
+ result[field.name] = readVarint()
95
+ break
96
+ case 'uint64':
97
+ result[field.name] = readVarint64()
98
+ break
99
+ case 'int32':
100
+ result[field.name] = readZigzagInt64()
101
+ break
102
+ case 'int64':
103
+ result[field.name] = readZigzagInt64()
104
+ break
105
+ case 'string':
106
+ case 'image':
107
+ case 'ipfs':
108
+ result[field.name] = readString()
109
+ break
110
+ case 'uint16[]': {
111
+ const len = readVarint()
112
+ const arr: number[] = []
113
+ for (let i = 0; i < len; i++) arr.push(readVarint())
114
+ result[field.name] = arr
115
+ break
116
+ }
117
+ case 'uint64[]': {
118
+ const len = readVarint()
119
+ const arr: bigint[] = []
120
+ for (let i = 0; i < len; i++) arr.push(readVarint64())
121
+ result[field.name] = arr
122
+ break
123
+ }
124
+ default:
125
+ throw new Error(`Unknown type: ${field.type}`)
126
+ }
127
+ }
128
+
129
+ return result
130
+ }
@@ -0,0 +1,338 @@
1
+ import {Serializer} from '@wharfkit/antelope'
2
+ import {getItem} from '../data/catalog'
3
+ import {
4
+ getModuleCapabilityType,
5
+ MODULE_BATTERY,
6
+ MODULE_CRAFTER,
7
+ MODULE_ENGINE,
8
+ MODULE_GATHERER,
9
+ MODULE_GENERATOR,
10
+ MODULE_HAULER,
11
+ MODULE_LOADER,
12
+ MODULE_STORAGE,
13
+ MODULE_WARP,
14
+ } from '../capabilities/modules'
15
+ import {decodeStat, decodeCraftedItemStats} from '../derivation/crafting'
16
+ import {getStatDefinitions} from '../derivation/stats'
17
+ import type {ResourceCategory} from '../types'
18
+ import {Types as ServerTypes} from '../contracts/server'
19
+ import {
20
+ buildEntityDescription,
21
+ computeCrafterDrain,
22
+ computeCrafterSpeed,
23
+ computeEngineDrain,
24
+ computeEngineThrust,
25
+ computeGathererDepth,
26
+ computeGathererDrain,
27
+ computeGathererYield,
28
+ computeGeneratorCap,
29
+ computeGeneratorRech,
30
+ computeCargoBayCapacity,
31
+ computeBatteryBankCapacity,
32
+ computeHaulerCapacity,
33
+ computeHaulerDrain,
34
+ computeHaulerEfficiency,
35
+ computeLoaderMass,
36
+ computeLoaderThrust,
37
+ computeWarpRange,
38
+ } from './description'
39
+
40
+ export type AtomicAttributeType =
41
+ | 'string'
42
+ | 'uint8'
43
+ | 'uint16'
44
+ | 'uint32'
45
+ | 'uint64'
46
+ | 'int32'
47
+ | 'image'
48
+ | 'ipfs'
49
+ | 'UINT16_VEC'
50
+ | 'UINT64_VEC'
51
+
52
+ export interface ImmutableEntry {
53
+ first: string
54
+ second: [AtomicAttributeType, unknown]
55
+ }
56
+
57
+ export interface ImmutableModuleSlot {
58
+ type?: number | string | bigint
59
+ installed?: {item_id: number | string | bigint; stats: number | string | bigint}
60
+ }
61
+
62
+ export function moduleSlotsForImmutable(
63
+ modules: ServerTypes.module_entry[]
64
+ ): ImmutableModuleSlot[] {
65
+ return modules.map((m) => ({
66
+ type: Number(m.type.toString()),
67
+ installed: m.installed
68
+ ? {
69
+ item_id: Number(m.installed.item_id.toString()),
70
+ stats: BigInt(m.installed.stats.toString()),
71
+ }
72
+ : undefined,
73
+ }))
74
+ }
75
+
76
+ const IMAGE_HOST_URL = 'https://item.shiploadgame.com/item'
77
+
78
+ function bytesToBase64Url(bytes: Uint8Array): string {
79
+ let binary = ''
80
+ for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]!)
81
+ const b64 = btoa(binary)
82
+ return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
83
+ }
84
+
85
+ export function computeNftImageUrl(
86
+ item: {item_id: number; stats: bigint; modules: ImmutableModuleSlot[]; quantity: number},
87
+ originX: number,
88
+ originY: number
89
+ ): string {
90
+ const payload = ServerTypes.nft_item_payload.from({
91
+ item: {
92
+ item_id: item.item_id,
93
+ stats: String(item.stats),
94
+ modules: item.modules,
95
+ quantity: item.quantity,
96
+ },
97
+ location: {x: String(originX), y: String(originY)},
98
+ })
99
+ const bytes = Serializer.encode({object: payload}).array
100
+ return `${IMAGE_HOST_URL}/${bytesToBase64Url(bytes)}.png`
101
+ }
102
+
103
+ function commonBaseImmutable(
104
+ quantity: number,
105
+ stats: bigint,
106
+ originX: number,
107
+ originY: number,
108
+ img: string
109
+ ): ImmutableEntry[] {
110
+ return [
111
+ {first: 'quantity', second: ['uint32', quantity]},
112
+ {first: 'stats', second: ['uint64', String(stats)]},
113
+ {first: 'origin_x', second: ['int32', originX]},
114
+ {first: 'origin_y', second: ['int32', originY]},
115
+ {first: 'img', second: ['string', img]},
116
+ {first: 'deposit_amount', second: ['uint64', '0']},
117
+ {first: 'deposit_token', second: ['uint64', '0']},
118
+ {first: 'deposit_symbol', second: ['uint64', '0']},
119
+ ]
120
+ }
121
+
122
+ export function buildResourceImmutable(
123
+ itemId: number,
124
+ quantity: number,
125
+ stats: bigint,
126
+ originX: number,
127
+ originY: number
128
+ ): ImmutableEntry[] {
129
+ const item = getItem(itemId)
130
+ const cat = item.category
131
+ if (!cat) throw new Error(`Resource item ${itemId} has no category`)
132
+ const definitions = getStatDefinitions(cat as ResourceCategory)
133
+ const img = computeNftImageUrl(
134
+ {item_id: itemId, stats, modules: [], quantity},
135
+ originX,
136
+ originY
137
+ )
138
+ const base = commonBaseImmutable(quantity, stats, originX, originY, img)
139
+ base.push({first: definitions[0].key, second: ['uint16', decodeStat(stats, 0)]})
140
+ base.push({first: definitions[1].key, second: ['uint16', decodeStat(stats, 1)]})
141
+ base.push({first: definitions[2].key, second: ['uint16', decodeStat(stats, 2)]})
142
+ return base
143
+ }
144
+
145
+ export function buildComponentImmutable(
146
+ itemId: number,
147
+ quantity: number,
148
+ stats: bigint,
149
+ originX: number,
150
+ originY: number
151
+ ): ImmutableEntry[] {
152
+ const img = computeNftImageUrl(
153
+ {item_id: itemId, stats, modules: [], quantity},
154
+ originX,
155
+ originY
156
+ )
157
+ const base = commonBaseImmutable(quantity, stats, originX, originY, img)
158
+ const decoded = decodeCraftedItemStats(itemId, stats)
159
+ for (const [key, value] of Object.entries(decoded)) {
160
+ base.push({first: key, second: ['uint16', value]})
161
+ }
162
+ return base
163
+ }
164
+
165
+ export function buildModuleImmutable(
166
+ itemId: number,
167
+ quantity: number,
168
+ stats: bigint,
169
+ originX: number,
170
+ originY: number
171
+ ): ImmutableEntry[] {
172
+ const img = computeNftImageUrl(
173
+ {item_id: itemId, stats, modules: [], quantity},
174
+ originX,
175
+ originY
176
+ )
177
+ const base = commonBaseImmutable(quantity, stats, originX, originY, img)
178
+ const subtype = getModuleCapabilityType(itemId)
179
+ const item = getItem(itemId)
180
+ switch (subtype) {
181
+ case MODULE_ENGINE: {
182
+ const vol = decodeStat(stats, 0)
183
+ const thm = decodeStat(stats, 1)
184
+ base.push({first: 'volatility', second: ['uint16', vol]})
185
+ base.push({first: 'thermal', second: ['uint16', thm]})
186
+ base.push({first: 'thrust', second: ['uint32', computeEngineThrust(vol)]})
187
+ base.push({first: 'drain', second: ['uint16', computeEngineDrain(thm)]})
188
+ break
189
+ }
190
+ case MODULE_GENERATOR: {
191
+ const res = decodeStat(stats, 0)
192
+ const ref = decodeStat(stats, 1)
193
+ base.push({first: 'resonance', second: ['uint16', res]})
194
+ base.push({first: 'reflectivity', second: ['uint16', ref]})
195
+ base.push({first: 'capacity', second: ['uint16', computeGeneratorCap(res)]})
196
+ base.push({first: 'recharge', second: ['uint16', computeGeneratorRech(ref)]})
197
+ break
198
+ }
199
+ case MODULE_GATHERER: {
200
+ const str = decodeStat(stats, 0)
201
+ const tol = decodeStat(stats, 1)
202
+ const con = decodeStat(stats, 2)
203
+ const ref = decodeStat(stats, 3)
204
+ base.push({first: 'strength', second: ['uint16', str]})
205
+ base.push({first: 'tolerance', second: ['uint16', tol]})
206
+ base.push({first: 'conductivity', second: ['uint16', con]})
207
+ base.push({first: 'reflectivity', second: ['uint16', ref]})
208
+ base.push({first: 'yield', second: ['uint16', computeGathererYield(str)]})
209
+ base.push({first: 'drain', second: ['uint16', computeGathererDrain(con)]})
210
+ base.push({first: 'depth', second: ['uint16', computeGathererDepth(tol, item.tier)]})
211
+ break
212
+ }
213
+ case MODULE_LOADER: {
214
+ const fin = decodeStat(stats, 0)
215
+ const pla = decodeStat(stats, 1)
216
+ base.push({first: 'fineness', second: ['uint16', fin]})
217
+ base.push({first: 'plasticity', second: ['uint16', pla]})
218
+ base.push({first: 'mass', second: ['uint32', computeLoaderMass(fin)]})
219
+ base.push({first: 'thrust', second: ['uint16', computeLoaderThrust(pla)]})
220
+ break
221
+ }
222
+ case MODULE_WARP: {
223
+ const res = decodeStat(stats, 0)
224
+ base.push({first: 'resonance', second: ['uint16', res]})
225
+ base.push({first: 'range', second: ['uint32', computeWarpRange(res)]})
226
+ break
227
+ }
228
+ case MODULE_CRAFTER: {
229
+ const rea = decodeStat(stats, 0)
230
+ const con = decodeStat(stats, 1)
231
+ base.push({first: 'reactivity', second: ['uint16', rea]})
232
+ base.push({first: 'conductivity', second: ['uint16', con]})
233
+ base.push({first: 'speed', second: ['uint16', computeCrafterSpeed(rea)]})
234
+ base.push({first: 'drain', second: ['uint16', computeCrafterDrain(con)]})
235
+ break
236
+ }
237
+ case MODULE_STORAGE: {
238
+ const str = decodeStat(stats, 0)
239
+ const den = decodeStat(stats, 1)
240
+ const hrd = decodeStat(stats, 2)
241
+ const com = decodeStat(stats, 3)
242
+ base.push({first: 'strength', second: ['uint16', str]})
243
+ base.push({first: 'density', second: ['uint16', den]})
244
+ base.push({first: 'hardness', second: ['uint16', hrd]})
245
+ base.push({first: 'cohesion', second: ['uint16', com]})
246
+ base.push({
247
+ first: 'capacity',
248
+ second: ['uint32', computeCargoBayCapacity(str, den, hrd, com)],
249
+ })
250
+ break
251
+ }
252
+ case MODULE_BATTERY: {
253
+ const vol = decodeStat(stats, 0)
254
+ const thm = decodeStat(stats, 1)
255
+ const pla = decodeStat(stats, 2)
256
+ const ins = decodeStat(stats, 3)
257
+ base.push({first: 'volatility', second: ['uint16', vol]})
258
+ base.push({first: 'thermal', second: ['uint16', thm]})
259
+ base.push({first: 'plasticity', second: ['uint16', pla]})
260
+ base.push({first: 'insulation', second: ['uint16', ins]})
261
+ base.push({
262
+ first: 'capacity',
263
+ second: ['uint32', computeBatteryBankCapacity(vol, thm, pla, ins)],
264
+ })
265
+ break
266
+ }
267
+ case MODULE_HAULER: {
268
+ const res = decodeStat(stats, 0)
269
+ const pla = decodeStat(stats, 1)
270
+ const ref = decodeStat(stats, 2)
271
+ base.push({first: 'resonance', second: ['uint16', res]})
272
+ base.push({first: 'plasticity', second: ['uint16', pla]})
273
+ base.push({first: 'reflectivity', second: ['uint16', ref]})
274
+ base.push({first: 'capacity', second: ['uint8', computeHaulerCapacity(res)]})
275
+ base.push({first: 'efficiency', second: ['uint16', computeHaulerEfficiency(pla)]})
276
+ base.push({first: 'drain', second: ['uint16', computeHaulerDrain(ref)]})
277
+ break
278
+ }
279
+ }
280
+ return base
281
+ }
282
+
283
+ export function buildEntityImmutable(
284
+ itemId: number,
285
+ quantity: number,
286
+ stats: bigint,
287
+ originX: number,
288
+ originY: number,
289
+ modules: ImmutableModuleSlot[]
290
+ ): ImmutableEntry[] {
291
+ const moduleItems: number[] = []
292
+ const moduleStats: string[] = []
293
+ for (const m of modules) {
294
+ if (m.installed) {
295
+ moduleItems.push(Number(m.installed.item_id))
296
+ moduleStats.push(String(m.installed.stats))
297
+ } else {
298
+ moduleItems.push(0)
299
+ moduleStats.push('0')
300
+ }
301
+ }
302
+ const img = computeNftImageUrl({item_id: itemId, stats, modules, quantity}, originX, originY)
303
+ const base = commonBaseImmutable(quantity, stats, originX, originY, img)
304
+ base.push({first: 'module_items', second: ['UINT16_VEC', moduleItems]})
305
+ base.push({first: 'module_stats', second: ['UINT64_VEC', moduleStats]})
306
+ const description = buildEntityDescription(
307
+ itemId,
308
+ stats,
309
+ moduleItems,
310
+ moduleStats.map((s) => BigInt(s))
311
+ )
312
+ base.push({first: 'description', second: ['string', description]})
313
+ return base
314
+ }
315
+
316
+ export function buildImmutableData(
317
+ itemId: number,
318
+ quantity: number,
319
+ stats: bigint,
320
+ originX: number,
321
+ originY: number,
322
+ modules: ImmutableModuleSlot[] = []
323
+ ): ImmutableEntry[] {
324
+ const item = getItem(itemId)
325
+ if (item.type === 'resource') {
326
+ return buildResourceImmutable(itemId, quantity, stats, originX, originY)
327
+ }
328
+ if (item.type === 'component') {
329
+ return buildComponentImmutable(itemId, quantity, stats, originX, originY)
330
+ }
331
+ if (item.type === 'module') {
332
+ return buildModuleImmutable(itemId, quantity, stats, originX, originY)
333
+ }
334
+ if (item.type === 'entity') {
335
+ return buildEntityImmutable(itemId, quantity, stats, originX, originY, modules)
336
+ }
337
+ throw new Error(`Unsupported item type for wrap: ${item.type}`)
338
+ }