@hashgraphonline/standards-sdk 0.0.157-canary.2 → 0.0.157-canary.4
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/dist/cjs/hcs-10/base-client.d.ts +1 -1
- package/dist/cjs/hcs-10/base-client.d.ts.map +1 -1
- package/dist/cjs/hcs-10/sdk.d.ts +1 -1
- package/dist/cjs/hcs-10/sdk.d.ts.map +1 -1
- package/dist/cjs/hcs-11/agent-builder.d.ts +2 -0
- package/dist/cjs/hcs-11/agent-builder.d.ts.map +1 -1
- package/dist/cjs/hcs-11/client.d.ts +2 -0
- package/dist/cjs/hcs-11/client.d.ts.map +1 -1
- package/dist/cjs/hcs-11/types.d.ts +4 -2
- package/dist/cjs/hcs-11/types.d.ts.map +1 -1
- package/dist/cjs/hcs-15/index.d.ts +3 -0
- package/dist/cjs/hcs-15/index.d.ts.map +1 -0
- package/dist/cjs/hcs-15/petal-manager.d.ts +27 -0
- package/dist/cjs/hcs-15/petal-manager.d.ts.map +1 -0
- package/dist/cjs/hcs-15/types.d.ts +41 -0
- package/dist/cjs/hcs-15/types.d.ts.map +1 -0
- package/dist/cjs/hcs-16/flora-account-manager.d.ts.map +1 -1
- package/dist/cjs/hcs-16/types.d.ts +3 -5
- package/dist/cjs/hcs-16/types.d.ts.map +1 -1
- package/dist/cjs/hcs-18/discovery.d.ts +1 -0
- package/dist/cjs/hcs-18/discovery.d.ts.map +1 -1
- package/dist/cjs/hcs-18/types.d.ts +1 -0
- package/dist/cjs/hcs-18/types.d.ts.map +1 -1
- package/dist/cjs/hcs-20/sdk.d.ts.map +1 -1
- package/dist/cjs/hcs-7/evm-bridge.d.ts.map +1 -1
- package/dist/cjs/hcs-7/redis-cache.d.ts.map +1 -1
- package/dist/cjs/hcs-7/wasm-bridge.d.ts.map +1 -1
- package/dist/cjs/{index-xZmk-HBD.cjs → index-CJsmyij2.cjs} +12 -12
- package/dist/cjs/index-CJsmyij2.cjs.map +1 -0
- package/dist/cjs/{index-CyAbgH2f-Cv8mxAhS.cjs → index-CyAbgH2f-Dn5WQtPx.cjs} +2 -2
- package/dist/cjs/{index-CyAbgH2f-Cv8mxAhS.cjs.map → index-CyAbgH2f-Dn5WQtPx.cjs.map} +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/services/types.d.ts +1 -1
- package/dist/cjs/services/types.d.ts.map +1 -1
- package/dist/cjs/standards-sdk.cjs +1 -1
- package/dist/cjs/{standards-sdk.es45-BxAtDqsy-ELd4REyl.cjs → standards-sdk.es45-BxAtDqsy-CiMtsDbD.cjs} +2 -2
- package/dist/cjs/{standards-sdk.es45-BxAtDqsy-ELd4REyl.cjs.map → standards-sdk.es45-BxAtDqsy-CiMtsDbD.cjs.map} +1 -1
- package/dist/cjs/{standards-sdk.es46-BI8ACeLM-DRmcgoN0.cjs → standards-sdk.es46-BI8ACeLM-DFYgO-fP.cjs} +2 -2
- package/dist/cjs/{standards-sdk.es46-BI8ACeLM-DRmcgoN0.cjs.map → standards-sdk.es46-BI8ACeLM-DFYgO-fP.cjs.map} +1 -1
- package/dist/cjs/{standards-sdk.es47-DbkY3FlD-DfMpZygD.cjs → standards-sdk.es47-DbkY3FlD-2qTWs2BD.cjs} +2 -2
- package/dist/cjs/{standards-sdk.es47-DbkY3FlD-DfMpZygD.cjs.map → standards-sdk.es47-DbkY3FlD-2qTWs2BD.cjs.map} +1 -1
- package/dist/cjs/{standards-sdk.es48-DzT9Qc3F-CmCkJl3x.cjs → standards-sdk.es48-DzT9Qc3F-Byz0fQnW.cjs} +2 -2
- package/dist/cjs/{standards-sdk.es48-DzT9Qc3F-CmCkJl3x.cjs.map → standards-sdk.es48-DzT9Qc3F-Byz0fQnW.cjs.map} +1 -1
- package/dist/cjs/utils/logger.d.ts.map +1 -1
- package/dist/es/hcs-10/base-client.d.ts +1 -1
- package/dist/es/hcs-10/base-client.d.ts.map +1 -1
- package/dist/es/hcs-10/sdk.d.ts +1 -1
- package/dist/es/hcs-10/sdk.d.ts.map +1 -1
- package/dist/es/hcs-11/agent-builder.d.ts +2 -0
- package/dist/es/hcs-11/agent-builder.d.ts.map +1 -1
- package/dist/es/hcs-11/client.d.ts +2 -0
- package/dist/es/hcs-11/client.d.ts.map +1 -1
- package/dist/es/hcs-11/types.d.ts +4 -2
- package/dist/es/hcs-11/types.d.ts.map +1 -1
- package/dist/es/hcs-15/index.d.ts +3 -0
- package/dist/es/hcs-15/index.d.ts.map +1 -0
- package/dist/es/hcs-15/petal-manager.d.ts +27 -0
- package/dist/es/hcs-15/petal-manager.d.ts.map +1 -0
- package/dist/es/hcs-15/types.d.ts +41 -0
- package/dist/es/hcs-15/types.d.ts.map +1 -0
- package/dist/es/hcs-16/flora-account-manager.d.ts.map +1 -1
- package/dist/es/hcs-16/types.d.ts +3 -5
- package/dist/es/hcs-16/types.d.ts.map +1 -1
- package/dist/es/hcs-18/discovery.d.ts +1 -0
- package/dist/es/hcs-18/discovery.d.ts.map +1 -1
- package/dist/es/hcs-18/types.d.ts +1 -0
- package/dist/es/hcs-18/types.d.ts.map +1 -1
- package/dist/es/hcs-20/sdk.d.ts.map +1 -1
- package/dist/es/hcs-7/evm-bridge.d.ts.map +1 -1
- package/dist/es/hcs-7/redis-cache.d.ts.map +1 -1
- package/dist/es/hcs-7/wasm-bridge.d.ts.map +1 -1
- package/dist/es/index.d.ts +1 -1
- package/dist/es/services/types.d.ts +1 -1
- package/dist/es/services/types.d.ts.map +1 -1
- package/dist/es/standards-sdk.es.js +19 -22
- package/dist/es/standards-sdk.es.js.map +1 -1
- package/dist/es/standards-sdk.es10.js +1 -1
- package/dist/es/standards-sdk.es11.js +10 -2
- package/dist/es/standards-sdk.es11.js.map +1 -1
- package/dist/es/standards-sdk.es12.js +1 -1
- package/dist/es/standards-sdk.es13.js +1 -1
- package/dist/es/standards-sdk.es13.js.map +1 -1
- package/dist/es/standards-sdk.es14.js +1 -1
- package/dist/es/standards-sdk.es15.js +39 -9
- package/dist/es/standards-sdk.es15.js.map +1 -1
- package/dist/es/standards-sdk.es16.js +1 -0
- package/dist/es/standards-sdk.es16.js.map +1 -1
- package/dist/es/standards-sdk.es18.js +1 -1
- package/dist/es/standards-sdk.es19.js +1 -1
- package/dist/es/standards-sdk.es2.js +1 -1
- package/dist/es/standards-sdk.es20.js +2 -2
- package/dist/es/standards-sdk.es23.js +2 -2
- package/dist/es/standards-sdk.es25.js +4 -2
- package/dist/es/standards-sdk.es25.js.map +1 -1
- package/dist/es/standards-sdk.es26.js +2 -2
- package/dist/es/standards-sdk.es27.js +152 -6
- package/dist/es/standards-sdk.es27.js.map +1 -1
- package/dist/es/standards-sdk.es28.js +26 -189
- package/dist/es/standards-sdk.es28.js.map +1 -1
- package/dist/es/standards-sdk.es29.js +446 -26
- package/dist/es/standards-sdk.es29.js.map +1 -1
- package/dist/es/standards-sdk.es3.js +1 -1
- package/dist/es/standards-sdk.es3.js.map +1 -1
- package/dist/es/standards-sdk.es30.js +6 -342
- package/dist/es/standards-sdk.es30.js.map +1 -1
- package/dist/es/standards-sdk.es31.js +202 -6
- package/dist/es/standards-sdk.es31.js.map +1 -1
- package/dist/es/standards-sdk.es32.js +34 -202
- package/dist/es/standards-sdk.es32.js.map +1 -1
- package/dist/es/standards-sdk.es33.js +529 -34
- package/dist/es/standards-sdk.es33.js.map +1 -1
- package/dist/es/standards-sdk.es34.js +87 -501
- package/dist/es/standards-sdk.es34.js.map +1 -1
- package/dist/es/standards-sdk.es35.js +19 -98
- package/dist/es/standards-sdk.es35.js.map +1 -1
- package/dist/es/standards-sdk.es36.js +156 -18
- package/dist/es/standards-sdk.es36.js.map +1 -1
- package/dist/es/standards-sdk.es37.js +146 -150
- package/dist/es/standards-sdk.es37.js.map +1 -1
- package/dist/es/standards-sdk.es38.js +561 -134
- package/dist/es/standards-sdk.es38.js.map +1 -1
- package/dist/es/standards-sdk.es39.js +562 -543
- package/dist/es/standards-sdk.es39.js.map +1 -1
- package/dist/es/standards-sdk.es4.js +6 -4
- package/dist/es/standards-sdk.es4.js.map +1 -1
- package/dist/es/standards-sdk.es40.js +4 -601
- package/dist/es/standards-sdk.es40.js.map +1 -1
- package/dist/es/standards-sdk.es41.js +415 -4
- package/dist/es/standards-sdk.es41.js.map +1 -1
- package/dist/es/standards-sdk.es42.js +1536 -389
- package/dist/es/standards-sdk.es42.js.map +1 -1
- package/dist/es/standards-sdk.es43.js +133 -1541
- package/dist/es/standards-sdk.es43.js.map +1 -1
- package/dist/es/standards-sdk.es44.js +7 -155
- package/dist/es/standards-sdk.es44.js.map +1 -1
- package/dist/es/standards-sdk.es45.js +2 -7
- package/dist/es/standards-sdk.es45.js.map +1 -1
- package/dist/es/standards-sdk.es46.js +5 -5
- package/dist/es/standards-sdk.es48.js +1 -1
- package/dist/es/standards-sdk.es49.js +1 -1
- package/dist/es/standards-sdk.es5.js +4 -6
- package/dist/es/standards-sdk.es5.js.map +1 -1
- package/dist/es/standards-sdk.es50.js +1 -1
- package/dist/es/standards-sdk.es51.js +1 -1
- package/dist/es/standards-sdk.es52.js +1 -1
- package/dist/es/standards-sdk.es54.js +7135 -2
- package/dist/es/standards-sdk.es54.js.map +1 -1
- package/dist/es/standards-sdk.es55.js +7134 -41
- package/dist/es/standards-sdk.es55.js.map +1 -1
- package/dist/es/standards-sdk.es56.js +37 -37
- package/dist/es/standards-sdk.es56.js.map +1 -1
- package/dist/es/standards-sdk.es57.js +37 -37
- package/dist/es/standards-sdk.es57.js.map +1 -1
- package/dist/es/standards-sdk.es58.js +78 -72
- package/dist/es/standards-sdk.es58.js.map +1 -1
- package/dist/es/standards-sdk.es59.js +41 -7134
- package/dist/es/standards-sdk.es59.js.map +1 -1
- package/dist/es/standards-sdk.es6.js.map +1 -1
- package/dist/es/standards-sdk.es7.js +28 -22
- package/dist/es/standards-sdk.es7.js.map +1 -1
- package/dist/es/standards-sdk.es8.js +5 -5
- package/dist/es/utils/logger.d.ts.map +1 -1
- package/package.json +37 -35
- package/dist/cjs/hcs-21/index.d.ts +0 -4
- package/dist/cjs/hcs-21/index.d.ts.map +0 -1
- package/dist/cjs/hcs-21/petal-account-manager.d.ts +0 -52
- package/dist/cjs/hcs-21/petal-account-manager.d.ts.map +0 -1
- package/dist/cjs/hcs-21/types.d.ts +0 -44
- package/dist/cjs/hcs-21/types.d.ts.map +0 -1
- package/dist/cjs/index-xZmk-HBD.cjs.map +0 -1
- package/dist/es/hcs-21/index.d.ts +0 -4
- package/dist/es/hcs-21/index.d.ts.map +0 -1
- package/dist/es/hcs-21/petal-account-manager.d.ts +0 -52
- package/dist/es/hcs-21/petal-account-manager.d.ts.map +0 -1
- package/dist/es/hcs-21/types.d.ts +0 -44
- package/dist/es/hcs-21/types.d.ts.map +0 -1
- package/dist/es/standards-sdk.es60.js +0 -7144
- package/dist/es/standards-sdk.es60.js.map +0 -1
|
@@ -1,347 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class FloraAccountManager {
|
|
7
|
-
constructor(client, network, logger) {
|
|
8
|
-
this.client = client;
|
|
9
|
-
this.logger = logger || new Logger({ module: "FloraAccountManager" });
|
|
10
|
-
this.network = network || "testnet";
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Create a Flora account with threshold key and required topics
|
|
14
|
-
*/
|
|
15
|
-
async createFlora(config) {
|
|
16
|
-
try {
|
|
17
|
-
this.logger.info("Creating Flora account");
|
|
18
|
-
const keyList = new KeyList(
|
|
19
|
-
config.members.map((m) => m.publicKey),
|
|
20
|
-
config.threshold
|
|
21
|
-
);
|
|
22
|
-
const transaction = new AccountCreateTransaction().setKey(keyList).setInitialBalance(new Hbar(config.initialBalance || 1)).setMaxAutomaticTokenAssociations(
|
|
23
|
-
config.maxAutomaticTokenAssociations || -1
|
|
24
|
-
);
|
|
25
|
-
const response = await transaction.execute(this.client);
|
|
26
|
-
const receipt = await response.getReceipt(this.client);
|
|
27
|
-
if (!receipt.accountId) {
|
|
28
|
-
throw new Error("Failed to create Flora account");
|
|
29
|
-
}
|
|
30
|
-
const floraAccountId = receipt.accountId;
|
|
31
|
-
const floraAccount = await this.createFloraAccount(keyList, config);
|
|
32
|
-
const topics = await this.createFloraTopics(
|
|
33
|
-
floraAccount,
|
|
34
|
-
keyList,
|
|
35
|
-
config
|
|
36
|
-
);
|
|
37
|
-
await this.createFloraProfile(floraAccount, topics, config);
|
|
38
|
-
this.logger.info("Flora created successfully", {
|
|
39
|
-
floraAccountId: floraAccount.toString(),
|
|
40
|
-
topics: {
|
|
41
|
-
communication: topics.communication.toString(),
|
|
42
|
-
transaction: topics.transaction.toString(),
|
|
43
|
-
state: topics.state.toString()
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
return {
|
|
47
|
-
floraAccountId: floraAccount,
|
|
48
|
-
topics,
|
|
49
|
-
keyList,
|
|
50
|
-
transactionId: ""
|
|
51
|
-
};
|
|
52
|
-
} catch (error) {
|
|
53
|
-
this.logger.error("Failed to create Flora", error);
|
|
54
|
-
throw error;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Build KeyList from member public keys
|
|
59
|
-
*/
|
|
60
|
-
async buildKeyList(members, threshold) {
|
|
61
|
-
const keyList = new KeyList();
|
|
62
|
-
keyList.setThreshold(threshold);
|
|
63
|
-
for (const member of members) {
|
|
64
|
-
if (member.publicKey) {
|
|
65
|
-
keyList.push(member.publicKey);
|
|
66
|
-
} else {
|
|
67
|
-
const accountInfo = await new AccountInfoQuery().setAccountId(member.accountId).execute(this.client);
|
|
68
|
-
keyList.push(accountInfo.key);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return keyList;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Create the Flora account with threshold key
|
|
75
|
-
*/
|
|
76
|
-
async createFloraAccount(keyList, config) {
|
|
77
|
-
const transaction = new AccountCreateTransaction().setKey(keyList).setInitialBalance(new Hbar(config.initialBalance || 20)).setMaxAutomaticTokenAssociations(
|
|
78
|
-
config.maxAutomaticTokenAssociations || -1
|
|
79
|
-
);
|
|
80
|
-
const response = await transaction.execute(this.client);
|
|
81
|
-
const receipt = await response.getReceipt(this.client);
|
|
82
|
-
if (!receipt.accountId) {
|
|
83
|
-
throw new FloraError(
|
|
84
|
-
"Failed to create Flora account",
|
|
85
|
-
"ACCOUNT_CREATION_FAILED"
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
return receipt.accountId;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Create the three required Flora topics with HCS-16 memo format
|
|
92
|
-
*/
|
|
93
|
-
async createFloraTopics(floraAccountId, adminKey, config) {
|
|
94
|
-
const submitKeyList = new KeyList();
|
|
95
|
-
submitKeyList.setThreshold(1);
|
|
96
|
-
for (const member of config.members) {
|
|
97
|
-
if (member.publicKey) {
|
|
98
|
-
submitKeyList.push(member.publicKey);
|
|
99
|
-
} else {
|
|
100
|
-
throw new FloraError(
|
|
101
|
-
`Public key required for member ${member.accountId}`,
|
|
102
|
-
"MISSING_PUBLIC_KEY"
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
const [communication, transaction, state] = await Promise.all([
|
|
107
|
-
this.createTopic(
|
|
108
|
-
floraAccountId,
|
|
109
|
-
FloraTopicType.COMMUNICATION,
|
|
110
|
-
adminKey,
|
|
111
|
-
submitKeyList,
|
|
112
|
-
config.customFees
|
|
113
|
-
),
|
|
114
|
-
this.createTopic(
|
|
115
|
-
floraAccountId,
|
|
116
|
-
FloraTopicType.TRANSACTION,
|
|
117
|
-
adminKey,
|
|
118
|
-
submitKeyList,
|
|
119
|
-
config.customFees
|
|
120
|
-
),
|
|
121
|
-
this.createTopic(
|
|
122
|
-
floraAccountId,
|
|
123
|
-
FloraTopicType.STATE,
|
|
124
|
-
adminKey,
|
|
125
|
-
submitKeyList,
|
|
126
|
-
config.customFees
|
|
127
|
-
)
|
|
128
|
-
]);
|
|
129
|
-
return { communication, transaction, state };
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Create a single topic with HCS-16 memo format
|
|
133
|
-
*/
|
|
134
|
-
async createTopic(floraAccountId, topicType, adminKey, submitKey, customFees) {
|
|
135
|
-
const memo = `hcs-16:${floraAccountId}:${topicType}`;
|
|
136
|
-
const transaction = new TopicCreateTransaction().setTopicMemo(memo).setAdminKey(adminKey).setSubmitKey(submitKey);
|
|
137
|
-
if (customFees && customFees.length > 0) {
|
|
138
|
-
const fees = customFees.map(
|
|
139
|
-
(fee) => new CustomFixedFee().setAmount(fee.amount).setFeeCollectorAccountId(
|
|
140
|
-
AccountId.fromString(fee.feeCollectorAccountId)
|
|
141
|
-
)
|
|
142
|
-
);
|
|
143
|
-
transaction.setCustomFees(fees);
|
|
144
|
-
}
|
|
145
|
-
const response = await transaction.execute(this.client);
|
|
146
|
-
const receipt = await response.getReceipt(this.client);
|
|
147
|
-
if (!receipt.topicId) {
|
|
148
|
-
throw new FloraError(
|
|
149
|
-
`Failed to create ${FloraTopicType[topicType]} topic`,
|
|
150
|
-
"TOPIC_CREATION_FAILED"
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
return receipt.topicId;
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Create and store Flora profile using HCS-11
|
|
157
|
-
*/
|
|
158
|
-
async createFloraProfile(floraAccountId, topics, config) {
|
|
159
|
-
this.logger.info("Creating Flora profile using HCS-11");
|
|
160
|
-
const operatorMember = config.members[0];
|
|
161
|
-
if (!operatorMember.privateKey) {
|
|
162
|
-
throw new FloraError(
|
|
163
|
-
"First member must have private key to create profile",
|
|
164
|
-
"MISSING_PRIVATE_KEY"
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
const hcs11Client = new HCS11Client({
|
|
168
|
-
network: this.network,
|
|
169
|
-
auth: {
|
|
170
|
-
operatorId: floraAccountId.toString(),
|
|
171
|
-
privateKey: operatorMember.privateKey
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
const floraBuilder = new FloraBuilder().setDisplayName(config.displayName).setMembers(config.members).setThreshold(config.threshold).setTopics({
|
|
175
|
-
communication: topics.communication.toString(),
|
|
176
|
-
transaction: topics.transaction.toString(),
|
|
177
|
-
state: topics.state.toString()
|
|
178
|
-
}).setPolicies(config.policies);
|
|
179
|
-
if (config.bio) {
|
|
180
|
-
floraBuilder.setBio(config.bio);
|
|
181
|
-
}
|
|
182
|
-
if (config.metadata) {
|
|
183
|
-
floraBuilder.setMetadata(config.metadata);
|
|
184
|
-
}
|
|
185
|
-
const profile = floraBuilder.build();
|
|
186
|
-
const inscriptionResult = await hcs11Client.createAndInscribeProfile(
|
|
187
|
-
profile,
|
|
188
|
-
true
|
|
189
|
-
);
|
|
190
|
-
if (!inscriptionResult.success) {
|
|
191
|
-
throw new FloraError(
|
|
192
|
-
`Failed to inscribe Flora profile: ${inscriptionResult.error}`,
|
|
193
|
-
"PROFILE_INSCRIPTION_FAILED"
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
this.logger.info("Flora profile inscribed successfully", {
|
|
197
|
-
profileTopicId: inscriptionResult.profileTopicId,
|
|
198
|
-
transactionId: inscriptionResult.transactionId
|
|
199
|
-
});
|
|
200
|
-
return inscriptionResult.profileTopicId;
|
|
201
|
-
}
|
|
202
|
-
/**
|
|
203
|
-
* Send a message to a Flora topic
|
|
204
|
-
*/
|
|
205
|
-
async sendFloraMessage(topicId, message) {
|
|
206
|
-
try {
|
|
207
|
-
message.p = "hcs-16";
|
|
208
|
-
const transaction = new TopicMessageSubmitTransaction().setTopicId(topicId).setMessage(JSON.stringify(message));
|
|
209
|
-
const response = await transaction.execute(this.client);
|
|
210
|
-
await response.getReceipt(this.client);
|
|
211
|
-
this.logger.debug("Flora message sent", {
|
|
212
|
-
topicId: topicId.toString(),
|
|
213
|
-
operation: message.op
|
|
214
|
-
});
|
|
215
|
-
} catch (error) {
|
|
216
|
-
this.logger.error("Failed to send Flora message", error);
|
|
217
|
-
throw error;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* Send flora_created notification to all members
|
|
222
|
-
*/
|
|
223
|
-
async notifyFloraCreated(result, memberTopics) {
|
|
224
|
-
const message = {
|
|
225
|
-
p: "hcs-16",
|
|
226
|
-
op: FloraOperation.FLORA_CREATED,
|
|
227
|
-
operator_id: `${this.client.operatorAccountId}@${result.floraAccountId}`,
|
|
228
|
-
flora_account_id: result.floraAccountId.toString(),
|
|
229
|
-
topics: {
|
|
230
|
-
communication: result.topics.communication.toString(),
|
|
231
|
-
transaction: result.topics.transaction.toString(),
|
|
232
|
-
state: result.topics.state.toString()
|
|
233
|
-
}
|
|
234
|
-
};
|
|
235
|
-
await Promise.all(
|
|
236
|
-
memberTopics.map((topicId) => this.sendFloraMessage(topicId, message))
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Parse HCS-16 topic memo
|
|
241
|
-
*/
|
|
242
|
-
parseTopicMemo(memo) {
|
|
243
|
-
const match = memo.match(/^hcs-16:([0-9.]+):(\d)$/);
|
|
244
|
-
if (!match) {
|
|
245
|
-
return null;
|
|
246
|
-
}
|
|
247
|
-
return {
|
|
248
|
-
protocol: "hcs-16",
|
|
249
|
-
floraAccountId: match[1],
|
|
250
|
-
topicType: parseInt(match[2])
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Create a proposal for a scheduled transaction
|
|
255
|
-
*/
|
|
256
|
-
async createTransactionProposal(transactionTopicId, scheduledTxId, description, operatorAccountId, floraAccountId) {
|
|
257
|
-
const message = {
|
|
258
|
-
p: "hcs-16",
|
|
259
|
-
op: FloraOperation.TX_PROPOSAL,
|
|
260
|
-
operator_id: `${operatorAccountId}@${floraAccountId}`,
|
|
261
|
-
scheduled_tx_id: scheduledTxId,
|
|
262
|
-
description,
|
|
263
|
-
m: description
|
|
264
|
-
};
|
|
265
|
-
await this.sendFloraMessage(transactionTopicId, message);
|
|
266
|
-
}
|
|
267
|
-
/**
|
|
268
|
-
* Submit a state update to the state topic
|
|
269
|
-
*/
|
|
270
|
-
async submitStateUpdate(stateTopicId, stateHash, operatorAccountId, floraAccountId, epoch) {
|
|
271
|
-
const message = {
|
|
272
|
-
p: "hcs-16",
|
|
273
|
-
op: FloraOperation.STATE_UPDATE,
|
|
274
|
-
operator_id: `${operatorAccountId}@${floraAccountId}`,
|
|
275
|
-
hash: stateHash,
|
|
276
|
-
epoch,
|
|
277
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
278
|
-
};
|
|
279
|
-
await this.sendFloraMessage(stateTopicId, message);
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* Create a generic HCS-16 transaction topic with HIP-991 support
|
|
283
|
-
* This can be used for any HCS-16 compliant topic, not just Flora accounts
|
|
284
|
-
*/
|
|
285
|
-
async createTransactionTopic(config) {
|
|
286
|
-
const transaction = new TopicCreateTransaction().setTopicMemo(config.memo);
|
|
287
|
-
if (config.adminKey) {
|
|
288
|
-
transaction.setAdminKey(config.adminKey);
|
|
289
|
-
}
|
|
290
|
-
if (config.submitKey) {
|
|
291
|
-
transaction.setSubmitKey(config.submitKey);
|
|
292
|
-
}
|
|
293
|
-
if (config.feeScheduleKey) {
|
|
294
|
-
transaction.setFeeScheduleKey(config.feeScheduleKey);
|
|
295
|
-
}
|
|
296
|
-
if (config.customFees && config.customFees.length > 0) {
|
|
297
|
-
const fees = config.customFees.map((fee) => {
|
|
298
|
-
const customFee = new CustomFixedFee().setAmount(fee.amount).setFeeCollectorAccountId(
|
|
299
|
-
AccountId.fromString(fee.feeCollectorAccountId)
|
|
300
|
-
);
|
|
301
|
-
if (fee.denominatingTokenId) {
|
|
302
|
-
customFee.setDenominatingTokenId(
|
|
303
|
-
TokenId.fromString(fee.denominatingTokenId)
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
return customFee;
|
|
307
|
-
});
|
|
308
|
-
transaction.setCustomFees(fees);
|
|
309
|
-
}
|
|
310
|
-
if (config.feeExemptKeys && config.feeExemptKeys.length > 0) {
|
|
311
|
-
transaction.setFeeExemptKeyList(config.feeExemptKeys);
|
|
312
|
-
}
|
|
313
|
-
const response = await transaction.execute(this.client);
|
|
314
|
-
const receipt = await response.getReceipt(this.client);
|
|
315
|
-
if (!receipt.topicId) {
|
|
316
|
-
throw new FloraError(
|
|
317
|
-
"Failed to create transaction topic",
|
|
318
|
-
"TOPIC_CREATION_FAILED"
|
|
319
|
-
);
|
|
320
|
-
}
|
|
321
|
-
this.logger.info("Created HCS-16 transaction topic", {
|
|
322
|
-
topicId: receipt.topicId.toString(),
|
|
323
|
-
memo: config.memo,
|
|
324
|
-
hasFees: !!config.customFees
|
|
325
|
-
});
|
|
326
|
-
return receipt.topicId;
|
|
327
|
-
}
|
|
328
|
-
/**
|
|
329
|
-
* Submit HCS-16 compliant credit purchase message
|
|
330
|
-
*/
|
|
331
|
-
async submitCreditPurchase(topicId, purchaser, amount, floraAccountId) {
|
|
332
|
-
const message = {
|
|
333
|
-
p: "hcs-16",
|
|
334
|
-
op: FloraOperation.CREDIT_PURCHASE,
|
|
335
|
-
operator_id: floraAccountId ? `${purchaser}@${floraAccountId}` : purchaser,
|
|
336
|
-
amount,
|
|
337
|
-
purchaser,
|
|
338
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
339
|
-
m: `Purchase ${amount} credits`
|
|
340
|
-
};
|
|
341
|
-
await this.sendFloraMessage(topicId, message);
|
|
1
|
+
class StateHashError extends Error {
|
|
2
|
+
constructor(message, code) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.code = code;
|
|
5
|
+
this.name = "StateHashError";
|
|
342
6
|
}
|
|
343
7
|
}
|
|
344
8
|
export {
|
|
345
|
-
|
|
9
|
+
StateHashError
|
|
346
10
|
};
|
|
347
11
|
//# sourceMappingURL=standards-sdk.es30.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standards-sdk.es30.js","sources":["../../src/hcs-16/flora-account-manager.ts"],"sourcesContent":["import {\n Client,\n AccountCreateTransaction,\n AccountUpdateTransaction,\n AccountInfoQuery,\n TopicCreateTransaction,\n TopicMessageSubmitTransaction,\n KeyList,\n PublicKey,\n PrivateKey,\n Hbar,\n AccountId,\n TopicId,\n CustomFixedFee,\n TokenId,\n} from '@hashgraph/sdk';\nimport { Logger } from '../utils/logger';\nimport {\n FloraConfig,\n FloraCreationResult,\n FloraTopics,\n FloraTopicType,\n FloraMessage,\n FloraOperation,\n FloraProfile,\n FloraError,\n FloraMember,\n TransactionTopicConfig,\n TransactionTopicFee,\n CreditPurchaseMessage,\n} from './types';\nimport { HCS11Client, FloraBuilder } from '../hcs-11';\nimport type { NetworkType } from '../utils/types';\n\n/**\n * HCS-16 Flora Account Manager\n * Manages creation and operation of Flora multi-party accounts\n */\nexport class FloraAccountManager {\n private readonly logger: Logger;\n private readonly network: NetworkType;\n\n constructor(\n private readonly client: Client,\n network?: NetworkType,\n logger?: Logger,\n ) {\n this.logger = logger || new Logger({ module: 'FloraAccountManager' });\n this.network = network || 'testnet';\n }\n\n /**\n * Create a Flora account with threshold key and required topics\n */\n async createFlora(config: FloraConfig): Promise<FloraCreationResult> {\n try {\n this.logger.info('Creating Flora account');\n\n const keyList = new KeyList(\n config.members.map(m => m.publicKey),\n config.threshold,\n );\n\n const transaction = new AccountCreateTransaction()\n .setKey(keyList)\n .setInitialBalance(new Hbar(config.initialBalance || 1))\n .setMaxAutomaticTokenAssociations(\n config.maxAutomaticTokenAssociations || -1,\n );\n\n const response = await transaction.execute(this.client);\n const receipt = await response.getReceipt(this.client);\n\n if (!receipt.accountId) {\n throw new Error('Failed to create Flora account');\n }\n\n const floraAccountId = receipt.accountId;\n\n const floraAccount = await this.createFloraAccount(keyList, config);\n\n const topics = await this.createFloraTopics(\n floraAccount,\n keyList,\n config,\n );\n await this.createFloraProfile(floraAccount, topics, config);\n\n this.logger.info('Flora created successfully', {\n floraAccountId: floraAccount.toString(),\n topics: {\n communication: topics.communication.toString(),\n transaction: topics.transaction.toString(),\n state: topics.state.toString(),\n },\n });\n\n return {\n floraAccountId: floraAccount,\n topics,\n keyList,\n transactionId: '',\n };\n } catch (error) {\n this.logger.error('Failed to create Flora', error);\n throw error;\n }\n }\n\n /**\n * Build KeyList from member public keys\n */\n private async buildKeyList(\n members: FloraMember[],\n threshold: number,\n ): Promise<KeyList> {\n const keyList = new KeyList();\n keyList.setThreshold(threshold);\n\n for (const member of members) {\n if (member.publicKey) {\n keyList.push(member.publicKey);\n } else {\n const accountInfo = await new AccountInfoQuery()\n .setAccountId(member.accountId)\n .execute(this.client);\n keyList.push(accountInfo.key);\n }\n }\n\n return keyList;\n }\n\n /**\n * Create the Flora account with threshold key\n */\n private async createFloraAccount(\n keyList: KeyList,\n config: FloraConfig,\n ): Promise<AccountId> {\n const transaction = new AccountCreateTransaction()\n .setKey(keyList)\n .setInitialBalance(new Hbar(config.initialBalance || 20))\n .setMaxAutomaticTokenAssociations(\n config.maxAutomaticTokenAssociations || -1,\n );\n\n const response = await transaction.execute(this.client);\n const receipt = await response.getReceipt(this.client);\n\n if (!receipt.accountId) {\n throw new FloraError(\n 'Failed to create Flora account',\n 'ACCOUNT_CREATION_FAILED',\n );\n }\n\n return receipt.accountId;\n }\n\n /**\n * Create the three required Flora topics with HCS-16 memo format\n */\n private async createFloraTopics(\n floraAccountId: AccountId,\n adminKey: KeyList,\n config: FloraConfig,\n ): Promise<FloraTopics> {\n const submitKeyList = new KeyList();\n submitKeyList.setThreshold(1);\n\n for (const member of config.members) {\n if (member.publicKey) {\n submitKeyList.push(member.publicKey);\n } else {\n throw new FloraError(\n `Public key required for member ${member.accountId}`,\n 'MISSING_PUBLIC_KEY',\n );\n }\n }\n\n const [communication, transaction, state] = await Promise.all([\n this.createTopic(\n floraAccountId,\n FloraTopicType.COMMUNICATION,\n adminKey,\n submitKeyList,\n config.customFees,\n ),\n this.createTopic(\n floraAccountId,\n FloraTopicType.TRANSACTION,\n adminKey,\n submitKeyList,\n config.customFees,\n ),\n this.createTopic(\n floraAccountId,\n FloraTopicType.STATE,\n adminKey,\n submitKeyList,\n config.customFees,\n ),\n ]);\n\n return { communication, transaction, state };\n }\n\n /**\n * Create a single topic with HCS-16 memo format\n */\n private async createTopic(\n floraAccountId: AccountId,\n topicType: FloraTopicType,\n adminKey: KeyList,\n submitKey: KeyList,\n customFees?: Array<{ amount: number; feeCollectorAccountId: string }>,\n ): Promise<TopicId> {\n const memo = `hcs-16:${floraAccountId}:${topicType}`;\n\n const transaction = new TopicCreateTransaction()\n .setTopicMemo(memo)\n .setAdminKey(adminKey)\n .setSubmitKey(submitKey);\n\n if (customFees && customFees.length > 0) {\n const fees = customFees.map(fee =>\n new CustomFixedFee()\n .setAmount(fee.amount)\n .setFeeCollectorAccountId(\n AccountId.fromString(fee.feeCollectorAccountId),\n ),\n );\n transaction.setCustomFees(fees);\n }\n\n const response = await transaction.execute(this.client);\n const receipt = await response.getReceipt(this.client);\n\n if (!receipt.topicId) {\n throw new FloraError(\n `Failed to create ${FloraTopicType[topicType]} topic`,\n 'TOPIC_CREATION_FAILED',\n );\n }\n\n return receipt.topicId;\n }\n\n /**\n * Create and store Flora profile using HCS-11\n */\n private async createFloraProfile(\n floraAccountId: AccountId,\n topics: FloraTopics,\n config: FloraConfig,\n ): Promise<string> {\n this.logger.info('Creating Flora profile using HCS-11');\n\n const operatorMember = config.members[0];\n if (!operatorMember.privateKey) {\n throw new FloraError(\n 'First member must have private key to create profile',\n 'MISSING_PRIVATE_KEY',\n );\n }\n\n const hcs11Client = new HCS11Client({\n network: this.network,\n auth: {\n operatorId: floraAccountId.toString(),\n privateKey: operatorMember.privateKey,\n },\n });\n\n const floraBuilder = new FloraBuilder()\n .setDisplayName(config.displayName)\n .setMembers(config.members)\n .setThreshold(config.threshold)\n .setTopics({\n communication: topics.communication.toString(),\n transaction: topics.transaction.toString(),\n state: topics.state.toString(),\n })\n .setPolicies(config.policies);\n\n if (config.bio) {\n floraBuilder.setBio(config.bio);\n }\n\n if (config.metadata) {\n floraBuilder.setMetadata(config.metadata);\n }\n\n const profile = floraBuilder.build();\n\n const inscriptionResult = await hcs11Client.createAndInscribeProfile(\n profile,\n true,\n );\n\n if (!inscriptionResult.success) {\n throw new FloraError(\n `Failed to inscribe Flora profile: ${inscriptionResult.error}`,\n 'PROFILE_INSCRIPTION_FAILED',\n );\n }\n\n this.logger.info('Flora profile inscribed successfully', {\n profileTopicId: inscriptionResult.profileTopicId,\n transactionId: inscriptionResult.transactionId,\n });\n\n return inscriptionResult.profileTopicId;\n }\n\n /**\n * Send a message to a Flora topic\n */\n async sendFloraMessage(\n topicId: string | TopicId,\n message: FloraMessage,\n ): Promise<void> {\n try {\n message.p = 'hcs-16';\n\n const transaction = new TopicMessageSubmitTransaction()\n .setTopicId(topicId)\n .setMessage(JSON.stringify(message));\n\n const response = await transaction.execute(this.client);\n await response.getReceipt(this.client);\n\n this.logger.debug('Flora message sent', {\n topicId: topicId.toString(),\n operation: message.op,\n });\n } catch (error) {\n this.logger.error('Failed to send Flora message', error);\n throw error;\n }\n }\n\n /**\n * Send flora_created notification to all members\n */\n async notifyFloraCreated(\n result: FloraCreationResult,\n memberTopics: string[],\n ): Promise<void> {\n const message: FloraMessage = {\n p: 'hcs-16',\n op: FloraOperation.FLORA_CREATED,\n operator_id: `${this.client.operatorAccountId}@${result.floraAccountId}`,\n flora_account_id: result.floraAccountId.toString(),\n topics: {\n communication: result.topics.communication.toString(),\n transaction: result.topics.transaction.toString(),\n state: result.topics.state.toString(),\n },\n };\n\n await Promise.all(\n memberTopics.map(topicId => this.sendFloraMessage(topicId, message)),\n );\n }\n\n /**\n * Parse HCS-16 topic memo\n */\n parseTopicMemo(memo: string): {\n protocol: string;\n floraAccountId: string;\n topicType: FloraTopicType;\n } | null {\n const match = memo.match(/^hcs-16:([0-9.]+):(\\d)$/);\n if (!match) {\n return null;\n }\n\n return {\n protocol: 'hcs-16',\n floraAccountId: match[1],\n topicType: parseInt(match[2]) as FloraTopicType,\n };\n }\n\n /**\n * Create a proposal for a scheduled transaction\n */\n async createTransactionProposal(\n transactionTopicId: string | TopicId,\n scheduledTxId: string,\n description: string,\n operatorAccountId: string,\n floraAccountId: string,\n ): Promise<void> {\n const message: FloraMessage = {\n p: 'hcs-16',\n op: FloraOperation.TX_PROPOSAL,\n operator_id: `${operatorAccountId}@${floraAccountId}`,\n scheduled_tx_id: scheduledTxId,\n description,\n m: description,\n };\n\n await this.sendFloraMessage(transactionTopicId, message);\n }\n\n /**\n * Submit a state update to the state topic\n */\n async submitStateUpdate(\n stateTopicId: string | TopicId,\n stateHash: string,\n operatorAccountId: string,\n floraAccountId: string,\n epoch?: number,\n ): Promise<void> {\n const message: FloraMessage = {\n p: 'hcs-16',\n op: FloraOperation.STATE_UPDATE,\n operator_id: `${operatorAccountId}@${floraAccountId}`,\n hash: stateHash,\n epoch,\n timestamp: new Date().toISOString(),\n };\n\n await this.sendFloraMessage(stateTopicId, message);\n }\n\n /**\n * Create a generic HCS-16 transaction topic with HIP-991 support\n * This can be used for any HCS-16 compliant topic, not just Flora accounts\n */\n async createTransactionTopic(\n config: TransactionTopicConfig,\n ): Promise<TopicId> {\n const transaction = new TopicCreateTransaction().setTopicMemo(config.memo);\n\n if (config.adminKey) {\n transaction.setAdminKey(config.adminKey);\n }\n if (config.submitKey) {\n transaction.setSubmitKey(config.submitKey);\n }\n if (config.feeScheduleKey) {\n transaction.setFeeScheduleKey(config.feeScheduleKey);\n }\n\n if (config.customFees && config.customFees.length > 0) {\n const fees = config.customFees.map(fee => {\n const customFee = new CustomFixedFee()\n .setAmount(fee.amount)\n .setFeeCollectorAccountId(\n AccountId.fromString(fee.feeCollectorAccountId),\n );\n\n if (fee.denominatingTokenId) {\n customFee.setDenominatingTokenId(\n TokenId.fromString(fee.denominatingTokenId),\n );\n }\n\n return customFee;\n });\n transaction.setCustomFees(fees);\n }\n\n if (config.feeExemptKeys && config.feeExemptKeys.length > 0) {\n (transaction as any).setFeeExemptKeyList(config.feeExemptKeys);\n }\n\n const response = await transaction.execute(this.client);\n const receipt = await response.getReceipt(this.client);\n\n if (!receipt.topicId) {\n throw new FloraError(\n 'Failed to create transaction topic',\n 'TOPIC_CREATION_FAILED',\n );\n }\n\n this.logger.info('Created HCS-16 transaction topic', {\n topicId: receipt.topicId.toString(),\n memo: config.memo,\n hasFees: !!config.customFees,\n });\n\n return receipt.topicId;\n }\n\n /**\n * Submit HCS-16 compliant credit purchase message\n */\n async submitCreditPurchase(\n topicId: string | TopicId,\n purchaser: string,\n amount: number,\n floraAccountId?: string,\n ): Promise<void> {\n const message: CreditPurchaseMessage = {\n p: 'hcs-16',\n op: FloraOperation.CREDIT_PURCHASE,\n operator_id: floraAccountId\n ? `${purchaser}@${floraAccountId}`\n : purchaser,\n amount,\n purchaser,\n timestamp: new Date().toISOString(),\n m: `Purchase ${amount} credits`,\n };\n\n await this.sendFloraMessage(topicId, message);\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"standards-sdk.es30.js","sources":["../../src/hcs-17/types.ts"],"sourcesContent":["import { PublicKey } from '@hashgraph/sdk';\n\n/**\n * HCS-17 Topic state information\n */\nexport interface TopicState {\n topicId: string;\n latestRunningHash: string;\n}\n\n/**\n * HCS-17 Account state hash input\n */\nexport interface AccountStateInput {\n accountId: string;\n publicKey: PublicKey | string;\n topics: TopicState[];\n}\n\n/**\n * HCS-17 Composite state hash input for Flora/Bloom\n */\nexport interface CompositeStateInput {\n compositeAccountId: string;\n compositePublicKeyFingerprint: string;\n memberStates: Array<{\n accountId: string;\n stateHash: string;\n }>;\n compositeTopics: TopicState[];\n}\n\n/**\n * HCS-17 State hash message format\n */\nexport interface StateHashMessage {\n p: 'hcs-17';\n op: 'state_hash';\n state_hash: string;\n topics: string[];\n account_id: string;\n timestamp?: string;\n m?: string;\n}\n\n/**\n * HCS-17 State hash result\n */\nexport interface StateHashResult {\n stateHash: string;\n accountId: string;\n timestamp: Date;\n topicCount: number;\n}\n\n/**\n * HCS-17 Composite state hash result\n */\nexport interface CompositeStateHashResult extends StateHashResult {\n memberCount: number;\n compositeTopicCount: number;\n}\n\n/**\n * HCS-17 errors\n */\nexport class StateHashError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n ) {\n super(message);\n this.name = 'StateHashError';\n }\n}\n"],"names":[],"mappings":"AAkEO,MAAM,uBAAuB,MAAM;AAAA,EACxC,YACE,SACgB,MAChB;AACA,UAAM,OAAO;AAFG,SAAA,OAAA;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;"}
|
|
@@ -1,11 +1,207 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { createHash } from "crypto";
|
|
2
|
+
import { Logger } from "./standards-sdk.es34.js";
|
|
3
|
+
import { StateHashError } from "./standards-sdk.es30.js";
|
|
4
|
+
class StateHashCalculator {
|
|
5
|
+
constructor(logger) {
|
|
6
|
+
this.logger = logger || new Logger({ module: "StateHashCalculator" });
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Calculate state hash for a single account
|
|
10
|
+
* StateHash = SHA384(topicId_1 || latestRunningHash_1 || ... || topicId_n || latestRunningHash_n || account_publicKey)
|
|
11
|
+
*/
|
|
12
|
+
calculateAccountStateHash(input) {
|
|
13
|
+
try {
|
|
14
|
+
this.logger.debug("Calculating account state hash", {
|
|
15
|
+
accountId: input.accountId,
|
|
16
|
+
topicCount: input.topics.length
|
|
17
|
+
});
|
|
18
|
+
const sortedTopics = [...input.topics].sort(
|
|
19
|
+
(a, b) => a.topicId.localeCompare(b.topicId)
|
|
20
|
+
);
|
|
21
|
+
let concatenated = "";
|
|
22
|
+
for (const topic of sortedTopics) {
|
|
23
|
+
concatenated += topic.topicId + topic.latestRunningHash;
|
|
24
|
+
}
|
|
25
|
+
const publicKeyString = typeof input.publicKey === "string" ? input.publicKey : input.publicKey.toString();
|
|
26
|
+
concatenated += publicKeyString;
|
|
27
|
+
const hash = createHash("sha384");
|
|
28
|
+
hash.update(concatenated);
|
|
29
|
+
const stateHash = hash.digest("hex");
|
|
30
|
+
this.logger.debug("Account state hash calculated", {
|
|
31
|
+
accountId: input.accountId,
|
|
32
|
+
stateHash
|
|
33
|
+
});
|
|
34
|
+
return {
|
|
35
|
+
stateHash,
|
|
36
|
+
accountId: input.accountId,
|
|
37
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
38
|
+
topicCount: input.topics.length
|
|
39
|
+
};
|
|
40
|
+
} catch (error) {
|
|
41
|
+
this.logger.error("Failed to calculate account state hash", error);
|
|
42
|
+
throw new StateHashError(
|
|
43
|
+
"Failed to calculate account state hash",
|
|
44
|
+
"CALCULATION_FAILED"
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Calculate composite state hash for Flora/Bloom
|
|
50
|
+
* CompositeStateHash = SHA384(
|
|
51
|
+
* Σ_sorted(accountId_i || StateHash_i) ||
|
|
52
|
+
* Σ_sorted(topicId_j || runningHash_j) ||
|
|
53
|
+
* composite_publicKeyFingerprint
|
|
54
|
+
* )
|
|
55
|
+
*/
|
|
56
|
+
calculateCompositeStateHash(input) {
|
|
57
|
+
try {
|
|
58
|
+
this.logger.debug("Calculating composite state hash", {
|
|
59
|
+
compositeAccountId: input.compositeAccountId,
|
|
60
|
+
memberCount: input.memberStates.length,
|
|
61
|
+
topicCount: input.compositeTopics.length
|
|
62
|
+
});
|
|
63
|
+
const sortedMembers = [...input.memberStates].sort(
|
|
64
|
+
(a, b) => a.accountId.localeCompare(b.accountId)
|
|
65
|
+
);
|
|
66
|
+
const sortedTopics = [...input.compositeTopics].sort(
|
|
67
|
+
(a, b) => a.topicId.localeCompare(b.topicId)
|
|
68
|
+
);
|
|
69
|
+
let concatenated = "";
|
|
70
|
+
for (const member of sortedMembers) {
|
|
71
|
+
concatenated += member.accountId + member.stateHash;
|
|
72
|
+
}
|
|
73
|
+
for (const topic of sortedTopics) {
|
|
74
|
+
concatenated += topic.topicId + topic.latestRunningHash;
|
|
75
|
+
}
|
|
76
|
+
concatenated += input.compositePublicKeyFingerprint;
|
|
77
|
+
const hash = createHash("sha384");
|
|
78
|
+
hash.update(concatenated);
|
|
79
|
+
const stateHash = hash.digest("hex");
|
|
80
|
+
this.logger.debug("Composite state hash calculated", {
|
|
81
|
+
compositeAccountId: input.compositeAccountId,
|
|
82
|
+
stateHash
|
|
83
|
+
});
|
|
84
|
+
return {
|
|
85
|
+
stateHash,
|
|
86
|
+
accountId: input.compositeAccountId,
|
|
87
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
88
|
+
topicCount: input.compositeTopics.length,
|
|
89
|
+
memberCount: input.memberStates.length,
|
|
90
|
+
compositeTopicCount: input.compositeTopics.length
|
|
91
|
+
};
|
|
92
|
+
} catch (error) {
|
|
93
|
+
this.logger.error("Failed to calculate composite state hash", error);
|
|
94
|
+
throw new StateHashError(
|
|
95
|
+
"Failed to calculate composite state hash",
|
|
96
|
+
"CALCULATION_FAILED"
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Calculate deterministic public key fingerprint for KeyList/Threshold keys
|
|
102
|
+
* Used for Flora/Bloom accounts
|
|
103
|
+
*/
|
|
104
|
+
calculateKeyFingerprint(keys, threshold) {
|
|
105
|
+
try {
|
|
106
|
+
const sortedKeys = [...keys].sort(
|
|
107
|
+
(a, b) => a.toString().localeCompare(b.toString())
|
|
108
|
+
);
|
|
109
|
+
const keyData = {
|
|
110
|
+
threshold,
|
|
111
|
+
keys: sortedKeys.map((k) => k.toString())
|
|
112
|
+
};
|
|
113
|
+
const hash = createHash("sha384");
|
|
114
|
+
hash.update(JSON.stringify(keyData));
|
|
115
|
+
return hash.digest("hex");
|
|
116
|
+
} catch (error) {
|
|
117
|
+
this.logger.error("Failed to calculate key fingerprint", error);
|
|
118
|
+
throw new StateHashError(
|
|
119
|
+
"Failed to calculate key fingerprint",
|
|
120
|
+
"FINGERPRINT_FAILED"
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Create HCS-17 state hash message
|
|
126
|
+
*/
|
|
127
|
+
createStateHashMessage(stateHash, accountId, topicIds, memo) {
|
|
128
|
+
return {
|
|
129
|
+
p: "hcs-17",
|
|
130
|
+
op: "state_hash",
|
|
131
|
+
state_hash: stateHash,
|
|
132
|
+
topics: topicIds,
|
|
133
|
+
account_id: accountId,
|
|
134
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
135
|
+
m: memo
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Verify state hash by recalculating
|
|
140
|
+
*/
|
|
141
|
+
async verifyStateHash(input, expectedHash) {
|
|
142
|
+
try {
|
|
143
|
+
let calculatedHash;
|
|
144
|
+
if ("publicKey" in input) {
|
|
145
|
+
const result = this.calculateAccountStateHash(input);
|
|
146
|
+
calculatedHash = result.stateHash;
|
|
147
|
+
} else {
|
|
148
|
+
const result = this.calculateCompositeStateHash(input);
|
|
149
|
+
calculatedHash = result.stateHash;
|
|
150
|
+
}
|
|
151
|
+
const isValid = calculatedHash === expectedHash;
|
|
152
|
+
const accountId = "accountId" in input ? input.accountId : input.compositeAccountId;
|
|
153
|
+
this.logger.debug("State hash verification", {
|
|
154
|
+
accountId,
|
|
155
|
+
isValid,
|
|
156
|
+
expected: expectedHash,
|
|
157
|
+
calculated: calculatedHash
|
|
158
|
+
});
|
|
159
|
+
return isValid;
|
|
160
|
+
} catch (error) {
|
|
161
|
+
this.logger.error("Failed to verify state hash", error);
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Get latest running hashes for topics (mock implementation)
|
|
167
|
+
* In production, this would query the actual Hedera network
|
|
168
|
+
*/
|
|
169
|
+
async getTopicRunningHashes(topicIds) {
|
|
170
|
+
return topicIds.map((topicId) => ({
|
|
171
|
+
topicId,
|
|
172
|
+
latestRunningHash: createHash("sha256").update(`mock-hash-${topicId}-${Date.now()}`).digest("hex").substring(0, 48)
|
|
173
|
+
}));
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Calculate and publish state hash to a topic
|
|
177
|
+
*/
|
|
178
|
+
async publishStateHash(stateHash, accountId, topicIds, publishTopicId, client) {
|
|
179
|
+
try {
|
|
180
|
+
const message = this.createStateHashMessage(
|
|
181
|
+
stateHash,
|
|
182
|
+
accountId,
|
|
183
|
+
topicIds,
|
|
184
|
+
"State synchronization"
|
|
185
|
+
);
|
|
186
|
+
const { TopicMessageSubmitTransaction } = await import("@hashgraph/sdk");
|
|
187
|
+
const transaction = new TopicMessageSubmitTransaction().setTopicId(publishTopicId).setMessage(JSON.stringify(message));
|
|
188
|
+
const response = await transaction.execute(client);
|
|
189
|
+
await response.getReceipt(client);
|
|
190
|
+
this.logger.info("State hash published", {
|
|
191
|
+
accountId,
|
|
192
|
+
topicId: publishTopicId,
|
|
193
|
+
stateHash
|
|
194
|
+
});
|
|
195
|
+
} catch (error) {
|
|
196
|
+
this.logger.error("Failed to publish state hash", error);
|
|
197
|
+
throw new StateHashError(
|
|
198
|
+
"Failed to publish state hash",
|
|
199
|
+
"PUBLISH_FAILED"
|
|
200
|
+
);
|
|
201
|
+
}
|
|
6
202
|
}
|
|
7
203
|
}
|
|
8
204
|
export {
|
|
9
|
-
|
|
205
|
+
StateHashCalculator
|
|
10
206
|
};
|
|
11
207
|
//# sourceMappingURL=standards-sdk.es31.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standards-sdk.es31.js","sources":["../../src/hcs-17/types.ts"],"sourcesContent":["import { PublicKey } from '@hashgraph/sdk';\n\n/**\n * HCS-17 Topic state information\n */\nexport interface TopicState {\n topicId: string;\n latestRunningHash: string;\n}\n\n/**\n * HCS-17 Account state hash input\n */\nexport interface AccountStateInput {\n accountId: string;\n publicKey: PublicKey | string;\n topics: TopicState[];\n}\n\n/**\n * HCS-17 Composite state hash input for Flora/Bloom\n */\nexport interface CompositeStateInput {\n compositeAccountId: string;\n compositePublicKeyFingerprint: string;\n memberStates: Array<{\n accountId: string;\n stateHash: string;\n }>;\n compositeTopics: TopicState[];\n}\n\n/**\n * HCS-17 State hash message format\n */\nexport interface StateHashMessage {\n p: 'hcs-17';\n op: 'state_hash';\n state_hash: string;\n topics: string[];\n account_id: string;\n timestamp?: string;\n m?: string;\n}\n\n/**\n * HCS-17 State hash result\n */\nexport interface StateHashResult {\n stateHash: string;\n accountId: string;\n timestamp: Date;\n topicCount: number;\n}\n\n/**\n * HCS-17 Composite state hash result\n */\nexport interface CompositeStateHashResult extends StateHashResult {\n memberCount: number;\n compositeTopicCount: number;\n}\n\n/**\n * HCS-17 errors\n */\nexport class StateHashError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n ) {\n super(message);\n this.name = 'StateHashError';\n }\n}\n"],"names":[],"mappings":"AAkEO,MAAM,uBAAuB,MAAM;AAAA,EACxC,YACE,SACgB,MAChB;AACA,UAAM,OAAO;AAFG,SAAA,OAAA;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;"}
|
|
1
|
+
{"version":3,"file":"standards-sdk.es31.js","sources":["../../src/hcs-17/state-hash-calculator.ts"],"sourcesContent":["import { createHash } from 'crypto';\nimport { PublicKey, KeyList, Key } from '@hashgraph/sdk';\nimport { Logger } from '../utils/logger';\nimport {\n AccountStateInput,\n CompositeStateInput,\n StateHashResult,\n CompositeStateHashResult,\n StateHashMessage,\n TopicState,\n StateHashError,\n} from './types';\n\n/**\n * HCS-17 State Hash Calculator\n * Calculates state hashes for accounts and composite accounts (Flora/Bloom)\n */\nexport class StateHashCalculator {\n private readonly logger: Logger;\n\n constructor(logger?: Logger) {\n this.logger = logger || new Logger({ module: 'StateHashCalculator' });\n }\n\n /**\n * Calculate state hash for a single account\n * StateHash = SHA384(topicId_1 || latestRunningHash_1 || ... || topicId_n || latestRunningHash_n || account_publicKey)\n */\n calculateAccountStateHash(input: AccountStateInput): StateHashResult {\n try {\n this.logger.debug('Calculating account state hash', {\n accountId: input.accountId,\n topicCount: input.topics.length,\n });\n\n const sortedTopics = [...input.topics].sort((a, b) =>\n a.topicId.localeCompare(b.topicId),\n );\n\n let concatenated = '';\n for (const topic of sortedTopics) {\n concatenated += topic.topicId + topic.latestRunningHash;\n }\n\n const publicKeyString =\n typeof input.publicKey === 'string'\n ? input.publicKey\n : input.publicKey.toString();\n concatenated += publicKeyString;\n const hash = createHash('sha384');\n hash.update(concatenated);\n const stateHash = hash.digest('hex');\n\n this.logger.debug('Account state hash calculated', {\n accountId: input.accountId,\n stateHash,\n });\n\n return {\n stateHash,\n accountId: input.accountId,\n timestamp: new Date(),\n topicCount: input.topics.length,\n };\n } catch (error) {\n this.logger.error('Failed to calculate account state hash', error);\n throw new StateHashError(\n 'Failed to calculate account state hash',\n 'CALCULATION_FAILED',\n );\n }\n }\n\n /**\n * Calculate composite state hash for Flora/Bloom\n * CompositeStateHash = SHA384(\n * Σ_sorted(accountId_i || StateHash_i) ||\n * Σ_sorted(topicId_j || runningHash_j) ||\n * composite_publicKeyFingerprint\n * )\n */\n calculateCompositeStateHash(\n input: CompositeStateInput,\n ): CompositeStateHashResult {\n try {\n this.logger.debug('Calculating composite state hash', {\n compositeAccountId: input.compositeAccountId,\n memberCount: input.memberStates.length,\n topicCount: input.compositeTopics.length,\n });\n\n const sortedMembers = [...input.memberStates].sort((a, b) =>\n a.accountId.localeCompare(b.accountId),\n );\n\n const sortedTopics = [...input.compositeTopics].sort((a, b) =>\n a.topicId.localeCompare(b.topicId),\n );\n\n let concatenated = '';\n for (const member of sortedMembers) {\n concatenated += member.accountId + member.stateHash;\n }\n\n for (const topic of sortedTopics) {\n concatenated += topic.topicId + topic.latestRunningHash;\n }\n\n concatenated += input.compositePublicKeyFingerprint;\n const hash = createHash('sha384');\n hash.update(concatenated);\n const stateHash = hash.digest('hex');\n\n this.logger.debug('Composite state hash calculated', {\n compositeAccountId: input.compositeAccountId,\n stateHash,\n });\n\n return {\n stateHash,\n accountId: input.compositeAccountId,\n timestamp: new Date(),\n topicCount: input.compositeTopics.length,\n memberCount: input.memberStates.length,\n compositeTopicCount: input.compositeTopics.length,\n };\n } catch (error) {\n this.logger.error('Failed to calculate composite state hash', error);\n throw new StateHashError(\n 'Failed to calculate composite state hash',\n 'CALCULATION_FAILED',\n );\n }\n }\n\n /**\n * Calculate deterministic public key fingerprint for KeyList/Threshold keys\n * Used for Flora/Bloom accounts\n */\n calculateKeyFingerprint(keys: PublicKey[], threshold: number): string {\n try {\n const sortedKeys = [...keys].sort((a, b) =>\n a.toString().localeCompare(b.toString()),\n );\n\n const keyData = {\n threshold,\n keys: sortedKeys.map(k => k.toString()),\n };\n const hash = createHash('sha384');\n hash.update(JSON.stringify(keyData));\n return hash.digest('hex');\n } catch (error) {\n this.logger.error('Failed to calculate key fingerprint', error);\n throw new StateHashError(\n 'Failed to calculate key fingerprint',\n 'FINGERPRINT_FAILED',\n );\n }\n }\n\n /**\n * Create HCS-17 state hash message\n */\n createStateHashMessage(\n stateHash: string,\n accountId: string,\n topicIds: string[],\n memo?: string,\n ): StateHashMessage {\n return {\n p: 'hcs-17',\n op: 'state_hash',\n state_hash: stateHash,\n topics: topicIds,\n account_id: accountId,\n timestamp: new Date().toISOString(),\n m: memo,\n };\n }\n\n /**\n * Verify state hash by recalculating\n */\n async verifyStateHash(\n input: AccountStateInput | CompositeStateInput,\n expectedHash: string,\n ): Promise<boolean> {\n try {\n let calculatedHash: string;\n\n if ('publicKey' in input) {\n const result = this.calculateAccountStateHash(input);\n calculatedHash = result.stateHash;\n } else {\n const result = this.calculateCompositeStateHash(input);\n calculatedHash = result.stateHash;\n }\n\n const isValid = calculatedHash === expectedHash;\n\n const accountId =\n 'accountId' in input ? input.accountId : input.compositeAccountId;\n this.logger.debug('State hash verification', {\n accountId,\n isValid,\n expected: expectedHash,\n calculated: calculatedHash,\n });\n\n return isValid;\n } catch (error) {\n this.logger.error('Failed to verify state hash', error);\n return false;\n }\n }\n\n /**\n * Get latest running hashes for topics (mock implementation)\n * In production, this would query the actual Hedera network\n */\n async getTopicRunningHashes(topicIds: string[]): Promise<TopicState[]> {\n return topicIds.map(topicId => ({\n topicId,\n latestRunningHash: createHash('sha256')\n .update(`mock-hash-${topicId}-${Date.now()}`)\n .digest('hex')\n .substring(0, 48),\n }));\n }\n\n /**\n * Calculate and publish state hash to a topic\n */\n async publishStateHash(\n stateHash: string,\n accountId: string,\n topicIds: string[],\n publishTopicId: string,\n client: any,\n ): Promise<void> {\n try {\n const message = this.createStateHashMessage(\n stateHash,\n accountId,\n topicIds,\n 'State synchronization',\n );\n\n const { TopicMessageSubmitTransaction } = await import('@hashgraph/sdk');\n\n const transaction = new TopicMessageSubmitTransaction()\n .setTopicId(publishTopicId)\n .setMessage(JSON.stringify(message));\n\n const response = await transaction.execute(client);\n await response.getReceipt(client);\n\n this.logger.info('State hash published', {\n accountId,\n topicId: publishTopicId,\n stateHash,\n });\n } catch (error) {\n this.logger.error('Failed to publish state hash', error);\n throw new StateHashError(\n 'Failed to publish state hash',\n 'PUBLISH_FAILED',\n );\n }\n }\n}\n"],"names":[],"mappings":"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"}
|