@xyo-network/chain-services 1.0.1
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/LICENSE +165 -0
- package/README.md +57 -0
- package/babel.config.json +6 -0
- package/dist/neutral/index.mjs +972 -0
- package/dist/neutral/index.mjs.map +1 -0
- package/dist/types/AccountBalance/XyoChainAccountBalanceService.d.ts +24 -0
- package/dist/types/AccountBalance/XyoChainAccountBalanceService.d.ts.map +1 -0
- package/dist/types/AccountBalance/index.d.ts +2 -0
- package/dist/types/AccountBalance/index.d.ts.map +1 -0
- package/dist/types/BaseService.d.ts +22 -0
- package/dist/types/BaseService.d.ts.map +1 -0
- package/dist/types/BlockProducer/XyoBlockProducer.d.ts +50 -0
- package/dist/types/BlockProducer/XyoBlockProducer.d.ts.map +1 -0
- package/dist/types/BlockProducer/index.d.ts +2 -0
- package/dist/types/BlockProducer/index.d.ts.map +1 -0
- package/dist/types/BlockReward/EvmBlockRewardService.d.ts +16 -0
- package/dist/types/BlockReward/EvmBlockRewardService.d.ts.map +1 -0
- package/dist/types/BlockReward/XyoBlockRewardService.d.ts +23 -0
- package/dist/types/BlockReward/XyoBlockRewardService.d.ts.map +1 -0
- package/dist/types/BlockReward/index.d.ts +3 -0
- package/dist/types/BlockReward/index.d.ts.map +1 -0
- package/dist/types/ChainIndexService.d.ts +42 -0
- package/dist/types/ChainIndexService.d.ts.map +1 -0
- package/dist/types/ChainValidator/XyoValidator.d.ts +26 -0
- package/dist/types/ChainValidator/XyoValidator.d.ts.map +1 -0
- package/dist/types/ChainValidator/index.d.ts +3 -0
- package/dist/types/ChainValidator/index.d.ts.map +1 -0
- package/dist/types/ChainValidator/model/Validator.d.ts +7 -0
- package/dist/types/ChainValidator/model/Validator.d.ts.map +1 -0
- package/dist/types/ChainValidator/model/index.d.ts +2 -0
- package/dist/types/ChainValidator/model/index.d.ts.map +1 -0
- package/dist/types/CreatableService.d.ts +9 -0
- package/dist/types/CreatableService.d.ts.map +1 -0
- package/dist/types/Election/XyoElectionService.d.ts +13 -0
- package/dist/types/Election/XyoElectionService.d.ts.map +1 -0
- package/dist/types/Election/index.d.ts +2 -0
- package/dist/types/Election/index.d.ts.map +1 -0
- package/dist/types/PendingTransactions/PendingTransactions.d.ts +25 -0
- package/dist/types/PendingTransactions/PendingTransactions.d.ts.map +1 -0
- package/dist/types/PendingTransactions/index.d.ts +2 -0
- package/dist/types/PendingTransactions/index.d.ts.map +1 -0
- package/dist/types/StakeIntent/XyoStakeIntentService.d.ts +48 -0
- package/dist/types/StakeIntent/XyoStakeIntentService.d.ts.map +1 -0
- package/dist/types/StakeIntent/index.d.ts +2 -0
- package/dist/types/StakeIntent/index.d.ts.map +1 -0
- package/dist/types/StakeIntent/lib/getBlockSignedStakeDeclarations.d.ts +4 -0
- package/dist/types/StakeIntent/lib/getBlockSignedStakeDeclarations.d.ts.map +1 -0
- package/dist/types/StakeIntent/lib/index.d.ts +2 -0
- package/dist/types/StakeIntent/lib/index.d.ts.map +1 -0
- package/dist/types/Staker/Evm/Evm.d.ts +39 -0
- package/dist/types/Staker/Evm/Evm.d.ts.map +1 -0
- package/dist/types/Staker/Evm/index.d.ts +2 -0
- package/dist/types/Staker/Evm/index.d.ts.map +1 -0
- package/dist/types/Staker/index.d.ts +2 -0
- package/dist/types/Staker/index.d.ts.map +1 -0
- package/dist/types/XyoChainBlockNumberIterator.d.ts +21 -0
- package/dist/types/XyoChainBlockNumberIterator.d.ts.map +1 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/index.d.ts.map +1 -0
- package/package.json +101 -0
- package/src/AccountBalance/XyoChainAccountBalanceService.ts +49 -0
- package/src/AccountBalance/index.ts +1 -0
- package/src/BaseService.ts +64 -0
- package/src/BlockProducer/XyoBlockProducer.ts +184 -0
- package/src/BlockProducer/index.ts +1 -0
- package/src/BlockReward/EvmBlockRewardService.ts +42 -0
- package/src/BlockReward/XyoBlockRewardService.ts +73 -0
- package/src/BlockReward/index.ts +2 -0
- package/src/ChainIndexService.ts +48 -0
- package/src/ChainValidator/XyoValidator.ts +55 -0
- package/src/ChainValidator/index.ts +2 -0
- package/src/ChainValidator/model/Validator.ts +7 -0
- package/src/ChainValidator/model/index.ts +1 -0
- package/src/CreatableService.ts +16 -0
- package/src/Election/XyoElectionService.ts +45 -0
- package/src/Election/index.ts +1 -0
- package/src/PendingTransactions/PendingTransactions.ts +168 -0
- package/src/PendingTransactions/index.ts +1 -0
- package/src/StakeIntent/XyoStakeIntentService.ts +214 -0
- package/src/StakeIntent/index.ts +1 -0
- package/src/StakeIntent/lib/getBlockSignedStakeDeclarations.ts +44 -0
- package/src/StakeIntent/lib/index.ts +1 -0
- package/src/Staker/Evm/Evm.ts +117 -0
- package/src/Staker/Evm/index.ts +1 -0
- package/src/Staker/index.ts +1 -0
- package/src/XyoChainBlockNumberIterator.ts +98 -0
- package/src/index.ts +11 -0
- package/xy.config.ts +10 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { filterAs } from '@xylabs/array'
|
|
2
|
+
import { asElevated, asOptionalChainStakeIntent, BlockBoundWitness, ChainStakeIntent, ChainStakeIntentSchema, Intent } from '@xyo-network/chain-model'
|
|
3
|
+
import { exists } from '@xylabs/exists'
|
|
4
|
+
import { Hash } from '@xylabs/hex'
|
|
5
|
+
import { ArchivistInstance } from '@xyo-network/archivist-model'
|
|
6
|
+
import { asOptionalBoundWitness, BoundWitness } from '@xyo-network/boundwitness-model'
|
|
7
|
+
import { payloadSchemasContains } from '@xyo-network/boundwitness-validator'
|
|
8
|
+
import { BoundWitnessWrapper } from '@xyo-network/boundwitness-wrapper'
|
|
9
|
+
|
|
10
|
+
export const getBlockSignedStakeDeclarations = async (block: BlockBoundWitness, archivist: ArchivistInstance, intent: Intent): Promise<ChainStakeIntent[]> => {
|
|
11
|
+
// Get payloads in block
|
|
12
|
+
const blockData = await archivist.get(block.payload_hashes)
|
|
13
|
+
// Filter Payloads in block to BoundWitnesses
|
|
14
|
+
const bwsFromBlock = filterAs(blockData, asOptionalBoundWitness)
|
|
15
|
+
// Filter to BoundWitnesses with StakeIntent payloads
|
|
16
|
+
const bwsFromBlockWithDeclarations = bwsFromBlock.filter(bw => payloadSchemasContains(bw, ChainStakeIntentSchema))
|
|
17
|
+
// Filter to only valid signed BWs
|
|
18
|
+
const validBlockBwsWithDeclarations = await filterToValidSignedBoundWitnesses(bwsFromBlockWithDeclarations)
|
|
19
|
+
return (await Promise.all(validBlockBwsWithDeclarations.map(async (bw) => {
|
|
20
|
+
// Get staked intent hashes from signed declarations
|
|
21
|
+
const stakeIntentHashes = validBlockBwsWithDeclarations
|
|
22
|
+
.flatMap(mapBoundWitnessToStakeIntentHashes)
|
|
23
|
+
.filter(exists)
|
|
24
|
+
// Get staked intent payloads
|
|
25
|
+
const payloads = await archivist.get(stakeIntentHashes)
|
|
26
|
+
// Filter payloads to staked intents
|
|
27
|
+
const stakeIntents = filterAs(filterAs(payloads, asOptionalChainStakeIntent), asElevated)
|
|
28
|
+
// that are producers
|
|
29
|
+
.filter(p => p.intent === intent)
|
|
30
|
+
// where the issuer of the intent is also the signer of this BW
|
|
31
|
+
.filter(p => bw.addresses.includes(p.from))
|
|
32
|
+
|
|
33
|
+
return stakeIntents
|
|
34
|
+
}))).flat()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const filterToValidSignedBoundWitnesses = async (bws: BoundWitness[]): Promise<BoundWitness[]> => {
|
|
38
|
+
const validBwIndexes = await Promise.all(bws.map(bw => BoundWitnessWrapper.parse(bw).getValid()))
|
|
39
|
+
return bws.filter((_, index) => validBwIndexes[index])
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const mapBoundWitnessToStakeIntentHashes = (bw: BoundWitness): (Hash | undefined)[] => {
|
|
43
|
+
return bw.payload_schemas.map((schema, index) => schema === ChainStakeIntentSchema ? bw.payload_hashes[index] : undefined)
|
|
44
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './getBlockSignedStakeDeclarations.ts'
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { BaseServiceParams, ChainService } from '@xyo-network/chain-model'
|
|
2
|
+
import { Address, toAddress } from '@xylabs/hex'
|
|
3
|
+
import { Base } from '@xylabs/object'
|
|
4
|
+
import { Promisable } from '@xylabs/promise'
|
|
5
|
+
import { StakedXyoChain, StakedXyoChain__factory as StakedXyoChainFactory } from '@xyo-network/typechain'
|
|
6
|
+
import { getAddress } from 'ethers/address'
|
|
7
|
+
import { ContractRunner } from 'ethers/providers'
|
|
8
|
+
|
|
9
|
+
export interface EvmChainServiceParams extends BaseServiceParams {
|
|
10
|
+
contract: StakedXyoChain
|
|
11
|
+
id: Address
|
|
12
|
+
runner: ContractRunner
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A class that represents a chain stake as backed by an EVM smart contract
|
|
17
|
+
*/
|
|
18
|
+
export class EvmChainService extends Base<EvmChainServiceParams> implements ChainService {
|
|
19
|
+
private _contract: StakedXyoChain
|
|
20
|
+
|
|
21
|
+
protected constructor(params: EvmChainServiceParams) {
|
|
22
|
+
super(params)
|
|
23
|
+
this._contract = params.contract
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get id(): Address {
|
|
27
|
+
return this.params.id
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get runner(): ContractRunner {
|
|
31
|
+
return this.params.runner
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static create({
|
|
35
|
+
id, runner, logger, traceProvider, meterProvider,
|
|
36
|
+
}: Omit<EvmChainServiceParams, 'contract'>): Promisable<EvmChainService> {
|
|
37
|
+
const contractAddress = getAddress(id)
|
|
38
|
+
const contract: StakedXyoChain = StakedXyoChainFactory.connect(contractAddress, runner)
|
|
39
|
+
return new this({
|
|
40
|
+
id, contract, runner, logger, traceProvider, meterProvider,
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async active(): Promise<bigint> {
|
|
45
|
+
return await this._contract.active()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async activeByAddressStaked(address: string): Promise<bigint> {
|
|
49
|
+
return await this._contract.activeByAddressStaked(getAddress(address))
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async activeByStaker(address: string): Promise<bigint> {
|
|
53
|
+
return await this._contract.activeByStaker(getAddress(address))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async addStake(staked: string, amount: bigint): Promise<boolean> {
|
|
57
|
+
const result = await this._contract.addStake(getAddress(staked), amount)
|
|
58
|
+
await result.wait()
|
|
59
|
+
return true
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async chainId(): Promise<Address> {
|
|
63
|
+
return toAddress(await this._contract.chainId())
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async forkedAtBlockNumber(): Promise<bigint> {
|
|
67
|
+
return await this._contract.forkedAtBlockNumber()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async forkedAtHash(): Promise<bigint> {
|
|
71
|
+
return await this._contract.forkedAtHash()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async forkedChainId(): Promise<Address> {
|
|
75
|
+
return toAddress(await this._contract.forkedChainId())
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async minWithdrawalBlocks(): Promise<bigint> {
|
|
79
|
+
return await this._contract.minWithdrawalBlocks()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async pending(): Promise<bigint> {
|
|
83
|
+
return await this._contract.pending()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async pendingByStaker(staker: string): Promise<bigint> {
|
|
87
|
+
return await this._contract.pendingByStaker(getAddress(staker))
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async removeStake(slot: bigint): Promise<boolean> {
|
|
91
|
+
const result = await this._contract.removeStake(slot)
|
|
92
|
+
await result.wait()
|
|
93
|
+
return true
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async rewardsContract(): Promise<string> {
|
|
97
|
+
return await this._contract.rewardsContract()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async stakingTokenAddress(): Promise<string> {
|
|
101
|
+
return await this._contract.stakingTokenAddress()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async withdrawStake(slot: bigint): Promise<boolean> {
|
|
105
|
+
const result = await this._contract.withdrawStake(slot)
|
|
106
|
+
await result.wait()
|
|
107
|
+
return true
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async withdrawn(): Promise<bigint> {
|
|
111
|
+
return await this._contract.withdrawn()
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async withdrawnByStaker(staker: string): Promise<bigint> {
|
|
115
|
+
return await this._contract.withdrawnByStaker(getAddress(staker))
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Evm.ts'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Evm/index.ts'
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { assertEx } from '@xylabs/assert'
|
|
2
|
+
import {
|
|
3
|
+
asBlockBoundWitness, asOptionalBlockBoundWitness, BlockBoundWitness, ChainIdentification,
|
|
4
|
+
ChainIteratorEventData,
|
|
5
|
+
EventingChainBlockNumberIterator,
|
|
6
|
+
isBlockBoundWitness,
|
|
7
|
+
XyoChainIteratorParams,
|
|
8
|
+
} from '@xyo-network/chain-model'
|
|
9
|
+
import { BaseParams } from '@xylabs/object'
|
|
10
|
+
import { ArchivistInstance } from '@xyo-network/archivist-model'
|
|
11
|
+
import { BaseEmitter } from '@xyo-network/module-event-emitter'
|
|
12
|
+
import { PayloadBuilder } from '@xyo-network/payload-builder'
|
|
13
|
+
import { LRUCache } from 'lru-cache'
|
|
14
|
+
|
|
15
|
+
export class XyoChainBlockNumberIterator extends BaseEmitter<BaseParams, ChainIteratorEventData> implements EventingChainBlockNumberIterator {
|
|
16
|
+
protected _blocksByBlockNumber = new LRUCache<number, BlockBoundWitness>({ max: 10_000 })
|
|
17
|
+
protected _chainArchivist: ArchivistInstance
|
|
18
|
+
protected _chainInformation: ChainIdentification
|
|
19
|
+
protected _head: BlockBoundWitness
|
|
20
|
+
|
|
21
|
+
protected constructor(params: XyoChainIteratorParams) {
|
|
22
|
+
super({})
|
|
23
|
+
const { chainArchivist: archivist, head } = params
|
|
24
|
+
this._chainArchivist = archivist
|
|
25
|
+
this._head = head
|
|
26
|
+
this._chainInformation = { id: head.chain }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get archivist(): ArchivistInstance { return this._chainArchivist }
|
|
30
|
+
|
|
31
|
+
get chainIdentification(): ChainIdentification { return this._chainInformation }
|
|
32
|
+
|
|
33
|
+
static async create(params: XyoChainIteratorParams): Promise<XyoChainBlockNumberIterator> {
|
|
34
|
+
await Promise.resolve()
|
|
35
|
+
return new XyoChainBlockNumberIterator(params)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async get(block: number): Promise<BlockBoundWitness> {
|
|
39
|
+
// Bail early if the block requested is newer than the current head
|
|
40
|
+
assertEx(this._head.block >= block, () => `Block requested is newer than the current head [${block}]`)
|
|
41
|
+
const cached = this._blocksByBlockNumber.get(block)
|
|
42
|
+
if (cached) return cached
|
|
43
|
+
// Start at the current head and traverse backwards until the requested block is found
|
|
44
|
+
const startingBlock = this._head
|
|
45
|
+
const currentBlockHash = await PayloadBuilder.hash(startingBlock)
|
|
46
|
+
let [currentBlock] = await this._chainArchivist.get([currentBlockHash])
|
|
47
|
+
while (currentBlock) {
|
|
48
|
+
assertEx(asBlockBoundWitness(currentBlock), () => `Expected hash to be a block bound witness [${currentBlock._hash}]`)
|
|
49
|
+
if (isBlockBoundWitness(currentBlock)) {
|
|
50
|
+
this._blocksByBlockNumber.set(currentBlock.block, currentBlock)
|
|
51
|
+
if (currentBlock.block === block) {
|
|
52
|
+
return currentBlock
|
|
53
|
+
}
|
|
54
|
+
const { previous } = currentBlock
|
|
55
|
+
if (!previous) break
|
|
56
|
+
[currentBlock] = await this._chainArchivist.get([previous])
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
throw new Error(`Block not found: ${block}`)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async head(): Promise<BlockBoundWitness> {
|
|
63
|
+
return await Promise.resolve(this._head)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async next(block: number): Promise<BlockBoundWitness | undefined> {
|
|
67
|
+
const currentBlock = block
|
|
68
|
+
const nextBlockNumber = currentBlock + 1
|
|
69
|
+
return await this.get(nextBlockNumber)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// TODO: Decide on inclusive/exclusive (probably need inclusive to account for chain head)
|
|
73
|
+
// and then communicate via method name and documentation
|
|
74
|
+
async previous(block: number | undefined = undefined, count: number = 1): Promise<BlockBoundWitness[]> {
|
|
75
|
+
const results: BlockBoundWitness[] = []
|
|
76
|
+
let currentBlock: BlockBoundWitness | undefined = block ? (await this.get(block)) : this._head
|
|
77
|
+
while (currentBlock && results.length < count) {
|
|
78
|
+
if (isBlockBoundWitness(currentBlock)) {
|
|
79
|
+
results.push(currentBlock)
|
|
80
|
+
const { previous } = currentBlock
|
|
81
|
+
if (!previous) break
|
|
82
|
+
const nextBlock = await this._chainArchivist.get([previous])
|
|
83
|
+
currentBlock = asOptionalBlockBoundWitness(nextBlock[0])
|
|
84
|
+
} else {
|
|
85
|
+
const hash = PayloadBuilder.hash(currentBlock)
|
|
86
|
+
assertEx(asBlockBoundWitness(currentBlock), () => `Expected hash to be a block bound witness [${hash}]`)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return results
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async updateHead(head: BlockBoundWitness): Promise<void> {
|
|
93
|
+
// Async to allow for re-indexing, etc.
|
|
94
|
+
await Promise.resolve()
|
|
95
|
+
this._head = head
|
|
96
|
+
void this.emit('headUpdated', { blocks: [head] })
|
|
97
|
+
}
|
|
98
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './AccountBalance/index.ts'
|
|
2
|
+
export * from './BaseService.ts'
|
|
3
|
+
export * from './BlockProducer/index.ts'
|
|
4
|
+
export * from './BlockReward/index.ts'
|
|
5
|
+
export * from './ChainValidator/index.ts'
|
|
6
|
+
export * from './CreatableService.ts'
|
|
7
|
+
export * from './Election/index.ts'
|
|
8
|
+
export * from './PendingTransactions/index.ts'
|
|
9
|
+
export * from './StakeIntent/index.ts'
|
|
10
|
+
export * from './Staker/index.ts'
|
|
11
|
+
export * from './XyoChainBlockNumberIterator.ts'
|