-
Notifications
You must be signed in to change notification settings - Fork 2
/
setup.ts
379 lines (339 loc) · 11.8 KB
/
setup.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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
delete process.env.NODE_ENV;
import dotenv from 'dotenv';
const result = dotenv.config({ debug: true });
if (result.error) {
throw result.error;
}
/**
* See @href https://wiki.polkadot.network/docs/en/learn-identity
*/
import { ApiPromise, WsProvider, Keyring } from '@polkadot/api';
import { AddressOrPair, ApiTypes, SubmittableExtrinsic } from '@polkadot/api/types';
import { KeyringPair } from '@polkadot/keyring/types';
import { blake2AsHex } from '@polkadot/util-crypto';
import config from 'app/config';
import Config from 'types/config';
import { AnyJson, ISubmittableResult } from '@polkadot/types/types';
// DEFUALT FEE is 1 Unit
const DEFAULT_REGISTRAR_FEE = 1000000000000;
const DEFAULT_DEMOCRACY_VOTE_FEE = 1000000000000;
const DEFAULT_DEMOCRACY_PROPOSAL_FEE = 1000000000000;
const DEFAULT_SLEEP_INTERVAL = 6;
function sleep(seconds: number): Promise<void> {
return new Promise((resolve) => {
setTimeout(resolve, seconds * 1000);
});
}
let self: any = null;
class Chain {
private readonly wsProvider: WsProvider;
private readonly keyring: Keyring;
private api: ApiPromise;
private myself: KeyringPair;
alice: KeyringPair;
bob: KeyringPair;
charlie: KeyringPair;
dave: KeyringPair;
eve: KeyringPair;
/**
* A wrapped APIs for block chain
* @constructor
*/
constructor(config: Config) {
self = this;
self.wsProvider = new WsProvider(`${config.chain.protocol}://${config.chain.provider}:${config.chain.port}`);
self.keyring = new Keyring({ type: 'sr25519' });
}
async connect() {
if (!self.api) {
self.api = await ApiPromise.create({
provider: self.wsProvider,
types: {
Address: 'MultiAddress',
LookupSource: 'MultiAddress',
},
});
}
if (!self.myself) {
self.myself = self.keyring.addFromUri('//Alice');
self.alice = self.keyring.addFromUri('//Alice');
self.bob = self.keyring.addFromUri('//Bob');
self.charlie = self.keyring.addFromUri('//Charlie');
self.dave = self.keyring.addFromUri('//Dave');
self.eve = self.keyring.addFromUri('//Eve');
}
return self.api;
}
async signAndSend(tx: SubmittableExtrinsic<ApiTypes>, account: AddressOrPair) {
try {
const block = await tx.signAndSend(account);
return block;
} catch (error) {
console.log(`Error occurs:`);
console.trace(error);
}
return null;
}
/**
* identity
*/
async identityRegistrars() {
await self.connect();
const registrars = await self.api.query.identity.registrars();
console.log(`[identity.registrars]: ${registrars}`);
return registrars;
}
async identityIdentityOf() {
await self.connect();
const identityOf = await self.api.query.identity.identityOf(self.myself!.address);
console.log(`[identity.identityOf]: ${identityOf.toHuman()}`);
return identityOf;
}
async identityAddRegistrar(registrarAccount: string | Uint8Array) {
await self.connect();
const tx = self.api.tx.identity.addRegistrar(registrarAccount);
console.log(`[identity.addRegistrar]: ${tx}`);
return tx;
}
async identitySetFee(account: AddressOrPair, regIndex = 0, fee = DEFAULT_REGISTRAR_FEE) {
const tx = self.api.tx.identity.setFee(regIndex, fee);
await self.signAndSend(tx, account);
console.log(`[identity.setFee]: ${tx}`);
return tx;
}
/**
* democracy
*/
async democracyPublicPropCount() {
await self.connect();
const publicPropCount = await self.api.query.democracy.publicPropCount();
console.log(`[democracy.publicPropCount]: ${publicPropCount}`);
return publicPropCount;
}
async democracyPublicProps() {
await self.connect();
const publicProps = await self.api.query.democracy.publicProps();
console.log(`[democracy.publicProposals]: ${publicProps}`);
return publicProps;
}
async democracyReferendumCount() {
await self.connect();
const referendumCount = await self.api.query.democracy.referendumCount();
console.log(`[democracy.referendumCount]: ${referendumCount}`);
return referendumCount;
}
async democracyReferendumInfoOf() {
await self.connect();
const referendumCount = await self.democracyReferendumCount();
let referendumInfo = [];
// TODO_CHECK, I've added .toNumber() here as the ReferendumIndex type wasn't happy being used as a number
for (let i = 0; i < referendumCount.toNumber(); i++) {
const info = await self.api.query.democracy.referendumInfoOf(i);
console.log(`[democracy.referendumInfoOf]: ${info}`);
referendumInfo.push(info);
}
return referendumInfo;
}
async democracyPropose(
account: AddressOrPair,
func: (args: any) => Promise<SubmittableExtrinsic<'promise', ISubmittableResult>>,
args: any,
value = DEFAULT_DEMOCRACY_PROPOSAL_FEE
) {
await self.connect();
// @ts-ignore
const result = await func(...args);
const encodedProposal = result.method.toHex();
console.log('encodeProposal: ', encodedProposal);
const preimage = blake2AsHex(encodedProposal);
console.log('preimage: ', preimage);
const tx = self.api.tx.democracy.propose(preimage, value);
await self.signAndSend(tx, account);
console.log(`[democracy.propose]: ${tx}`);
return tx;
}
async democracyNotePreimage(
account: AddressOrPair,
func: (args: any) => Promise<SubmittableExtrinsic<'promise', ISubmittableResult>>,
args: any
) {
await self.connect();
// @ts-ignore
const result = await func(...args);
const encodedProposal = result.method.toHex();
const tx = self.api.tx.preimage.notePreimage(encodedProposal);
await self.signAndSend(tx, account);
console.log(`[democracy.notePreimage]: ${tx}`);
return tx;
}
async democracyVote(account: AddressOrPair, balance = DEFAULT_DEMOCRACY_VOTE_FEE) {
await self.connect();
const referendumInfo = await self.democracyReferendumInfoOf();
const vote = {
Standard: {
vote: true,
conviction: 'None',
// 0.1 Unit
// balance: 1000000000000000
balance: balance,
},
};
console.log(`vote on referendumInfo: ${referendumInfo[referendumInfo.length - 1]}`);
const tx = self.api.tx.democracy.vote(referendumInfo.length - 1, vote);
await self.signAndSend(tx, account);
console.log(`[democracy.vote]: ${tx}`);
return tx;
}
async proxyProxies(account: string | Uint8Array) {
await self.connect();
const resp = await self.api.query.proxy.proxies(account);
console.log(`[proxy.proxies]: ${resp}`);
return resp;
}
async proxyAddProxy(
account: AddressOrPair,
delegateAccount: string | Uint8Array,
proxyType = 'IdentityJudgement',
delay = 0
) {
await self.connect();
// TODO_CHECK 'IdentityJudgement' doesn't match the types allowed in the polkadot library
// @ts-ignore
const tx = self.api.tx.proxy.addProxy(delegateAccount, proxyType, delay);
const resp = await self.signAndSend(tx, account);
console.log(`[identity.RequestJudgement] tx: ${tx}`);
console.log(`[identity.RequestJudgement] resp: ${resp}`);
return [tx, resp];
}
async disconnect() {
console.log(`Disconnect from chain`);
await self.api.disconnect();
}
/**
* @description set up a registrar for an account
*/
async setupRegistrar(registrarAccount: KeyringPair) {
// FIXME: Enforce address mapping
const account2registrar: {
[key: string]: string;
} = {
'5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY': '15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5',
'5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty': '14E5nqKAp3oAJcmzgZhUD2RcptBeUBScxKHgJKU4HPNcKVf3',
};
console.log(`[setupRegistrar] Try to add registrar: `);
console.log(registrarAccount.toJson());
console.log(account2registrar[`${registrarAccount.address}`]);
await self.connect();
/**
* check if there is a registrar
*/
let registrars = await self.identityRegistrars();
if (registrars.length > 0) {
for (let registrar of registrars.toArray()) {
const account = (
registrar.value.toJSON() as {
[index: string]: AnyJson;
}
).account;
console.log(`registrar.value: ${account}`);
console.log(`registrarAccount: ${registrarAccount.address}`);
if (`${account}` === `${account2registrar[registrarAccount.address]}`) {
console.log(`registrar already existed, ignore.`);
return;
}
}
}
/**
* create a proposal for registrar
*/
let publicProps = await self.democracyPublicProps();
await sleep(DEFAULT_SLEEP_INTERVAL);
if (`${publicProps.length}` === '0') {
// TODO_CHECK self code isn't being hit, and it's throwing build errors (remove the 2 ts-ignores to highlight the issues)
// @ts-ignore
await self.democracyNotePreimage(self.alice, self.identityAddRegistrar, [
account2registrar[`${registrarAccount.address}`],
]);
await sleep(DEFAULT_SLEEP_INTERVAL);
// @ts-ignore
await self.democracyPropose(self.alice, self.identityAddRegistrar, [
account2registrar[`${registrarAccount.address}`],
]);
await sleep(DEFAULT_SLEEP_INTERVAL);
}
let referendumInfo = await self.democracyReferendumInfoOf();
// Make sure there is at least one referendum in array
while (`${referendumInfo.length}` === '0') {
await sleep(DEFAULT_SLEEP_INTERVAL);
referendumInfo = await self.democracyReferendumInfoOf();
}
// Extract latest referendum from given array
let lastReferendumInfo = referendumInfo[referendumInfo.length - 1];
// Make sure self referendum is `isOngoing` status
while (!lastReferendumInfo.value.isOngoing) {
await sleep(DEFAULT_SLEEP_INTERVAL);
referendumInfo = await self.democracyReferendumInfoOf();
lastReferendumInfo = referendumInfo[referendumInfo.length - 1];
}
// Now we can safely vote self proposal
await self.democracyVote(self.alice);
await sleep(DEFAULT_SLEEP_INTERVAL);
/**
* query the result of registrar
*/
registrars = await self.identityRegistrars();
let waiting = true;
let regIndex = -1;
while (waiting) {
await sleep(DEFAULT_SLEEP_INTERVAL);
registrars = await self.identityRegistrars();
console.log(`Number of existed registrars: ${registrars.length}`);
for (let registrar of registrars) {
regIndex += 1;
const account = (
registrar.value.toJSON() as {
[index: string]: AnyJson;
}
).account;
console.log(`registrar.value: ${registrar}`);
console.log(`registrarAccount: ${registrarAccount.address}`);
if (`${account}` === `${account2registrar[registrarAccount.address]}`) {
waiting = false;
break;
}
}
}
/**
* set registrar fee and query results
*/
const fee = DEFAULT_REGISTRAR_FEE;
await self.identitySetFee(registrarAccount, regIndex, fee);
await sleep(DEFAULT_SLEEP_INTERVAL);
await self.identityRegistrars();
}
}
(async () => {
// we need `as Config` here until we update the encrypted config file to TS
const chain = new Chain(config as Config);
await chain.connect();
await chain.setupRegistrar(chain.alice);
const resp = await chain.proxyProxies(chain.alice.address);
let shouldAddProxy = true;
if (resp && resp[0]) {
for (let tmp of resp[0]) {
const delegateAccount = `${tmp.delegate}`;
console.log(delegateAccount);
if (delegateAccount === '16D2eVuK5SWfwvtFD3gVdBC2nc2BafK31BY6PrbZHBAGew7L') {
shouldAddProxy = false;
break;
}
}
}
if (shouldAddProxy) {
console.log('Should add proxy');
await chain.proxyAddProxy(chain.alice, chain.eve.address);
} else {
console.log('No need to add proxy');
}
await chain.disconnect();
})();