suidouble 1.0.4 → 1.0.5

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
@@ -168,7 +168,9 @@ while (events.hasNextPage) {
168
168
 
169
169
  ##### subscribing to events
170
170
 
171
- You can subscribe to Sui's contract events on package's module level. No types-etc filters for now ( @todo? )
171
+ *** Subscribe to Events is deprecated in Sui SDK *** You should plan to use different architecture in your application.
172
+
173
+ You can subscribe to Sui's contract events on package's module level.
172
174
 
173
175
  ```javascript
174
176
  const module = await contract.getModule('suidouble_chat');
@@ -228,7 +230,7 @@ arguments.push(contract.arg('u128', 5555));
228
230
  arguments.push(contract.arg('u256', 6666));
229
231
  arguments.push(contract.arg('address', '0xd9a95d7cc137f71dd7766f02791536453062a7509e9f461620cc4f583b09134c'));
230
232
  arguments.push(contract.arg('string', 'some utf-8 💧string'));
231
- arguments.push(contract.arg('vector<u8>', 222)); // works for other vectors with primitive contents, e.g. u128, bool etc
233
+ arguments.push(contract.arg('vector<u8>', [222,111,211])); // works for other vectors with primitive contents, e.g. u128, bool etc
232
234
  ```
233
235
 
234
236
  Take a look at unit test covering all types arguments [here](test/different_types_args.test.js)
package/lib/SuiCoin.js CHANGED
@@ -1,4 +1,4 @@
1
- const { Commands } = require('@mysten/sui/transactions');
1
+ const { Commands, Transaction } = require('@mysten/sui/transactions');
2
2
  const { bcs } = require('@mysten/sui/bcs');
3
3
  // console.log(bcs);
4
4
 
@@ -102,6 +102,15 @@ class SuiCoin {
102
102
  return '0x'+this._coinType;
103
103
  }
104
104
 
105
+ /**
106
+ * Move type for the Coin object of this coin type
107
+ *
108
+ * @type {string}
109
+ */
110
+ get coinObjectType() {
111
+ return '0x2::coin::Coin<'+this.coinType+'>';
112
+ }
113
+
105
114
  get decimals() {
106
115
  if (this.metadata) {
107
116
  return this.metadata.decimals;
@@ -202,6 +211,15 @@ class SuiCoin {
202
211
  return totalAmount;
203
212
  }
204
213
 
214
+ /**
215
+ * Returns TransactionObjectArgument with Coin of amount to be used in tranasctions
216
+ *
217
+ * @param {import('@mysten/sui/transactions').Transaction} txb - Native SUI SDK Transaction
218
+ * @param {string} owner - address of the owner
219
+ * @param {BigInt|string} amount - amount of coin. BigIng or String to be normalized via Coin decimals, "0.05" for 0.05 sui
220
+ * @param {boolean} addEmptyCoins - attach coins == 0 to the list
221
+ * @returns {import('@mysten/sui/transactions').TransactionObjectArgument}
222
+ */
205
223
  async coinOfAmountToTxCoin(txb, owner, amount, addEmptyCoins = false) {
206
224
  const normalizedAmount = await this.lazyNormalizeAmount(amount);
207
225
 
@@ -215,12 +233,6 @@ class SuiCoin {
215
233
  if (coinIds.length == 1) {
216
234
  // only one coin object enough to cover the expense
217
235
  if (this.isSUI()) {
218
- // console.log(txb.gas);
219
- // console.log(bcs);
220
- // console.log(txb.pure(bcs.vector(bcs.u64).serialize([expectedAmountAsBigInt])));
221
- // console.log(txb.pure.u64(expectedAmountAsBigInt));
222
- // txb.pure(bcs.vector(bcs.u64).serialize(['0x123']))
223
-
224
236
  const coinInput = txb.add(Commands.SplitCoins(txb.gas, [txb.pure.u64(expectedAmountAsBigInt)]));
225
237
  return coinInput;
226
238
  } else {
package/lib/SuiCoins.js CHANGED
@@ -97,6 +97,12 @@ class SuiCoins extends SuiCommonMethods {
97
97
  return coinType;
98
98
  }
99
99
 
100
+ /**
101
+ * Return instance of SuiCoin of specific type
102
+ *
103
+ * @param {string} coinType - MoveType, or 'SUI' as helper
104
+ * @returns {SuiCoin}
105
+ */
100
106
  get(coinType) {
101
107
  const normalizedCoinType = this.normalizeCoinType(coinType);
102
108
  let suiCoin = this._coins[normalizedCoinType];
@@ -42,6 +42,10 @@ class SuiInBrowser extends SuiCommonMethods {
42
42
  return await this._activeAdapter.signAndExecuteTransactionBlock(params);
43
43
  }
44
44
 
45
+ async signAndExecuteTransaction(params) {
46
+ return await this._activeAdapter.signAndExecuteTransaction(params);
47
+ }
48
+
45
49
  get client() {
46
50
  return this._client;
47
51
  }
@@ -8,6 +8,8 @@ const Feature = {
8
8
  EVENTS: 'standard:events',
9
9
  SUI_SIGN_AND_EXECUTE_TX_BLOCK: 'sui:signAndExecuteTransactionBlock',
10
10
  SUI_SIGN_TX_BLOCK: 'sui:signTransactionBlock',
11
+ SUI_SIGN_AND_EXECUTE_TX: 'sui:signAndExecuteTransaction',
12
+ SUI_SIGN_TX: 'sui:signTransaction',
11
13
  SUI_SIGN_MESSAGE: 'sui:signMessage'
12
14
  };
13
15
 
@@ -30,10 +32,30 @@ class SuiInBrowserAdapter extends SuiCommonMethods {
30
32
  this._isConnected = false;
31
33
  }
32
34
 
35
+ async signAndExecuteTransaction(params) {
36
+ if (this.hasFeature(Feature.SUI_SIGN_AND_EXECUTE_TX)) {
37
+ return await this.getFeature(Feature.SUI_SIGN_AND_EXECUTE_TX).signAndExecuteTransaction(params);
38
+ } else {
39
+ // outdated wallet?
40
+ params.transactionBlock = params.transaction;
41
+ return await this.getFeature(Feature.SUI_SIGN_AND_EXECUTE_TX_BLOCK).signAndExecuteTransactionBlock(params);
42
+ }
43
+ }
44
+
33
45
  async signAndExecuteTransactionBlock(params) {
34
46
  return await this.getFeature(Feature.SUI_SIGN_AND_EXECUTE_TX_BLOCK).signAndExecuteTransactionBlock(params);
35
47
  }
36
48
 
49
+ async signTransactionBlock(params) {
50
+ if (this.hasFeature(Feature.SUI_SIGN_TX)) {
51
+ return await this.getFeature(Feature.SUI_SIGN_TX).signTransaction(params);
52
+ } else {
53
+ // outdated wallet?
54
+ params.transactionBlock = params.transaction;
55
+ return await this.getFeature(Feature.SUI_SIGN_TX_BLOCK).signTransactionBlock(params);
56
+ }
57
+ }
58
+
37
59
  async signTransactionBlock(params) {
38
60
  return await this.getFeature(Feature.SUI_SIGN_TX_BLOCK).signTransactionBlock(params);
39
61
  }
@@ -1,7 +1,6 @@
1
- // const { spawn } = require('child_process');
1
+ const net = require("net");
2
2
  const SuiCliCommands = require('./SuiCliCommands.js');
3
3
  const SuiCommonMethods = require('./SuiCommonMethods.js');
4
- // const { JsonRpcProvider, localnetConnection, devnetConnection } = require('@mysten/sui.js');
5
4
 
6
5
  const { SuiClient, getFullnodeUrl, SuiHTTPTransport } = require('@mysten/sui/client');
7
6
  const SuiUtils = require('./SuiUtils.js');
@@ -65,6 +64,51 @@ class SuiLocalTestValidator extends SuiCommonMethods {
65
64
  }
66
65
  }
67
66
 
67
+ async isPortThere(port) {
68
+ const Socket = net.Socket;
69
+ const socket = new Socket();
70
+
71
+ let __waitPortPromiseResolver = null;
72
+ const __waitPortPromise = new Promise((res)=>{ __waitPortPromiseResolver = res; });
73
+
74
+ setTimeout(()=>{
75
+ socket.destroy();
76
+ __waitPortPromiseResolver(false);
77
+ }, 3000);
78
+ socket.on("connect", () => {
79
+ __waitPortPromiseResolver(true);
80
+ });
81
+ socket.on("error", () =>
82
+ {
83
+ __waitPortPromiseResolver(false);
84
+ });
85
+ socket.on("timeout", () => {
86
+ __waitPortPromiseResolver(false);
87
+ });
88
+
89
+ socket.connect(port, "0.0.0.0");
90
+
91
+ const portIsThere = await __waitPortPromise;
92
+ socket.destroy();
93
+
94
+ return portIsThere;
95
+ }
96
+
97
+ async waitForPort(port, timeout) {
98
+ this.log('waiting for port', port);
99
+ const startedCheckingAt = (new Date()).getTime();
100
+ let portIsThere = false;
101
+ do {
102
+ portIsThere = await this.isPortThere(port);
103
+ this.log('checking for port', port, 'is there:', portIsThere);
104
+ if (!portIsThere) {
105
+ await new Promise((res)=>setTimeout(res, 500));
106
+ }
107
+ } while (!portIsThere && (startedCheckingAt + timeout) > ((new Date()).getTime()));
108
+
109
+ return portIsThere;
110
+ }
111
+
68
112
  async launch() {
69
113
  if (this._active) {
70
114
  return this;
@@ -73,12 +117,22 @@ class SuiLocalTestValidator extends SuiCommonMethods {
73
117
  this.log('launching sui-test-validator ...');
74
118
 
75
119
  try {
76
- const params = [];
77
- if (this._epochDuration) {
78
- params.push('--epoch-duration-ms');
79
- params.push(this._epochDuration);
120
+ try {
121
+ // try sui-test-validator
122
+ const params = [];
123
+ if (this._epochDuration) {
124
+ params.push('--epoch-duration-ms');
125
+ params.push(this._epochDuration);
126
+ }
127
+ this._child = await SuiCliCommands.spawn('sui-test-validator', params, { RUST_LOG: 'consensus=off' });
128
+ } catch (e) {
129
+ this.log('can not launch sui-test-validator. Trying to run "sui start"...');
130
+ const params = [];
131
+ params.push('start');
132
+ params.push('--with-faucet');
133
+ params.push('--force-regenesis');
134
+ this._child = await SuiCliCommands.spawn('sui', params, { RUST_LOG: 'off,sui_node=info' });
80
135
  }
81
- this._child = await SuiCliCommands.spawn('sui-test-validator', params, { RUST_LOG: 'consensus=off' });
82
136
  } catch (e) {
83
137
  if (this._testFallbackEnabled) {
84
138
  // can't start local node. Let's switch to sui:dev
@@ -93,29 +147,6 @@ class SuiLocalTestValidator extends SuiCommonMethods {
93
147
  throw e;
94
148
  }
95
149
  }
96
-
97
- this.__readyLaunchedPromiseResolver = null;
98
- this.__readyLaunchedPromise = new Promise((res)=>{
99
- this.__readyLaunchedPromiseResolver = res;
100
- });
101
-
102
- this._child.stdout.on('data', (data) => {
103
- this.log(`stdout:\n${data}`);
104
- if ((`${data}`).indexOf('Fullnode RPC URL') !== -1) {
105
- this._active = true;
106
-
107
- this.log('sui-test-validator launched');
108
- this.__readyLaunchedPromiseResolver();
109
- }
110
- });
111
-
112
- this._child.stderr.on('data', (data) => {
113
- this.log(`stderr: ${data}`);
114
- });
115
-
116
- this._child.on('error', (error) => {
117
- this.log(`error: ${error.message}`);
118
- });
119
150
 
120
151
  this._child.on('close', (code) => {
121
152
  this._active = false;
@@ -131,7 +162,9 @@ class SuiLocalTestValidator extends SuiCommonMethods {
131
162
  process.on('SIGINT', cleanExit); // catch ctrl-c
132
163
  process.on('SIGTERM', cleanExit); // catch kill
133
164
 
134
- await this.__readyLaunchedPromise;
165
+ this._active = await this.waitForPort(9123, 30000);
166
+
167
+ // await this.__readyLaunchedPromise;
135
168
 
136
169
  return this;
137
170
  }
package/lib/SuiMaster.js CHANGED
@@ -102,6 +102,11 @@ class SuiMaster extends SuiCommonMethods {
102
102
  return SuiUtils;
103
103
  }
104
104
 
105
+ /**
106
+ * Instance of SuiCoins class connected to this SuiMaster
107
+ *
108
+ * @type {SuiCoins}
109
+ */
105
110
  get suiCoins() {
106
111
  return this._suiCoins;
107
112
  }
@@ -110,12 +115,8 @@ class SuiMaster extends SuiCommonMethods {
110
115
  return BigInt(MIST_PER_SUI);
111
116
  }
112
117
 
113
- get TransactionBlock() {
114
- return TransactionBlock;
115
- }
116
-
117
- get Transactions() {
118
- return Transactions;
118
+ get Transaction() {
119
+ return Transaction;
119
120
  }
120
121
 
121
122
  get Commands() {
@@ -7,8 +7,18 @@ const SuiEvent = require('./SuiEvent.js');
7
7
 
8
8
  const { Transaction } = require('@mysten/sui/transactions');
9
9
  const { normalizeSuiAddress } = require('@mysten/sui/utils');
10
+ const SuiMaster = require('./SuiMaster.js');
11
+ const SuiPackage = require('./SuiPackage.js');
10
12
 
11
13
  class SuiPackageModule extends SuiCommonMethods {
14
+
15
+ /**
16
+ * SuiPackageModule constructor
17
+ * @param {Object} params - Initialization parameters
18
+ * @param {SuiPackage} params.package - instance of SuiPackage this module is part of
19
+ * @param {SuiMaster} params.suiMaster - instance of SuiMaster
20
+ * @param {string} params.moduleName - name of the Move module
21
+ */
12
22
  constructor(params = {}) {
13
23
  super(params);
14
24
 
@@ -16,6 +26,7 @@ class SuiPackageModule extends SuiCommonMethods {
16
26
  if (!this._package) {
17
27
  throw new Error('package is required for SuiPackageModule');
18
28
  }
29
+
19
30
  this._suiMaster = params.suiMaster;
20
31
  if (!this._suiMaster) {
21
32
  throw new Error('suiMaster is requried for SuiPackageModule');
@@ -25,9 +36,6 @@ class SuiPackageModule extends SuiCommonMethods {
25
36
  throw new Error('moduleName is required for SuiPackageModule');
26
37
  }
27
38
 
28
- // this._objects = {};
29
- // this._objectsArray = [];
30
-
31
39
  // we need to get very first version's address of this package to use for types, so we are doing this in separate call
32
40
  this._checkedOnChain = false;
33
41
  this._normalizedMoveModule = {};
@@ -154,10 +162,10 @@ class SuiPackageModule extends SuiCommonMethods {
154
162
  // vector<Coin<SUI>>
155
163
  const ownerAddress = this._suiMaster.address;
156
164
 
157
- const suiCoin = await this._suiMaster.suiCoins.get(param.type);
158
- const txCoinToSend = await suiCoin.coinOfAmountToTxCoin(tx, ownerAddress, param.amount);
165
+ const suiCoin = await this._suiMaster.suiCoins.get(param[0].type);
166
+ const txCoinToSend = await suiCoin.coinOfAmountToTxCoin(tx, ownerAddress, param[0].amount);
159
167
 
160
- callArgs.push(tx.makeMoveVec({ objects: [txCoinToSend]}));
168
+ callArgs.push(tx.makeMoveVec({ type: suiCoin.coinObjectType, elements: [txCoinToSend]}));
161
169
  } else {
162
170
  callArgs.push(tx.pure(param));
163
171
  }
package/lib/SuiUtils.js CHANGED
@@ -128,7 +128,7 @@ class SuiUtils extends SuiCommonMethods {
128
128
  client = SuiUtils.suiClientFor('mainnet');
129
129
  providerName = 'sui:mainnet';
130
130
  } else {
131
- if (clientParam && clientParam.constructor && clientParam.constructor.name && clientParam.constructor.name == 'SuiClient') {
131
+ if (clientParam && clientParam.constructor && (clientParam.endpoint || clientParam.transport)) {
132
132
  client = clientParam;
133
133
  let url = '';
134
134
  if (clientParam.endpoint) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "suidouble",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "Set of provider, package and object classes for javascript representation of Sui Move smart contracts. Use same code for publishing, upgrading, integration testing, interaction with smart contracts and integration in browser web3 dapps",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -21,13 +21,18 @@
21
21
  "author": "Jeka Kiselyov <jeka911@gmail.com> (https://github.com/jeka-kiselyov)",
22
22
  "license": "Apache-2.0",
23
23
  "dependencies": {
24
- "@mysten/sui": "^1.0.4",
24
+ "@mysten/sui": "1.0.5",
25
25
  "@wallet-standard/core": "^1.0.3",
26
26
  "websocket": "^1.0.35"
27
27
  },
28
28
  "devDependencies": {
29
29
  "tap": "^16.3.4"
30
30
  },
31
+ "browser": {
32
+ "child_process": false,
33
+ "fs": false,
34
+ "path": false
35
+ },
31
36
  "tap": {
32
37
  "branches": 90,
33
38
  "lines": 90,
@@ -152,33 +152,36 @@ test('can find a package on the blockchain by expected module name (in owned)',
152
152
  t.equal(contract.version, 2);
153
153
  });
154
154
 
155
- test('subscribe to module events', async t => {
156
- const module = await contract.getModule('suidouble_chat');
157
- await module.subscribeEvents();
155
+ // Event websocket subscriptions are going to be deprecated.
156
+ // test('subscribe to module events', async t => {
157
+ // const module = await contract.getModule('suidouble_chat');
158
+ // await module.subscribeEvents();
158
159
 
159
- let gotEventChatTopMessageCreated = false;
160
- let gotEventChatResponseCreated = false;
160
+ // let gotEventChatTopMessageCreated = false;
161
+ // let gotEventChatResponseCreated = false;
161
162
 
162
- module.addEventListener('ChatTopMessageCreated', (event)=>{
163
- gotEventChatTopMessageCreated = event;
164
- });
165
- module.addEventListener('ChatResponseCreated', (event)=>{
166
- gotEventChatResponseCreated = event.detail; // .detail is reference to event itself. To support CustomEvent pattern
167
- });
163
+ // module.addEventListener('ChatTopMessageCreated', (event)=>{
164
+ // gotEventChatTopMessageCreated = event;
165
+ // });
166
+ // module.addEventListener('ChatResponseCreated', (event)=>{
167
+ // gotEventChatResponseCreated = event.detail; // .detail is reference to event itself. To support CustomEvent pattern
168
+ // });
168
169
 
169
- await contract.moveCall('suidouble_chat', 'post', [chatShopObjectId, contract.arg('string', 'the message'), contract.arg('string', 'metadata')]);
170
- await new Promise((res)=>setTimeout(res, 300)); // got events without timeout, but just to be sure.
170
+ // await contract.moveCall('suidouble_chat', 'post', [chatShopObjectId, contract.arg('string', 'the message'), contract.arg('string', 'metadata')]);
171
+ // await new Promise((res)=>setTimeout(res, 300)); // got events without timeout, but just to be sure.
171
172
 
172
- t.ok(gotEventChatTopMessageCreated);
173
- t.ok(gotEventChatResponseCreated);
173
+ // t.ok(gotEventChatTopMessageCreated);
174
+ // t.ok(gotEventChatResponseCreated);
174
175
 
175
- // just some checks that events have data by contract's architecture
176
- t.ok(gotEventChatResponseCreated.parsedJson.top_message_id == gotEventChatTopMessageCreated.parsedJson.id);
177
- t.ok(gotEventChatTopMessageCreated.parsedJson.top_response_id == gotEventChatResponseCreated.parsedJson.id);
176
+ // // just some checks that events have data by contract's architecture
177
+ // t.ok(gotEventChatResponseCreated.parsedJson.top_message_id == gotEventChatTopMessageCreated.parsedJson.id);
178
+ // t.ok(gotEventChatTopMessageCreated.parsedJson.top_response_id == gotEventChatResponseCreated.parsedJson.id);
178
179
 
179
- // unsubscribing from events, to close websocket
180
- await module.unsubscribeEvents();
181
- });
180
+ // // unsubscribing from events, to close websocket
181
+ // await module.unsubscribeEvents();
182
+
183
+ // t.end();
184
+ // });
182
185
 
183
186
  test('execute contract methods', async t => {
184
187
  const moveCallResult = await contract.moveCall('suidouble_chat', 'post', [chatShopObjectId, contract.arg('string', 'the message'), contract.arg('string', 'metadata')]);
@@ -346,9 +349,7 @@ test('testing move call with coins', async t => {
346
349
 
347
350
  t.ok(foundChatTopMessage);
348
351
  t.ok(foundChatResponse);
349
-
350
- // messageTextAsBytes = [].slice.call(new TextEncoder().encode(messageText)); // regular array with utf data
351
- // suidouble_chat contract store text a bytes (easier to work with unicode things), let's convert it back to js string
352
+
352
353
  foundText = new TextDecoder().decode(new Uint8Array(foundText));
353
354
 
354
355
  t.equal(foundText, longMessageYouCanNotPostForFree);
@@ -358,6 +359,19 @@ test('testing move call with coins', async t => {
358
359
  t.ok( balanceNow <= (balanceWas - 400000000000n) );
359
360
  });
360
361
 
362
+ test('testing move call with vector<Coin<..>>', async t => {
363
+ const balanceWas = await suiMaster.getBalance();
364
+ const longMessageYouCanNotPostForFree = ('message ').padEnd(500, 'test');
365
+
366
+ // you can pass vector of coin, wrapping it's definition in array
367
+ const moveCallResult = await contract.moveCall('suidouble_chat', 'post_pay_with_coin_vector', [chatShopObjectId, [{type: 'SUI', amount: 400000000000n}], contract.arg('string', longMessageYouCanNotPostForFree), contract.arg('string', 'metadata')]);
368
+ // it's the wrapper over the same move function we've already tested, so lets keep the unit simple:
369
+ t.ok(moveCallResult.created.length > 0);
370
+
371
+ const balanceNow = await suiMaster.getBalance();
372
+ t.ok( balanceNow <= (balanceWas - 400000000000n) ); // vector<Coin<SUI>> paid
373
+ });
374
+
361
375
  test('testing move call deleting object', async t => {
362
376
 
363
377
  console.error('chatResponseToDelete', chatResponseToDelete);
@@ -21,6 +21,6 @@ dependencies = [
21
21
  ]
22
22
 
23
23
  [move.toolchain-version]
24
- compiler-version = "1.25.1"
24
+ compiler-version = "1.30.1"
25
25
  edition = "2024.beta"
26
26
  flavor = "sui"
@@ -22,6 +22,6 @@ dependencies = [
22
22
  ]
23
23
 
24
24
  [move.toolchain-version]
25
- compiler-version = "1.25.1"
26
- edition = "legacy"
25
+ compiler-version = "1.30.1"
26
+ edition = "2024.beta"
27
27
  flavor = "sui"
@@ -3,7 +3,7 @@ module suidouble_chat::suidouble_chat {
3
3
  use sui::object::{Self, UID, ID};
4
4
  use sui::transfer;
5
5
  use sui::tx_context::{Self, TxContext};
6
- use std::vector::length;
6
+ use std::vector::{Self, length};
7
7
 
8
8
  use sui::dynamic_object_field::{Self};
9
9
 
@@ -12,6 +12,7 @@ module suidouble_chat::suidouble_chat {
12
12
  use sui::balance::{Self, Balance};
13
13
 
14
14
  use std::debug;
15
+ use sui::pay;
15
16
 
16
17
  use sui::event::emit;
17
18
 
@@ -168,6 +169,17 @@ module suidouble_chat::suidouble_chat {
168
169
  transfer::share_object(chat_top_message);
169
170
  }
170
171
 
172
+ public entry fun post_pay_with_coin_vector(
173
+ chat_shop: &mut ChatShop,
174
+ coins: vector<Coin<SUI>>,
175
+ text: vector<u8>,
176
+ metadata: vector<u8>,
177
+ ctx: &mut TxContext
178
+ ) {
179
+ let base = vector::pop_back(&mut coins);
180
+ pay::join_vec(&mut base, coins);
181
+ post_pay(chat_shop, base, text, metadata, ctx);
182
+ }
171
183
 
172
184
  /// Mint (post) a ChatResponse object
173
185
  public entry fun reply(