@xian-tech/client 0.1.5 → 0.1.7

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 CHANGED
@@ -14,3 +14,15 @@ It does not own:
14
14
  - browser wallet discovery
15
15
  - wallet-provider event contracts
16
16
  - framework-specific bindings
17
+
18
+ ```mermaid
19
+ flowchart LR
20
+ App["JS or TS app"] --> Client["XianClient"]
21
+ Client --> Queries["ABCI and HTTP queries"]
22
+ Client --> Tx["Build, sign, and broadcast tx"]
23
+ Client --> Watch["Websocket subscriptions"]
24
+ Signer["Ed25519Signer"] --> Tx
25
+ Queries --> Node["Xian node"]
26
+ Tx --> Node
27
+ Watch --> Dashboard["Dashboard websocket"]
28
+ ```
package/dist/client.d.ts CHANGED
@@ -1,10 +1,18 @@
1
1
  import { WatchApi } from "./watch.js";
2
- import type { BroadcastMode, BroadcastTxOptions, BuildTxRequest, ContractSendOptions, EstimateStampsOptions, EstimateStampsResult, GetTokenBalancesOptions, SimulateRequest, TokenApproveOptions, TokenTransferOptions, TransactionReceipt, TransactionSubmission, WaitForTxOptions, XianClientOptions, XianTokenBalancesResult, XianSignedTransaction, XianSigner, XianUnsignedTransaction } from "./types.js";
2
+ import type { BroadcastMode, BroadcastTxOptions, BuildTxRequest, ContractSendOptions, DeployContractOptions, SubmitContractOptions, EstimateChiResult, GetShieldedWalletHistoryOptions, GetTokenBalancesOptions, SimulateRequest, TokenApproveOptions, TokenTransferOptions, TransactionReceipt, TransactionSubmission, WaitForTxOptions, XianClientOptions, XianShieldedWalletHistoryResult, XianTokenBalancesResult, XianSignedTransaction, XianSigner, XianUnsignedTransaction } from "./types.js";
3
+ /**
4
+ * Combine a caller-provided AbortSignal with a timeout-backed internal
5
+ * signal so either one triggering aborts the underlying fetch. We prefer
6
+ * AbortSignal.any when available; on older runtimes we stitch them
7
+ * together manually.
8
+ */
9
+ export declare function mergeAbortSignals(a: AbortSignal | undefined, b: AbortSignal | undefined): AbortSignal | undefined;
3
10
  export declare class XianClient {
4
11
  readonly rpcUrl: string;
5
12
  readonly dashboardUrl?: string;
6
13
  readonly watch: WatchApi;
7
14
  private readonly fetchFn;
15
+ private readonly requestTimeoutMs;
8
16
  private chainIdCache?;
9
17
  constructor(options: XianClientOptions);
10
18
  private requestJson;
@@ -27,11 +35,13 @@ export declare class XianClient {
27
35
  name: string | null;
28
36
  symbol: string | null;
29
37
  logoUrl: string | null;
38
+ logoSvg: string | null;
30
39
  }>;
31
40
  getTokenBalances(address: string, options?: GetTokenBalancesOptions): Promise<XianTokenBalancesResult>;
32
- getStampRate(): Promise<number | bigint | null>;
33
- getContract(contract: string): Promise<string | null>;
34
- getContractCode(contract: string): Promise<string | null>;
41
+ getShieldedWalletHistory(tagValue: string, options?: GetShieldedWalletHistoryOptions): Promise<XianShieldedWalletHistoryResult>;
42
+ getChiRate(): Promise<number | bigint | null>;
43
+ getContractSource(contract: string): Promise<string | null>;
44
+ getContractIr(contract: string): Promise<string | null>;
35
45
  getContractMethods(contract: string): Promise<{
36
46
  name: string;
37
47
  arguments: {
@@ -41,7 +51,7 @@ export declare class XianClient {
41
51
  }[]>;
42
52
  simulate(request: SimulateRequest): Promise<Record<string, unknown>>;
43
53
  call(request: SimulateRequest): Promise<unknown>;
44
- estimateStamps(request: SimulateRequest, options?: EstimateStampsOptions): Promise<EstimateStampsResult>;
54
+ estimateChi(request: SimulateRequest): Promise<EstimateChiResult>;
45
55
  buildTx(request: BuildTxRequest): Promise<XianUnsignedTransaction>;
46
56
  signTx(tx: XianUnsignedTransaction, signer: XianSigner): Promise<XianSignedTransaction>;
47
57
  broadcastTx(tx: XianSignedTransaction, options?: BroadcastTxOptions): Promise<TransactionSubmission>;
@@ -53,6 +63,8 @@ export declare class XianClient {
53
63
  timeoutMs?: number;
54
64
  pollIntervalMs?: number;
55
65
  }): Promise<TransactionSubmission>;
66
+ submitContract(request: SubmitContractOptions): Promise<TransactionSubmission>;
67
+ deployContract(request: DeployContractOptions): Promise<TransactionSubmission>;
56
68
  contract(name: string): ContractClient;
57
69
  token(name?: string): TokenClient;
58
70
  }
@@ -75,6 +87,7 @@ export declare class TokenClient {
75
87
  name: string | null;
76
88
  symbol: string | null;
77
89
  logoUrl: string | null;
90
+ logoSvg: string | null;
78
91
  }>;
79
92
  allowance(owner: string, spender: string): Promise<unknown>;
80
93
  transfer(options: TokenTransferOptions): Promise<TransactionSubmission>;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js CHANGED
@@ -1,12 +1,12 @@
1
- import { base64ToUtf8, bytesToHex, canonicalizeRuntime, decodeRuntime, encodeRuntime, parseXianNumber, sortKeysDeep, utf8ToBytes } from "./encoding.js";
1
+ import { base64ToUtf8, bytesToHex, canonicalizeRuntime, decodeRuntime, encodeRuntime, normalizeMaybeInteger, normalizeMaybeXianNumber, parseXianNumber, sortKeysDeep, utf8ToBytes } from "./encoding.js";
2
+ import { compileContractArtifacts } from "./compiler.js";
2
3
  import { isValidEd25519Signature } from "./ed25519.js";
3
4
  import { AbciError, RpcError, SimulationError, TransactionError, TransportError, TxTimeoutError } from "./errors.js";
4
5
  import { WatchApi } from "./watch.js";
5
6
  const EMPTY_ABCI_VALUE = "AA==";
6
7
  const DEFAULT_TIMEOUT_MS = 30_000;
7
8
  const DEFAULT_POLL_INTERVAL_MS = 500;
8
- const DEFAULT_STAMP_MARGIN = 0.2;
9
- const DEFAULT_MIN_STAMP_HEADROOM = 5_000;
9
+ const MAX_SAFE_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);
10
10
  function stripTrailingSlash(value) {
11
11
  return value.replace(/\/+$/, "");
12
12
  }
@@ -18,9 +18,9 @@ function isIdentifier(value) {
18
18
  }
19
19
  function isNonNegativeInteger(value) {
20
20
  if (typeof value === "bigint") {
21
- return value >= 0n;
21
+ return value >= 0n && value <= MAX_SAFE_BIGINT;
22
22
  }
23
- return typeof value === "number" && Number.isInteger(value) && value >= 0;
23
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
24
24
  }
25
25
  function isHexKey(value) {
26
26
  return /^[a-fA-F0-9]{64}$/.test(value);
@@ -42,6 +42,30 @@ function asRecord(value) {
42
42
  }
43
43
  return value;
44
44
  }
45
+ function isPlainObject(value) {
46
+ return Object.prototype.toString.call(value) === "[object Object]";
47
+ }
48
+ function validateTransactionJsonValue(value) {
49
+ if (value == null || typeof value === "string" || typeof value === "boolean") {
50
+ return true;
51
+ }
52
+ if (typeof value === "bigint") {
53
+ return true;
54
+ }
55
+ if (typeof value === "number") {
56
+ return Number.isSafeInteger(value);
57
+ }
58
+ if (value instanceof Uint8Array) {
59
+ return true;
60
+ }
61
+ if (Array.isArray(value)) {
62
+ return value.every((item) => validateTransactionJsonValue(item));
63
+ }
64
+ if (isPlainObject(value)) {
65
+ return Object.values(value).every((item) => validateTransactionJsonValue(item));
66
+ }
67
+ return false;
68
+ }
45
69
  function parseJsonResult(value) {
46
70
  try {
47
71
  return JSON.parse(value);
@@ -65,17 +89,40 @@ function normalizeMaybeString(value) {
65
89
  }
66
90
  return typeof value === "string" ? value : String(value);
67
91
  }
68
- function normalizeMaybeXianNumber(value) {
92
+ /**
93
+ * Coerce a chi_used value from a simulation response to a JS number.
94
+ *
95
+ * The simulator returns chi_used as either a number or a stringified
96
+ * integer. Historically we used Number.parseInt here, which silently
97
+ * truncates on scientific notation ("1e6" → 1), decimals ("1.5" → 1),
98
+ * and loses precision above 2^53 for large strings. parseXianNumber
99
+ * runs it through BigInt so oversized values are flagged. Chi values
100
+ * in practice fit comfortably inside Number.MAX_SAFE_INTEGER, so we
101
+ * collapse the bigint branch back to a number and throw only if the
102
+ * estimate is actually too large to represent safely — that can't
103
+ * happen today, but an explicit failure beats a silent mis-estimate.
104
+ */
105
+ function coerceChiEstimate(value) {
106
+ if (typeof value === "number" && Number.isFinite(value)) {
107
+ return value;
108
+ }
69
109
  if (value == null) {
70
- return null;
110
+ throw new Error("simulation response is missing chi_used");
71
111
  }
72
- if (typeof value === "number" || typeof value === "bigint") {
73
- return value;
112
+ const raw = String(value).trim();
113
+ if (raw === "") {
114
+ throw new Error("simulation response has empty chi_used");
74
115
  }
75
- if (typeof value === "string" && /^-?\d+$/.test(value)) {
76
- return parseXianNumber(value);
116
+ try {
117
+ const parsed = parseXianNumber(raw);
118
+ if (typeof parsed === "bigint") {
119
+ throw new Error(`chi_used estimate ${raw} exceeds Number.MAX_SAFE_INTEGER`);
120
+ }
121
+ return parsed;
122
+ }
123
+ catch (err) {
124
+ throw new Error(`invalid chi_used estimate ${JSON.stringify(value)}: ${err instanceof Error ? err.message : String(err)}`);
77
125
  }
78
- return null;
79
126
  }
80
127
  function clampPageSize(value, fallback) {
81
128
  if (typeof value !== "number" || !Number.isFinite(value)) {
@@ -92,9 +139,36 @@ function clampOffset(value) {
92
139
  function isPendingLookupReceipt(receipt) {
93
140
  return !receipt.success && receipt.txHash == null && receipt.transaction == null;
94
141
  }
142
+ /**
143
+ * Combine a caller-provided AbortSignal with a timeout-backed internal
144
+ * signal so either one triggering aborts the underlying fetch. We prefer
145
+ * AbortSignal.any when available; on older runtimes we stitch them
146
+ * together manually.
147
+ */
148
+ export function mergeAbortSignals(a, b) {
149
+ if (!a)
150
+ return b;
151
+ if (!b)
152
+ return a;
153
+ const anyFn = AbortSignal.any;
154
+ if (typeof anyFn === "function") {
155
+ return anyFn([a, b]);
156
+ }
157
+ const controller = new AbortController();
158
+ const forward = (signal) => {
159
+ if (signal.aborted) {
160
+ controller.abort(signal.reason);
161
+ return;
162
+ }
163
+ signal.addEventListener("abort", () => controller.abort(signal.reason), { once: true });
164
+ };
165
+ forward(a);
166
+ forward(b);
167
+ return controller.signal;
168
+ }
95
169
  function validatePayload(payload) {
96
170
  const keys = Object.keys(payload).sort();
97
- const expected = ["chain_id", "contract", "function", "kwargs", "nonce", "sender", "stamps_supplied"];
171
+ const expected = ["chain_id", "chi_supplied", "contract", "function", "kwargs", "nonce", "sender"];
98
172
  if (keys.join(",") !== expected.join(",")) {
99
173
  throw new TransactionError("payload must contain the canonical Xian transaction keys");
100
174
  }
@@ -110,20 +184,27 @@ function validatePayload(payload) {
110
184
  if (!isNonNegativeInteger(payload.nonce)) {
111
185
  throw new TransactionError("nonce must be a non-negative integer");
112
186
  }
113
- if (!isNonNegativeInteger(payload.stamps_supplied)) {
114
- throw new TransactionError("stamps_supplied must be a non-negative integer");
187
+ if (!isNonNegativeInteger(payload.chi_supplied)) {
188
+ throw new TransactionError("chi_supplied must be a non-negative integer");
189
+ }
190
+ if (!isPlainObject(payload.kwargs)) {
191
+ throw new TransactionError("kwargs must be an object");
115
192
  }
116
193
  for (const key of Object.keys(payload.kwargs)) {
117
194
  if (!isIdentifier(key)) {
118
195
  throw new TransactionError("kwargs keys must be valid identifiers");
119
196
  }
120
197
  }
198
+ if (!validateTransactionJsonValue(payload.kwargs)) {
199
+ throw new TransactionError("kwargs values must be JSON-compatible transaction values");
200
+ }
121
201
  }
122
202
  export class XianClient {
123
203
  rpcUrl;
124
204
  dashboardUrl;
125
205
  watch;
126
206
  fetchFn;
207
+ requestTimeoutMs;
127
208
  chainIdCache;
128
209
  constructor(options) {
129
210
  this.rpcUrl = stripTrailingSlash(options.rpcUrl);
@@ -132,19 +213,43 @@ export class XianClient {
132
213
  : undefined;
133
214
  this.chainIdCache = options.chainId;
134
215
  this.fetchFn = options.fetchFn ?? globalThis.fetch.bind(globalThis);
216
+ this.requestTimeoutMs =
217
+ options.requestTimeoutMs ?? DEFAULT_TIMEOUT_MS;
135
218
  this.watch = new WatchApi({
136
219
  dashboardUrl: this.dashboardUrl,
137
220
  webSocketFactory: options.webSocketFactory
138
221
  });
139
222
  }
140
- async requestJson(method, url) {
223
+ async requestJson(method, url, options) {
224
+ const timeoutMs = options?.timeoutMs ?? this.requestTimeoutMs;
225
+ const timeoutController = timeoutMs > 0 ? new AbortController() : undefined;
226
+ const timeoutHandle = timeoutController
227
+ ? setTimeout(() => timeoutController.abort(), timeoutMs)
228
+ : undefined;
229
+ const callerSignal = options?.signal;
230
+ const signal = mergeAbortSignals(callerSignal, timeoutController?.signal);
141
231
  let response;
142
232
  try {
143
- response = await this.fetchFn(url, { method });
233
+ response = await this.fetchFn(url, {
234
+ method,
235
+ headers: method === "POST" ? { "content-type": "application/json" } : undefined,
236
+ signal,
237
+ });
144
238
  }
145
239
  catch (error) {
240
+ if (callerSignal?.aborted) {
241
+ throw error;
242
+ }
243
+ if (timeoutController?.signal.aborted) {
244
+ throw new TransportError(`request timed out after ${timeoutMs}ms for ${url}`, { cause: error });
245
+ }
146
246
  throw new TransportError(`request failed for ${url}`, { cause: error });
147
247
  }
248
+ finally {
249
+ if (timeoutHandle !== undefined) {
250
+ clearTimeout(timeoutHandle);
251
+ }
252
+ }
148
253
  if (!response.ok) {
149
254
  throw new TransportError(`${method} ${url} returned ${response.status}`);
150
255
  }
@@ -269,24 +374,24 @@ export class XianClient {
269
374
  }
270
375
  }
271
376
  async getTokenMetadata(contract) {
272
- if (contract === "currency") {
273
- return {
274
- contract,
275
- name: "Xian",
276
- symbol: "Xian",
277
- logoUrl: null
278
- };
279
- }
280
377
  const [name, symbol, logoUrl] = await Promise.all([
281
378
  this.getState(contract, "metadata", ["token_name"]),
282
379
  this.getState(contract, "metadata", ["token_symbol"]),
283
380
  this.getState(contract, "metadata", ["token_logo_url"])
284
381
  ]);
382
+ const rawLogoUrl = normalizeMaybeString(logoUrl);
383
+ const normalizedLogoUrl = rawLogoUrl?.trim() ? rawLogoUrl.trim() : null;
384
+ let logoSvg = null;
385
+ if (!normalizedLogoUrl) {
386
+ const rawLogoSvg = normalizeMaybeString(await this.getState(contract, "metadata", ["token_logo_svg"]).catch(() => null));
387
+ logoSvg = rawLogoSvg?.trim() ? rawLogoSvg.trim() : null;
388
+ }
285
389
  return {
286
390
  contract,
287
391
  name: normalizeMaybeString(name),
288
392
  symbol: normalizeMaybeString(symbol),
289
- logoUrl: normalizeMaybeString(logoUrl)
393
+ logoUrl: normalizedLogoUrl,
394
+ logoSvg
290
395
  };
291
396
  }
292
397
  async getTokenBalances(address, options) {
@@ -323,8 +428,47 @@ export class XianClient {
323
428
  offset: Number(payload.offset ?? offset)
324
429
  };
325
430
  }
326
- async getStampRate() {
327
- const value = await this.getState("stamp_cost", "S", ["value"]);
431
+ async getShieldedWalletHistory(tagValue, options) {
432
+ const limit = clampPageSize(options?.limit, 100);
433
+ const afterNoteIndex = clampOffset(options?.afterNoteIndex);
434
+ const kind = typeof options?.kind === "string" && options.kind.trim().length > 0
435
+ ? options.kind.trim()
436
+ : "sync_hint";
437
+ const data = await this.abciQuery(`/shielded_wallet_history/${tagValue}/limit=${limit}/kind=${kind}/after_note_index=${afterNoteIndex}`);
438
+ const value = this.decodeAbciValue(asRecord(asRecord(data.result).response).value);
439
+ const payload = asRecord(value);
440
+ const items = Array.isArray(payload.items)
441
+ ? payload.items
442
+ .filter((item) => item != null && typeof item === "object")
443
+ .map((item) => ({
444
+ eventId: normalizeMaybeInteger(item.event_id),
445
+ txHash: normalizeMaybeString(item.tx_hash),
446
+ blockHeight: normalizeMaybeXianNumber(item.block_height),
447
+ txIndex: normalizeMaybeXianNumber(item.tx_index),
448
+ contract: normalizeMaybeString(item.contract),
449
+ function: normalizeMaybeString(item.function),
450
+ action: normalizeMaybeString(item.action),
451
+ outputIndex: normalizeMaybeXianNumber(item.output_index),
452
+ noteIndex: normalizeMaybeXianNumber(item.note_index),
453
+ commitment: normalizeMaybeString(item.commitment),
454
+ newRoot: normalizeMaybeString(item.new_root),
455
+ payloadHash: normalizeMaybeString(item.payload_hash),
456
+ tagKind: normalizeMaybeString(item.tag_kind),
457
+ tagValue: normalizeMaybeString(item.tag_value),
458
+ outputPayload: normalizeMaybeString(item.output_payload),
459
+ createdAt: normalizeMaybeString(item.created_at ?? item.created),
460
+ raw: item
461
+ }))
462
+ : [];
463
+ return {
464
+ available: payload.available !== false,
465
+ items,
466
+ limit: Number(payload.limit ?? limit),
467
+ afterNoteIndex: Number(payload.after_note_index ?? afterNoteIndex)
468
+ };
469
+ }
470
+ async getChiRate() {
471
+ const value = await this.getState("chi_cost", "S", ["value"]);
328
472
  if (value == null) {
329
473
  return null;
330
474
  }
@@ -333,16 +477,16 @@ export class XianClient {
333
477
  }
334
478
  return parseXianNumber(String(value));
335
479
  }
336
- async getContract(contract) {
337
- const data = await this.abciQuery(`/contract/${contract}`);
480
+ async getContractSource(contract) {
481
+ const data = await this.abciQuery(`/contract_source/${contract}`);
338
482
  const value = asRecord(asRecord(data.result).response).value;
339
483
  if (value == null || value === EMPTY_ABCI_VALUE) {
340
484
  return null;
341
485
  }
342
486
  return base64ToUtf8(String(value));
343
487
  }
344
- async getContractCode(contract) {
345
- const data = await this.abciQuery(`/contract_code/${contract}`);
488
+ async getContractIr(contract) {
489
+ const data = await this.abciQuery(`/contract_ir/${contract}`);
346
490
  const value = asRecord(asRecord(data.result).response).value;
347
491
  if (value == null || value === EMPTY_ABCI_VALUE) {
348
492
  return null;
@@ -405,34 +549,26 @@ export class XianClient {
405
549
  }
406
550
  return simulation.result;
407
551
  }
408
- async estimateStamps(request, options) {
552
+ async estimateChi(request) {
409
553
  const simulation = await this.simulate(request);
410
- const rawStamps = simulation.stamps_used;
411
- const estimated = typeof rawStamps === "number"
412
- ? rawStamps
413
- : Number.parseInt(String(rawStamps ?? "0"), 10);
414
- const stampMargin = options?.stampMargin ?? DEFAULT_STAMP_MARGIN;
415
- const minStampHeadroom = options?.minStampHeadroom ?? DEFAULT_MIN_STAMP_HEADROOM;
416
- const proportional = Math.ceil(estimated * stampMargin);
417
- const suggested = estimated + Math.max(proportional, minStampHeadroom);
554
+ const estimated = coerceChiEstimate(simulation.chi_used);
418
555
  return {
419
556
  estimated,
420
- suggested,
421
557
  simulation
422
558
  };
423
559
  }
424
560
  async buildTx(request) {
425
561
  const chainId = request.chainId ?? (await this.getChainId());
426
562
  const nonce = request.nonce ?? (await this.getNonce(request.sender));
427
- let stampsSupplied = request.stampsSupplied ?? request.stamps;
428
- if (stampsSupplied == null) {
429
- const estimate = await this.estimateStamps({
563
+ let chiSupplied = request.chiSupplied ?? request.chi;
564
+ if (chiSupplied == null) {
565
+ const estimate = await this.estimateChi({
430
566
  sender: request.sender,
431
567
  contract: request.contract,
432
568
  function: request.function,
433
569
  kwargs: request.kwargs
434
570
  });
435
- stampsSupplied = estimate.suggested;
571
+ chiSupplied = estimate.estimated;
436
572
  }
437
573
  const payload = sortKeysDeep({
438
574
  chain_id: chainId,
@@ -441,7 +577,7 @@ export class XianClient {
441
577
  kwargs: request.kwargs,
442
578
  nonce,
443
579
  sender: request.sender,
444
- stamps_supplied: stampsSupplied
580
+ chi_supplied: chiSupplied
445
581
  });
446
582
  validatePayload(payload);
447
583
  return { payload };
@@ -486,7 +622,7 @@ export class XianClient {
486
622
  message: "error" in data ? readErrorMessage(asRecord(data.error)) : undefined,
487
623
  mode,
488
624
  nonce: tx.payload.nonce,
489
- stampsSupplied: tx.payload.stamps_supplied,
625
+ chiSupplied: tx.payload.chi_supplied,
490
626
  response: data
491
627
  };
492
628
  if ("error" in data) {
@@ -550,6 +686,57 @@ export class XianClient {
550
686
  pollIntervalMs: request.pollIntervalMs
551
687
  });
552
688
  }
689
+ async submitContract(request) {
690
+ if (typeof request.signer.getAddress !== "function") {
691
+ throw new TransactionError("signer.getAddress() is required for contract submission");
692
+ }
693
+ if (!isPlainObject(request.deploymentArtifacts)) {
694
+ throw new TransactionError("deploymentArtifacts must be an object");
695
+ }
696
+ if ("runtime_code" in request.deploymentArtifacts) {
697
+ throw new TransactionError("deploymentArtifacts must not include runtime_code");
698
+ }
699
+ const hashes = request.deploymentArtifacts.hashes;
700
+ if (isPlainObject(hashes) && "runtime_code_sha256" in hashes) {
701
+ throw new TransactionError("deploymentArtifacts hashes must not include runtime_code_sha256");
702
+ }
703
+ const kwargs = {
704
+ name: request.name,
705
+ deployment_artifacts: request.deploymentArtifacts
706
+ };
707
+ if (request.args && Object.keys(request.args).length > 0) {
708
+ kwargs.constructor_args = request.args;
709
+ }
710
+ const sender = await request.signer.getAddress();
711
+ return this.sendTx({
712
+ sender,
713
+ contract: "submission",
714
+ function: "submit_contract",
715
+ kwargs,
716
+ signer: request.signer,
717
+ mode: request.mode,
718
+ waitForTx: request.waitForTx,
719
+ timeoutMs: request.timeoutMs,
720
+ pollIntervalMs: request.pollIntervalMs,
721
+ chi: request.chi,
722
+ nonce: request.nonce,
723
+ chainId: request.chainId
724
+ });
725
+ }
726
+ async deployContract(request) {
727
+ const { source, compiler, lint, vmProfile, ...submitRequest } = request;
728
+ const deploymentArtifacts = await compileContractArtifacts({
729
+ moduleName: request.name,
730
+ source,
731
+ compiler,
732
+ lint,
733
+ vmProfile
734
+ });
735
+ return this.submitContract({
736
+ ...submitRequest,
737
+ deploymentArtifacts
738
+ });
739
+ }
553
740
  contract(name) {
554
741
  return new ContractClient(this, name);
555
742
  }
@@ -594,7 +781,7 @@ export class ContractClient {
594
781
  waitForTx: options.waitForTx,
595
782
  timeoutMs: options.timeoutMs,
596
783
  pollIntervalMs: options.pollIntervalMs,
597
- stamps: options.stamps,
784
+ chi: options.chi,
598
785
  nonce: options.nonce,
599
786
  chainId: options.chainId
600
787
  });
@@ -634,7 +821,7 @@ export class TokenClient {
634
821
  waitForTx: options.waitForTx,
635
822
  timeoutMs: options.timeoutMs,
636
823
  pollIntervalMs: options.pollIntervalMs,
637
- stamps: options.stamps,
824
+ chi: options.chi,
638
825
  nonce: options.nonce,
639
826
  chainId: options.chainId
640
827
  });
@@ -657,7 +844,7 @@ export class TokenClient {
657
844
  waitForTx: options.waitForTx,
658
845
  timeoutMs: options.timeoutMs,
659
846
  pollIntervalMs: options.pollIntervalMs,
660
- stamps: options.stamps,
847
+ chi: options.chi,
661
848
  nonce: options.nonce,
662
849
  chainId: options.chainId
663
850
  });