@veil-cash/sdk 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,231 @@
1
+ # Veil SDK Reference
2
+
3
+ Detailed payload spec and SDK function signatures for agent integration.
4
+
5
+ ## Unsigned payload spec
6
+
7
+ All `--unsigned` CLI output and programmatic builders target **Base mainnet** (chain ID 8453).
8
+
9
+ ### Payload shape
10
+
11
+ ```json
12
+ {
13
+ "to": "0x...",
14
+ "data": "0x...",
15
+ "value": "0",
16
+ "chainId": 8453
17
+ }
18
+ ```
19
+
20
+ ### Field reference
21
+
22
+ | Field | Type | Required | Description |
23
+ |-------|------|----------|-------------|
24
+ | `to` | `string` | yes | Target contract address (`0x` + 40 hex chars) |
25
+ | `data` | `string` | yes | ABI-encoded calldata (`0x` + hex) |
26
+ | `value` | `string` | yes | ETH amount in wei as a **string** (e.g. `"0"`, `"1000000000000000000"` for 1 ETH) |
27
+ | `chainId` | `number` | yes | `8453` (Base) |
28
+
29
+ `value` must always be a string. For register and USDC transactions it is `"0"`.
30
+ For ETH deposits it equals the deposit amount in wei.
31
+
32
+ ### CLI extra fields
33
+
34
+ The CLI may include an `action` or `step` field for context:
35
+
36
+ - **Register**: `"action": "register"` or `"action": "changeDepositKey"`
37
+ - **Deposit**: `"step": "approve"` (USDC only) and `"step": "deposit"`
38
+
39
+ These fields are informational and can be ignored by the signer.
40
+ For `veil register --unsigned --force`, the CLI checks chain state first and chooses `"register"` vs `"changeDepositKey"` based on whether the address is already registered.
41
+
42
+ ---
43
+
44
+ ## SDK build functions
45
+
46
+ All functions are exported from `@veil-cash/sdk`.
47
+
48
+ ### Keypair
49
+
50
+ ```typescript
51
+ import { Keypair, VEIL_SIGNED_MESSAGE } from '@veil-cash/sdk';
52
+ import type { MessageSigner } from '@veil-cash/sdk';
53
+
54
+ // Random keypair
55
+ const keypair = new Keypair();
56
+
57
+ // Restore from saved private key
58
+ const restored = new Keypair('0xSAVED_VEIL_KEY');
59
+
60
+ // Derive from Ethereum wallet key (same keypair as frontend login)
61
+ const derived = await Keypair.fromWalletKey('0xWALLET_KEY');
62
+
63
+ // Derive from pre-computed EIP-191 signature
64
+ const fromSig = Keypair.fromSignature('0xSIG');
65
+
66
+ // Derive via external signer callback
67
+ const fromSigner = await Keypair.fromSigner(async (msg) => {
68
+ return await externalSignerService.personalSign(msg);
69
+ });
70
+
71
+ // Public deposit key (register on-chain)
72
+ keypair.depositKey(); // 0x-prefixed, 130 hex chars
73
+
74
+ // Private key (store securely)
75
+ keypair.privkey; // 0x-prefixed, 66 hex chars
76
+ ```
77
+
78
+ ### Register
79
+
80
+ ```typescript
81
+ import { buildRegisterTx, buildChangeDepositKeyTx } from '@veil-cash/sdk';
82
+ import type { TransactionData } from '@veil-cash/sdk';
83
+
84
+ // First-time registration
85
+ const tx: TransactionData = buildRegisterTx(depositKey, '0xSIGNER_ADDRESS');
86
+ // tx = { to: '0x...', data: '0x...' }
87
+
88
+ // Update existing deposit key
89
+ const changeTx: TransactionData = buildChangeDepositKeyTx(newDepositKey, '0xSIGNER_ADDRESS');
90
+ ```
91
+
92
+ `TransactionData` type:
93
+
94
+ ```typescript
95
+ interface TransactionData {
96
+ to: `0x${string}`;
97
+ data: `0x${string}`;
98
+ value?: bigint;
99
+ }
100
+ ```
101
+
102
+ For register transactions `value` is `undefined` (use `"0"` when forwarding to signer).
103
+
104
+ ### Deposit
105
+
106
+ ```typescript
107
+ import {
108
+ buildDepositETHTx,
109
+ buildDepositUSDCTx,
110
+ buildApproveUSDCTx,
111
+ buildDepositTx,
112
+ } from '@veil-cash/sdk';
113
+
114
+ // ETH deposit
115
+ const ethTx = buildDepositETHTx({ depositKey, amount: '0.1' });
116
+ // ethTx.value = 100000000000000000n (bigint)
117
+
118
+ // USDC deposit (two-step)
119
+ const approveTx = buildApproveUSDCTx({ amount: '100' });
120
+ const usdcTx = buildDepositUSDCTx({ depositKey, amount: '100' });
121
+
122
+ // Generic (routes to ETH or USDC builder)
123
+ const tx = buildDepositTx({ depositKey, amount: '0.1', token: 'ETH' });
124
+ ```
125
+
126
+ When serializing for a signer: `value` must be converted to a string (`tx.value?.toString() ?? '0'`).
127
+
128
+ ### Balance
129
+
130
+ ```typescript
131
+ import { getQueueBalance, getPrivateBalance } from '@veil-cash/sdk';
132
+
133
+ // Queue balance (pending deposits)
134
+ const queue = await getQueueBalance({
135
+ address: '0x...',
136
+ pool: 'eth',
137
+ });
138
+
139
+ // Private balance (in-pool UTXOs)
140
+ const priv = await getPrivateBalance({
141
+ keypair,
142
+ pool: 'eth',
143
+ });
144
+ ```
145
+
146
+ ---
147
+
148
+ ## CLI quick reference
149
+
150
+ Install globally: `npm install -g @veil-cash/sdk`
151
+
152
+ ### Environment variables
153
+
154
+ | Variable | Description |
155
+ |----------|-------------|
156
+ | `VEIL_KEY` | Veil private key (for ZK proofs) |
157
+ | `DEPOSIT_KEY` | Veil deposit key (public) |
158
+ | `WALLET_KEY` | Ethereum wallet private key (for signing) |
159
+ | `SIGNER_ADDRESS` | Ethereum address for unsigned/query flows when signing is external |
160
+ | `RPC_URL` | Base RPC URL (optional, defaults to public RPC) |
161
+ | `RELAY_URL` | Override relay base URL for relayed CLI operations |
162
+
163
+ `WALLET_KEY` and `SIGNER_ADDRESS` are mutually exclusive. Use `SIGNER_ADDRESS` only for address-only CLI flows.
164
+
165
+ ### Commands
166
+
167
+ ```bash
168
+ veil init # Derive keypair from WALLET_KEY (saves to .env.veil)
169
+ veil init --generate # Generate random keypair
170
+ veil init --signature 0x... # Derive from pre-computed EIP-191 signature
171
+ veil init --force # Overwrite existing keypair without prompting
172
+ veil init --no-save # Print keypair without saving to disk
173
+ veil init --json # Output keypair as JSON (no prompts, no file save)
174
+
175
+ veil keypair # Show current keypair (from VEIL_KEY)
176
+ veil keypair --json # Show current keypair as JSON
177
+
178
+ veil status # Check config, signing mode, registration, and relay health
179
+ veil status --json # Machine-readable status
180
+
181
+ SIGNER_ADDRESS=0x... veil register --unsigned # Unsigned register payload
182
+ SIGNER_ADDRESS=0x... veil register --unsigned --force # Unsigned register/change-key payload (depends on chain state)
183
+ veil register --unsigned --address 0x... # Unsigned register payload (explicit address)
184
+ veil register --json # Register and output result as JSON
185
+
186
+ veil deposit ETH 0.1 --unsigned # Unsigned ETH deposit payload
187
+ veil deposit USDC 100 --unsigned # Unsigned USDC deposit payload(s)
188
+ veil deposit ETH 0.1 --json # Deposit and output result as JSON
189
+
190
+ veil balance # All pool balances
191
+ veil balance --pool eth # ETH pool only
192
+ veil balance --pool usdc # USDC pool only
193
+ veil balance --json # Machine-readable balances
194
+ veil balance queue --pool eth # Queue-only balance
195
+ veil balance queue --address 0x... --json # Queue balance for explicit address
196
+ veil balance private --pool eth # Private-only balance
197
+ veil balance private --json # Private balance as JSON
198
+ ```
199
+
200
+ ### Error format
201
+
202
+ All CLI errors output JSON with a standardized `errorCode`:
203
+
204
+ ```json
205
+ {
206
+ "success": false,
207
+ "errorCode": "VEIL_KEY_MISSING",
208
+ "error": "VEIL_KEY required. Set VEIL_KEY env"
209
+ }
210
+ ```
211
+
212
+ Common codes: `VEIL_KEY_MISSING`, `WALLET_KEY_MISSING`, `DEPOSIT_KEY_MISSING`,
213
+ `CONFIG_CONFLICT`, `INVALID_AMOUNT`, `INSUFFICIENT_BALANCE`, `CONTRACT_ERROR`, `RPC_ERROR`.
214
+
215
+ ---
216
+
217
+ ## Deposit minimums
218
+
219
+ | Asset | Minimum (net) | Notes |
220
+ |-------|--------------|-------|
221
+ | ETH | 0.01 | Fee (0.3%) added automatically via on-chain `getDepositAmountWithFee` |
222
+ | USDC | 10 | Fee (0.3%) added automatically via on-chain `getDepositAmountWithFee` |
223
+
224
+ The CLI amount is the **net** amount that lands in the pool. The fee is calculated on-chain and added to the transaction automatically — users do not need to account for it.
225
+
226
+ ---
227
+
228
+ ## Links
229
+
230
+ - npm: [@veil-cash/sdk](https://www.npmjs.com/package/@veil-cash/sdk)
231
+ - Veil Cash: [https://veil.cash](https://veil.cash)
package/src/abi.ts CHANGED
@@ -101,18 +101,6 @@ export const ENTRY_ABI = [
101
101
  type: 'function',
102
102
  },
103
103
 
104
- // Queue cbBTC deposit
105
- {
106
- inputs: [
107
- { name: '_amount', type: 'uint256' },
108
- { name: '_depositKey', type: 'bytes' },
109
- ],
110
- name: 'queueBTC',
111
- outputs: [],
112
- stateMutability: 'nonpayable',
113
- type: 'function',
114
- },
115
-
116
104
  // Read deposit keys
117
105
  {
118
106
  inputs: [{ name: '', type: 'address' }],
package/src/addresses.ts CHANGED
@@ -14,9 +14,6 @@ export const ADDRESSES: NetworkAddresses = {
14
14
  usdcPool: '0x5c50d58E49C59d112680c187De2Bf989d2a91242',
15
15
  usdcQueue: '0x5530241b24504bF05C9a22e95A1F5458888e6a9B',
16
16
  usdcToken: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
17
- cbbtcPool: '0x51A021da774b4bBB59B47f7CB4ccd631337680BA',
18
- cbbtcQueue: '0x977741CaDF8D1431c4816C0993D32b02094cD35C',
19
- cbbtcToken: '0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf',
20
17
  chainId: 8453,
21
18
  relayUrl: 'https://veil-relay.up.railway.app',
22
19
  } as const;
@@ -37,12 +34,6 @@ export const POOL_CONFIG = {
37
34
  symbol: 'USDC',
38
35
  name: 'USD Coin',
39
36
  },
40
- cbbtc: {
41
- decimals: 8,
42
- displayDecimals: 6,
43
- symbol: 'cbBTC',
44
- name: 'Coinbase Bitcoin',
45
- },
46
37
  } as const;
47
38
 
48
39
  /**
@@ -55,30 +46,32 @@ export function getAddresses(): NetworkAddresses {
55
46
 
56
47
  /**
57
48
  * Get the pool contract address for a given pool
58
- * @param pool - Pool identifier ('eth', 'usdc', or 'cbbtc')
49
+ * @param pool - Pool identifier ('eth' or 'usdc')
59
50
  * @returns Pool contract address
60
51
  */
61
- export function getPoolAddress(pool: RelayPool): `0x${string}` {
52
+ export function getPoolAddress(
53
+ pool: RelayPool
54
+ ): `0x${string}` {
62
55
  const addresses = getAddresses();
63
56
  switch (pool) {
64
57
  case 'eth': return addresses.ethPool;
65
58
  case 'usdc': return addresses.usdcPool;
66
- case 'cbbtc': return addresses.cbbtcPool;
67
59
  default: throw new Error(`Unknown pool: ${pool}`);
68
60
  }
69
61
  }
70
62
 
71
63
  /**
72
64
  * Get the queue contract address for a given pool
73
- * @param pool - Pool identifier ('eth', 'usdc', or 'cbbtc')
65
+ * @param pool - Pool identifier ('eth' or 'usdc')
74
66
  * @returns Queue contract address
75
67
  */
76
- export function getQueueAddress(pool: RelayPool): `0x${string}` {
68
+ export function getQueueAddress(
69
+ pool: RelayPool
70
+ ): `0x${string}` {
77
71
  const addresses = getAddresses();
78
72
  switch (pool) {
79
73
  case 'eth': return addresses.ethQueue;
80
74
  case 'usdc': return addresses.usdcQueue;
81
- case 'cbbtc': return addresses.cbbtcQueue;
82
75
  default: throw new Error(`Unknown pool: ${pool}`);
83
76
  }
84
77
  }
package/src/balance.ts CHANGED
@@ -101,14 +101,14 @@ export async function getQueueBalance(options: {
101
101
  depositKey: `0x${string}`;
102
102
  };
103
103
 
104
- // Check if this deposit belongs to the user
104
+ // Report the net shielded amount so queue totals match what will land in private balance.
105
105
  if (deposit.fallbackReceiver.toLowerCase() === address.toLowerCase()) {
106
- totalQueueBalance += deposit.amountIn;
106
+ totalQueueBalance += deposit.shieldAmount;
107
107
  pendingDeposits.push({
108
108
  nonce: nonce.toString(),
109
109
  status: DEPOSIT_STATUS_MAP[deposit.status as keyof typeof DEPOSIT_STATUS_MAP] || 'pending',
110
- amount: formatUnits(deposit.amountIn, poolConfig.decimals),
111
- amountWei: deposit.amountIn.toString(),
110
+ amount: formatUnits(deposit.shieldAmount, poolConfig.decimals),
111
+ amountWei: deposit.shieldAmount.toString(),
112
112
  timestamp: new Date(Number(deposit.timestamp) * 1000).toISOString(),
113
113
  });
114
114
  }
@@ -6,12 +6,16 @@ import { Command } from 'commander';
6
6
  import { getQueueBalance, getPrivateBalance } from '../../balance.js';
7
7
  import { POOL_CONFIG } from '../../addresses.js';
8
8
  import { Keypair } from '../../keypair.js';
9
- import { getAddress } from '../wallet.js';
9
+ import { getWalletBalances } from '../wallet.js';
10
+ import { resolveAddress } from '../config.js';
10
11
  import { formatUnits } from 'viem';
11
12
  import { handleCLIError, CLIError, ErrorCode } from '../errors.js';
13
+ import { clearProgress, createProgressReporter, maskValue, printFields, printHeader, printJson, printLine, printList, printSection } from '../output.js';
14
+ import { createPrivateBalanceCommand } from './private-balance.js';
15
+ import { createQueueBalanceCommand } from './queue-balance.js';
12
16
  import type { RelayPool } from '../../types.js';
13
17
 
14
- const SUPPORTED_POOLS: RelayPool[] = ['eth', 'usdc', 'cbbtc'];
18
+ const SUPPORTED_POOLS: RelayPool[] = ['eth', 'usdc'];
15
19
 
16
20
  /**
17
21
  * Fetch balance for a single pool and return structured output
@@ -88,12 +92,16 @@ async function fetchPoolBalance(
88
92
  export function createBalanceCommand(): Command {
89
93
  const balance = new Command('balance')
90
94
  .description('Show queue and private balances (all pools by default)')
91
- .option('--pool <pool>', 'Pool to check (eth, usdc, cbbtc, or all)', 'all')
92
- .option('--wallet-key <key>', 'Ethereum wallet key (or set WALLET_KEY env)')
93
- .option('--address <address>', 'Address to check (or derived from wallet key)')
94
- .option('--veil-key <key>', 'Veil private key (or set VEIL_KEY env)')
95
- .option('--rpc-url <url>', 'RPC URL (or set RPC_URL env)')
96
- .option('--quiet', 'Suppress progress output')
95
+ .option('--pool <pool>', 'Pool to check (eth, usdc, or all)', 'all')
96
+ .option('--address <address>', 'Address to check (or derived from WALLET_KEY / SIGNER_ADDRESS)')
97
+ .option('--json', 'Output as JSON')
98
+ .addHelpText('after', `
99
+ Examples:
100
+ veil balance
101
+ veil balance --pool eth
102
+ veil balance queue --pool usdc
103
+ veil balance --json
104
+ `)
97
105
  .action(async (options) => {
98
106
  try {
99
107
  const poolArg = (options.pool || 'all').toLowerCase();
@@ -106,73 +114,153 @@ export function createBalanceCommand(): Command {
106
114
  const poolsToQuery: RelayPool[] = poolArg === 'all' ? [...SUPPORTED_POOLS] : [poolArg as RelayPool];
107
115
 
108
116
  // Get address
109
- let address: `0x${string}`;
110
- if (options.address) {
111
- address = options.address as `0x${string}`;
112
- } else {
113
- const walletKey = options.walletKey || process.env.WALLET_KEY;
114
- if (!walletKey) {
115
- throw new CLIError(ErrorCode.WALLET_KEY_MISSING, 'Must provide --address or --wallet-key (or set WALLET_KEY env)');
116
- }
117
- address = getAddress(walletKey as `0x${string}`);
117
+ const resolvedAddress = resolveAddress({ address: options.address }, { required: true });
118
+ if (!resolvedAddress) {
119
+ throw new CLIError(
120
+ ErrorCode.WALLET_KEY_MISSING,
121
+ 'Must provide --address, set SIGNER_ADDRESS, or set WALLET_KEY env.',
122
+ );
118
123
  }
124
+ const address = resolvedAddress.address;
119
125
 
120
126
  // Get keypair for private balance
121
- const veilKey = options.veilKey || process.env.VEIL_KEY;
127
+ const veilKey = process.env.VEIL_KEY;
122
128
  const keypair = veilKey ? new Keypair(veilKey) : null;
123
129
 
124
- const rpcUrl = options.rpcUrl || process.env.RPC_URL;
130
+ const rpcUrl = process.env.RPC_URL;
125
131
 
126
- // Progress callback
127
- const onProgress = options.quiet
128
- ? undefined
129
- : (stage: string, detail?: string) => {
130
- const msg = detail ? `${stage}: ${detail}` : stage;
131
- process.stderr.write(`\r\x1b[K${msg}`);
132
- };
132
+ const onProgress = createProgressReporter();
133
133
 
134
134
  // Get deposit key if available
135
135
  const depositKey = process.env.DEPOSIT_KEY || (keypair ? keypair.depositKey() : null);
136
136
 
137
137
  // Single pool mode -- flat output (backwards compatible)
138
138
  if (poolsToQuery.length === 1) {
139
- const poolResult = await fetchPoolBalance(poolsToQuery[0], address, keypair, rpcUrl, onProgress);
139
+ const [poolResult, walletBalances] = await Promise.all([
140
+ fetchPoolBalance(poolsToQuery[0], address, keypair, rpcUrl, onProgress),
141
+ getWalletBalances(address, rpcUrl),
142
+ ]);
140
143
 
141
- // Clear progress line
142
- if (!options.quiet) process.stderr.write('\r\x1b[K');
144
+ clearProgress();
143
145
 
144
146
  const output = {
145
147
  address,
146
148
  depositKey: depositKey || null,
149
+ wallet: walletBalances,
147
150
  ...poolResult,
148
151
  };
149
152
 
150
- console.log(JSON.stringify(output, null, 2));
153
+ if (options.json) {
154
+ printJson(output);
155
+ return;
156
+ }
157
+
158
+ printCombinedBalanceHuman(output);
151
159
  return;
152
160
  }
153
161
 
154
162
  // All pools mode -- nested output
155
- const pools: Record<string, unknown>[] = [];
156
- for (const pool of poolsToQuery) {
157
- const poolResult = await fetchPoolBalance(pool, address, keypair, rpcUrl, onProgress);
158
- pools.push(poolResult);
159
- }
163
+ const poolPromises = poolsToQuery.map(pool =>
164
+ fetchPoolBalance(pool, address, keypair, rpcUrl, onProgress),
165
+ );
166
+ const [walletBalances, ...poolResults] = await Promise.all([
167
+ getWalletBalances(address, rpcUrl),
168
+ ...poolPromises,
169
+ ]);
160
170
 
161
- // Clear progress line
162
- if (!options.quiet) process.stderr.write('\r\x1b[K');
171
+ clearProgress();
163
172
 
164
173
  const output = {
165
174
  address,
166
175
  depositKey: depositKey || null,
167
- pools,
176
+ wallet: walletBalances,
177
+ pools: poolResults,
168
178
  };
169
179
 
170
- console.log(JSON.stringify(output, null, 2));
180
+ if (options.json) {
181
+ printJson(output);
182
+ return;
183
+ }
184
+
185
+ printMultiPoolBalanceHuman(output);
171
186
  } catch (error) {
172
- process.stderr.write('\r\x1b[K');
187
+ clearProgress();
173
188
  handleCLIError(error);
174
189
  }
175
190
  });
176
191
 
192
+ balance.addCommand(createQueueBalanceCommand('queue'));
193
+ balance.addCommand(createPrivateBalanceCommand('private'));
194
+
177
195
  return balance;
178
196
  }
197
+
198
+ function printPoolHuman(output: Record<string, unknown>): void {
199
+ const symbol = output.symbol as string;
200
+
201
+ printSection(`${output.pool}`);
202
+ printFields([
203
+ { label: 'Total', value: `${output.totalBalance} ${symbol}` },
204
+ ]);
205
+
206
+ const privateData = output.private as { balance?: string | null; balanceWei?: string; utxoCount?: number; note?: string; utxos?: Array<{ index: number; amount: string }> };
207
+ if (privateData.note) {
208
+ printFields([
209
+ { label: 'Private', value: privateData.note },
210
+ ]);
211
+ } else {
212
+ printFields([
213
+ { label: 'Private', value: `${privateData.balance} ${symbol}` },
214
+ ]);
215
+ }
216
+
217
+ const queueData = output.queue as { balance: string; balanceWei: string; count: number; deposits: Array<{ nonce: string | number; amount: string; status: string }> };
218
+ printFields([
219
+ { label: 'Queue', value: `${queueData.balance} ${symbol}` },
220
+ { label: 'Pending', value: queueData.count },
221
+ ]);
222
+ if (queueData.deposits.length > 0) {
223
+ printList(
224
+ queueData.deposits.map((d) => `nonce ${d.nonce}: ${d.amount} (${d.status})`)
225
+ );
226
+ }
227
+ }
228
+
229
+ function printCombinedBalanceHuman(output: Record<string, unknown>): void {
230
+ const wallet = output.wallet as { eth: string; usdc: string };
231
+
232
+ printHeader(`${output.pool} Balance`);
233
+ printFields([
234
+ { label: 'Address', value: output.address },
235
+ { label: 'Deposit key', value: typeof output.depositKey === 'string' ? maskValue(output.depositKey) : 'not set' },
236
+ ]);
237
+
238
+ printSection('Wallet (public)');
239
+ printFields([
240
+ { label: 'ETH', value: `${wallet.eth} ETH` },
241
+ { label: 'USDC', value: `${wallet.usdc} USDC` },
242
+ ]);
243
+
244
+ printPoolHuman(output);
245
+ printLine();
246
+ }
247
+
248
+ function printMultiPoolBalanceHuman(output: { address: string; depositKey: string | null; wallet: { eth: string; usdc: string }; pools: Record<string, unknown>[] }): void {
249
+ printHeader('Balances');
250
+ printFields([
251
+ { label: 'Address', value: output.address },
252
+ { label: 'Deposit key', value: output.depositKey ? maskValue(output.depositKey) : 'not set' },
253
+ ]);
254
+
255
+ printSection('Wallet (public)');
256
+ printFields([
257
+ { label: 'ETH', value: `${output.wallet.eth} ETH` },
258
+ { label: 'USDC', value: `${output.wallet.usdc} USDC` },
259
+ ]);
260
+
261
+ for (const poolResult of output.pools) {
262
+ printPoolHuman(poolResult);
263
+ }
264
+
265
+ printLine();
266
+ }