/
validator-hiro.service.ts
101 lines (93 loc) · 3.18 KB
/
validator-hiro.service.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import { ApiClientService } from '@meta-protocols-oracle/api';
import {
generateOrderHash,
signOrderHash,
} from '@meta-protocols-oracle/brc20-indexer';
import {
Unobservable,
getLogger,
stringifyJSON,
} from '@meta-protocols-oracle/commons';
import { Enums } from '@meta-protocols-oracle/types';
import { ValidatorProcessInterface } from '@meta-protocols-oracle/validator';
import { Inject, Injectable } from '@nestjs/common';
import { pubKeyfromPrivKey, publicKeyToString } from '@stacks/transactions';
import { Observable, concatMap, from } from 'rxjs';
import { env } from '../env';
import { getIndexerTxOnBlock$ } from '../validator/validator';
@Injectable()
export class DefaultValidatorHiroService implements ValidatorProcessInterface {
private readonly logger = getLogger('validator-hiro');
constructor(
@Inject(ApiClientService) private readonly api: ApiClientService,
) {}
async submitIndexerTx(
tx: Unobservable<ReturnType<typeof getIndexerTxOnBlock$>>,
) {
const order_hash = generateOrderHash({
amt: tx.transfer_send.amount,
decimals: tx.decimals,
from: Buffer.from(tx.transfer_send.from_address, 'hex'),
to: Buffer.from(tx.transfer_send.to_address, 'hex'),
'from-bal': BigInt(tx.from_bal),
'to-bal': BigInt(tx.to_bal),
'bitcoin-tx': Buffer.from(tx.tx, 'hex'),
tick: tx.ticker,
output: BigInt(tx.location.vout),
offset: BigInt(tx.location.satpoint),
});
const signature = await signOrderHash(
env().STACKS_VALIDATOR_ACCOUNT_SECRET,
order_hash,
);
this.logger.verbose(`submitting ${tx.tx_id}`);
const pubkey = publicKeyToString(
pubKeyfromPrivKey(env().STACKS_VALIDATOR_ACCOUNT_SECRET),
);
return this.api
.indexer()
.txs()
.post({
type: Enums.ValidatorName.enum.hiro,
header: tx.header,
height: tx.height,
tx_hash: tx.tx,
satpoint: tx.location.satpoint.toString(),
proof_hashes: tx.proof.hashes,
tx_index: tx.proof['tx-index'].toString(10),
tree_depth: tx.proof['tree-depth'].toString(10),
from: tx.transfer_send.from_address,
to: tx.transfer_send.to_address,
output: tx.location.vout.toString(),
tick: tx.ticker,
amt: tx.transfer_send.amount.toString(),
decimals: tx.decimals.toString(),
from_bal: tx.from_bal.toString(),
to_bal: tx.to_bal.toString(),
order_hash: order_hash.toString('hex'),
signature: signature.toString('hex'),
signer: env().STACKS_VALIDATOR_ACCOUNT_ADDRESS,
signer_pubkey: pubkey,
});
}
processBlock$(block: number): Observable<unknown> {
return getIndexerTxOnBlock$(block, this.api).pipe(
concatMap(tx => {
return from(
this.submitIndexerTx(tx).catch(err => {
getLogger('validator-hiro').error(
`error submitting tx: ${
tx.tx_id
}, error: ${err}, tx: ${stringifyJSON(tx)}`,
);
}),
);
}),
);
}
}
const ValidatorHiroServiceProvider = {
provide: ValidatorProcessInterface,
useClass: DefaultValidatorHiroService,
};
export default ValidatorHiroServiceProvider;