@shipload/sdk 1.0.0-next.62 → 1.0.0-next.64

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 (62) hide show
  1. package/lib/shipload.d.ts +1829 -501
  2. package/lib/shipload.js +4325 -1324
  3. package/lib/shipload.js.map +1 -1
  4. package/lib/shipload.m.js +4286 -1389
  5. package/lib/shipload.m.js.map +1 -1
  6. package/lib/testing.d.ts +391 -13
  7. package/lib/testing.js +1692 -107
  8. package/lib/testing.js.map +1 -1
  9. package/lib/testing.m.js +1693 -108
  10. package/lib/testing.m.js.map +1 -1
  11. package/package.json +2 -2
  12. package/src/contracts/fund.ts +201 -0
  13. package/src/contracts/platform.ts +73 -1
  14. package/src/contracts/server.ts +946 -26
  15. package/src/contracts/token.ts +170 -0
  16. package/src/contracts.ts +2 -0
  17. package/src/data/catalog.test.ts +19 -0
  18. package/src/data/catalog.ts +6 -0
  19. package/src/data/charters.json +198 -0
  20. package/src/data/citizenry-adjectives.json +42 -0
  21. package/src/data/citizenry-nouns.json +50 -0
  22. package/src/data/entities.json +36 -0
  23. package/src/data/item-ids.ts +2 -0
  24. package/src/data/items.json +12 -0
  25. package/src/data/kind-registry.json +20 -3
  26. package/src/data/kind-registry.ts +5 -0
  27. package/src/data/metadata.ts +14 -0
  28. package/src/data/recipes.json +0 -48
  29. package/src/derivation/capabilities.ts +14 -0
  30. package/src/derivation/recipe-usage.test.ts +0 -2
  31. package/src/format.ts +9 -0
  32. package/src/index-module.ts +32 -2
  33. package/src/influence/categories.ts +32 -0
  34. package/src/influence/charters.test.ts +126 -0
  35. package/src/influence/charters.ts +120 -0
  36. package/src/influence/citizenry.ts +38 -0
  37. package/src/influence/constants.ts +63 -0
  38. package/src/influence/decay.ts +20 -0
  39. package/src/influence/decomp.ts +95 -0
  40. package/src/influence/demand.ts +106 -0
  41. package/src/influence/duration.ts +36 -0
  42. package/src/influence/index.ts +63 -0
  43. package/src/influence/influence.test.ts +317 -0
  44. package/src/influence/quality.ts +25 -0
  45. package/src/influence/valuation.ts +154 -0
  46. package/src/managers/actions.ts +227 -5
  47. package/src/managers/balances.test.ts +161 -0
  48. package/src/managers/balances.ts +93 -0
  49. package/src/managers/construction.ts +11 -1
  50. package/src/managers/context.ts +18 -0
  51. package/src/managers/entities.ts +11 -0
  52. package/src/managers/index.ts +15 -1
  53. package/src/managers/influence.test.ts +58 -0
  54. package/src/managers/influence.ts +347 -0
  55. package/src/managers/nft.test.ts +8 -1
  56. package/src/managers/nft.ts +56 -16
  57. package/src/nft/description.ts +7 -0
  58. package/src/scheduling/task-cargo.ts +1 -0
  59. package/src/shipload.ts +10 -0
  60. package/src/types.ts +23 -4
  61. package/src/utils/hash.ts +4 -0
  62. package/src/utils/system.ts +1 -5
@@ -4,7 +4,7 @@ import type {ServerContract} from '../contracts'
4
4
  import {PlotManager} from './plot'
5
5
  import {getItem} from '../data/catalog'
6
6
  import {calc_build_duration} from '../capabilities/crafting'
7
- import {getLanes, getTasks} from '../scheduling/schedule'
7
+ import {getLanes, getTasks, isIdle} from '../scheduling/schedule'
8
8
  import {HoldKind, TaskType} from '../types'
9
9
  import type {
10
10
  BuildableTarget,
@@ -34,6 +34,16 @@ export class ConstructionManager extends BaseManager {
34
34
  return null
35
35
  }
36
36
 
37
+ canAbandon(
38
+ entity: ServerContract.Types.entity_row,
39
+ cargo: ServerContract.Types.cargo_row[]
40
+ ): boolean {
41
+ if (entity.kind.toString() !== 'plot') return false
42
+ if (!isIdle(entity)) return false
43
+ if (Number(entity.cargomass) !== 0) return false
44
+ return !cargo.some((c) => c.entity_id.equals(entity.id))
45
+ }
46
+
37
47
  eligibleSources(
38
48
  target: BuildableTarget,
39
49
  entities: ServerContract.Types.entity_info[],
@@ -11,7 +11,9 @@ import {EpochsManager} from './epochs'
11
11
  import {ActionsManager} from './actions'
12
12
  import {ClusterManager} from './cluster'
13
13
  import {NftManager} from './nft'
14
+ import {BalancesManager} from './balances'
14
15
  import {JobsManager} from './jobs'
16
+ import {InfluenceManager} from './influence'
15
17
  import {SubscriptionsManager} from '../subscriptions/manager'
16
18
 
17
19
  export class GameContext {
@@ -23,7 +25,9 @@ export class GameContext {
23
25
  private _actions?: ActionsManager
24
26
  private _clusters?: ClusterManager
25
27
  private _nft?: NftManager
28
+ private _balances?: BalancesManager
26
29
  private _jobs?: JobsManager
30
+ private _influence?: InfluenceManager
27
31
  private _subscriptions?: SubscriptionsManager
28
32
  private _subscriptionsUrl?: string
29
33
 
@@ -93,6 +97,13 @@ export class GameContext {
93
97
  return this._nft
94
98
  }
95
99
 
100
+ get balances(): BalancesManager {
101
+ if (!this._balances) {
102
+ this._balances = new BalancesManager(this)
103
+ }
104
+ return this._balances
105
+ }
106
+
96
107
  get jobs(): JobsManager {
97
108
  if (!this._jobs) {
98
109
  this._jobs = new JobsManager(this)
@@ -100,6 +111,13 @@ export class GameContext {
100
111
  return this._jobs
101
112
  }
102
113
 
114
+ get influence(): InfluenceManager {
115
+ if (!this._influence) {
116
+ this._influence = new InfluenceManager(this)
117
+ }
118
+ return this._influence
119
+ }
120
+
103
121
  setSubscriptionsUrl(url: string) {
104
122
  this._subscriptionsUrl = url
105
123
  }
@@ -55,6 +55,17 @@ export class EntitiesManager extends BaseManager {
55
55
  return result as ServerContract.Types.entity_summary[]
56
56
  }
57
57
 
58
+ async getDepot(
59
+ depotId: UInt64Type,
60
+ owner: NameType | ServerContract.Types.player_row
61
+ ): Promise<ServerContract.Types.depot_view> {
62
+ const result = await this.server.readonly('getdepot', {
63
+ depot_id: depotId,
64
+ owner: this.resolveOwner(owner),
65
+ })
66
+ return result as ServerContract.Types.depot_view
67
+ }
68
+
58
69
  private resolveOwner(owner: NameType | ServerContract.Types.player_row): Name {
59
70
  if (typeof owner === 'object' && owner !== null && 'owner' in owner) {
60
71
  return owner.owner
@@ -11,8 +11,22 @@ export {ActionsManager} from './actions'
11
11
  export {ClusterManager, computeFreeCells} from './cluster'
12
12
  export type {GridCell, ClusterCell, Cluster} from './cluster'
13
13
  export {NftManager} from './nft'
14
- export type {NftConfigForItem} from './nft'
14
+ export type {NftConfigForItem, WrapConfig, WrapGate} from './nft'
15
+ export {BalancesManager} from './balances'
16
+ export type {DepositConfig, PlatformBalance, TokenBalance} from './balances'
15
17
  export {JobsManager} from './jobs'
18
+ export {InfluenceManager} from './influence'
19
+ export type {
20
+ CharterProgress,
21
+ ContributePreview,
22
+ ContributePreviewRow,
23
+ FoundedWorld,
24
+ InfluenceStanding,
25
+ PendingBallot,
26
+ VoteCast,
27
+ VoteOption,
28
+ VoteStandings,
29
+ } from './influence'
16
30
  export {PlotManager} from './plot'
17
31
  export type {PlotProgress, PlotProgressInputRow} from './plot'
18
32
  export {ConstructionManager} from './construction'
@@ -0,0 +1,58 @@
1
+ import {describe, expect, it} from 'bun:test'
2
+ import {Name, UInt64} from '@wharfkit/antelope'
3
+ import {InfluenceManager} from './influence'
4
+ import {coordsToLocationId} from '../types'
5
+
6
+ const LOCATION = {x: 12, y: 34}
7
+
8
+ function managerWith(rowsByTable: Record<string, unknown[]>) {
9
+ const calls: {table: string; scope: unknown}[] = []
10
+ const table = (name: string, scope: unknown) => {
11
+ calls.push({table: name, scope})
12
+ return {all: async () => rowsByTable[name] ?? []}
13
+ }
14
+ const ctx = {server: {table}} as never
15
+ return {manager: new InfluenceManager(ctx), calls}
16
+ }
17
+
18
+ describe('InfluenceManager scoped table reads', () => {
19
+ it('getVoteCasts scopes infvote by location and maps fields', async () => {
20
+ const {manager, calls} = managerWith({
21
+ infvote: [{account: Name.from('eggmaple.gm'), epoch: 5, node_id: 2, weight: '100'}],
22
+ })
23
+ const rows = await manager.getVoteCasts(LOCATION)
24
+ expect(calls).toHaveLength(1)
25
+ expect(calls[0].table).toBe('infvote')
26
+ expect(UInt64.from(calls[0].scope as never).equals(coordsToLocationId(LOCATION))).toBe(true)
27
+ expect(rows).toEqual([
28
+ {account: Name.from('eggmaple.gm'), epoch: 5, nodeId: 2, weight: 100n},
29
+ ])
30
+ })
31
+
32
+ it('getVoteTallies scopes inftally by location and maps fields', async () => {
33
+ const {manager, calls} = managerWith({
34
+ inftally: [{node_id: 3, total: '250'}],
35
+ })
36
+ const rows = await manager.getVoteTallies(LOCATION)
37
+ expect(calls[0].table).toBe('inftally')
38
+ expect(UInt64.from(calls[0].scope as never).equals(coordsToLocationId(LOCATION))).toBe(true)
39
+ expect(rows).toEqual([{nodeId: 3, total: 250n}])
40
+ })
41
+
42
+ it('getBuiltCharters scopes charters by location and maps fields', async () => {
43
+ const {manager, calls} = managerWith({
44
+ charters: [{node_id: 1, entity_id: '9001'}],
45
+ })
46
+ const rows = await manager.getBuiltCharters(LOCATION)
47
+ expect(calls[0].table).toBe('charters')
48
+ expect(UInt64.from(calls[0].scope as never).equals(coordsToLocationId(LOCATION))).toBe(true)
49
+ expect(rows).toEqual([{nodeId: 1, entityId: 9001n}])
50
+ })
51
+
52
+ it('returns an empty array when the scope has no rows', async () => {
53
+ const {manager} = managerWith({})
54
+ expect(await manager.getVoteCasts(LOCATION)).toEqual([])
55
+ expect(await manager.getVoteTallies(LOCATION)).toEqual([])
56
+ expect(await manager.getBuiltCharters(LOCATION)).toEqual([])
57
+ })
58
+ })
@@ -0,0 +1,347 @@
1
+ import {Int64, Name, type NameType, UInt64} from '@wharfkit/antelope'
2
+ import {BaseManager} from './base'
3
+ import type {ServerContract} from '../contracts'
4
+ import {coordsToLocationId, locationIdToCoords, type CoordinatesType} from '../types'
5
+ import {
6
+ type BuiltCharter,
7
+ citizenryName,
8
+ contributeDuration,
9
+ decayActive,
10
+ deriveDemand,
11
+ pricingFromWeights,
12
+ valueCargoItem,
13
+ type DemandTriple,
14
+ type DemandView,
15
+ type ValuedItem,
16
+ } from '../influence'
17
+ import {getItem} from '../data/catalog'
18
+
19
+ export interface InfluenceStanding {
20
+ epoch: number
21
+ playerLifetime: bigint
22
+ playerActive: bigint
23
+ standingLifetime: bigint
24
+ standingActive: bigint
25
+ locationLifetime: bigint
26
+ locationActive: bigint
27
+ watermark: bigint
28
+ mandate: number
29
+ founder: Name
30
+ founded: number
31
+ }
32
+
33
+ export interface CharterProgress {
34
+ locationId: bigint
35
+ lifetime: bigint
36
+ watermark: bigint
37
+ surplus: bigint
38
+ mandate: number
39
+ nextCost: bigint
40
+ prereqsMet: boolean
41
+ buildable: boolean
42
+ epoch: number
43
+ built: {nodeId: number; completedEpoch: number; entityId: bigint}[]
44
+ }
45
+
46
+ export interface ContributePreviewRow {
47
+ itemId: number
48
+ quantity: number
49
+ stats: bigint
50
+ valueAtomic: bigint
51
+ massKg: number
52
+ }
53
+
54
+ export interface ContributePreview {
55
+ rows: ContributePreviewRow[]
56
+ totalAtomic: bigint
57
+ totalMassKg: number
58
+ durationSeconds: number
59
+ demand: DemandView
60
+ }
61
+
62
+ export interface FoundedWorld {
63
+ locationId: bigint
64
+ x: number
65
+ y: number
66
+ lifetime: bigint
67
+ active: bigint
68
+ watermark: bigint
69
+ chosen: number
70
+ chosenEpoch: number
71
+ mandate: number | undefined
72
+ founder: Name
73
+ founded: number
74
+ }
75
+
76
+ export interface VoteOption {
77
+ nodeId: number
78
+ cost: bigint
79
+ eligible: boolean
80
+ total: bigint | undefined
81
+ }
82
+
83
+ export interface VoteStandings {
84
+ locationId: bigint
85
+ epoch: number
86
+ chosen: number
87
+ chosenEpoch: number
88
+ mandate: number
89
+ ballotEpoch: number | undefined
90
+ talliesSuppressed: boolean
91
+ options: VoteOption[]
92
+ }
93
+
94
+ export interface VoteCast {
95
+ account: Name
96
+ epoch: number
97
+ nodeId: number
98
+ weight: bigint
99
+ }
100
+
101
+ export interface PendingBallot {
102
+ locationId: bigint
103
+ x: number
104
+ y: number
105
+ epoch: number
106
+ }
107
+
108
+ function big(value: unknown): bigint {
109
+ return BigInt(String(value))
110
+ }
111
+
112
+ export class InfluenceManager extends BaseManager {
113
+ async getStanding(owner: NameType, location: CoordinatesType): Promise<InfluenceStanding> {
114
+ const result = (await this.server.readonly('getinfluence', {
115
+ owner: Name.from(owner),
116
+ x: Int64.from(location.x),
117
+ y: Int64.from(location.y),
118
+ })) as ServerContract.Types.influence_totals
119
+
120
+ return {
121
+ epoch: Number(result.epoch),
122
+ playerLifetime: big(result.player_lifetime),
123
+ playerActive: big(result.player_active),
124
+ standingLifetime: big(result.standing_lifetime),
125
+ standingActive: big(result.standing_active),
126
+ locationLifetime: big(result.location_lifetime),
127
+ locationActive: big(result.location_active),
128
+ watermark: big(result.watermark),
129
+ mandate: Number(result.mandate),
130
+ founder: Name.from(result.founder),
131
+ founded: Number(result.founded),
132
+ }
133
+ }
134
+
135
+ async getDemandConfig(): Promise<DemandTriple> {
136
+ const row = (await this.server.table('infdemand').get()) as
137
+ | ServerContract.Types.infdemand_row
138
+ | undefined
139
+ if (!row) throw new Error('influence demand config is unset')
140
+ return {peak: big(row.peak), base: big(row.base), floor: big(row.floor)}
141
+ }
142
+
143
+ async getQualityDivisor(): Promise<number> {
144
+ const row = (await this.server.table('infquality').get()) as
145
+ | ServerContract.Types.infquality_row
146
+ | undefined
147
+ if (!row) throw new Error('influence quality config is unset')
148
+ return Number(row.d1)
149
+ }
150
+
151
+ async getWeights(): Promise<ServerContract.Types.infweight_row[]> {
152
+ return (await this.server.table('infweight').all()) as ServerContract.Types.infweight_row[]
153
+ }
154
+
155
+ async getDemand(location: CoordinatesType): Promise<DemandView> {
156
+ const game = await this.getGame()
157
+ const state = await this.getState()
158
+ const triple = await this.getDemandConfig()
159
+ return deriveDemand({
160
+ gameSeed: game.config.seed,
161
+ epochSeed: state.epochSeed,
162
+ coordinates: location,
163
+ triple,
164
+ })
165
+ }
166
+
167
+ async getCitizenryName(location: CoordinatesType): Promise<string | undefined> {
168
+ const game = await this.getGame()
169
+ return citizenryName(game.config.seed, location)
170
+ }
171
+
172
+ async previewContribution(
173
+ location: CoordinatesType,
174
+ bundle: ValuedItem[],
175
+ opts: {altitudeZ?: number} = {}
176
+ ): Promise<ContributePreview> {
177
+ const [demand, d1, weights] = await Promise.all([
178
+ this.getDemand(location),
179
+ this.getQualityDivisor(),
180
+ this.getWeights(),
181
+ ])
182
+ const pricing = pricingFromWeights(
183
+ d1,
184
+ weights.map((w) => ({
185
+ category: Number(w.category),
186
+ tier: Number(w.tier),
187
+ weightFp: big(w.weight_fp),
188
+ }))
189
+ )
190
+
191
+ let totalAtomic = 0n
192
+ let totalMassKg = 0
193
+ const rows = bundle.map((item) => {
194
+ const valueAtomic = valueCargoItem(item, demand, pricing)
195
+ const massKg = getItem(item.itemId).mass * item.quantity
196
+ totalAtomic += valueAtomic
197
+ totalMassKg += massKg
198
+ return {...item, valueAtomic, massKg}
199
+ })
200
+
201
+ return {
202
+ rows,
203
+ totalAtomic,
204
+ totalMassKg,
205
+ durationSeconds: contributeDuration(totalMassKg, opts.altitudeZ ?? 0),
206
+ demand,
207
+ }
208
+ }
209
+
210
+ async getCharter(location: CoordinatesType): Promise<CharterProgress> {
211
+ const result = (await this.server.readonly('getcharter', {
212
+ x: Int64.from(location.x),
213
+ y: Int64.from(location.y),
214
+ })) as ServerContract.Types.charter_result
215
+
216
+ return {
217
+ locationId: big(result.location_id),
218
+ lifetime: big(result.lifetime),
219
+ watermark: big(result.watermark),
220
+ surplus: big(result.surplus),
221
+ mandate: Number(result.mandate),
222
+ nextCost: big(result.next_cost),
223
+ prereqsMet: Boolean(result.prereqs_met),
224
+ buildable: Boolean(result.buildable),
225
+ epoch: Number(result.epoch),
226
+ built: result.built.map((b) => ({
227
+ nodeId: Number(b.node_id),
228
+ completedEpoch: Number(b.completed_epoch),
229
+ entityId: big(b.entity_id),
230
+ })),
231
+ }
232
+ }
233
+
234
+ async getPools(): Promise<ServerContract.Types.pool_view[]> {
235
+ const result = (await this.server.readonly('getpools')) as ServerContract.Types.pools_result
236
+ return result.pools
237
+ }
238
+
239
+ async getPool(category: number, tier: number): Promise<ServerContract.Types.pool_pair_result> {
240
+ return (await this.server.readonly('getpool', {
241
+ category,
242
+ tier,
243
+ })) as ServerContract.Types.pool_pair_result
244
+ }
245
+
246
+ async getMintConfig(): Promise<ServerContract.Types.mintcfg_result> {
247
+ return (await this.server.readonly('getmintcfg')) as ServerContract.Types.mintcfg_result
248
+ }
249
+
250
+ async getVotes(location: CoordinatesType): Promise<VoteStandings> {
251
+ const result = (await this.server.readonly('getvotes', {
252
+ x: Int64.from(location.x),
253
+ y: Int64.from(location.y),
254
+ })) as ServerContract.Types.vote_result
255
+
256
+ const epoch = Number(result.epoch)
257
+ const rawBallotEpoch = Number(result.ballot_epoch)
258
+ const ballotEpoch = rawBallotEpoch === 0 ? undefined : rawBallotEpoch
259
+ const talliesSuppressed = ballotEpoch !== undefined && ballotEpoch < epoch
260
+
261
+ return {
262
+ locationId: big(result.location_id),
263
+ epoch,
264
+ chosen: Number(result.chosen),
265
+ chosenEpoch: Number(result.chosen_epoch),
266
+ mandate: Number(result.effective),
267
+ ballotEpoch,
268
+ talliesSuppressed,
269
+ options: result.options.map((option) => ({
270
+ nodeId: Number(option.node_id),
271
+ cost: big(option.cost),
272
+ eligible: Boolean(option.eligible),
273
+ total: talliesSuppressed ? undefined : big(option.total),
274
+ })),
275
+ }
276
+ }
277
+
278
+ async getVoteCasts(location: CoordinatesType): Promise<VoteCast[]> {
279
+ const rows = (await this.server
280
+ .table('infvote', coordsToLocationId(location))
281
+ .all()) as ServerContract.Types.infvote_row[]
282
+ return rows.map((row) => ({
283
+ account: Name.from(row.account),
284
+ epoch: Number(row.epoch),
285
+ nodeId: Number(row.node_id),
286
+ weight: big(row.weight),
287
+ }))
288
+ }
289
+
290
+ async getVoteTallies(location: CoordinatesType): Promise<{nodeId: number; total: bigint}[]> {
291
+ const rows = (await this.server
292
+ .table('inftally', coordsToLocationId(location))
293
+ .all()) as ServerContract.Types.inftally_row[]
294
+ return rows.map((row) => ({nodeId: Number(row.node_id), total: big(row.total)}))
295
+ }
296
+
297
+ async getBuiltCharters(location: CoordinatesType): Promise<BuiltCharter[]> {
298
+ const rows = (await this.server
299
+ .table('charters', coordsToLocationId(location))
300
+ .all()) as ServerContract.Types.charters_row[]
301
+ return rows.map((row) => ({nodeId: Number(row.node_id), entityId: big(row.entity_id)}))
302
+ }
303
+
304
+ async getBallotQueue(): Promise<PendingBallot[]> {
305
+ const rows = (await this.server
306
+ .table('infballot')
307
+ .all()) as ServerContract.Types.infballot_row[]
308
+ return rows.map((row) => ({
309
+ locationId: big(row.location_id),
310
+ ...locationIdToCoords(row.location_id),
311
+ epoch: Number(row.epoch),
312
+ }))
313
+ }
314
+
315
+ async getFoundedWorlds(opts: {withMandate?: boolean} = {}): Promise<FoundedWorld[]> {
316
+ const rows = (await this.server.table('infloc').all()) as ServerContract.Types.infloc_row[]
317
+ const epoch = (await this.getState()).epoch
318
+ const worlds = rows.map((row) => ({
319
+ locationId: big(row.location_id),
320
+ ...locationIdToCoords(row.location_id),
321
+ lifetime: big(row.lifetime),
322
+ active: decayActive(
323
+ big(row.active),
324
+ Math.max(0, Number(epoch) - Number(row.last_update_epoch))
325
+ ),
326
+ watermark: big(row.watermark),
327
+ chosen: Number(row.chosen),
328
+ chosenEpoch: Number(row.chosen_epoch),
329
+ mandate: undefined as number | undefined,
330
+ founder: Name.from(row.founder),
331
+ founded: Number(row.founded),
332
+ }))
333
+
334
+ if (!opts.withMandate) return worlds
335
+
336
+ for (const world of worlds) {
337
+ world.mandate = (await this.getCharter({x: world.x, y: world.y})).mandate
338
+ }
339
+ return worlds
340
+ }
341
+
342
+ async isFounded(location: CoordinatesType): Promise<boolean> {
343
+ const id = coordsToLocationId(location)
344
+ const row = await this.server.table('infloc').get(UInt64.from(id))
345
+ return row !== undefined
346
+ }
347
+ }
@@ -1,5 +1,5 @@
1
1
  import {describe, expect, test} from 'bun:test'
2
- import {resolveLockedAmount} from './nft'
2
+ import {resolveLockedAmount, wrapCostKey} from './nft'
3
3
 
4
4
  describe('resolveLockedAmount', () => {
5
5
  test('no fee refunds the full cost', () => {
@@ -12,3 +12,10 @@ describe('resolveLockedAmount', () => {
12
12
  expect(resolveLockedAmount(101n, 250)).toBe(99n) // fee = floor(101*250/10000)=2
13
13
  })
14
14
  })
15
+
16
+ describe('wrapCostKey', () => {
17
+ test('packs item type and tier as (type << 8) | tier', () => {
18
+ expect(wrapCostKey(0, 1).toString()).toBe('1')
19
+ expect(wrapCostKey(3, 2).toString()).toBe('770')
20
+ })
21
+ })
@@ -1,4 +1,4 @@
1
- import {UInt64, type UInt64Type} from '@wharfkit/antelope'
1
+ import {Name, UInt64, type NameType, type UInt64Type} from '@wharfkit/antelope'
2
2
  import {BaseManager} from './base'
3
3
  import type {PlatformContract, ServerContract} from '../contracts'
4
4
 
@@ -7,6 +7,17 @@ export interface NftConfigForItem {
7
7
  schemaName: string
8
8
  }
9
9
 
10
+ export interface WrapConfig {
11
+ feePctBasisPoints: number
12
+ feeAccount: Name
13
+ }
14
+
15
+ export interface WrapGate {
16
+ owner: Name
17
+ game: Name
18
+ lastAssetId: UInt64
19
+ }
20
+
10
21
  export interface WrapDeposit {
11
22
  cost: bigint
12
23
  refund: bigint
@@ -21,8 +32,13 @@ export function resolveLockedAmount(cost: bigint, feePctBasisPoints: number): bi
21
32
  return cost - fee
22
33
  }
23
34
 
35
+ export function wrapCostKey(itemType: number, tier: number): UInt64 {
36
+ return UInt64.from((BigInt(itemType) << 8n) | BigInt(tier))
37
+ }
38
+
24
39
  export class NftManager extends BaseManager {
25
40
  private cache = new Map<string, NftConfigForItem | null>()
41
+ private wrapConfig?: WrapConfig | null
26
42
 
27
43
  async getNftConfigForItem(itemId: UInt64Type): Promise<NftConfigForItem | undefined> {
28
44
  const id = UInt64.from(itemId)
@@ -40,31 +56,55 @@ export class NftManager extends BaseManager {
40
56
  return result ?? undefined
41
57
  }
42
58
 
43
- async getWrapDeposit(itemType: number, tier: number): Promise<WrapDeposit | null> {
44
- const key = UInt64.from((BigInt(itemType) << 8n) | BigInt(tier))
45
- const costRow = (await this.server.table('wrapcost').get(key)) as
59
+ async getWrapConfig(reload = false): Promise<WrapConfig | null> {
60
+ if (!reload && this.wrapConfig !== undefined) {
61
+ return this.wrapConfig
62
+ }
63
+ const row = (await this.server.table('wrapconfig').get()) as
64
+ | ServerContract.Types.wrapconfig_row
65
+ | undefined
66
+ this.wrapConfig = row
67
+ ? {feePctBasisPoints: Number(row.fee_pct), feeAccount: row.fee_account}
68
+ : null
69
+ return this.wrapConfig
70
+ }
71
+
72
+ async getWrapCost(itemType: number, tier: number): Promise<bigint> {
73
+ const row = (await this.server.table('wrapcost').get(wrapCostKey(itemType, tier))) as
46
74
  | ServerContract.Types.wrapcost_row
47
75
  | undefined
48
- const cost = costRow ? BigInt(costRow.amount.toString()) : 0n
76
+ return row ? BigInt(row.amount.toString()) : 0n
77
+ }
78
+
79
+ async getWrapDeposit(
80
+ itemType: number,
81
+ tier: number,
82
+ opts: {reload?: boolean} = {}
83
+ ): Promise<WrapDeposit | null> {
84
+ const cost = await this.getWrapCost(itemType, tier)
49
85
  if (cost === 0n) return null
50
86
 
51
- const cfg = (await this.server.table('wrapconfig').get()) as
52
- | ServerContract.Types.wrapconfig_row
53
- | undefined
54
- const feePctBasisPoints = cfg ? Number(cfg.fee_pct) : 0
87
+ const wrapConfig = await this.getWrapConfig(opts.reload)
88
+ const feePctBasisPoints = wrapConfig ? wrapConfig.feePctBasisPoints : 0
55
89
 
56
- const depositCfg = (await this.platform.table('depositcfg').get()) as
57
- | PlatformContract.Types.depositcfg_row
58
- | undefined
59
- if (!depositCfg) return null
90
+ const depositConfig = await this.context.balances.getDepositConfig(opts.reload)
91
+ if (!depositConfig) return null
60
92
 
61
93
  return {
62
94
  cost,
63
95
  refund: resolveLockedAmount(cost, feePctBasisPoints),
64
96
  feePct: feePctBasisPoints / 100,
65
- symbol: depositCfg.token_symbol.code.toString(),
66
- precision: depositCfg.token_symbol.precision,
67
- tokenContract: depositCfg.token_contract.toString(),
97
+ symbol: depositConfig.symbol.code.toString(),
98
+ precision: depositConfig.symbol.precision,
99
+ tokenContract: depositConfig.tokenContract.toString(),
68
100
  }
69
101
  }
102
+
103
+ async getWrapGate(owner: NameType): Promise<WrapGate | null> {
104
+ const row = (await this.platform.table('wrapgate').get(Name.from(owner))) as
105
+ | PlatformContract.Types.wrapgate_row
106
+ | undefined
107
+ if (!row) return null
108
+ return {owner: row.owner, game: row.game, lastAssetId: row.last_asset_id}
109
+ }
70
110
  }
@@ -85,6 +85,11 @@ export function computeBaseCapacityWorkshop(stats: bigint): number {
85
85
  return Math.floor(5_000_000 * 6 ** (s / 1998))
86
86
  }
87
87
 
88
+ export function computeBaseCapacityDepot(stats: bigint): number {
89
+ const s = decodeStat(stats, 0) + decodeStat(stats, 2)
90
+ return Math.floor(50_000_000 * 6 ** (s / 1998))
91
+ }
92
+
88
93
  const CAPACITY_FN_BY_KIND: Record<string, (stats: bigint) => number> = {
89
94
  ship: computeBaseCapacityShip,
90
95
  warehouse: computeBaseCapacityWarehouse,
@@ -95,6 +100,8 @@ const CAPACITY_FN_BY_KIND: Record<string, (stats: bigint) => number> = {
95
100
  mdriver: computeBaseCapacityContainer,
96
101
  mcatcher: computeBaseCapacityContainer,
97
102
  container: computeBaseCapacityContainer,
103
+ nexus: computeBaseCapacityContainer,
104
+ depot: computeBaseCapacityDepot,
98
105
  }
99
106
 
100
107
  export function computeBaseCapacityForEntity(itemId: number, stats: bigint): number {
@@ -34,6 +34,7 @@ export function taskCargoChanges(task: ServerContract.Types.task): TaskCargoChan
34
34
  case TaskType.GATHER:
35
35
  return task.couplings.length > 0 ? [] : items.map((i) => toChange(i, 'in'))
36
36
  case TaskType.UNLOAD:
37
+ case TaskType.CONTRIBUTE:
37
38
  return items.map((i) => toChange(i, 'out'))
38
39
  case TaskType.CRAFT:
39
40
  return [