Skip to content

Commit 9cce251

Browse files
authored
feat: add timestamp to the base entity (DAP-4763) (#24)
1 parent 04053b3 commit 9cce251

File tree

5 files changed

+85
-24
lines changed

5 files changed

+85
-24
lines changed

libs/engine/src/app/services/base/base.entity.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ export type EntityId = string
66

77
export class Base {
88
id: EntityId = ''
9+
blockNumber: number = 0
10+
timestamp: number = 0
911

1012
get authorId(): string {
1113
return this.id.split(KeyDelimiter)[0]

libs/engine/src/app/services/base/base.repository.ts

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const RecursiveWildcardKey = '**'
1515
const KeyDelimiter = '/'
1616
const EmptyValue = ''
1717
const SelfKey = ''
18+
const BlockNumberKey = ':block'
1819

1920
// ToDo:
2021
type EntityId = string
@@ -37,15 +38,16 @@ export class BaseRepository<T extends Base> {
3738
}
3839

3940
const keys = [authorId, SettingsKey, ProjectIdKey, this._entityKey, localId]
40-
const queryResult = await this.socialDb.get([
41-
[...keys, RecursiveWildcardKey].join(KeyDelimiter),
42-
])
41+
const queryResult = await this.socialDb.get(
42+
[[...keys, RecursiveWildcardKey].join(KeyDelimiter)],
43+
{ withBlockHeight: true }
44+
)
4345

44-
const item = SocialDbService.getValueByKey(keys, queryResult)
46+
const itemWithMeta = SocialDbService.getValueByKey(keys, queryResult)
4547

46-
if (!item) return null
48+
if (!itemWithMeta) return null
4749

48-
return this._makeItemFromSocialDb(id, item)
50+
return this._makeItemFromSocialDb(id, itemWithMeta)
4951
}
5052

5153
async getItems(options?: { authorId?: EntityId; localId?: EntityId }): Promise<T[]> {
@@ -55,16 +57,17 @@ export class BaseRepository<T extends Base> {
5557
const keys = [authorId, SettingsKey, ProjectIdKey, this._entityKey, localId]
5658

5759
// ToDo: out of gas
58-
const queryResult = await this.socialDb.get([
59-
[...keys, RecursiveWildcardKey].join(KeyDelimiter),
60-
])
60+
const queryResult = await this.socialDb.get(
61+
[[...keys, RecursiveWildcardKey].join(KeyDelimiter)],
62+
{ withBlockHeight: true }
63+
)
6164

6265
const mutationsByKey = SocialDbService.splitObjectByDepth(queryResult, keys.length)
6366

64-
const items = Object.entries(mutationsByKey).map(([key, item]: [string, any]) => {
67+
const items = Object.entries(mutationsByKey).map(([key, itemWithMeta]: [string, any]) => {
6568
const [accountId, , , , localMutationId] = key.split(KeyDelimiter)
6669
const itemId = [accountId, this._entityKey, localMutationId].join(KeyDelimiter)
67-
return this._makeItemFromSocialDb(itemId, item)
70+
return this._makeItemFromSocialDb(itemId, itemWithMeta)
6871
})
6972

7073
return items
@@ -188,10 +191,14 @@ export class BaseRepository<T extends Base> {
188191
}
189192
}
190193

191-
private _makeItemFromSocialDb(id: EntityId, raw: Value): T {
194+
private _makeItemFromSocialDb(id: EntityId, rawWithMeta: Value): T {
195+
const raw = BaseRepository._clearObjectFromMeta(rawWithMeta)
192196
const entity = new this.EntityType()
193197

194198
entity.id = id
199+
entity.blockNumber = rawWithMeta[BlockNumberKey]
200+
// ToDo: calculate it like localId and authorId?
201+
entity.timestamp = this.socialDb.getTimestampByBlockHeight(entity.blockNumber)
195202

196203
// for each property in the entity type get column metadata
197204

@@ -282,4 +289,26 @@ export class BaseRepository<T extends Base> {
282289
? 1 + Math.max(-1, ...Object.values(o).map((v) => BaseRepository._objectDepth(v)))
283290
: 0
284291
}
292+
293+
private static _removeBlockKeys(obj: Value): Value {
294+
return typeof obj === 'object'
295+
? Object.fromEntries(
296+
Object.entries(obj)
297+
.filter(([key]) => key !== BlockNumberKey)
298+
.map(([key, value]) => [key, this._removeBlockKeys(value)])
299+
)
300+
: obj
301+
}
302+
303+
private static _replaceEmptyKeyWithValue(obj: Value): Value {
304+
if (!obj || typeof obj !== 'object') return obj
305+
if (Object.keys(obj).length === 1 && SelfKey in obj) return obj[SelfKey]
306+
return Object.fromEntries(
307+
Object.entries(obj).map(([key, value]) => [key, this._replaceEmptyKeyWithValue(value)])
308+
)
309+
}
310+
311+
private static _clearObjectFromMeta(obj: Value): Value {
312+
return this._replaceEmptyKeyWithValue(this._removeBlockKeys(obj))
313+
}
285314
}

libs/engine/src/app/services/near-signer/near-signer.service.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ export class NearSigner {
2525
constructor(
2626
private _selector: WalletSelector,
2727
private _localDb: LocalDbService,
28-
private _nearConfig: NearConfig
28+
public nearConfig: NearConfig
2929
) {
3030
this.provider = new nearAPI.providers.JsonRpcProvider({
31-
url: _nearConfig.nodeUrl,
31+
url: nearConfig.nodeUrl,
3232
})
3333
}
3434

@@ -58,7 +58,7 @@ export class NearSigner {
5858
}
5959

6060
async call(contractName: string, methodName: string, args: any, gas?: string, deposit?: string) {
61-
if (contractName === this._nearConfig.contractName) {
61+
if (contractName === this.nearConfig.contractName) {
6262
const account = await this._createConnectionForContract(contractName)
6363

6464
// No session key for this contract
@@ -127,13 +127,13 @@ export class NearSigner {
127127

128128
if (!loggedInAccountId) throw new Error('Not logged in')
129129

130-
const keyForContract = await keyStore.getKey(this._nearConfig.networkId, loggedInAccountId)
130+
const keyForContract = await keyStore.getKey(this.nearConfig.networkId, loggedInAccountId)
131131

132132
if (!keyForContract) return null
133133

134134
const near = await nearAPI.connect({
135135
keyStore,
136-
networkId: this._nearConfig.networkId,
136+
networkId: this.nearConfig.networkId,
137137
nodeUrl: this.provider.connection.url,
138138
headers: {},
139139
})
@@ -206,7 +206,7 @@ export class NearSigner {
206206
})
207207

208208
const keyStore = this._getKeyStoreForContract(contractName)
209-
await keyStore.setKey(this._nearConfig.networkId, accountId, keyPair)
209+
await keyStore.setKey(this.nearConfig.networkId, accountId, keyPair)
210210

211211
localStorage.setItem(
212212
`${contractName}_wallet_auth_key`,

libs/engine/src/app/services/social-db/social-db.service.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export type StorageView = {
1212
export type Value = any
1313

1414
const KeyDelimiter = '/'
15+
const BlockNumberKey = ':block'
1516

1617
const EstimatedKeyValueSize = 40 * 3 + 8 + 12
1718
const EstimatedNodeSize = 40 * 2 + 8 + 10
@@ -109,8 +110,13 @@ export class SocialDbService {
109110
private _contractName: string
110111
) {}
111112

112-
async get(keys: string[]): Promise<Value> {
113-
return await this._signer.view(this._contractName, 'get', { keys })
113+
async get(keys: string[], options: { withBlockHeight?: boolean } = {}): Promise<Value> {
114+
return await this._signer.view(this._contractName, 'get', {
115+
keys,
116+
options: {
117+
with_block_height: options.withBlockHeight,
118+
},
119+
})
114120
}
115121

116122
async keys(keys: string[]): Promise<string[]> {
@@ -178,16 +184,20 @@ export class SocialDbService {
178184
)
179185
}
180186

181-
async setMultiple(data: Value[]): Promise<void> {
182-
return this.set(mergeDeep({}, ...data))
183-
}
184-
185187
async delete(keys: string[]): Promise<void> {
186188
const data = await this.get(keys)
187189
const nullData = SocialDbService._nullifyData(data)
188190
await this.set(nullData)
189191
}
190192

193+
// ToDo: move to repository?
194+
// ToDo: approximate timestamp
195+
getTimestampByBlockHeight(blockHeight: number): number {
196+
// ToDo: time reference was private, fix it
197+
const { avgBlockTime, height, timestamp } = this._signer.nearConfig.timeReference
198+
return (blockHeight - height) * avgBlockTime + timestamp
199+
}
200+
191201
private async _getAccountStorage(accountId: string): Promise<StorageView | null> {
192202
const resp = await this._signer.view(this._contractName, 'get_account_storage', {
193203
account_id: accountId,
@@ -239,6 +249,9 @@ export class SocialDbService {
239249

240250
const result: any = {}
241251
for (const key in obj) {
252+
// ToDo: should be it here?
253+
if (key === BlockNumberKey) continue
254+
242255
const newPath = [...path, key]
243256
const nestedResult = this.splitObjectByDepth(obj[key], depth - 1, newPath)
244257
for (const nestedKey in nestedResult) {

libs/engine/src/constants.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,20 @@ export type BuiltInLayoutManagers = {
44
ear: string
55
}
66

7+
export type TimeReference = {
8+
timestamp: number
9+
height: number
10+
avgBlockTime: number
11+
}
12+
713
export type NearConfig = {
814
networkId: string
915
nodeUrl: string
1016
contractName: string
1117
walletUrl: string
1218
defaultMutationId: string
1319
layoutManagers: BuiltInLayoutManagers
20+
timeReference: TimeReference
1421
}
1522

1623
export const NearConfigs: { [networkId: string]: NearConfig } = {
@@ -25,6 +32,11 @@ export const NearConfigs: { [networkId: string]: NearConfig } = {
2532
horizontal: 'bos.dapplets.near/widget/DefaultLayoutManager',
2633
ear: 'bos.dapplets.near/widget/ContextActionsGroup',
2734
},
35+
timeReference: {
36+
timestamp: 1727135249210,
37+
height: 128753799,
38+
avgBlockTime: 1250, // https://nearblocks.io/
39+
},
2840
},
2941
testnet: {
3042
networkId: 'testnet',
@@ -37,6 +49,11 @@ export const NearConfigs: { [networkId: string]: NearConfig } = {
3749
horizontal: 'bos.dapplets.testnet/widget/DefaultLayoutManager',
3850
ear: 'bos.dapplets.testnet/widget/ContextActionsGroup',
3951
},
52+
timeReference: {
53+
timestamp: 1727135126349,
54+
height: 175046986,
55+
avgBlockTime: 1000, // https://testnet.nearblocks.io/
56+
},
4057
},
4158
}
4259

0 commit comments

Comments
 (0)