@ar.io/sdk 3.19.0-alpha.1 → 3.19.0-alpha.11
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/README.md +155 -7
- package/bundles/web.bundle.min.js +88 -72
- package/lib/cjs/cli/cli.js +14 -1
- package/lib/cjs/cli/commands/antCommands.js +82 -16
- package/lib/cjs/cli/options.js +36 -1
- package/lib/cjs/cli/utils.js +25 -11
- package/lib/cjs/common/ant.js +452 -12
- package/lib/cjs/common/io.js +2 -2
- package/lib/cjs/constants.js +2 -1
- package/lib/cjs/types/ant.js +18 -0
- package/lib/cjs/utils/ant.js +4 -0
- package/lib/cjs/utils/ao.js +3 -1
- package/lib/cjs/utils/processes.js +1 -1
- package/lib/cjs/version.js +1 -1
- package/lib/esm/cli/cli.js +16 -3
- package/lib/esm/cli/commands/antCommands.js +81 -17
- package/lib/esm/cli/options.js +35 -0
- package/lib/esm/cli/utils.js +24 -11
- package/lib/esm/common/ant.js +453 -13
- package/lib/esm/common/io.js +2 -2
- package/lib/esm/constants.js +1 -0
- package/lib/esm/types/ant.js +18 -0
- package/lib/esm/utils/ant.js +4 -0
- package/lib/esm/utils/ao.js +3 -1
- package/lib/esm/utils/processes.js +1 -1
- package/lib/esm/version.js +1 -1
- package/lib/types/cli/commands/antCommands.d.ts +12 -0
- package/lib/types/cli/options.d.ts +25 -0
- package/lib/types/cli/types.d.ts +1 -0
- package/lib/types/cli/utils.d.ts +15 -2
- package/lib/types/common/ant.d.ts +167 -6
- package/lib/types/common/io.d.ts +1 -1
- package/lib/types/constants.d.ts +1 -0
- package/lib/types/types/ant.d.ts +131 -9
- package/lib/types/types/common.d.ts +31 -0
- package/lib/types/utils/ant.d.ts +4 -0
- package/lib/types/utils/ao.d.ts +2 -1
- package/lib/types/utils/processes.d.ts +1 -1
- package/lib/types/version.d.ts +1 -1
- package/package.json +3 -2
package/lib/esm/cli/cli.js
CHANGED
|
@@ -19,12 +19,12 @@ import { program } from 'commander';
|
|
|
19
19
|
import { AOProcess, spawnANT } from '../node/index.js';
|
|
20
20
|
import { mARIOToken } from '../types/token.js';
|
|
21
21
|
import { version } from '../version.js';
|
|
22
|
-
import { setAntBaseNameCLICommand, setAntRecordCLICommand, } from './commands/antCommands.js';
|
|
22
|
+
import { setAntBaseNameCLICommand, setAntRecordCLICommand, setAntUndernameCLICommand, transferRecordOwnershipCLICommand, upgradeAntCLICommand, } from './commands/antCommands.js';
|
|
23
23
|
import { buyRecordCLICommand, extendLeaseCLICommand, increaseUndernameLimitCLICommand, requestPrimaryNameCLICommand, setPrimaryNameCLICommand, upgradeRecordCLICommand, } from './commands/arnsPurchaseCommands.js';
|
|
24
24
|
import { cancelWithdrawal, decreaseDelegateStake, decreaseOperatorStake, delegateStake, increaseOperatorStake, instantWithdrawal, joinNetwork, leaveNetwork, redelegateStake, saveObservations, updateGatewaySettings, } from './commands/gatewayWriteCommands.js';
|
|
25
25
|
import { getAllGatewayVaults, getAllowedDelegates, getArNSRecord, getArNSReservedName, getArNSReturnedName, getCostDetails, getDelegations, getEpoch, getGateway, getGatewayDelegates, getGatewayVaults, getPrescribedNames, getPrescribedObservers, getPrimaryName, getTokenCost, getVault, listAllDelegatesCLICommand, listAntsForAddress, listArNSRecords, listArNSRecordsForAddress, listArNSReservedNames, listArNSReturnedNames, listGateways, resolveArNSName, } from './commands/readCommands.js';
|
|
26
26
|
import { createVaultCLICommand, extendVaultCLICommand, increaseVaultCLICommand, revokeVaultCLICommand, transferCLICommand, vaultedTransferCLICommand, } from './commands/transfer.js';
|
|
27
|
-
import { addressAndVaultIdOptions, antStateOptions, arnsPurchaseOptions, buyRecordOptions, decreaseDelegateStakeOptions, delegateStakeOptions, epochOptions, getVaultOptions, globalOptions, joinNetworkOptions, operatorStakeOptions, optionMap, paginationAddressOptions, paginationOptions, redelegateStakeOptions, setAntBaseNameOptions, setAntUndernameOptions, tokenCostOptions, transferOptions, updateGatewaySettingsOptions, vaultedTransferOptions, writeActionOptions, } from './options.js';
|
|
27
|
+
import { addressAndVaultIdOptions, antStateOptions, arnsPurchaseOptions, buyRecordOptions, decreaseDelegateStakeOptions, delegateStakeOptions, epochOptions, getVaultOptions, globalOptions, joinNetworkOptions, operatorStakeOptions, optionMap, paginationAddressOptions, paginationOptions, redelegateStakeOptions, setAntBaseNameOptions, setAntUndernameOptions, tokenCostOptions, transferOptions, transferRecordOwnershipOptions, updateGatewaySettingsOptions, upgradeAntOptions, vaultedTransferOptions, writeActionOptions, } from './options.js';
|
|
28
28
|
import { applyOptions, arioProcessIdFromOptions, assertConfirmationPrompt, customTagsFromOptions, epochInputFromOptions, formatARIOWithCommas, getANTStateFromOptions, getLoggerFromOptions, makeCommand, paginationParamsFromOptions, readANTFromOptions, readARIOFromOptions, requiredAddressFromOptions, requiredAoSignerFromOptions, requiredProcessIdFromOptions, requiredStringArrayFromOptions, requiredStringFromOptions, writeANTFromOptions, } from './utils.js';
|
|
29
29
|
applyOptions(program
|
|
30
30
|
.name('ar.io')
|
|
@@ -534,6 +534,7 @@ makeCommand({
|
|
|
534
534
|
state,
|
|
535
535
|
signer: requiredAoSignerFromOptions(options),
|
|
536
536
|
logger: getLoggerFromOptions(options),
|
|
537
|
+
...(options.module !== undefined ? { module: options.module } : {}),
|
|
537
538
|
});
|
|
538
539
|
return {
|
|
539
540
|
processId: antProcessId,
|
|
@@ -630,7 +631,19 @@ makeCommand({
|
|
|
630
631
|
name: 'set-ant-undername',
|
|
631
632
|
description: 'Set an undername of an ANT process',
|
|
632
633
|
options: setAntUndernameOptions,
|
|
633
|
-
action:
|
|
634
|
+
action: setAntUndernameCLICommand,
|
|
635
|
+
});
|
|
636
|
+
makeCommand({
|
|
637
|
+
name: 'transfer-record',
|
|
638
|
+
description: 'Transfer ownership of a specific record (undername) to another address',
|
|
639
|
+
options: transferRecordOwnershipOptions,
|
|
640
|
+
action: transferRecordOwnershipCLICommand,
|
|
641
|
+
});
|
|
642
|
+
makeCommand({
|
|
643
|
+
name: 'upgrade-ant',
|
|
644
|
+
description: 'Upgrade an ANT by forking it to the latest version and reassigning names',
|
|
645
|
+
options: upgradeAntOptions,
|
|
646
|
+
action: upgradeAntCLICommand,
|
|
634
647
|
});
|
|
635
648
|
makeCommand({
|
|
636
649
|
name: 'set-ant-ticker',
|
|
@@ -1,42 +1,106 @@
|
|
|
1
|
-
import { assertConfirmationPrompt, customTagsFromOptions, defaultTtlSecondsCLI, requiredStringFromOptions, writeANTFromOptions, } from '../utils.js';
|
|
1
|
+
import { antRecordMetadataFromOptions, arioProcessIdFromOptions, assertConfirmationPrompt, booleanFromOptions, customTagsFromOptions, defaultTtlSecondsCLI, readARIOFromOptions, requiredStringFromOptions, stringArrayFromOptions, writeANTFromOptions, } from '../utils.js';
|
|
2
2
|
/** @deprecated -- use set-ant-base-name and set-ant-undername */
|
|
3
3
|
export async function setAntRecordCLICommand(o) {
|
|
4
4
|
const ttlSeconds = +(o.ttlSeconds ?? defaultTtlSecondsCLI);
|
|
5
5
|
const undername = requiredStringFromOptions(o, 'undername');
|
|
6
6
|
const transactionId = requiredStringFromOptions(o, 'transactionId');
|
|
7
7
|
const writeAnt = writeANTFromOptions(o);
|
|
8
|
-
|
|
9
|
-
await assertConfirmationPrompt(`Are you sure you want to set this record on the ANT process ${writeAnt.processId}?\n${JSON.stringify({ undername, transactionId, ttlSeconds }, null, 2)}`, o);
|
|
10
|
-
}
|
|
11
|
-
return writeANTFromOptions(o).setRecord({
|
|
8
|
+
const recordParams = {
|
|
12
9
|
undername,
|
|
13
10
|
transactionId,
|
|
14
11
|
ttlSeconds,
|
|
15
|
-
|
|
12
|
+
...antRecordMetadataFromOptions(o),
|
|
13
|
+
};
|
|
14
|
+
if (!o.skipConfirmation) {
|
|
15
|
+
await assertConfirmationPrompt(`Are you sure you want to set this record on the ANT process ${writeAnt.processId}?\n${JSON.stringify(recordParams, null, 2)}`, o);
|
|
16
|
+
}
|
|
17
|
+
return writeANTFromOptions(o).setRecord(recordParams, customTagsFromOptions(o));
|
|
16
18
|
}
|
|
17
19
|
export async function setAntBaseNameCLICommand(o) {
|
|
18
20
|
const ttlSeconds = +(o.ttlSeconds ?? defaultTtlSecondsCLI);
|
|
19
21
|
const transactionId = requiredStringFromOptions(o, 'transactionId');
|
|
22
|
+
const params = {
|
|
23
|
+
transactionId,
|
|
24
|
+
ttlSeconds,
|
|
25
|
+
...antRecordMetadataFromOptions(o),
|
|
26
|
+
};
|
|
20
27
|
const writeAnt = writeANTFromOptions(o);
|
|
21
28
|
if (!o.skipConfirmation) {
|
|
22
|
-
await assertConfirmationPrompt(`Are you sure you want to set this base name on the ANT process ${writeAnt.processId}?\n${JSON.stringify(
|
|
29
|
+
await assertConfirmationPrompt(`Are you sure you want to set this base name on the ANT process ${writeAnt.processId}?\n${JSON.stringify(params, null, 2)}`, o);
|
|
23
30
|
}
|
|
24
|
-
return writeANTFromOptions(o).setBaseNameRecord(
|
|
25
|
-
transactionId,
|
|
26
|
-
ttlSeconds,
|
|
27
|
-
}, customTagsFromOptions(o));
|
|
31
|
+
return writeANTFromOptions(o).setBaseNameRecord(params, customTagsFromOptions(o));
|
|
28
32
|
}
|
|
29
33
|
export async function setAntUndernameCLICommand(o) {
|
|
30
34
|
const ttlSeconds = +(o.ttlSeconds ?? defaultTtlSecondsCLI);
|
|
31
35
|
const undername = requiredStringFromOptions(o, 'undername');
|
|
32
36
|
const transactionId = requiredStringFromOptions(o, 'transactionId');
|
|
33
|
-
const
|
|
34
|
-
if (!o.skipConfirmation) {
|
|
35
|
-
await assertConfirmationPrompt(`Are you sure you want to set this undername on the ANT process ${writeAnt.processId}?\n${JSON.stringify({ undername, transactionId, ttlSeconds }, null, 2)}`, o);
|
|
36
|
-
}
|
|
37
|
-
return writeANTFromOptions(o).setUndernameRecord({
|
|
37
|
+
const params = {
|
|
38
38
|
undername,
|
|
39
39
|
transactionId,
|
|
40
40
|
ttlSeconds,
|
|
41
|
-
|
|
41
|
+
...antRecordMetadataFromOptions(o),
|
|
42
|
+
};
|
|
43
|
+
const writeAnt = writeANTFromOptions(o);
|
|
44
|
+
if (!o.skipConfirmation) {
|
|
45
|
+
await assertConfirmationPrompt(`Are you sure you want to set this undername on the ANT process ${writeAnt.processId}?\n${JSON.stringify(params, null, 2)}`, o);
|
|
46
|
+
}
|
|
47
|
+
return writeANTFromOptions(o).setUndernameRecord(params, customTagsFromOptions(o));
|
|
48
|
+
}
|
|
49
|
+
export async function transferRecordOwnershipCLICommand(o) {
|
|
50
|
+
const undername = requiredStringFromOptions(o, 'undername');
|
|
51
|
+
const recipient = requiredStringFromOptions(o, 'recipient');
|
|
52
|
+
const writeAnt = writeANTFromOptions(o);
|
|
53
|
+
if (!o.skipConfirmation) {
|
|
54
|
+
await assertConfirmationPrompt(`Are you sure you want to transfer ownership of "${undername}" to "${recipient}" on ANT process ${writeAnt.processId}?\n${JSON.stringify({ undername, recipient }, null, 2)}`, o);
|
|
55
|
+
}
|
|
56
|
+
return writeANTFromOptions(o).transferRecord({ undername, recipient }, customTagsFromOptions(o));
|
|
57
|
+
}
|
|
58
|
+
export async function upgradeAntCLICommand(o) {
|
|
59
|
+
const writeAnt = writeANTFromOptions(o);
|
|
60
|
+
const arioProcessId = arioProcessIdFromOptions(o);
|
|
61
|
+
const ario = readARIOFromOptions(o);
|
|
62
|
+
const reassignAffiliatedNames = booleanFromOptions(o, 'reassignAffiliatedNames');
|
|
63
|
+
const names = stringArrayFromOptions(o, 'names') || [];
|
|
64
|
+
if (reassignAffiliatedNames) {
|
|
65
|
+
// Fetch all ArNS records that point to this ANT process
|
|
66
|
+
const allRecords = await ario.getArNSRecords({
|
|
67
|
+
filters: {
|
|
68
|
+
processId: writeAnt.processId,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
// Filter records that belong to this ANT
|
|
72
|
+
const affiliatedNames = allRecords.items.map((record) => record.name);
|
|
73
|
+
names.push(...affiliatedNames);
|
|
74
|
+
}
|
|
75
|
+
if (names.length === 0) {
|
|
76
|
+
throw new Error('No names to reassign');
|
|
77
|
+
}
|
|
78
|
+
if (!o.skipConfirmation) {
|
|
79
|
+
await assertConfirmationPrompt(`Upgrade all names affiliated with this ANT on ARIO process?\n` +
|
|
80
|
+
`ARIO Process ID: ${arioProcessId}\n` +
|
|
81
|
+
`ANT Process ID: ${writeAnt.processId}\n` +
|
|
82
|
+
`Names that will be reassigned (${names.length}): ${names.join(', ')}`, o);
|
|
83
|
+
}
|
|
84
|
+
const result = reassignAffiliatedNames
|
|
85
|
+
? await writeANTFromOptions(o).upgrade({
|
|
86
|
+
reassignAffiliatedNames,
|
|
87
|
+
arioProcessId,
|
|
88
|
+
})
|
|
89
|
+
: await writeANTFromOptions(o).upgrade({
|
|
90
|
+
names,
|
|
91
|
+
arioProcessId,
|
|
92
|
+
});
|
|
93
|
+
// Serialize error objects for JSON compatibility
|
|
94
|
+
const serializedFailedReassignedNames = {};
|
|
95
|
+
for (const [name, failure] of Object.entries(result.failedReassignedNames)) {
|
|
96
|
+
serializedFailedReassignedNames[name] = {
|
|
97
|
+
id: failure.id,
|
|
98
|
+
error: failure.error.message,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
forkedProcessId: result.forkedProcessId,
|
|
103
|
+
reassignedNames: result.reassignedNames,
|
|
104
|
+
failedReassignedNames: serializedFailedReassignedNames,
|
|
105
|
+
};
|
|
42
106
|
}
|
package/lib/esm/cli/options.js
CHANGED
|
@@ -251,6 +251,14 @@ export const optionMap = {
|
|
|
251
251
|
description: 'The keywords for the ANT',
|
|
252
252
|
type: 'array',
|
|
253
253
|
},
|
|
254
|
+
owner: {
|
|
255
|
+
alias: '--owner <owner>',
|
|
256
|
+
description: 'The owner address for the record',
|
|
257
|
+
},
|
|
258
|
+
displayName: {
|
|
259
|
+
alias: '--display-name <displayName>',
|
|
260
|
+
description: 'The display name for the record',
|
|
261
|
+
},
|
|
254
262
|
names: {
|
|
255
263
|
alias: '--names <names...>',
|
|
256
264
|
description: 'The names to interact with',
|
|
@@ -286,6 +294,10 @@ export const optionMap = {
|
|
|
286
294
|
alias: '--logo <logo>',
|
|
287
295
|
description: 'The ANT logo',
|
|
288
296
|
},
|
|
297
|
+
module: {
|
|
298
|
+
alias: '--module <module>',
|
|
299
|
+
description: 'The module ID to use for spawning the ANT process',
|
|
300
|
+
},
|
|
289
301
|
token: {
|
|
290
302
|
alias: '-t, --token <type>',
|
|
291
303
|
description: 'Crypto token type for wallet or action',
|
|
@@ -300,6 +312,11 @@ export const optionMap = {
|
|
|
300
312
|
alias: '--referrer <referrer>',
|
|
301
313
|
description: 'The referrer for ArNS purchase tracking',
|
|
302
314
|
},
|
|
315
|
+
reassignAffiliatedNames: {
|
|
316
|
+
alias: '--reassign-affiliated-names',
|
|
317
|
+
description: 'Reassign all affiliated names to the new process',
|
|
318
|
+
type: 'boolean',
|
|
319
|
+
},
|
|
303
320
|
};
|
|
304
321
|
export const walletOptions = [
|
|
305
322
|
optionMap.walletFile,
|
|
@@ -408,14 +425,32 @@ export const antStateOptions = [
|
|
|
408
425
|
optionMap.controllers,
|
|
409
426
|
optionMap.ttlSeconds,
|
|
410
427
|
optionMap.logo,
|
|
428
|
+
optionMap.module,
|
|
411
429
|
];
|
|
412
430
|
export const setAntBaseNameOptions = [
|
|
413
431
|
optionMap.processId,
|
|
414
432
|
optionMap.transactionId,
|
|
415
433
|
optionMap.ttlSeconds,
|
|
434
|
+
optionMap.owner,
|
|
435
|
+
optionMap.displayName,
|
|
436
|
+
optionMap.logo,
|
|
437
|
+
optionMap.description,
|
|
438
|
+
optionMap.keywords,
|
|
416
439
|
...writeActionOptions,
|
|
417
440
|
];
|
|
418
441
|
export const setAntUndernameOptions = [
|
|
419
442
|
...setAntBaseNameOptions,
|
|
420
443
|
optionMap.undername,
|
|
421
444
|
];
|
|
445
|
+
export const upgradeAntOptions = [
|
|
446
|
+
optionMap.processId,
|
|
447
|
+
optionMap.names,
|
|
448
|
+
optionMap.reassignAffiliatedNames,
|
|
449
|
+
...writeActionOptions,
|
|
450
|
+
];
|
|
451
|
+
export const transferRecordOwnershipOptions = [
|
|
452
|
+
optionMap.processId,
|
|
453
|
+
optionMap.undername,
|
|
454
|
+
optionMap.recipient,
|
|
455
|
+
...writeActionOptions,
|
|
456
|
+
];
|
package/lib/esm/cli/utils.js
CHANGED
|
@@ -18,7 +18,7 @@ import { connect } from '@permaweb/aoconnect';
|
|
|
18
18
|
import { program } from 'commander';
|
|
19
19
|
import { readFileSync } from 'fs';
|
|
20
20
|
import prompts from 'prompts';
|
|
21
|
-
import { ANT, ANTRegistry, ANT_REGISTRY_ID, AOProcess, ARIO, ARIOToken, ARIO_DEVNET_PROCESS_ID, ARIO_MAINNET_PROCESS_ID, ARIO_TESTNET_PROCESS_ID, ArweaveSigner, Logger, createAoSigner, fromB64Url, fundFromOptions, initANTStateForAddress, isValidFundFrom, isValidIntent, mARIOToken, sha256B64Url, validIntents, } from '../node/index.js';
|
|
21
|
+
import { ANT, ANTRegistry, ANT_REGISTRY_ID, ANT_REGISTRY_TESTNET_ID, AOProcess, ARIO, ARIOToken, ARIO_DEVNET_PROCESS_ID, ARIO_MAINNET_PROCESS_ID, ARIO_TESTNET_PROCESS_ID, ArweaveSigner, Logger, createAoSigner, fromB64Url, fundFromOptions, initANTStateForAddress, isValidFundFrom, isValidIntent, mARIOToken, sha256B64Url, validIntents, } from '../node/index.js';
|
|
22
22
|
import { globalOptions } from './options.js';
|
|
23
23
|
export const defaultTtlSecondsCLI = 3600;
|
|
24
24
|
export function stringifyJsonForCLIDisplay(json) {
|
|
@@ -78,6 +78,15 @@ export function arioProcessIdFromOptions({ arioProcessId, devnet, testnet, }) {
|
|
|
78
78
|
}
|
|
79
79
|
return ARIO_MAINNET_PROCESS_ID;
|
|
80
80
|
}
|
|
81
|
+
export function antRegistryIdFromOptions({ antRegistryProcessId, testnet, }) {
|
|
82
|
+
if (antRegistryProcessId !== undefined) {
|
|
83
|
+
return antRegistryProcessId;
|
|
84
|
+
}
|
|
85
|
+
if (testnet) {
|
|
86
|
+
return ANT_REGISTRY_TESTNET_ID;
|
|
87
|
+
}
|
|
88
|
+
return ANT_REGISTRY_ID;
|
|
89
|
+
}
|
|
81
90
|
function walletFromOptions({ privateKey, walletFile, }) {
|
|
82
91
|
if (privateKey !== undefined) {
|
|
83
92
|
return JSON.parse(privateKey);
|
|
@@ -125,18 +134,9 @@ export function readARIOFromOptions(options) {
|
|
|
125
134
|
paymentUrl: options.paymentUrl,
|
|
126
135
|
});
|
|
127
136
|
}
|
|
128
|
-
export function ANTRegistryProcessFromOptions(options) {
|
|
129
|
-
return new AOProcess({
|
|
130
|
-
processId: options.antRegistryProcessId ?? ANT_REGISTRY_ID,
|
|
131
|
-
ao: connect({
|
|
132
|
-
MODE: 'legacy',
|
|
133
|
-
CU_URL: options.cuUrl,
|
|
134
|
-
}),
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
137
|
export function readANTRegistryFromOptions(options) {
|
|
138
138
|
return ANTRegistry.init({
|
|
139
|
-
process:
|
|
139
|
+
process: aoProcessFromOptions(options),
|
|
140
140
|
hyperbeamUrl: options.hyperbeamUrl,
|
|
141
141
|
});
|
|
142
142
|
}
|
|
@@ -507,3 +507,16 @@ export function assertLockLengthInRange(lockLengthMs, assertMin = true) {
|
|
|
507
507
|
throw new Error(`Lock length must be at least 14 days (1209600000 ms). Provided lock length: ${lockLengthMs} ms`);
|
|
508
508
|
}
|
|
509
509
|
}
|
|
510
|
+
export function antRecordMetadataFromOptions(options) {
|
|
511
|
+
return {
|
|
512
|
+
...(options.owner != null &&
|
|
513
|
+
options.owner !== '' && { owner: options.owner }),
|
|
514
|
+
...(options.displayName != null &&
|
|
515
|
+
options.displayName !== '' && { displayName: options.displayName }),
|
|
516
|
+
...(options.logo != null && options.logo !== '' && { logo: options.logo }),
|
|
517
|
+
...(options.description != null &&
|
|
518
|
+
options.description !== '' && { description: options.description }),
|
|
519
|
+
...(options.keywords != null &&
|
|
520
|
+
options.keywords.length > 0 && { keywords: options.keywords }),
|
|
521
|
+
};
|
|
522
|
+
}
|