@aztec/aztec 0.0.1-commit.2448fdb → 0.0.1-commit.2606882

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.
Files changed (95) hide show
  1. package/dest/bin/index.js +1 -1
  2. package/dest/cli/aztec_start_action.d.ts +1 -1
  3. package/dest/cli/aztec_start_action.d.ts.map +1 -1
  4. package/dest/cli/aztec_start_action.js +10 -6
  5. package/dest/cli/aztec_start_options.d.ts +2 -2
  6. package/dest/cli/aztec_start_options.d.ts.map +1 -1
  7. package/dest/cli/aztec_start_options.js +2 -15
  8. package/dest/cli/cmds/compile.d.ts +1 -1
  9. package/dest/cli/cmds/compile.d.ts.map +1 -1
  10. package/dest/cli/cmds/compile.js +6 -8
  11. package/dest/cli/cmds/profile.d.ts +1 -1
  12. package/dest/cli/cmds/profile.d.ts.map +1 -1
  13. package/dest/cli/cmds/profile.js +1 -1
  14. package/dest/cli/cmds/profile_gates.d.ts +2 -2
  15. package/dest/cli/cmds/profile_gates.d.ts.map +1 -1
  16. package/dest/cli/cmds/profile_gates.js +21 -3
  17. package/dest/cli/cmds/standby.d.ts +3 -5
  18. package/dest/cli/cmds/standby.d.ts.map +1 -1
  19. package/dest/cli/cmds/standby.js +3 -3
  20. package/dest/cli/cmds/start_bot.d.ts +1 -1
  21. package/dest/cli/cmds/start_bot.d.ts.map +1 -1
  22. package/dest/cli/cmds/start_bot.js +3 -3
  23. package/dest/cli/cmds/start_node.d.ts +1 -1
  24. package/dest/cli/cmds/start_node.d.ts.map +1 -1
  25. package/dest/cli/cmds/start_node.js +5 -9
  26. package/dest/cli/cmds/start_prover_agent.js +2 -2
  27. package/dest/cli/cmds/start_prover_broker.d.ts +1 -1
  28. package/dest/cli/cmds/start_prover_broker.d.ts.map +1 -1
  29. package/dest/cli/cmds/start_prover_broker.js +11 -8
  30. package/dest/cli/cmds/start_txe.d.ts +2 -2
  31. package/dest/cli/cmds/start_txe.d.ts.map +1 -1
  32. package/dest/cli/cmds/start_txe.js +6 -5
  33. package/dest/cli/cmds/utils/warn_if_aztec_version_mismatch.d.ts +1 -1
  34. package/dest/cli/cmds/utils/warn_if_aztec_version_mismatch.d.ts.map +1 -1
  35. package/dest/cli/cmds/utils/warn_if_aztec_version_mismatch.js +2 -3
  36. package/dest/cli/util.js +10 -8
  37. package/dest/local-network/auth_registry.d.ts +5 -0
  38. package/dest/local-network/auth_registry.d.ts.map +1 -0
  39. package/dest/local-network/auth_registry.js +17 -0
  40. package/dest/local-network/banana_fpc.d.ts +3 -2
  41. package/dest/local-network/banana_fpc.d.ts.map +1 -1
  42. package/dest/local-network/banana_fpc.js +11 -7
  43. package/dest/local-network/local-network.d.ts +5 -28
  44. package/dest/local-network/local-network.d.ts.map +1 -1
  45. package/dest/local-network/local-network.js +36 -14
  46. package/dest/testing/anvil_test_watcher.d.ts +15 -3
  47. package/dest/testing/anvil_test_watcher.d.ts.map +1 -1
  48. package/dest/testing/anvil_test_watcher.js +32 -4
  49. package/dest/testing/cheat_codes.d.ts +10 -5
  50. package/dest/testing/cheat_codes.d.ts.map +1 -1
  51. package/dest/testing/cheat_codes.js +40 -6
  52. package/dest/testing/epoch_test_settler.d.ts +1 -1
  53. package/dest/testing/epoch_test_settler.d.ts.map +1 -1
  54. package/dest/testing/epoch_test_settler.js +6 -4
  55. package/dest/testing/index.d.ts +2 -2
  56. package/dest/testing/index.d.ts.map +1 -1
  57. package/package.json +34 -34
  58. package/scripts/add_crate.sh +11 -60
  59. package/scripts/aztec.sh +7 -4
  60. package/scripts/init.sh +5 -5
  61. package/scripts/new.sh +2 -2
  62. package/scripts/setup_workspace.sh +3 -2
  63. package/scripts/templates/blank/contract/Nargo.toml +6 -0
  64. package/scripts/templates/blank/contract/src/main.nr +10 -0
  65. package/scripts/templates/blank/test/Nargo.toml +7 -0
  66. package/scripts/templates/blank/test/src/lib.nr +11 -0
  67. package/scripts/templates/counter/contract/Nargo.toml +7 -0
  68. package/scripts/templates/counter/contract/src/main.nr +48 -0
  69. package/scripts/templates/counter/test/Nargo.toml +7 -0
  70. package/scripts/templates/counter/test/src/lib.nr +32 -0
  71. package/src/bin/index.ts +1 -1
  72. package/src/cli/aztec_start_action.ts +11 -6
  73. package/src/cli/aztec_start_options.ts +9 -23
  74. package/src/cli/cmds/compile.ts +7 -9
  75. package/src/cli/cmds/profile.ts +2 -1
  76. package/src/cli/cmds/profile_gates.ts +20 -4
  77. package/src/cli/cmds/standby.ts +4 -4
  78. package/src/cli/cmds/start_bot.ts +3 -5
  79. package/src/cli/cmds/start_node.ts +5 -9
  80. package/src/cli/cmds/start_prover_agent.ts +2 -2
  81. package/src/cli/cmds/start_prover_broker.ts +10 -11
  82. package/src/cli/cmds/start_txe.ts +7 -5
  83. package/src/cli/cmds/utils/warn_if_aztec_version_mismatch.ts +2 -3
  84. package/src/cli/util.ts +9 -9
  85. package/src/local-network/auth_registry.ts +19 -0
  86. package/src/local-network/banana_fpc.ts +12 -8
  87. package/src/local-network/local-network.ts +39 -13
  88. package/src/testing/anvil_test_watcher.ts +43 -3
  89. package/src/testing/cheat_codes.ts +54 -7
  90. package/src/testing/epoch_test_settler.ts +3 -4
  91. package/src/testing/index.ts +1 -1
  92. package/dest/cli/cmds/start_archiver.d.ts +0 -9
  93. package/dest/cli/cmds/start_archiver.d.ts.map +0 -1
  94. package/dest/cli/cmds/start_archiver.js +0 -48
  95. package/src/cli/cmds/start_archiver.ts +0 -50
@@ -2,6 +2,7 @@ import { EthCheatCodes, RollupCheatCodes } from '@aztec/ethereum/test';
2
2
  import { SlotNumber } from '@aztec/foundation/branded-types';
3
3
  import { createLogger } from '@aztec/foundation/log';
4
4
  import type { DateProvider } from '@aztec/foundation/timer';
5
+ import type { AutomineSequencer } from '@aztec/sequencer-client';
5
6
  import type { AztecNode, AztecNodeDebug } from '@aztec/stdlib/interfaces/client';
6
7
 
7
8
  /**
@@ -18,15 +19,22 @@ export class CheatCodes {
18
19
  public eth: EthCheatCodes,
19
20
  /** Cheat codes for the Aztec Rollup contract on L1. */
20
21
  public rollup: RollupCheatCodes,
22
+ /** When wired, redirects time-warps through the AutomineSequencer queue (test-only). */
23
+ private automine?: AutomineSequencer,
21
24
  ) {}
22
25
 
23
- static async create(rpcUrls: string[], node: AztecNode, dateProvider: DateProvider): Promise<CheatCodes> {
26
+ static async create(
27
+ rpcUrls: string[],
28
+ node: AztecNode,
29
+ dateProvider: DateProvider,
30
+ automine?: AutomineSequencer,
31
+ ): Promise<CheatCodes> {
24
32
  const ethCheatCodes = new EthCheatCodes(rpcUrls, dateProvider);
25
33
  const rollupCheatCodes = new RollupCheatCodes(
26
34
  ethCheatCodes,
27
35
  await node.getNodeInfo().then(n => n.l1ContractAddresses),
28
36
  );
29
- return new CheatCodes(ethCheatCodes, rollupCheatCodes);
37
+ return new CheatCodes(ethCheatCodes, rollupCheatCodes, automine);
30
38
  }
31
39
 
32
40
  /**
@@ -36,7 +44,7 @@ export class CheatCodes {
36
44
  * @param node - The Aztec node used to force an empty block to be mined.
37
45
  * @param targetTimestamp - The target timestamp to warp to (in seconds)
38
46
  */
39
- async warpL2TimeAtLeastTo(node: AztecNodeDebug, targetTimestamp: bigint | number) {
47
+ async warpL2TimeAtLeastTo(node: AztecNode & AztecNodeDebug, targetTimestamp: bigint | number) {
40
48
  const targetBigInt = BigInt(targetTimestamp);
41
49
  const currentTimestamp = BigInt(await this.eth.lastBlockTimestamp());
42
50
 
@@ -46,10 +54,20 @@ export class CheatCodes {
46
54
  );
47
55
  }
48
56
 
57
+ // AutomineSequencer owns time control through its serial queue — delegate to keep warps atomic
58
+ // with respect to any in-flight build, and avoid the mineBlock-loop hack below.
59
+ // `warpTo` internally builds an empty L2 checkpoint, which auto-mines exactly one L1 block at
60
+ // the target slot boundary, so no separate `node.mineBlock()` is needed here.
61
+ if (this.automine) {
62
+ await this.automine.warpTo(Number(targetBigInt));
63
+ return;
64
+ }
65
+
49
66
  const currentSlot = await this.rollup.getSlot();
50
67
  const targetSlot = await this.rollup.getSlotAt(targetBigInt);
51
68
 
52
69
  let effectiveTimestamp = targetBigInt;
70
+ let effectiveTargetSlot = targetSlot;
53
71
 
54
72
  if (targetSlot <= currentSlot) {
55
73
  // Target lands in the same (or earlier) slot — auto-adjust to the next slot's start.
@@ -60,10 +78,32 @@ export class CheatCodes {
60
78
  `Auto-adjusting to start of slot ${nextSlot} at timestamp ${nextSlotTimestamp}.`,
61
79
  );
62
80
  effectiveTimestamp = nextSlotTimestamp;
81
+ effectiveTargetSlot = nextSlot;
63
82
  }
64
83
 
65
84
  await this.eth.warp(effectiveTimestamp, { resetBlockInterval: true });
66
- await node.mineBlock();
85
+
86
+ // The sequencer's polling loop may have a `work()` cycle in flight that captured pre-warp slot/timestamp values
87
+ // just before our warp landed. That cycle would mine an L2 block at the stale slot — the L1 sync prunes such a
88
+ // block from the canonical chain, but it lingers in local world state and the PXE will use it as the anchor for
89
+ // subsequent txs, leading to `expiration_timestamp` values that are already in the past relative to L1. Mine
90
+ // until we observe an L2 block at (or past) the post-warp slot, ensuring the next tx anchors to a fresh block.
91
+ const maxAttempts = 5;
92
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
93
+ await node.mineBlock();
94
+ const blockData = await node.getBlockData('latest');
95
+ const blockSlot = blockData?.header.globalVariables.slotNumber;
96
+ if (blockSlot !== undefined && BigInt(blockSlot) >= BigInt(effectiveTargetSlot)) {
97
+ return;
98
+ }
99
+ this.logger.warn(
100
+ `warpL2TimeAtLeastTo: mined L2 block at slot ${blockSlot}, expected at least ${effectiveTargetSlot}. ` +
101
+ `Retrying mineBlock (attempt ${attempt}/${maxAttempts}).`,
102
+ );
103
+ }
104
+ throw new Error(
105
+ `warpL2TimeAtLeastTo: failed to mine an L2 block at or past slot ${effectiveTargetSlot} after ${maxAttempts} attempts.`,
106
+ );
67
107
  }
68
108
 
69
109
  /**
@@ -73,13 +113,20 @@ export class CheatCodes {
73
113
  * @param node - The Aztec node used to force an empty block to be mined.
74
114
  * @param duration - The duration to advance time by (in seconds)
75
115
  */
76
- async warpL2TimeAtLeastBy(node: AztecNodeDebug, duration: bigint | number) {
116
+ async warpL2TimeAtLeastBy(node: AztecNode & AztecNodeDebug, duration: bigint | number) {
77
117
  if (BigInt(duration) <= 0n) {
78
118
  throw new Error(`warpL2TimeAtLeastBy: duration must be positive, got ${duration} seconds.`);
79
119
  }
80
120
 
81
- const currentTimestamp = await this.eth.lastBlockTimestamp();
82
- const targetTimestamp = BigInt(currentTimestamp) + BigInt(duration);
121
+ // Advance relative to whichever clock leads. A live sequencer mines L2 blocks at slot boundaries that can run
122
+ // ahead of anvil's L1 timestamp, so basing the target on L1 alone would advance the L2 timestamp by less than
123
+ // `duration`. Anchoring to the latest L2 block timestamp when it leads guarantees the post-warp L2 block is at
124
+ // least `duration` ahead of the current one.
125
+ const currentL1Timestamp = BigInt(await this.eth.lastBlockTimestamp());
126
+ const latestBlockData = await node.getBlockData('latest');
127
+ const latestL2Timestamp = latestBlockData ? BigInt(latestBlockData.header.globalVariables.timestamp) : 0n;
128
+ const baseTimestamp = latestL2Timestamp > currentL1Timestamp ? latestL2Timestamp : currentL1Timestamp;
129
+ const targetTimestamp = baseTimestamp + BigInt(duration);
83
130
  await this.warpL2TimeAtLeastTo(node, targetTimestamp);
84
131
  }
85
132
  }
@@ -31,8 +31,7 @@ export class EpochTestSettler {
31
31
  }
32
32
 
33
33
  async handleEpochReadyToProve(epoch: EpochNumber): Promise<boolean> {
34
- const checkpointedBlocks = await this.l2BlockSource.getCheckpointedBlocksForEpoch(epoch);
35
- const blocks = checkpointedBlocks.map(b => b.block);
34
+ const blocks = await this.l2BlockSource.getBlocks({ epoch, onlyCheckpointed: true });
36
35
  this.log.info(
37
36
  `Settling epoch ${epoch} with blocks ${blocks[0]?.header.getBlockNumber()} to ${blocks.at(-1)?.header.getBlockNumber()}`,
38
37
  { blocks: blocks.map(b => b.toBlockInfo()) },
@@ -53,12 +52,12 @@ export class EpochTestSettler {
53
52
 
54
53
  const outHash = computeEpochOutHash(messagesInEpoch);
55
54
  if (!outHash.isZero()) {
56
- await this.rollupCheatCodes.insertOutbox(epoch, outHash.toBigInt());
55
+ await this.rollupCheatCodes.insertOutbox(epoch, messagesInEpoch.length, outHash.toBigInt());
57
56
  } else {
58
57
  this.log.info(`No L2 to L1 messages in epoch ${epoch}`);
59
58
  }
60
59
 
61
- const lastCheckpoint = checkpointedBlocks.at(-1)?.checkpointNumber;
60
+ const lastCheckpoint = blocks.at(-1)?.checkpointNumber;
62
61
  if (lastCheckpoint !== undefined) {
63
62
  await this.rollupCheatCodes.markAsProven(lastCheckpoint);
64
63
  } else {
@@ -1,4 +1,4 @@
1
- export { AnvilTestWatcher } from './anvil_test_watcher.js';
1
+ export { AnvilTestWatcher, type AnvilTestWatcherOpts } from './anvil_test_watcher.js';
2
2
  export { EthCheatCodes, RollupCheatCodes } from '@aztec/ethereum/test';
3
3
  export { CheatCodes } from './cheat_codes.js';
4
4
  export { EpochTestSettler } from './epoch_test_settler.js';
@@ -1,9 +0,0 @@
1
- import { type ArchiverConfig } from '@aztec/archiver';
2
- import type { NamespacedApiHandlers } from '@aztec/foundation/json-rpc/server';
3
- import { type DataStoreConfig } from '@aztec/kv-store/config';
4
- export type { ArchiverConfig, DataStoreConfig };
5
- /** Starts a standalone archiver. */
6
- export declare function startArchiver(options: any, signalHandlers: (() => Promise<void>)[], services: NamespacedApiHandlers): Promise<{
7
- config: ArchiverConfig & DataStoreConfig;
8
- }>;
9
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhcnRfYXJjaGl2ZXIuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9jbGkvY21kcy9zdGFydF9hcmNoaXZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsS0FBSyxjQUFjLEVBQW9FLE1BQU0saUJBQWlCLENBQUM7QUFJeEgsT0FBTyxLQUFLLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUMvRSxPQUFPLEVBQUUsS0FBSyxlQUFlLEVBQXNCLE1BQU0sd0JBQXdCLENBQUM7QUFNbEYsWUFBWSxFQUFFLGNBQWMsRUFBRSxlQUFlLEVBQUUsQ0FBQztBQUVoRCxvQ0FBb0M7QUFDcEMsd0JBQXNCLGFBQWEsQ0FDakMsT0FBTyxFQUFFLEdBQUcsRUFDWixjQUFjLEVBQUUsQ0FBQyxNQUFNLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxFQUFFLEVBQ3ZDLFFBQVEsRUFBRSxxQkFBcUIsR0FDOUIsT0FBTyxDQUFDO0lBQUUsTUFBTSxFQUFFLGNBQWMsR0FBRyxlQUFlLENBQUE7Q0FBRSxDQUFDLENBK0J2RCJ9
@@ -1 +0,0 @@
1
- {"version":3,"file":"start_archiver.d.ts","sourceRoot":"","sources":["../../../src/cli/cmds/start_archiver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAoE,MAAM,iBAAiB,CAAC;AAIxH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC/E,OAAO,EAAE,KAAK,eAAe,EAAsB,MAAM,wBAAwB,CAAC;AAMlF,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,CAAC;AAEhD,oCAAoC;AACpC,wBAAsB,aAAa,CACjC,OAAO,EAAE,GAAG,EACZ,cAAc,EAAE,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,EACvC,QAAQ,EAAE,qBAAqB,GAC9B,OAAO,CAAC;IAAE,MAAM,EAAE,cAAc,GAAG,eAAe,CAAA;CAAE,CAAC,CA+BvD"}
@@ -1,48 +0,0 @@
1
- import { archiverConfigMappings, createArchiver, getArchiverConfigFromEnv } from '@aztec/archiver';
2
- import { createLogger } from '@aztec/aztec.js/log';
3
- import { blobClientConfigMapping, createBlobClient } from '@aztec/blob-client/client';
4
- import { getL1Config } from '@aztec/cli/config';
5
- import { dataConfigMappings } from '@aztec/kv-store/config';
6
- import { ArchiverApiSchema } from '@aztec/stdlib/interfaces/server';
7
- import { getConfigEnvVars as getTelemetryClientConfig, initTelemetryClient } from '@aztec/telemetry-client';
8
- import { extractRelevantOptions } from '../util.js';
9
- /** Starts a standalone archiver. */ export async function startArchiver(options, signalHandlers, services) {
10
- const envConfig = getArchiverConfigFromEnv();
11
- const cliOptions = extractRelevantOptions(options, {
12
- ...archiverConfigMappings,
13
- ...dataConfigMappings,
14
- ...blobClientConfigMapping
15
- }, 'archiver');
16
- let archiverConfig = {
17
- ...envConfig,
18
- ...cliOptions
19
- };
20
- archiverConfig.dataStoreMapSizeKb = archiverConfig.archiverStoreMapSizeKb ?? archiverConfig.dataStoreMapSizeKb;
21
- if (!archiverConfig.l1Contracts.registryAddress || archiverConfig.l1Contracts.registryAddress.isZero()) {
22
- throw new Error('L1 registry address is required to start an Archiver');
23
- }
24
- const { addresses, config: l1Config } = await getL1Config(archiverConfig.l1Contracts.registryAddress, archiverConfig.l1RpcUrls, archiverConfig.l1ChainId);
25
- archiverConfig.l1Contracts = addresses;
26
- archiverConfig = {
27
- ...archiverConfig,
28
- ...l1Config
29
- };
30
- const telemetry = await initTelemetryClient(getTelemetryClientConfig());
31
- const blobClient = createBlobClient(archiverConfig, {
32
- logger: createLogger('archiver:blob-client:client')
33
- });
34
- const archiver = await createArchiver(archiverConfig, {
35
- telemetry,
36
- blobClient
37
- }, {
38
- blockUntilSync: true
39
- });
40
- services.archiver = [
41
- archiver,
42
- ArchiverApiSchema
43
- ];
44
- signalHandlers.push(archiver.stop);
45
- return {
46
- config: archiverConfig
47
- };
48
- }
@@ -1,50 +0,0 @@
1
- import { type ArchiverConfig, archiverConfigMappings, createArchiver, getArchiverConfigFromEnv } from '@aztec/archiver';
2
- import { createLogger } from '@aztec/aztec.js/log';
3
- import { type BlobClientConfig, blobClientConfigMapping, createBlobClient } from '@aztec/blob-client/client';
4
- import { getL1Config } from '@aztec/cli/config';
5
- import type { NamespacedApiHandlers } from '@aztec/foundation/json-rpc/server';
6
- import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config';
7
- import { ArchiverApiSchema } from '@aztec/stdlib/interfaces/server';
8
- import { getConfigEnvVars as getTelemetryClientConfig, initTelemetryClient } from '@aztec/telemetry-client';
9
-
10
- import { extractRelevantOptions } from '../util.js';
11
-
12
- export type { ArchiverConfig, DataStoreConfig };
13
-
14
- /** Starts a standalone archiver. */
15
- export async function startArchiver(
16
- options: any,
17
- signalHandlers: (() => Promise<void>)[],
18
- services: NamespacedApiHandlers,
19
- ): Promise<{ config: ArchiverConfig & DataStoreConfig }> {
20
- const envConfig = getArchiverConfigFromEnv();
21
- const cliOptions = extractRelevantOptions<ArchiverConfig & DataStoreConfig & BlobClientConfig>(
22
- options,
23
- { ...archiverConfigMappings, ...dataConfigMappings, ...blobClientConfigMapping },
24
- 'archiver',
25
- );
26
-
27
- let archiverConfig = { ...envConfig, ...cliOptions };
28
- archiverConfig.dataStoreMapSizeKb = archiverConfig.archiverStoreMapSizeKb ?? archiverConfig.dataStoreMapSizeKb;
29
-
30
- if (!archiverConfig.l1Contracts.registryAddress || archiverConfig.l1Contracts.registryAddress.isZero()) {
31
- throw new Error('L1 registry address is required to start an Archiver');
32
- }
33
-
34
- const { addresses, config: l1Config } = await getL1Config(
35
- archiverConfig.l1Contracts.registryAddress,
36
- archiverConfig.l1RpcUrls,
37
- archiverConfig.l1ChainId,
38
- );
39
-
40
- archiverConfig.l1Contracts = addresses;
41
- archiverConfig = { ...archiverConfig, ...l1Config };
42
-
43
- const telemetry = await initTelemetryClient(getTelemetryClientConfig());
44
- const blobClient = createBlobClient(archiverConfig, { logger: createLogger('archiver:blob-client:client') });
45
- const archiver = await createArchiver(archiverConfig, { telemetry, blobClient }, { blockUntilSync: true });
46
- services.archiver = [archiver, ArchiverApiSchema];
47
- signalHandlers.push(archiver.stop);
48
-
49
- return { config: archiverConfig };
50
- }