-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #24 from RoboVault/utils/timestamp
Utils/timestamp
- Loading branch information
Showing
6 changed files
with
406 additions
and
771 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,49 +1,86 @@ | ||
import { supportedChains } from '../chains.ts' | ||
import { scope } from '../deps.ts' | ||
import { | ||
any, | ||
array, | ||
bigint, | ||
boolean, | ||
literal, | ||
object, | ||
optional, | ||
record, | ||
regex, | ||
safeParse, | ||
special, | ||
string, | ||
union, | ||
url, | ||
} from 'https://deno.land/x/valibot@v0.8.0/mod.ts' | ||
|
||
export const parseArkiveManifest = scope({ | ||
manifest: { | ||
name: 'string', | ||
'version?': /^v\d+$/, | ||
dataSources: Object.fromEntries( | ||
Object.keys(supportedChains).map((chain) => [`${chain}?`, 'dataSource']), | ||
) as Record<`${keyof typeof supportedChains}?`, 'dataSource'>, | ||
entities: 'entity[]', | ||
'schemaComposerCustomizer?': 'Function', | ||
}, | ||
dataSource: { | ||
options: 'chainOptions', | ||
'contracts?': 'contract[]', | ||
'blockHandlers?': 'blockHandler[]', | ||
}, | ||
entity: { | ||
model: 'Function', | ||
list: 'boolean', | ||
name: 'string', | ||
}, | ||
chainOptions: { | ||
blockRange: 'bigint', | ||
rpcUrl: 'string', | ||
}, | ||
contract: { | ||
id: 'string', | ||
abi: 'any[]', | ||
sources: 'source[]', | ||
events: 'eventSource[]', | ||
'factorySources?': 'any', | ||
}, | ||
blockHandler: { | ||
handler: 'Function', | ||
startBlockHeight: 'bigint|"live"', | ||
blockInterval: 'bigint', | ||
name: 'string', | ||
}, | ||
source: { | ||
address: '/^0x[a-fA-F0-9]{40}$/ | "*"', | ||
startBlockHeight: 'bigint', | ||
}, | ||
eventSource: { | ||
name: 'string', | ||
handler: 'Function', | ||
const manifestSchema = object({ | ||
name: string('Name must be a string'), | ||
version: optional(string('Invalid version', [regex(/^v\d+$/)])), | ||
dataSources: record(object({ | ||
options: object({ | ||
blockRange: bigint('Block range must be a bigint'), | ||
rpcUrl: string('RPC URL must be a string', [ | ||
url('RPC URL must be a valid URL'), | ||
]), | ||
}), | ||
contracts: optional(array(object({ | ||
id: string('Contract ID must be a string'), | ||
abi: array(any()), | ||
sources: array(object({ | ||
address: union([ | ||
string( | ||
'Address must either be a valid hexstring or a wildcard (\'*\')', | ||
[ | ||
regex(/^0x[a-fA-F0-9]{40}$/, 'Address must be a valid hexstring'), | ||
], | ||
), | ||
literal('*', 'Address can be \'*\''), | ||
]), | ||
startBlockHeight: bigint('Start block height must be a bigint'), | ||
})), | ||
events: array(object({ | ||
name: string('Event name must be a string'), | ||
handler: special((input) => typeof input === 'function'), | ||
})), | ||
factorySources: optional( | ||
record( | ||
record( | ||
string('Factory sources must be a record of records of strings'), | ||
), | ||
), | ||
), | ||
}))), | ||
blockHandlers: optional(array(object({ | ||
handler: special((input) => typeof input === 'function'), | ||
startBlockHeight: union( | ||
[ | ||
bigint('Start block height must be a bigint'), | ||
literal('live', 'Start block height can be \'live\''), | ||
], | ||
), | ||
blockInterval: bigint('Block interval must be a bigint'), | ||
name: string('Block handler name must be a string'), | ||
}))), | ||
})), | ||
entities: array(object({ | ||
model: special((input) => typeof input === 'function'), | ||
list: boolean('Entity\'s list property must be a boolean'), | ||
name: string('Entity\'s name property must be a string'), | ||
})), | ||
schemaComposerCustomizer: optional( | ||
special((input) => typeof input === 'function'), | ||
), | ||
}) | ||
|
||
export const parseArkiveManifest = { | ||
manifest: (manifest: unknown) => { | ||
const result = safeParse(manifestSchema, manifest) | ||
if (result.success) { | ||
return { data: result.data } | ||
} else { | ||
return { problems: result.error.issues } | ||
} | ||
}, | ||
}).compile() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { Store } from "../arkiver/store.ts"; | ||
import { PublicClient } from "../deps.ts"; | ||
|
||
/** | ||
* Returns the closest timestamp to the given timestamp that is divisible by the given interval (in ms) rounded down. | ||
* @param timestamp | ||
* @param interval | ||
* @returns | ||
*/ | ||
export const getClosestTimestamp = (timestamp: number, interval: number) => { | ||
return timestamp - (timestamp % interval); | ||
}; | ||
|
||
export const getTimestampFromBlockNumber = async ( | ||
params: { | ||
client: PublicClient; | ||
store: Store; | ||
blockNumber: bigint; | ||
group?: { | ||
blockTimeMs: number; | ||
groupTimeMs: number; | ||
}; | ||
}, | ||
) => { | ||
let adjustedBlockNumber = params.blockNumber; | ||
|
||
if (params.group) { | ||
const blocks = Math.floor( | ||
params.group.groupTimeMs / params.group.blockTimeMs, | ||
); | ||
adjustedBlockNumber = params.blockNumber - | ||
(params.blockNumber % BigInt(blocks)); | ||
} | ||
|
||
return Number( | ||
await params.store.retrieve( | ||
`blockNumberTimestamp:${adjustedBlockNumber}`, | ||
async () => { | ||
const block = await params.client.getBlock({ | ||
blockNumber: adjustedBlockNumber, | ||
}); | ||
return block.timestamp; | ||
}, | ||
), | ||
) * 1000; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export { bigIntDivToFloat, bigIntToFloat } from "./src/utils/bigint.ts"; | ||
export { getClosestTimestamp, getTimestampFromBlockNumber } from "./src/utils/timestamp.ts" |