@ocap/resolver 1.28.8 → 1.29.0
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/esm/api.d.mts +24 -0
- package/esm/api.mjs +53 -0
- package/esm/hooks.d.mts +153 -0
- package/esm/hooks.mjs +267 -0
- package/esm/index.d.mts +201 -0
- package/esm/index.mjs +1327 -0
- package/esm/migration-chain.d.mts +52 -0
- package/esm/migration-chain.mjs +97 -0
- package/esm/package.mjs +5 -0
- package/esm/token-cache.d.mts +20 -0
- package/esm/token-cache.mjs +26 -0
- package/esm/token-distribution.d.mts +166 -0
- package/esm/token-distribution.mjs +241 -0
- package/esm/token-flow.d.mts +139 -0
- package/esm/token-flow.mjs +330 -0
- package/esm/types.d.mts +115 -0
- package/esm/types.mjs +1 -0
- package/lib/_virtual/rolldown_runtime.cjs +29 -0
- package/lib/api.cjs +54 -0
- package/lib/api.d.cts +24 -0
- package/lib/hooks.cjs +274 -0
- package/lib/hooks.d.cts +153 -0
- package/lib/index.cjs +1343 -0
- package/lib/index.d.cts +201 -0
- package/lib/migration-chain.cjs +99 -0
- package/lib/migration-chain.d.cts +52 -0
- package/lib/package.cjs +11 -0
- package/lib/token-cache.cjs +27 -0
- package/lib/token-cache.d.cts +20 -0
- package/lib/token-distribution.cjs +243 -0
- package/lib/token-distribution.d.cts +166 -0
- package/lib/token-flow.cjs +336 -0
- package/lib/token-flow.d.cts +139 -0
- package/lib/types.cjs +0 -0
- package/lib/types.d.cts +115 -0
- package/package.json +49 -21
- package/lib/api.js +0 -71
- package/lib/hooks.js +0 -339
- package/lib/index.js +0 -1486
- package/lib/migration-chain.js +0 -144
- package/lib/token-cache.js +0 -40
- package/lib/token-distribution.js +0 -358
- package/lib/token-flow.js +0 -445
package/esm/api.d.mts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { TIndexedTokenState } from "./types.mjs";
|
|
2
|
+
import { Router } from "express";
|
|
3
|
+
|
|
4
|
+
//#region src/api.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Resolver interface for API handler
|
|
8
|
+
*/
|
|
9
|
+
interface IAPIResolver {
|
|
10
|
+
getTokenState(params: {
|
|
11
|
+
address: string;
|
|
12
|
+
}): Promise<TIndexedTokenState | null>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Parameters for createAPIHandler
|
|
16
|
+
*/
|
|
17
|
+
interface ICreateAPIHandlerParams {
|
|
18
|
+
resolver: IAPIResolver;
|
|
19
|
+
}
|
|
20
|
+
declare function createAPIHandler({
|
|
21
|
+
resolver
|
|
22
|
+
}: ICreateAPIHandlerParams): Router;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { createAPIHandler };
|
package/esm/api.mjs
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { getNftBGColorFromDid, getSvg } from "@arcblock/nft-display";
|
|
2
|
+
import { Router } from "express";
|
|
3
|
+
|
|
4
|
+
//#region src/api.ts
|
|
5
|
+
function createAPIHandler({ resolver }) {
|
|
6
|
+
const router = Router();
|
|
7
|
+
router.get("/token/display", async (req, res) => {
|
|
8
|
+
const { address } = req.query;
|
|
9
|
+
if (!address) return res.status(400).json({ error: "address is required" });
|
|
10
|
+
const token = await resolver.getTokenState({ address });
|
|
11
|
+
if (!token) return res.status(400).json({ error: "token not found" });
|
|
12
|
+
let iconUri = "";
|
|
13
|
+
if (token.icon) {
|
|
14
|
+
let iconWidth = 40;
|
|
15
|
+
let iconHeight = 40;
|
|
16
|
+
const viewBoxMatch = token.icon.match(/viewBox\s*=\s*["']([^"']+)["']/i);
|
|
17
|
+
if (viewBoxMatch) {
|
|
18
|
+
const viewBoxValues = viewBoxMatch[1].split(/\s+/);
|
|
19
|
+
if (viewBoxValues.length >= 4) {
|
|
20
|
+
iconWidth = +viewBoxValues[2] || iconWidth;
|
|
21
|
+
iconHeight = +viewBoxValues[3] || iconHeight;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const fixedSvg = token.icon.replace(/<svg([^>]*)>/, `<svg$1 width="${iconWidth}" height="${iconHeight}">`);
|
|
25
|
+
iconUri = `data:image/svg+xml;base64,${Buffer.from(fixedSvg).toString("base64")}`;
|
|
26
|
+
}
|
|
27
|
+
const svg = getSvg({
|
|
28
|
+
color: getNftBGColorFromDid(address),
|
|
29
|
+
did: address,
|
|
30
|
+
variant: "app-passport",
|
|
31
|
+
verifiable: true,
|
|
32
|
+
chain: "arcblock",
|
|
33
|
+
header: {
|
|
34
|
+
icon: iconUri,
|
|
35
|
+
name: token.symbol
|
|
36
|
+
},
|
|
37
|
+
issuer: {
|
|
38
|
+
name: token.symbol,
|
|
39
|
+
icon: iconUri
|
|
40
|
+
},
|
|
41
|
+
extra: {
|
|
42
|
+
key: "Name",
|
|
43
|
+
value: token.name
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
res.setHeader("Content-Type", "image/svg+xml");
|
|
47
|
+
res.send(svg);
|
|
48
|
+
});
|
|
49
|
+
return router;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
//#endregion
|
|
53
|
+
export { createAPIHandler };
|
package/esm/hooks.d.mts
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { IIndexDB, IIndexTable, IPipelineLogger, IRollupValidator, ITokenCache, TIndexedAccountState, TIndexedRollupState, TIndexedRollupValidator, TIndexedStakeState, TIndexedTransaction, TTransactionReceipt } from "./types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/hooks.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Rollup state for hooks (subset of TIndexedRollupState)
|
|
7
|
+
*/
|
|
8
|
+
interface IHooksRollupState {
|
|
9
|
+
address: string;
|
|
10
|
+
tokenAddress: string;
|
|
11
|
+
genesisTime: string;
|
|
12
|
+
seedValidators: IRollupValidator[];
|
|
13
|
+
validators: IRollupValidator[];
|
|
14
|
+
totalDepositAmount?: string;
|
|
15
|
+
totalWithdrawAmount?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Token balance in array format
|
|
19
|
+
*/
|
|
20
|
+
interface ITokenBalance {
|
|
21
|
+
address: string;
|
|
22
|
+
balance: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Stake state for hooks (with array-format tokens)
|
|
26
|
+
*/
|
|
27
|
+
interface IHooksStakeState {
|
|
28
|
+
address: string;
|
|
29
|
+
receiver: string;
|
|
30
|
+
tokens: ITokenBalance[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Transaction state in context
|
|
34
|
+
*/
|
|
35
|
+
interface ITxState {
|
|
36
|
+
time: string;
|
|
37
|
+
receipts: TTransactionReceipt[];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Hook context passed to handlers
|
|
41
|
+
*/
|
|
42
|
+
interface IHooksContext {
|
|
43
|
+
txType?: string;
|
|
44
|
+
txState?: ITxState;
|
|
45
|
+
rollupState?: {
|
|
46
|
+
address: string;
|
|
47
|
+
tokenAddress?: string;
|
|
48
|
+
};
|
|
49
|
+
blockState?: {
|
|
50
|
+
signatures: Array<{
|
|
51
|
+
signer: string;
|
|
52
|
+
}>;
|
|
53
|
+
};
|
|
54
|
+
stateSnapshot?: Record<string, unknown>;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Transaction for hooks
|
|
58
|
+
*/
|
|
59
|
+
interface IHooksTx {
|
|
60
|
+
code: string;
|
|
61
|
+
hash: string;
|
|
62
|
+
time: string;
|
|
63
|
+
receipts?: TTransactionReceipt[];
|
|
64
|
+
tx: {
|
|
65
|
+
from: string;
|
|
66
|
+
signatures?: Array<{
|
|
67
|
+
signer: string;
|
|
68
|
+
}>;
|
|
69
|
+
itxJson: {
|
|
70
|
+
type_url: string;
|
|
71
|
+
address?: string;
|
|
72
|
+
receiver?: string;
|
|
73
|
+
rollup?: string;
|
|
74
|
+
endpoint?: string;
|
|
75
|
+
token?: {
|
|
76
|
+
value: string;
|
|
77
|
+
};
|
|
78
|
+
actualFee?: string;
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Block for rollup block hooks
|
|
84
|
+
*/
|
|
85
|
+
interface IHooksBlock {
|
|
86
|
+
hash: string;
|
|
87
|
+
height: number;
|
|
88
|
+
genesisTime: string;
|
|
89
|
+
validators: string[];
|
|
90
|
+
proposer: string;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Block context for rollup block hooks
|
|
94
|
+
*/
|
|
95
|
+
interface IHooksBlockContext {
|
|
96
|
+
rollupState: {
|
|
97
|
+
address: string;
|
|
98
|
+
};
|
|
99
|
+
txStates: Array<{
|
|
100
|
+
type: string;
|
|
101
|
+
hash: string;
|
|
102
|
+
tx: {
|
|
103
|
+
itxJson: {
|
|
104
|
+
actualFee: string;
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
}>;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* IndexDB interface for hooks (specialized tables)
|
|
111
|
+
*/
|
|
112
|
+
interface IHooksIndexDB extends IIndexDB {
|
|
113
|
+
account: IIndexTable<TIndexedAccountState> & {
|
|
114
|
+
get(address: string): Promise<TIndexedAccountState>;
|
|
115
|
+
};
|
|
116
|
+
rollup: IIndexTable<TIndexedRollupState> & {
|
|
117
|
+
get(address: string): Promise<IHooksRollupState | null>;
|
|
118
|
+
update(address: string, doc: unknown): Promise<void>;
|
|
119
|
+
};
|
|
120
|
+
rollupValidator: IIndexTable<TIndexedRollupValidator> & {
|
|
121
|
+
get(address: string): Promise<TIndexedRollupValidator | null>;
|
|
122
|
+
insert(doc: TIndexedRollupValidator): Promise<TIndexedRollupValidator>;
|
|
123
|
+
update(address: string, doc: TIndexedRollupValidator): Promise<void>;
|
|
124
|
+
};
|
|
125
|
+
stake: IIndexTable<TIndexedStakeState> & {
|
|
126
|
+
get(address: string): Promise<IHooksStakeState>;
|
|
127
|
+
};
|
|
128
|
+
tx: IIndexTable<TIndexedTransaction> & {
|
|
129
|
+
get(hash: string): Promise<unknown>;
|
|
130
|
+
update(hash: string, updates: unknown): Promise<void>;
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Resolver interface for hooks
|
|
135
|
+
*/
|
|
136
|
+
interface IHooksResolver {
|
|
137
|
+
indexdb: IHooksIndexDB;
|
|
138
|
+
logger: IPipelineLogger;
|
|
139
|
+
tokenDistribution: {
|
|
140
|
+
updateByTx: (tx: IHooksTx, ctx: IHooksContext) => Promise<void>;
|
|
141
|
+
};
|
|
142
|
+
buildMigrationChain: () => Promise<void>;
|
|
143
|
+
tokenCache?: ITokenCache;
|
|
144
|
+
}
|
|
145
|
+
declare const onCreateRollup: (rollup: IHooksRollupState, _ctx: unknown, indexdb: IHooksIndexDB) => void;
|
|
146
|
+
declare const onCreateRollupBlock: (block: IHooksBlock, ctx: IHooksBlockContext, indexdb: IHooksIndexDB) => void;
|
|
147
|
+
declare const onUpdateToken: (tokenDoc: {
|
|
148
|
+
address: string;
|
|
149
|
+
[key: string]: unknown;
|
|
150
|
+
}, _ctx: unknown, _indexdb: unknown, resolver: IHooksResolver) => void;
|
|
151
|
+
declare const onCreateTx: (tx: IHooksTx, ctx: IHooksContext, resolver: IHooksResolver) => Promise<void>;
|
|
152
|
+
//#endregion
|
|
153
|
+
export { onCreateRollup, onCreateRollupBlock, onCreateTx, onUpdateToken };
|
package/esm/hooks.mjs
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { name } from "./package.mjs";
|
|
2
|
+
import { toStakeAddress } from "@arcblock/did-util";
|
|
3
|
+
import { BN } from "@ocap/util";
|
|
4
|
+
import createDebug from "debug";
|
|
5
|
+
import merge from "lodash/merge.js";
|
|
6
|
+
import pick from "lodash/pick.js";
|
|
7
|
+
|
|
8
|
+
//#region src/hooks.ts
|
|
9
|
+
const debug = createDebug(name);
|
|
10
|
+
const onCreateRollup = (rollup, _ctx, indexdb) => {
|
|
11
|
+
rollup.seedValidators.forEach(async (x) => {
|
|
12
|
+
const { pk, address, endpoint } = x;
|
|
13
|
+
try {
|
|
14
|
+
const account = await indexdb.account.get(address);
|
|
15
|
+
if (!account) return;
|
|
16
|
+
await indexdb.rollupValidator.insert({
|
|
17
|
+
rollup: rollup.address,
|
|
18
|
+
pk,
|
|
19
|
+
address,
|
|
20
|
+
endpoint,
|
|
21
|
+
moniker: account.moniker,
|
|
22
|
+
joinTime: rollup.genesisTime,
|
|
23
|
+
leaveTime: "",
|
|
24
|
+
genesisTime: account.genesisTime,
|
|
25
|
+
renaissanceTime: account.renaissanceTime,
|
|
26
|
+
totalStake: "0",
|
|
27
|
+
revokedStake: "0",
|
|
28
|
+
availableStake: "0",
|
|
29
|
+
totalGain: "0",
|
|
30
|
+
proposedBlockCount: 0,
|
|
31
|
+
verifiedBlockCount: 0,
|
|
32
|
+
latestBlockHeight: 0,
|
|
33
|
+
latestBlockHash: ""
|
|
34
|
+
});
|
|
35
|
+
debug("create rollup validator", address);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.error("create rollup validator error", err);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
const ensureRollupValidator = async (address, rollupAddress, ctx, indexdb, upsert = true) => {
|
|
42
|
+
const doc = await indexdb.rollupValidator.get(address);
|
|
43
|
+
if (doc) {
|
|
44
|
+
doc.rollup = rollupAddress;
|
|
45
|
+
if (!doc.pk || !doc.endpoint) {
|
|
46
|
+
const validator$1 = (await indexdb.rollup.get(rollupAddress)).validators.find((v) => v.address === address);
|
|
47
|
+
doc.pk = validator$1.pk;
|
|
48
|
+
doc.endpoint = validator$1.endpoint;
|
|
49
|
+
await indexdb.rollupValidator.update(address, doc);
|
|
50
|
+
return doc;
|
|
51
|
+
}
|
|
52
|
+
return doc;
|
|
53
|
+
}
|
|
54
|
+
if (upsert === false) throw new Error("Rollup validator not found and upsert is disabled");
|
|
55
|
+
const stakeAddress = toStakeAddress(address, rollupAddress);
|
|
56
|
+
const [rollup, stake, account] = await Promise.all([
|
|
57
|
+
indexdb.rollup.get(rollupAddress),
|
|
58
|
+
indexdb.stake.get(stakeAddress),
|
|
59
|
+
indexdb.account.get(address)
|
|
60
|
+
]);
|
|
61
|
+
if (!stake || !account || !rollup) throw new Error("Required state not found for rollup validator");
|
|
62
|
+
const token = stake.tokens.find((t) => t.address === rollup.tokenAddress);
|
|
63
|
+
const validator = rollup.validators.find((v) => v.address === address);
|
|
64
|
+
debug("ensure rollup validator", {
|
|
65
|
+
txType: ctx.txType,
|
|
66
|
+
address,
|
|
67
|
+
rollupAddress,
|
|
68
|
+
validator,
|
|
69
|
+
token
|
|
70
|
+
});
|
|
71
|
+
return indexdb.rollupValidator.insert({
|
|
72
|
+
rollup: rollup.address,
|
|
73
|
+
address,
|
|
74
|
+
pk: validator ? validator.pk : "",
|
|
75
|
+
endpoint: validator ? validator.endpoint : "",
|
|
76
|
+
moniker: account.moniker,
|
|
77
|
+
joinTime: "",
|
|
78
|
+
leaveTime: "",
|
|
79
|
+
genesisTime: ctx.txState.time,
|
|
80
|
+
renaissanceTime: ctx.txState.time,
|
|
81
|
+
totalStake: token.balance,
|
|
82
|
+
revokedStake: "0",
|
|
83
|
+
availableStake: "0",
|
|
84
|
+
totalGain: "0",
|
|
85
|
+
proposedBlockCount: 0,
|
|
86
|
+
verifiedBlockCount: 0,
|
|
87
|
+
latestBlockHeight: 0,
|
|
88
|
+
latestBlockHash: ""
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
const onCreateRollupBlock = (block, ctx, indexdb) => {
|
|
92
|
+
const { validators, proposer } = block;
|
|
93
|
+
const { rollupState, txStates } = ctx;
|
|
94
|
+
validators.forEach(async (address) => {
|
|
95
|
+
try {
|
|
96
|
+
const doc = await ensureRollupValidator(address, rollupState.address, ctx, indexdb);
|
|
97
|
+
doc.rollup = rollupState.address;
|
|
98
|
+
doc.renaissanceTime = block.genesisTime;
|
|
99
|
+
doc.verifiedBlockCount += 1;
|
|
100
|
+
if (address === proposer) {
|
|
101
|
+
doc.latestBlockHeight = block.height;
|
|
102
|
+
doc.latestBlockHash = block.hash;
|
|
103
|
+
doc.proposedBlockCount += 1;
|
|
104
|
+
}
|
|
105
|
+
await indexdb.rollupValidator.update(address, doc);
|
|
106
|
+
debug("update rollup validator on new block", address);
|
|
107
|
+
} catch (err) {
|
|
108
|
+
console.error("update rollup validator error", err);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
if (Array.isArray(txStates)) txStates.filter((x) => x.type === "withdraw_token_v2").forEach(async (x) => {
|
|
112
|
+
try {
|
|
113
|
+
const updates = merge(await indexdb.tx.get(x.hash), pick(x, ["tx.itxJson.actualFee"]));
|
|
114
|
+
await indexdb.tx.update(x.hash, updates);
|
|
115
|
+
debug("update tx on new block", x.hash);
|
|
116
|
+
} catch (err) {
|
|
117
|
+
console.error("update tx on new block error", err);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
const updateRollupStats = async (address, value, key, indexdb) => {
|
|
122
|
+
try {
|
|
123
|
+
const doc = await indexdb.rollup.get(address);
|
|
124
|
+
if (!doc) return;
|
|
125
|
+
const rollupDoc = doc;
|
|
126
|
+
rollupDoc[key] = new BN(rollupDoc[key] || "0").add(new BN(value)).toString(10);
|
|
127
|
+
await indexdb.rollup.update(address, doc);
|
|
128
|
+
debug("update rollup", address);
|
|
129
|
+
} catch (err) {
|
|
130
|
+
console.error("update rollup error", err);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const onDepositTokenV2 = async (tx, ctx, indexdb) => {
|
|
134
|
+
const { signatures } = tx.tx;
|
|
135
|
+
const { rollupState } = ctx;
|
|
136
|
+
signatures.forEach(async ({ signer: address }) => {
|
|
137
|
+
try {
|
|
138
|
+
const doc = await ensureRollupValidator(address, rollupState.address, ctx, indexdb);
|
|
139
|
+
doc.renaissanceTime = tx.time;
|
|
140
|
+
await indexdb.rollupValidator.update(address, doc);
|
|
141
|
+
debug("update rollup validator on deposit", address);
|
|
142
|
+
} catch (err) {
|
|
143
|
+
console.error("update rollup validator error", err);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
const amount = new BN(tx.tx.itxJson.token.value).add(new BN(tx.tx.itxJson.actualFee));
|
|
147
|
+
await updateRollupStats(ctx.rollupState.address, amount.toString(), "totalDepositAmount", indexdb);
|
|
148
|
+
};
|
|
149
|
+
const onClaimBlockReward = async (tx, ctx, indexdb) => {
|
|
150
|
+
const { rollupState, blockState } = ctx;
|
|
151
|
+
const { signatures } = blockState;
|
|
152
|
+
await Promise.all(signatures.map(async ({ signer }) => {
|
|
153
|
+
try {
|
|
154
|
+
const doc = await ensureRollupValidator(signer, rollupState.address, ctx, indexdb);
|
|
155
|
+
const receipt = tx.receipts.find((x) => x.address === signer);
|
|
156
|
+
if (receipt) {
|
|
157
|
+
const change = receipt.changes.find((x) => x.target === rollupState.tokenAddress && new BN(x.value).isNeg() === false);
|
|
158
|
+
doc.totalGain = new BN(doc.totalGain).add(new BN(change.value).abs()).toString(10);
|
|
159
|
+
await indexdb.rollupValidator.update(signer, doc);
|
|
160
|
+
debug("update rollup validator on claim reward", signer);
|
|
161
|
+
}
|
|
162
|
+
} catch (err) {
|
|
163
|
+
console.error("update rollup validator on claim reward error", err);
|
|
164
|
+
}
|
|
165
|
+
}));
|
|
166
|
+
};
|
|
167
|
+
const onJoinRollup = async (tx, ctx, indexdb) => {
|
|
168
|
+
try {
|
|
169
|
+
const address = tx.tx.from;
|
|
170
|
+
const doc = await ensureRollupValidator(address, tx.tx.itxJson.rollup, ctx, indexdb);
|
|
171
|
+
doc.endpoint = tx.tx.itxJson.endpoint;
|
|
172
|
+
doc.joinTime = tx.time;
|
|
173
|
+
doc.renaissanceTime = tx.time;
|
|
174
|
+
doc.leaveTime = "";
|
|
175
|
+
await indexdb.rollupValidator.update(address, doc);
|
|
176
|
+
debug("join rollup validator", address);
|
|
177
|
+
} catch (err) {
|
|
178
|
+
console.error("join rollup validator error", err);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
const onLeaveRollup = async (tx, ctx, indexdb) => {
|
|
182
|
+
try {
|
|
183
|
+
const address = tx.tx.from;
|
|
184
|
+
const doc = await ensureRollupValidator(address, tx.tx.itxJson.rollup, ctx, indexdb);
|
|
185
|
+
doc.leaveTime = tx.time;
|
|
186
|
+
doc.renaissanceTime = tx.time;
|
|
187
|
+
doc.rollup = "";
|
|
188
|
+
await indexdb.rollupValidator.update(address, doc);
|
|
189
|
+
debug("leave rollup validator", address);
|
|
190
|
+
} catch (err) {
|
|
191
|
+
console.error("leave rollup validator error", err);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
const onStake = async (tx, ctx, indexdb) => {
|
|
195
|
+
try {
|
|
196
|
+
const rollup = await indexdb.rollup.get(tx.tx.itxJson.receiver);
|
|
197
|
+
if (!rollup) return;
|
|
198
|
+
const address = tx.tx.from;
|
|
199
|
+
if (rollup.validators.find((x) => x.address === address) === void 0) return;
|
|
200
|
+
const stakeAddress = toStakeAddress(address, rollup.address);
|
|
201
|
+
const doc = await ensureRollupValidator(address, rollup.address, ctx, indexdb);
|
|
202
|
+
const change = ctx.txState.receipts.find((x) => x.address === stakeAddress).changes.find((x) => x.target === rollup.tokenAddress);
|
|
203
|
+
doc.totalStake = new BN(doc.totalStake).add(new BN(change.value).abs()).toString(10);
|
|
204
|
+
doc.renaissanceTime = tx.time;
|
|
205
|
+
await indexdb.rollupValidator.update(address, doc);
|
|
206
|
+
debug("stake rollup validator", address);
|
|
207
|
+
} catch (err) {
|
|
208
|
+
console.error("stake rollup validator error", err);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
const onClaimStake = async (tx, ctx, indexdb) => {
|
|
212
|
+
try {
|
|
213
|
+
const stake = await indexdb.stake.get(tx.tx.itxJson.address);
|
|
214
|
+
if (!stake) return;
|
|
215
|
+
const rollup = await indexdb.rollup.get(stake.receiver);
|
|
216
|
+
if (!rollup) return;
|
|
217
|
+
const address = tx.tx.from;
|
|
218
|
+
const doc = await ensureRollupValidator(address, rollup.address, ctx, indexdb, false);
|
|
219
|
+
const change = ctx.txState.receipts.find((x) => x.address === stake.address).changes.find((x) => x.target === rollup.tokenAddress);
|
|
220
|
+
doc.renaissanceTime = tx.time;
|
|
221
|
+
doc.totalStake = new BN(doc.totalStake).sub(new BN(change.value).abs()).toString(10);
|
|
222
|
+
doc.revokedStake = new BN(doc.revokedStake).add(new BN(change.value).abs()).toString(10);
|
|
223
|
+
await indexdb.rollupValidator.update(address, doc);
|
|
224
|
+
debug("claim-stake rollup validator", address);
|
|
225
|
+
} catch (err) {
|
|
226
|
+
console.error("claim-stake rollup validator error", err);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
const onAccountMigrate = async (_tx, resolver) => {
|
|
230
|
+
try {
|
|
231
|
+
await resolver.buildMigrationChain();
|
|
232
|
+
} catch (e) {
|
|
233
|
+
console.error("build migration chain error", e);
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
const onUpdateToken = (tokenDoc, _ctx, _indexdb, resolver) => {
|
|
237
|
+
if (resolver.tokenCache) resolver.tokenCache.set(tokenDoc.address, tokenDoc);
|
|
238
|
+
};
|
|
239
|
+
const onCreateTx = async (tx, ctx, resolver) => {
|
|
240
|
+
try {
|
|
241
|
+
await resolver.tokenDistribution.updateByTx(tx, ctx);
|
|
242
|
+
} catch (e) {
|
|
243
|
+
resolver.logger.error("Failed to update token distribution", {
|
|
244
|
+
error: e,
|
|
245
|
+
tx,
|
|
246
|
+
ctx
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
if (tx.code !== "OK") return;
|
|
250
|
+
const { indexdb } = resolver;
|
|
251
|
+
switch (tx.tx.itxJson.type_url) {
|
|
252
|
+
case "fg:t:stake": return onStake(tx, ctx, indexdb);
|
|
253
|
+
case "fg:t:claim_stake": return onClaimStake(tx, ctx, indexdb);
|
|
254
|
+
case "fg:t:join_rollup": return onJoinRollup(tx, ctx, indexdb);
|
|
255
|
+
case "fg:t:leave_rollup": return onLeaveRollup(tx, ctx, indexdb);
|
|
256
|
+
case "fg:t:deposit_token_v2": return onDepositTokenV2(tx, ctx, indexdb);
|
|
257
|
+
case "fg:t:claim_block_reward":
|
|
258
|
+
await onClaimBlockReward(tx, ctx, indexdb);
|
|
259
|
+
return;
|
|
260
|
+
case "fg:t:withdraw_token_v2": return updateRollupStats(ctx.rollupState.address, tx.tx.itxJson.token.value, "totalWithdrawAmount", indexdb);
|
|
261
|
+
case "fg:t:account_migrate": return onAccountMigrate(tx, resolver);
|
|
262
|
+
default:
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
//#endregion
|
|
267
|
+
export { onCreateRollup, onCreateRollupBlock, onCreateTx, onUpdateToken };
|
package/esm/index.d.mts
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { IAddressArgs, IAssetState, IChainConfig, IChunkIterator, IDataObject, IDelegateState, IEvidenceState, IFilter, IGetAccountStateArgs, IGetAccountTokensArgs, IGetStateParams, IIndexDB, IListChunksParams, IPaginatedResult, IPipelineLogger, IResolver, IResolverAccountState, IResolverChainInfo, IResolverContext, IResolverFactoryState, IResolverForgeStats, IResolverListAccountsResult, IResolverListAssetsResult, IResolverListDelegationsResult, IResolverListFactoriesResult, IResolverListRollupBlocksResult, IResolverListRollupValidatorsResult, IResolverListRollupsResult, IResolverListStakesResult, IResolverListTokenFactoriesResult, IResolverListTokensResult, IResolverListTransactionsResult, IResolverNetInfo, IResolverNodeInfo, IResolverPaging, IResolverParams, IResolverRollupState, IResolverStakeState, IResolverTransaction, IResolverValidatorsInfo, IRollupBlock, IStateDB, ITokenCache, ITokenFactoryState, ITokenFlowsResult, ITokenInfo, ITokenState, TIndexedTokenState, TRequestEstimateGas, TRequestGetEvidenceState, TRequestGetRollupBlock, TRequestGetTokenDistribution, TRequestGetTx, TRequestListAssetTransactions, TRequestListAssets, TRequestListDelegations, TRequestListFactories, TRequestListRollupBlocks, TRequestListRollupValidators, TRequestListRollups, TRequestListStakes, TRequestListTokenFactories, TRequestListTokenFlows, TRequestListTokens, TRequestListTopAccounts, TRequestListTransactions, TRequestSearch, TRequestVerifyAccountRisk, TTokenDistribution, TVerifyAccountRiskResult } from "./types.mjs";
|
|
2
|
+
import { MigrationChainManager } from "./migration-chain.mjs";
|
|
3
|
+
import { TokenDistributionManager } from "./token-distribution.mjs";
|
|
4
|
+
import { createExecutor } from "@ocap/tx-protocols";
|
|
5
|
+
import Queue from "queue";
|
|
6
|
+
|
|
7
|
+
//#region src/index.d.ts
|
|
8
|
+
declare const formatData: (data: IDataObject | null | undefined) => IDataObject | null | undefined;
|
|
9
|
+
declare const formatDelegationOps: (state: IDelegateState) => any;
|
|
10
|
+
declare class OCAPResolver implements IResolver {
|
|
11
|
+
statedb: IStateDB;
|
|
12
|
+
indexdb: IIndexDB;
|
|
13
|
+
filter?: IFilter;
|
|
14
|
+
config: IChainConfig;
|
|
15
|
+
chainAddr: string;
|
|
16
|
+
tokenItx: TIndexedTokenState | null;
|
|
17
|
+
consensus: string;
|
|
18
|
+
queue: Queue;
|
|
19
|
+
tokenCache: ITokenCache;
|
|
20
|
+
tokenDistribution: TokenDistributionManager;
|
|
21
|
+
executor: ReturnType<typeof createExecutor>;
|
|
22
|
+
logger: IPipelineLogger;
|
|
23
|
+
migrationChain?: MigrationChainManager;
|
|
24
|
+
formatTx: (tx: IResolverTransaction | null, ctx?: IResolverContext) => Promise<IResolverTransaction | null>;
|
|
25
|
+
/**
|
|
26
|
+
* Creates an instance of OCAPResolver.
|
|
27
|
+
* @param params Parameters to bootstrap the resolver
|
|
28
|
+
* @param params.statedb @ocap/statedb adapter
|
|
29
|
+
* @param params.indexdb @ocap/indexdb adapter
|
|
30
|
+
* @param params.config @ocap/config object
|
|
31
|
+
* @param params.filter bloom filter to do anti-replay check
|
|
32
|
+
* @param params.validateTokenConfig should we validate token supply and token-holder balance, should be disabled when starting an existing chain
|
|
33
|
+
*/
|
|
34
|
+
constructor({
|
|
35
|
+
statedb,
|
|
36
|
+
indexdb,
|
|
37
|
+
config,
|
|
38
|
+
filter,
|
|
39
|
+
validateTokenConfig,
|
|
40
|
+
logger
|
|
41
|
+
}: IResolverParams);
|
|
42
|
+
sendTx({
|
|
43
|
+
tx: txBase64,
|
|
44
|
+
extra
|
|
45
|
+
}: {
|
|
46
|
+
tx: string;
|
|
47
|
+
extra?: Record<string, unknown>;
|
|
48
|
+
}, ctx?: IResolverContext): Promise<string>;
|
|
49
|
+
getTx({
|
|
50
|
+
hash
|
|
51
|
+
}: TRequestGetTx, ctx?: IResolverContext): Promise<IResolverTransaction | null>;
|
|
52
|
+
getBlock(): null;
|
|
53
|
+
getBlocks(): never[];
|
|
54
|
+
getUnconfirmedTxs(): never[];
|
|
55
|
+
getChainInfo(): Promise<IResolverChainInfo>;
|
|
56
|
+
getNodeInfo(): Promise<IResolverNodeInfo>;
|
|
57
|
+
getNetInfo(): Promise<IResolverNetInfo>;
|
|
58
|
+
getValidatorsInfo(): Promise<IResolverValidatorsInfo>;
|
|
59
|
+
getConfig(): string;
|
|
60
|
+
getAccountState({
|
|
61
|
+
address,
|
|
62
|
+
traceMigration,
|
|
63
|
+
expandContext
|
|
64
|
+
}: IGetAccountStateArgs & {
|
|
65
|
+
traceMigration?: boolean;
|
|
66
|
+
}, ctx?: IResolverContext): Promise<IResolverAccountState | null>;
|
|
67
|
+
getStakeState({
|
|
68
|
+
address
|
|
69
|
+
}: IAddressArgs, ctx?: IResolverContext): Promise<IResolverStakeState | null>;
|
|
70
|
+
getRollupState({
|
|
71
|
+
address
|
|
72
|
+
}: IAddressArgs, ctx?: IResolverContext): Promise<IResolverRollupState | null>;
|
|
73
|
+
getRollupBlock({
|
|
74
|
+
hash,
|
|
75
|
+
height,
|
|
76
|
+
rollupAddress
|
|
77
|
+
}: Partial<TRequestGetRollupBlock>, ctx?: IResolverContext): Promise<IRollupBlock | null>;
|
|
78
|
+
getAssetState({
|
|
79
|
+
address
|
|
80
|
+
}: IAddressArgs, ctx?: IResolverContext): Promise<IAssetState | null>;
|
|
81
|
+
getEvidenceState({
|
|
82
|
+
hash
|
|
83
|
+
}: TRequestGetEvidenceState, ctx?: IResolverContext): Promise<IEvidenceState | null>;
|
|
84
|
+
getFactoryState({
|
|
85
|
+
address
|
|
86
|
+
}: IAddressArgs, ctx?: IResolverContext): Promise<IResolverFactoryState | null>;
|
|
87
|
+
getTokenState({
|
|
88
|
+
address
|
|
89
|
+
}: IAddressArgs, ctx?: IResolverContext): Promise<ITokenState | null>;
|
|
90
|
+
getTokenFactoryState(args: IAddressArgs, ctx?: IResolverContext): Promise<ITokenFactoryState | null>;
|
|
91
|
+
getDelegateState({
|
|
92
|
+
address
|
|
93
|
+
}: IAddressArgs, ctx?: IResolverContext): Promise<IDelegateState | null>;
|
|
94
|
+
getAccountTokens({
|
|
95
|
+
address,
|
|
96
|
+
token
|
|
97
|
+
}: IGetAccountTokensArgs, ctx?: IResolverContext): Promise<ITokenInfo[]>;
|
|
98
|
+
getForgeState(): Promise<Record<string, unknown>>;
|
|
99
|
+
getForgeStats(): Promise<IResolverForgeStats>;
|
|
100
|
+
listTransactions(args?: Partial<TRequestListTransactions>, ctx?: IResolverContext): Promise<IResolverListTransactionsResult>;
|
|
101
|
+
listAssets(args?: Partial<TRequestListAssets>, ctx?: IResolverContext): Promise<IResolverListAssetsResult>;
|
|
102
|
+
listAssetTransactions(args?: Partial<TRequestListAssetTransactions>, ctx?: IResolverContext): Promise<IResolverListTransactionsResult>;
|
|
103
|
+
listFactories(args?: Partial<TRequestListFactories>, ctx?: IResolverContext): Promise<IResolverListFactoriesResult>;
|
|
104
|
+
listTopAccounts(args?: Partial<TRequestListTopAccounts>): Promise<IResolverListAccountsResult>;
|
|
105
|
+
listTokens(args?: Partial<TRequestListTokens>, ctx?: IResolverContext): Promise<IResolverListTokensResult>;
|
|
106
|
+
listTokenFactories(args?: Partial<TRequestListTokenFactories>, ctx?: IResolverContext): Promise<IResolverListTokenFactoriesResult>;
|
|
107
|
+
listStakes(args?: Partial<TRequestListStakes>, ctx?: IResolverContext): Promise<IResolverListStakesResult>;
|
|
108
|
+
listRollups(args?: Partial<TRequestListRollups>, ctx?: IResolverContext): Promise<IResolverListRollupsResult>;
|
|
109
|
+
listRollupBlocks(args?: Partial<TRequestListRollupBlocks>, ctx?: IResolverContext): Promise<IResolverListRollupBlocksResult>;
|
|
110
|
+
listRollupValidators(args?: Partial<TRequestListRollupValidators>, ctx?: IResolverContext): Promise<IResolverListRollupValidatorsResult>;
|
|
111
|
+
listDelegations(args?: Partial<TRequestListDelegations>, ctx?: IResolverContext): Promise<IResolverListDelegationsResult>;
|
|
112
|
+
verifyAccountRisk(args?: Partial<TRequestVerifyAccountRisk>, ctx?: IResolverContext): Promise<TVerifyAccountRiskResult | null> | null;
|
|
113
|
+
listTokenFlows(args?: Partial<TRequestListTokenFlows>, ctx?: IResolverContext): Promise<ITokenFlowsResult> | never[];
|
|
114
|
+
search(args: Partial<TRequestSearch>): Promise<{
|
|
115
|
+
results: Array<{
|
|
116
|
+
type: string;
|
|
117
|
+
id: string;
|
|
118
|
+
}>;
|
|
119
|
+
}>;
|
|
120
|
+
estimateGas(args?: Partial<TRequestEstimateGas>): {
|
|
121
|
+
estimate: {
|
|
122
|
+
max: string;
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
listBlocks(): {
|
|
126
|
+
blocks: never[];
|
|
127
|
+
};
|
|
128
|
+
validateTokenConfig(): void;
|
|
129
|
+
runAsLambda<T>(fn: (txn?: unknown) => T | Promise<T>, args?: Record<string, unknown>): Promise<T>;
|
|
130
|
+
getChain(): Promise<Record<string, unknown> | null>;
|
|
131
|
+
getToken(id: string): Promise<ITokenState | null>;
|
|
132
|
+
initializeStateDB(): Promise<void>;
|
|
133
|
+
connectIndexDB(): void;
|
|
134
|
+
_getState<T = Record<string, unknown>>({
|
|
135
|
+
table,
|
|
136
|
+
id,
|
|
137
|
+
dataKey,
|
|
138
|
+
extraParams,
|
|
139
|
+
onRead,
|
|
140
|
+
expandContext,
|
|
141
|
+
ctx
|
|
142
|
+
}: IGetStateParams): Promise<T | null>;
|
|
143
|
+
_doPaginatedSearch<TArgs extends Record<string, unknown>>(fn: string, args: TArgs, listKey: string, dataKey?: string | string[] | null, ctx?: IResolverContext): Promise<Record<string, unknown>>;
|
|
144
|
+
_formatTx(tx: IResolverTransaction | null, ctx?: IResolverContext): Promise<IResolverTransaction | null>;
|
|
145
|
+
_getAllResults<T>(dataKey: string, fn: (paging: IResolverPaging) => Promise<any>, limit?: number): Promise<T[]>;
|
|
146
|
+
formatTokenArray(tokens: ITokenInfo[]): Promise<ITokenInfo[]>;
|
|
147
|
+
formatTokenMap(tokens: Record<string, string>): Promise<ITokenInfo[]>;
|
|
148
|
+
getTxTokenSymbols(tx: IResolverTransaction): Promise<ITokenInfo[]>;
|
|
149
|
+
fixReceiptTokens(tx: IResolverTransaction): IResolverTransaction;
|
|
150
|
+
fixTokenSymbols(tx: IResolverTransaction): IResolverTransaction;
|
|
151
|
+
enrichIndexContext(ctx: Record<string, unknown>): Record<string, unknown>;
|
|
152
|
+
formatDelegateState(state: IDelegateState): Promise<IDelegateState>;
|
|
153
|
+
buildMigrationChain(): Promise<void>;
|
|
154
|
+
getMigrationChain(): Promise<MigrationChainManager>;
|
|
155
|
+
/** fix accounts field of legacy approve_withdraw transactions in indexdb */
|
|
156
|
+
fixApproveWithdrawAccounts(): Promise<IResolverTransaction[]>;
|
|
157
|
+
updateTokenDistribution({
|
|
158
|
+
tokenAddress,
|
|
159
|
+
force
|
|
160
|
+
}?: {
|
|
161
|
+
tokenAddress?: string | undefined;
|
|
162
|
+
force?: boolean | undefined;
|
|
163
|
+
}): Promise<unknown>;
|
|
164
|
+
getTokenDistribution(args?: Partial<TRequestGetTokenDistribution>): Promise<TTokenDistribution | null>;
|
|
165
|
+
/**
|
|
166
|
+
* Fetch data in chunks to handle large datasets
|
|
167
|
+
* @param fn Function to fetch data
|
|
168
|
+
* @param params Chunk parameters
|
|
169
|
+
* @returns Iterator with next function
|
|
170
|
+
*/
|
|
171
|
+
listChunks<T extends Record<string, unknown>>(fn: (params: {
|
|
172
|
+
size: number;
|
|
173
|
+
cursor: number;
|
|
174
|
+
time?: string;
|
|
175
|
+
}) => Promise<{
|
|
176
|
+
[key: string]: T[];
|
|
177
|
+
}>, {
|
|
178
|
+
total,
|
|
179
|
+
concurrency,
|
|
180
|
+
chunkSize,
|
|
181
|
+
pageSize,
|
|
182
|
+
timeKey,
|
|
183
|
+
dataKey
|
|
184
|
+
}: IListChunksParams): IChunkIterator<T>;
|
|
185
|
+
listTransactionsChunks(args?: Partial<TRequestListTransactions>, {
|
|
186
|
+
chunkSize,
|
|
187
|
+
pageSize
|
|
188
|
+
}?: {
|
|
189
|
+
chunkSize?: number | undefined;
|
|
190
|
+
pageSize?: number | undefined;
|
|
191
|
+
}): Promise<IChunkIterator<IResolverTransaction>>;
|
|
192
|
+
listStakeChunks(args?: Partial<TRequestListStakes>, {
|
|
193
|
+
chunkSize,
|
|
194
|
+
pageSize
|
|
195
|
+
}?: {
|
|
196
|
+
chunkSize?: number | undefined;
|
|
197
|
+
pageSize?: number | undefined;
|
|
198
|
+
}): Promise<IChunkIterator<IResolverStakeState>>;
|
|
199
|
+
}
|
|
200
|
+
//#endregion
|
|
201
|
+
export { type IChainConfig, type IChunkIterator, type IDataObject, type IPaginatedResult, type IResolverPaging, type IResolverParams, type IResolverTransaction, type ITokenInfo, OCAPResolver as default, formatData, formatDelegationOps };
|