antd 0.1.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,549 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Proto-generated Ruby stubs — produced by `grpc_tools_ruby_protoc`.
4
+ # Run:
5
+ # grpc_tools_ruby_protoc \
6
+ # -I../../antd/proto \
7
+ # --ruby_out=lib --grpc_out=lib \
8
+ # antd/v1/common.proto antd/v1/health.proto antd/v1/data.proto \
9
+ # antd/v1/chunks.proto antd/v1/files.proto antd/v1/upload.proto
10
+ #
11
+ # The generated files are expected under lib/antd/v1/.
12
+
13
+ require "grpc"
14
+ require_relative "v1/health_services_pb"
15
+ require_relative "v1/data_services_pb"
16
+ require_relative "v1/chunks_services_pb"
17
+ require_relative "v1/files_services_pb"
18
+ require_relative "v1/upload_services_pb"
19
+ require_relative "v1/wallet_services_pb"
20
+
21
+ module Antd
22
+ DEFAULT_GRPC_TARGET = "localhost:50051"
23
+
24
+ # gRPC client for the antd daemon.
25
+ #
26
+ # Provides the same methods as the REST +Client+, but communicates over
27
+ # gRPC using the proto-generated stubs from +antd/v1/*.proto+.
28
+ class GrpcClient
29
+ # Creates a gRPC client using port discovery.
30
+ #
31
+ # Reads the daemon.port file to find the gRPC port. Falls back to the
32
+ # default target if the port file is not found.
33
+ #
34
+ # @return [Array(GrpcClient, String)] the client and the resolved target
35
+ def self.auto_discover
36
+ target = Antd::Discover.grpc_target
37
+ target = DEFAULT_GRPC_TARGET if target.empty?
38
+ [new(target: target), target]
39
+ end
40
+
41
+ # @param target [String] gRPC target address (default: "localhost:50051")
42
+ def initialize(target: DEFAULT_GRPC_TARGET)
43
+ @target = target
44
+ @health_stub = Antd::V1::HealthService::Stub.new(target, :this_channel_is_insecure)
45
+ @data_stub = Antd::V1::DataService::Stub.new(target, :this_channel_is_insecure)
46
+ @chunk_stub = Antd::V1::ChunkService::Stub.new(target, :this_channel_is_insecure)
47
+ @file_stub = Antd::V1::FileService::Stub.new(target, :this_channel_is_insecure)
48
+ @upload_stub = Antd::V1::UploadService::Stub.new(target, :this_channel_is_insecure)
49
+ @wallet_stub = Antd::V1::WalletService::Stub.new(target, :this_channel_is_insecure)
50
+ end
51
+
52
+ # --- Health ---
53
+
54
+ # Check daemon status.
55
+ # @return [HealthStatus]
56
+ def health
57
+ resp = grpc_call { @health_stub.check(Antd::V1::HealthCheckRequest.new) }
58
+ HealthStatus.new(
59
+ ok: resp.status == "ok",
60
+ network: resp.network,
61
+ version: resp.version,
62
+ evm_network: resp.evm_network,
63
+ uptime_seconds: resp.uptime_seconds,
64
+ build_commit: resp.build_commit,
65
+ payment_token_address: resp.payment_token_address,
66
+ payment_vault_address: resp.payment_vault_address
67
+ )
68
+ end
69
+
70
+ # --- Data ---
71
+
72
+ # Store private encrypted data. Returns the caller-held DataMap (hex).
73
+ # @param data [String] raw bytes
74
+ # @param payment_mode [String] PaymentMode::AUTO | MERKLE | SINGLE
75
+ # @return [DataPutResult]
76
+ def data_put(data, payment_mode: PaymentMode::AUTO)
77
+ req = Antd::V1::PutDataRequest.new(data: data.b, payment_mode: payment_mode)
78
+ resp = grpc_call { @data_stub.put(req) }
79
+ DataPutResult.new(data_map: resp.data_map, chunks_stored: resp.chunks_stored, payment_mode_used: resp.payment_mode_used)
80
+ end
81
+
82
+ # Retrieve private data from a caller-held DataMap (hex).
83
+ # @param data_map [String]
84
+ # @return [String] raw bytes
85
+ def data_get(data_map)
86
+ req = Antd::V1::GetDataRequest.new(data_map: data_map)
87
+ resp = grpc_call { @data_stub.get(req) }
88
+ resp.data
89
+ end
90
+
91
+ # Store public data. Returns the on-network DataMap address.
92
+ # @param data [String] raw bytes
93
+ # @param payment_mode [String] PaymentMode::AUTO | MERKLE | SINGLE
94
+ # @return [DataPutPublicResult]
95
+ def data_put_public(data, payment_mode: PaymentMode::AUTO)
96
+ req = Antd::V1::PutPublicDataRequest.new(data: data.b, payment_mode: payment_mode)
97
+ resp = grpc_call { @data_stub.put_public(req) }
98
+ DataPutPublicResult.new(address: resp.address, chunks_stored: resp.chunks_stored, payment_mode_used: resp.payment_mode_used)
99
+ end
100
+
101
+ # Retrieve public data by address.
102
+ # @param address [String] hex address
103
+ # @return [String] raw bytes
104
+ def data_get_public(address)
105
+ req = Antd::V1::GetPublicDataRequest.new(address: address)
106
+ resp = grpc_call { @data_stub.get_public(req) }
107
+ resp.data
108
+ end
109
+
110
+ # Stream private data from a caller-held DataMap (hex), one decrypt batch at
111
+ # a time, instead of buffering the whole object. The gRPC counterpart to
112
+ # +data_get+ and mirror of the REST client's +data_stream+.
113
+ #
114
+ # When a block is given, each raw byte chunk is yielded as it arrives and the
115
+ # method returns +nil+. When no block is given, an +Enumerator+ over the
116
+ # chunks is returned.
117
+ #
118
+ # @param data_map [String] hex-encoded DataMap
119
+ # @yieldparam chunk [String] a raw byte chunk of the decrypted payload
120
+ # @return [nil, Enumerator]
121
+ def data_stream(data_map, &block)
122
+ return enum_for(:data_stream, data_map) unless block_given?
123
+
124
+ req = Antd::V1::StreamDataRequest.new(data_map: data_map)
125
+ grpc_call { @data_stub.stream(req).each { |chunk| block.call(chunk.data) } }
126
+ nil
127
+ end
128
+
129
+ # Stream public data by address — the gRPC counterpart to +data_get_public+.
130
+ # Same block/Enumerator contract as +data_stream+.
131
+ #
132
+ # @param address [String] hex address
133
+ # @yieldparam chunk [String] a raw byte chunk of the decrypted payload
134
+ # @return [nil, Enumerator]
135
+ def data_stream_public(address, &block)
136
+ return enum_for(:data_stream_public, address) unless block_given?
137
+
138
+ req = Antd::V1::StreamPublicDataRequest.new(address: address)
139
+ grpc_call { @data_stub.stream_public(req).each { |chunk| block.call(chunk.data) } }
140
+ nil
141
+ end
142
+
143
+ # Like +data_stream+ but requests interleaved fetch-progress frames so the
144
+ # caller can drive a *determinate* progress bar. Sets the request's
145
+ # +include_progress+ flag and yields +DownloadFrame+s — each either a
146
+ # plaintext byte chunk (+frame.data+) or a +DownloadProgress+ update
147
+ # (+frame.progress+). The byte denominator is surfaced as a leading
148
+ # +DownloadFrame+ (+frame.meta+), read from the response's
149
+ # +x-content-length+ initial metadata before any chunk.
150
+ #
151
+ # When a block is given each frame is yielded and the method returns +nil+;
152
+ # otherwise an +Enumerator+ over the frames is returned.
153
+ #
154
+ # @param data_map [String] hex-encoded DataMap
155
+ # @yieldparam frame [DownloadFrame]
156
+ # @return [nil, Enumerator]
157
+ def data_stream_with_progress(data_map, &block)
158
+ return enum_for(:data_stream_with_progress, data_map) unless block_given?
159
+
160
+ req = Antd::V1::StreamDataRequest.new(data_map: data_map, include_progress: true)
161
+ grpc_call { stream_with_progress(@data_stub.stream(req, return_op: true), &block) }
162
+ nil
163
+ end
164
+
165
+ # Like +data_stream_public+ but requests interleaved fetch-progress frames.
166
+ # See +data_stream_with_progress+ for the contract.
167
+ #
168
+ # @param address [String] hex address
169
+ # @yieldparam frame [DownloadFrame]
170
+ # @return [nil, Enumerator]
171
+ def data_stream_public_with_progress(address, &block)
172
+ return enum_for(:data_stream_public_with_progress, address) unless block_given?
173
+
174
+ req = Antd::V1::StreamPublicDataRequest.new(address: address, include_progress: true)
175
+ grpc_call { stream_with_progress(@data_stub.stream_public(req, return_op: true), &block) }
176
+ nil
177
+ end
178
+
179
+ # Pre-upload cost breakdown for the given bytes.
180
+ # @param data [String] raw bytes
181
+ # @param payment_mode [String] PaymentMode::AUTO | MERKLE | SINGLE
182
+ # @return [UploadCostEstimate]
183
+ def data_cost(data, payment_mode: PaymentMode::AUTO)
184
+ req = Antd::V1::DataCostRequest.new(data: data.b, payment_mode: payment_mode)
185
+ resp = grpc_call { @data_stub.cost(req) }
186
+ UploadCostEstimate.new(
187
+ cost: resp.atto_tokens,
188
+ file_size: resp.file_size,
189
+ chunk_count: resp.chunk_count,
190
+ estimated_gas_cost_wei: resp.estimated_gas_cost_wei,
191
+ payment_mode: resp.payment_mode
192
+ )
193
+ end
194
+
195
+ # --- Chunks ---
196
+
197
+ # Store a raw chunk on the network.
198
+ # @param data [String] raw bytes
199
+ # @return [PutResult]
200
+ def chunk_put(data)
201
+ req = Antd::V1::PutChunkRequest.new(data: data.b)
202
+ resp = grpc_call { @chunk_stub.put(req) }
203
+ PutResult.new(cost: resp.cost.atto_tokens, address: resp.address)
204
+ end
205
+
206
+ # Retrieve a chunk by address.
207
+ # @param address [String] hex address
208
+ # @return [String] raw bytes
209
+ def chunk_get(address)
210
+ req = Antd::V1::GetChunkRequest.new(address: address)
211
+ resp = grpc_call { @chunk_stub.get(req) }
212
+ resp.data
213
+ end
214
+
215
+ # --- Files ---
216
+
217
+ # Upload a file privately. Returns the caller-held DataMap (hex).
218
+ # @param path [String] local file path
219
+ # @param payment_mode [String] PaymentMode::AUTO | MERKLE | SINGLE
220
+ # @return [FilePutResult]
221
+ def file_put(path, payment_mode: PaymentMode::AUTO)
222
+ req = Antd::V1::PutFileRequest.new(path: path, payment_mode: payment_mode)
223
+ resp = grpc_call { @file_stub.put(req) }
224
+ FilePutResult.new(
225
+ data_map: resp.data_map,
226
+ storage_cost_atto: resp.storage_cost_atto,
227
+ gas_cost_wei: resp.gas_cost_wei,
228
+ chunks_stored: resp.chunks_stored,
229
+ payment_mode_used: resp.payment_mode_used
230
+ )
231
+ end
232
+
233
+ # Download a private file from a caller-held DataMap.
234
+ # @param data_map [String]
235
+ # @param dest_path [String]
236
+ # @return [void]
237
+ def file_get(data_map, dest_path)
238
+ req = Antd::V1::GetFileRequest.new(data_map: data_map, dest_path: dest_path)
239
+ grpc_call { @file_stub.get(req) }
240
+ nil
241
+ end
242
+
243
+ # Upload a file publicly. Returns the on-network DataMap address.
244
+ # @param path [String] local file path
245
+ # @param payment_mode [String] PaymentMode::AUTO | MERKLE | SINGLE
246
+ # @return [FilePutPublicResult]
247
+ def file_put_public(path, payment_mode: PaymentMode::AUTO)
248
+ req = Antd::V1::PutFileRequest.new(path: path, payment_mode: payment_mode)
249
+ resp = grpc_call { @file_stub.put_public(req) }
250
+ FilePutPublicResult.new(
251
+ address: resp.address,
252
+ storage_cost_atto: resp.storage_cost_atto,
253
+ gas_cost_wei: resp.gas_cost_wei,
254
+ chunks_stored: resp.chunks_stored,
255
+ payment_mode_used: resp.payment_mode_used
256
+ )
257
+ end
258
+
259
+ # Download a public file from an on-network DataMap address.
260
+ # @param address [String]
261
+ # @param dest_path [String]
262
+ # @return [void]
263
+ def file_get_public(address, dest_path)
264
+ req = Antd::V1::GetFilePublicRequest.new(address: address, dest_path: dest_path)
265
+ grpc_call { @file_stub.get_public(req) }
266
+ nil
267
+ end
268
+
269
+ # Pre-upload cost breakdown for the file at +path+.
270
+ # @param path [String]
271
+ # @param is_public [Boolean]
272
+ # @param payment_mode [String] PaymentMode::AUTO | MERKLE | SINGLE
273
+ # @return [UploadCostEstimate]
274
+ def file_cost(path, is_public, payment_mode: PaymentMode::AUTO)
275
+ req = Antd::V1::FileCostRequest.new(
276
+ path: path,
277
+ is_public: is_public,
278
+ payment_mode: payment_mode
279
+ )
280
+ resp = grpc_call { @file_stub.cost(req) }
281
+ UploadCostEstimate.new(
282
+ cost: resp.atto_tokens,
283
+ file_size: resp.file_size,
284
+ chunk_count: resp.chunk_count,
285
+ estimated_gas_cost_wei: resp.estimated_gas_cost_wei,
286
+ payment_mode: resp.payment_mode
287
+ )
288
+ end
289
+
290
+ # --- External Signer (chunks) ---
291
+
292
+ # Prepare a single chunk for external-signer publish.
293
+ #
294
+ # When the chunk is already on-network the result has
295
+ # +already_stored: true+ and the caller can skip +finalize_chunk_upload+
296
+ # entirely.
297
+ #
298
+ # @param data [String] raw chunk bytes
299
+ # @return [PrepareChunkResult]
300
+ def prepare_chunk_upload(data)
301
+ req = Antd::V1::PrepareChunkRequest.new(data: data.b)
302
+ resp = grpc_call { @chunk_stub.prepare_chunk(req) }
303
+ PrepareChunkResult.new(
304
+ address: resp.address,
305
+ already_stored: resp.already_stored,
306
+ upload_id: resp.upload_id,
307
+ payment_type: resp.payment_type,
308
+ payments: resp.payments.map { |p|
309
+ PaymentInfo.new(
310
+ quote_hash: p.quote_hash,
311
+ rewards_address: p.rewards_address,
312
+ amount: p.amount
313
+ )
314
+ },
315
+ total_amount: resp.total_amount,
316
+ payment_vault_address: resp.payment_vault_address,
317
+ payment_token_address: resp.payment_token_address,
318
+ rpc_url: resp.rpc_url
319
+ )
320
+ end
321
+
322
+ # Submit a prepared chunk after external payment. Returns the chunk address.
323
+ #
324
+ # @param upload_id [String]
325
+ # @param tx_hashes [Hash<String, String>]
326
+ # @return [String] hex chunk address
327
+ def finalize_chunk_upload(upload_id, tx_hashes)
328
+ req = Antd::V1::FinalizeChunkRequest.new(
329
+ upload_id: upload_id,
330
+ tx_hashes: tx_hashes
331
+ )
332
+ resp = grpc_call { @chunk_stub.finalize_chunk(req) }
333
+ resp.address
334
+ end
335
+
336
+ # --- External Signer (uploads) ---
337
+
338
+ # Prepare a file upload for external signing.
339
+ #
340
+ # @param path [String] local file path on the daemon host
341
+ # @param visibility [String, nil] +"private"+ (default when nil) or
342
+ # +"public"+ to bundle the DataMap chunk into the same external-signer
343
+ # payment batch
344
+ # @return [PrepareUploadResult]
345
+ def prepare_upload(path, visibility: nil)
346
+ req = Antd::V1::PrepareFileUploadRequest.new(
347
+ path: path,
348
+ visibility: visibility.to_s
349
+ )
350
+ resp = grpc_call { @upload_stub.prepare_file_upload(req) }
351
+ build_prepare_upload_result(resp)
352
+ end
353
+
354
+ # Convenience wrapper for +prepare_upload(path, visibility: "public")+.
355
+ #
356
+ # @param path [String]
357
+ # @return [PrepareUploadResult]
358
+ def prepare_upload_public(path)
359
+ prepare_upload(path, visibility: "public")
360
+ end
361
+
362
+ # Prepare an in-memory data upload for external signing.
363
+ #
364
+ # @param data [String] raw bytes
365
+ # @param visibility [String, nil] same semantics as +prepare_upload+
366
+ # @return [PrepareUploadResult]
367
+ def prepare_data_upload(data, visibility: nil)
368
+ req = Antd::V1::PrepareDataUploadRequest.new(
369
+ data: data.b,
370
+ visibility: visibility.to_s
371
+ )
372
+ resp = grpc_call { @upload_stub.prepare_data_upload(req) }
373
+ build_prepare_upload_result(resp)
374
+ end
375
+
376
+ # Finalize a wave-batch upload after external payment.
377
+ #
378
+ # @param upload_id [String]
379
+ # @param tx_hashes [Hash<String, String>]
380
+ # @return [FinalizeUploadResult]
381
+ def finalize_upload(upload_id, tx_hashes)
382
+ req = Antd::V1::FinalizeUploadRequest.new(
383
+ upload_id: upload_id,
384
+ tx_hashes: tx_hashes
385
+ )
386
+ resp = grpc_call { @upload_stub.finalize_upload(req) }
387
+ FinalizeUploadResult.new(
388
+ address: resp.address,
389
+ chunks_stored: resp.chunks_stored.to_i,
390
+ data_map: resp.data_map,
391
+ data_map_address: resp.data_map_address
392
+ )
393
+ end
394
+
395
+ # Finalize a merkle-batch upload after the winning pool has been
396
+ # determined.
397
+ #
398
+ # @param upload_id [String]
399
+ # @param winner_pool_hash [String]
400
+ # @param store_data_map [Boolean]
401
+ # @return [FinalizeUploadResult]
402
+ def finalize_merkle_upload(upload_id, winner_pool_hash, store_data_map: false)
403
+ req = Antd::V1::FinalizeUploadRequest.new(
404
+ upload_id: upload_id,
405
+ winner_pool_hash: winner_pool_hash,
406
+ store_data_map: store_data_map
407
+ )
408
+ resp = grpc_call { @upload_stub.finalize_upload(req) }
409
+ FinalizeUploadResult.new(
410
+ address: resp.address,
411
+ chunks_stored: resp.chunks_stored.to_i,
412
+ data_map: resp.data_map,
413
+ data_map_address: resp.data_map_address
414
+ )
415
+ end
416
+
417
+ # --- Wallet ---
418
+
419
+ # V2-286: parity with REST Antd::Client. A missing daemon wallet emits
420
+ # GRPC::FailedPrecondition which grpc_call surfaces as PaymentError
421
+ # (established FailedPrecondition->Payment convention across all SDKs).
422
+
423
+ # @return [WalletAddress]
424
+ def wallet_address
425
+ resp = grpc_call { @wallet_stub.get_address(Antd::V1::GetWalletAddressRequest.new) }
426
+ WalletAddress.new(address: resp.address)
427
+ end
428
+
429
+ # @return [WalletBalance]
430
+ def wallet_balance
431
+ resp = grpc_call { @wallet_stub.get_balance(Antd::V1::GetWalletBalanceRequest.new) }
432
+ WalletBalance.new(balance: resp.balance, gas_balance: resp.gas_balance)
433
+ end
434
+
435
+ # @return [Boolean]
436
+ def wallet_approve
437
+ resp = grpc_call { @wallet_stub.approve(Antd::V1::WalletApproveRequest.new) }
438
+ resp.approved
439
+ end
440
+
441
+ private
442
+
443
+ # Drives a +return_op: true+ server-streaming call, yielding +DownloadFrame+s.
444
+ # The byte denominator is surfaced first as a leading +DownloadFrame+ (its
445
+ # +meta+ set), read from the response's +x-content-length+ initial metadata;
446
+ # then each wire +DataChunk+ is mapped to a data/progress frame.
447
+ #
448
+ # +op+ is a +GRPC::ActiveCall::Operation+ (from calling the stub method with
449
+ # +return_op: true+). +op.execute+ returns the response enumerator and starts
450
+ # the call; +op.metadata+ then holds the server's initial metadata. An
451
+ # absent or unparseable +x-content-length+ (older daemons) yields no Meta
452
+ # frame.
453
+ def stream_with_progress(op)
454
+ responses = op.execute
455
+ meta = meta_frame_from_op(op)
456
+ yield meta unless meta.nil?
457
+ responses.each { |chunk| yield frame_from_chunk(chunk) }
458
+ end
459
+
460
+ # Builds a leading byte-total +DownloadFrame+ from a server-stream
461
+ # operation's +x-content-length+ initial metadata, or +nil+ when the header
462
+ # is absent or unparseable.
463
+ def meta_frame_from_op(op)
464
+ md = op.metadata
465
+ return nil if md.nil?
466
+
467
+ value = md["x-content-length"]
468
+ return nil if value.nil?
469
+
470
+ value = value.first if value.is_a?(Array)
471
+ total = Integer(value, 10) rescue nil
472
+ return nil if total.nil?
473
+
474
+ DownloadFrame.new(meta: total)
475
+ end
476
+
477
+ # Maps a wire +DataChunk+ (oneof kind {data | progress}) onto a public
478
+ # +DownloadFrame+. A chunk with no arm set (shouldn't occur) is treated as
479
+ # an empty data frame, matching the antd-rust reference consumer.
480
+ def frame_from_chunk(chunk)
481
+ if chunk.kind == :progress
482
+ p = chunk.progress
483
+ DownloadFrame.new(progress: DownloadProgress.new(phase: p.phase, fetched: p.fetched, total: p.total))
484
+ else
485
+ DownloadFrame.new(data: chunk.data)
486
+ end
487
+ end
488
+
489
+ # Maps a PrepareUploadResponse proto into a +PrepareUploadResult+ struct,
490
+ # populating the merkle-only fields (+depth+, +pool_commitments+,
491
+ # +merkle_payment_timestamp+) only when +payment_type+ is +"merkle"+.
492
+ def build_prepare_upload_result(resp)
493
+ is_merkle = resp.payment_type == "merkle"
494
+ pool_commitments = if is_merkle
495
+ resp.pool_commitments.map { |pc|
496
+ PoolCommitmentEntry.new(
497
+ pool_hash: pc.pool_hash,
498
+ candidates: pc.candidates.map { |c|
499
+ CandidateNodeEntry.new(
500
+ rewards_address: c.rewards_address,
501
+ amount: c.amount
502
+ )
503
+ }
504
+ )
505
+ }
506
+ end
507
+ PrepareUploadResult.new(
508
+ upload_id: resp.upload_id,
509
+ payments: resp.payments.map { |p|
510
+ PaymentInfo.new(
511
+ quote_hash: p.quote_hash,
512
+ rewards_address: p.rewards_address,
513
+ amount: p.amount
514
+ )
515
+ },
516
+ total_amount: resp.total_amount,
517
+ payment_vault_address: resp.payment_vault_address,
518
+ payment_token_address: resp.payment_token_address,
519
+ rpc_url: resp.rpc_url,
520
+ payment_type: resp.payment_type,
521
+ depth: is_merkle ? resp.depth : nil,
522
+ pool_commitments: pool_commitments,
523
+ merkle_payment_timestamp: is_merkle ? resp.merkle_payment_timestamp.to_i : nil
524
+ )
525
+ end
526
+
527
+
528
+ # Executes a gRPC call and translates errors to Antd error types.
529
+ def grpc_call
530
+ yield
531
+ rescue GRPC::InvalidArgument => e
532
+ raise BadRequestError, e.message
533
+ rescue GRPC::NotFound => e
534
+ raise NotFoundError, e.message
535
+ rescue GRPC::AlreadyExists => e
536
+ raise AlreadyExistsError, e.message
537
+ rescue GRPC::ResourceExhausted => e
538
+ raise TooLargeError, e.message
539
+ rescue GRPC::Internal => e
540
+ raise InternalError, e.message
541
+ rescue GRPC::Unavailable => e
542
+ raise NetworkError, e.message
543
+ rescue GRPC::FailedPrecondition => e
544
+ raise PaymentError, e.message
545
+ rescue GRPC::BadStatus => e
546
+ raise AntdError.new(e.message, status_code: e.code)
547
+ end
548
+ end
549
+ end