@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.
- package/lib/shipload.d.ts +1829 -501
- package/lib/shipload.js +4325 -1324
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +4286 -1389
- package/lib/shipload.m.js.map +1 -1
- package/lib/testing.d.ts +391 -13
- package/lib/testing.js +1692 -107
- package/lib/testing.js.map +1 -1
- package/lib/testing.m.js +1693 -108
- package/lib/testing.m.js.map +1 -1
- package/package.json +2 -2
- package/src/contracts/fund.ts +201 -0
- package/src/contracts/platform.ts +73 -1
- package/src/contracts/server.ts +946 -26
- package/src/contracts/token.ts +170 -0
- package/src/contracts.ts +2 -0
- package/src/data/catalog.test.ts +19 -0
- package/src/data/catalog.ts +6 -0
- package/src/data/charters.json +198 -0
- package/src/data/citizenry-adjectives.json +42 -0
- package/src/data/citizenry-nouns.json +50 -0
- package/src/data/entities.json +36 -0
- package/src/data/item-ids.ts +2 -0
- package/src/data/items.json +12 -0
- package/src/data/kind-registry.json +20 -3
- package/src/data/kind-registry.ts +5 -0
- package/src/data/metadata.ts +14 -0
- package/src/data/recipes.json +0 -48
- package/src/derivation/capabilities.ts +14 -0
- package/src/derivation/recipe-usage.test.ts +0 -2
- package/src/format.ts +9 -0
- package/src/index-module.ts +32 -2
- package/src/influence/categories.ts +32 -0
- package/src/influence/charters.test.ts +126 -0
- package/src/influence/charters.ts +120 -0
- package/src/influence/citizenry.ts +38 -0
- package/src/influence/constants.ts +63 -0
- package/src/influence/decay.ts +20 -0
- package/src/influence/decomp.ts +95 -0
- package/src/influence/demand.ts +106 -0
- package/src/influence/duration.ts +36 -0
- package/src/influence/index.ts +63 -0
- package/src/influence/influence.test.ts +317 -0
- package/src/influence/quality.ts +25 -0
- package/src/influence/valuation.ts +154 -0
- package/src/managers/actions.ts +227 -5
- package/src/managers/balances.test.ts +161 -0
- package/src/managers/balances.ts +93 -0
- package/src/managers/construction.ts +11 -1
- package/src/managers/context.ts +18 -0
- package/src/managers/entities.ts +11 -0
- package/src/managers/index.ts +15 -1
- package/src/managers/influence.test.ts +58 -0
- package/src/managers/influence.ts +347 -0
- package/src/managers/nft.test.ts +8 -1
- package/src/managers/nft.ts +56 -16
- package/src/nft/description.ts +7 -0
- package/src/scheduling/task-cargo.ts +1 -0
- package/src/shipload.ts +10 -0
- package/src/types.ts +23 -4
- package/src/utils/hash.ts +4 -0
- 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[],
|
package/src/managers/context.ts
CHANGED
|
@@ -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
|
}
|
package/src/managers/entities.ts
CHANGED
|
@@ -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
|
package/src/managers/index.ts
CHANGED
|
@@ -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
|
+
}
|
package/src/managers/nft.test.ts
CHANGED
|
@@ -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
|
+
})
|
package/src/managers/nft.ts
CHANGED
|
@@ -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
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
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
|
|
52
|
-
|
|
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
|
|
57
|
-
|
|
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:
|
|
66
|
-
precision:
|
|
67
|
-
tokenContract:
|
|
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
|
}
|
package/src/nft/description.ts
CHANGED
|
@@ -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 [
|