@moveindustries/ts-sdk 5.1.4 → 5.1.6

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 (158) hide show
  1. package/README.md +241 -43
  2. package/dist/common/{accountAddress-DoqkxUqw.d.ts → accountAddress-CQEq9RVR.d.ts} +6 -2
  3. package/dist/common/chunk-56DGDNEY.js.map +1 -1
  4. package/dist/common/cli/index.d.ts +1 -1
  5. package/dist/common/cli/index.js.map +1 -1
  6. package/dist/common/index.d.ts +393 -55
  7. package/dist/common/index.js +32 -32
  8. package/dist/common/index.js.map +1 -1
  9. package/dist/esm/account/AbstractKeylessAccount.mjs +1 -1
  10. package/dist/esm/account/AbstractedAccount.mjs +1 -1
  11. package/dist/esm/account/Account.mjs +1 -1
  12. package/dist/esm/account/AccountUtils.mjs +1 -1
  13. package/dist/esm/account/DerivableAbstractedAccount.mjs +1 -1
  14. package/dist/esm/account/Ed25519Account.mjs +1 -1
  15. package/dist/esm/account/EphemeralKeyPair.mjs +1 -1
  16. package/dist/esm/account/FederatedKeylessAccount.mjs +1 -1
  17. package/dist/esm/account/KeylessAccount.mjs +1 -1
  18. package/dist/esm/account/MultiEd25519Account.mjs +1 -1
  19. package/dist/esm/account/MultiKeyAccount.mjs +1 -1
  20. package/dist/esm/account/SingleKeyAccount.mjs +1 -1
  21. package/dist/esm/account/index.mjs +1 -1
  22. package/dist/esm/account/utils.mjs +1 -1
  23. package/dist/esm/api/account/abstraction.mjs +1 -1
  24. package/dist/esm/api/account.mjs +1 -1
  25. package/dist/esm/api/coin.mjs +1 -1
  26. package/dist/esm/api/digitalAsset.mjs +1 -1
  27. package/dist/esm/api/faucet.mjs +1 -1
  28. package/dist/esm/api/fungibleAsset.mjs +1 -1
  29. package/dist/esm/api/general.mjs +1 -1
  30. package/dist/esm/api/index.d.mts +2 -2
  31. package/dist/esm/api/index.mjs +1 -1
  32. package/dist/esm/api/keyless.mjs +1 -1
  33. package/dist/esm/api/{ans.d.mts → mns.d.mts} +390 -52
  34. package/dist/esm/api/mns.mjs +2 -0
  35. package/dist/esm/api/movement.d.mts +4 -4
  36. package/dist/esm/api/movement.mjs +1 -1
  37. package/dist/esm/api/object.mjs +1 -1
  38. package/dist/esm/api/staking.mjs +1 -1
  39. package/dist/esm/api/table.mjs +1 -1
  40. package/dist/esm/api/transaction.mjs +1 -1
  41. package/dist/esm/api/transactionSubmission/build.mjs +1 -1
  42. package/dist/esm/api/transactionSubmission/management.mjs +1 -1
  43. package/dist/esm/api/transactionSubmission/sign.mjs +1 -1
  44. package/dist/esm/api/transactionSubmission/simulate.mjs +1 -1
  45. package/dist/esm/api/transactionSubmission/submit.mjs +1 -1
  46. package/dist/esm/api/utils.mjs +1 -1
  47. package/dist/esm/bcs/index.mjs +1 -1
  48. package/dist/esm/bcs/serializable/moveStructs.mjs +1 -1
  49. package/dist/esm/{chunk-SYXDZA4K.mjs → chunk-46MDTYYN.mjs} +2 -2
  50. package/dist/esm/chunk-4OOPIIBY.mjs +4 -0
  51. package/dist/esm/{chunk-47V7UGV5.mjs.map → chunk-4OOPIIBY.mjs.map} +1 -1
  52. package/dist/esm/{chunk-RUIFVDWM.mjs → chunk-5KMDJLYM.mjs} +2 -2
  53. package/dist/esm/{chunk-FJRPU2NH.mjs → chunk-7KIJGBLL.mjs} +2 -2
  54. package/dist/esm/{chunk-S4D2KBYN.mjs → chunk-7KOS7CFM.mjs} +2 -2
  55. package/dist/esm/{chunk-V3O2SBO4.mjs → chunk-ESZIZDLP.mjs} +2 -2
  56. package/dist/esm/{chunk-YFFYA5U3.mjs → chunk-HU7GK5PL.mjs} +2 -2
  57. package/dist/esm/chunk-P6MFR7W6.mjs +2 -0
  58. package/dist/esm/{chunk-PCESRJYO.mjs.map → chunk-P6MFR7W6.mjs.map} +1 -1
  59. package/dist/esm/{chunk-MEWW7VTQ.mjs → chunk-P7DJNENM.mjs} +2 -2
  60. package/dist/esm/{chunk-PHRRBT44.mjs → chunk-PG4MJ3CJ.mjs} +2 -2
  61. package/dist/esm/{chunk-NMFJJOGW.mjs → chunk-QT3RHJP2.mjs} +2 -2
  62. package/dist/esm/{chunk-CRCE7R4D.mjs → chunk-SZG3ZZGN.mjs} +2 -2
  63. package/dist/esm/{chunk-R2G23RIY.mjs → chunk-ZGBIH6MJ.mjs} +2 -2
  64. package/dist/esm/{chunk-NQUZ4UHR.mjs → chunk-ZZEJESMY.mjs} +2 -2
  65. package/dist/esm/client/core.mjs +1 -1
  66. package/dist/esm/client/get.mjs +1 -1
  67. package/dist/esm/client/index.mjs +1 -1
  68. package/dist/esm/client/post.mjs +1 -1
  69. package/dist/esm/core/crypto/abstraction.mjs +1 -1
  70. package/dist/esm/core/crypto/deserializationUtils.mjs +1 -1
  71. package/dist/esm/core/crypto/ed25519.mjs +1 -1
  72. package/dist/esm/core/crypto/ephemeral.mjs +1 -1
  73. package/dist/esm/core/crypto/federatedKeyless.mjs +1 -1
  74. package/dist/esm/core/crypto/index.mjs +1 -1
  75. package/dist/esm/core/crypto/keyless.mjs +1 -1
  76. package/dist/esm/core/crypto/multiEd25519.mjs +1 -1
  77. package/dist/esm/core/crypto/multiKey.mjs +1 -1
  78. package/dist/esm/core/crypto/proof.mjs +1 -1
  79. package/dist/esm/core/crypto/publicKey.mjs +1 -1
  80. package/dist/esm/core/crypto/secp256k1.mjs +1 -1
  81. package/dist/esm/core/crypto/secp256r1.mjs +1 -1
  82. package/dist/esm/core/crypto/signature.mjs +1 -1
  83. package/dist/esm/core/crypto/singleKey.mjs +1 -1
  84. package/dist/esm/core/crypto/utils.mjs +1 -1
  85. package/dist/esm/core/index.mjs +1 -1
  86. package/dist/esm/errors/index.mjs +1 -1
  87. package/dist/esm/index.d.mts +3 -3
  88. package/dist/esm/index.mjs +1 -1
  89. package/dist/esm/internal/abstraction.mjs +1 -1
  90. package/dist/esm/internal/account.mjs +1 -1
  91. package/dist/esm/internal/coin.mjs +1 -1
  92. package/dist/esm/internal/digitalAsset.mjs +1 -1
  93. package/dist/esm/internal/faucet.mjs +1 -1
  94. package/dist/esm/internal/fungibleAsset.mjs +1 -1
  95. package/dist/esm/internal/general.mjs +1 -1
  96. package/dist/esm/internal/keyless.mjs +1 -1
  97. package/dist/esm/internal/{ans.d.mts → mns.d.mts} +237 -19
  98. package/dist/esm/internal/mns.mjs +2 -0
  99. package/dist/esm/internal/object.mjs +1 -1
  100. package/dist/esm/internal/staking.mjs +1 -1
  101. package/dist/esm/internal/table.mjs +1 -1
  102. package/dist/esm/internal/transaction.mjs +1 -1
  103. package/dist/esm/internal/transactionSubmission.mjs +1 -1
  104. package/dist/esm/internal/utils/index.mjs +1 -1
  105. package/dist/esm/internal/utils/utils.mjs +1 -1
  106. package/dist/esm/internal/view.mjs +1 -1
  107. package/dist/esm/transactions/authenticator/account.mjs +1 -1
  108. package/dist/esm/transactions/authenticator/index.mjs +1 -1
  109. package/dist/esm/transactions/authenticator/transaction.mjs +1 -1
  110. package/dist/esm/transactions/index.mjs +1 -1
  111. package/dist/esm/transactions/instances/index.mjs +1 -1
  112. package/dist/esm/transactions/instances/moduleId.mjs +1 -1
  113. package/dist/esm/transactions/instances/multiAgentTransaction.mjs +1 -1
  114. package/dist/esm/transactions/instances/rawTransaction.mjs +1 -1
  115. package/dist/esm/transactions/instances/rotationProofChallenge.mjs +1 -1
  116. package/dist/esm/transactions/instances/signedTransaction.mjs +1 -1
  117. package/dist/esm/transactions/instances/simpleTransaction.mjs +1 -1
  118. package/dist/esm/transactions/instances/transactionPayload.mjs +1 -1
  119. package/dist/esm/transactions/management/accountSequenceNumber.mjs +1 -1
  120. package/dist/esm/transactions/management/index.mjs +1 -1
  121. package/dist/esm/transactions/management/transactionWorker.mjs +1 -1
  122. package/dist/esm/transactions/transactionBuilder/helpers.mjs +1 -1
  123. package/dist/esm/transactions/transactionBuilder/index.mjs +1 -1
  124. package/dist/esm/transactions/transactionBuilder/remoteAbi.mjs +1 -1
  125. package/dist/esm/transactions/transactionBuilder/signingMessage.mjs +1 -1
  126. package/dist/esm/transactions/transactionBuilder/transactionBuilder.mjs +1 -1
  127. package/dist/esm/transactions/typeTag/index.mjs +1 -1
  128. package/dist/esm/transactions/typeTag/parser.mjs +1 -1
  129. package/dist/esm/types/index.d.mts +1 -1
  130. package/dist/esm/types/indexer.d.mts +6 -2
  131. package/dist/esm/utils/index.mjs +1 -1
  132. package/dist/esm/utils/normalizeBundle.mjs +1 -1
  133. package/dist/esm/version.d.mts +1 -1
  134. package/dist/esm/version.mjs +1 -1
  135. package/package.json +19 -18
  136. package/src/api/{ans.ts → mns.ts} +437 -51
  137. package/src/api/movement.ts +5 -5
  138. package/src/internal/{ans.ts → mns.ts} +642 -84
  139. package/src/types/indexer.ts +6 -1
  140. package/src/version.ts +1 -1
  141. package/dist/esm/api/ans.mjs +0 -2
  142. package/dist/esm/chunk-47V7UGV5.mjs +0 -4
  143. package/dist/esm/chunk-PCESRJYO.mjs +0 -2
  144. package/dist/esm/internal/ans.mjs +0 -2
  145. /package/dist/esm/api/{ans.mjs.map → mns.mjs.map} +0 -0
  146. /package/dist/esm/{chunk-SYXDZA4K.mjs.map → chunk-46MDTYYN.mjs.map} +0 -0
  147. /package/dist/esm/{chunk-RUIFVDWM.mjs.map → chunk-5KMDJLYM.mjs.map} +0 -0
  148. /package/dist/esm/{chunk-FJRPU2NH.mjs.map → chunk-7KIJGBLL.mjs.map} +0 -0
  149. /package/dist/esm/{chunk-S4D2KBYN.mjs.map → chunk-7KOS7CFM.mjs.map} +0 -0
  150. /package/dist/esm/{chunk-V3O2SBO4.mjs.map → chunk-ESZIZDLP.mjs.map} +0 -0
  151. /package/dist/esm/{chunk-YFFYA5U3.mjs.map → chunk-HU7GK5PL.mjs.map} +0 -0
  152. /package/dist/esm/{chunk-MEWW7VTQ.mjs.map → chunk-P7DJNENM.mjs.map} +0 -0
  153. /package/dist/esm/{chunk-PHRRBT44.mjs.map → chunk-PG4MJ3CJ.mjs.map} +0 -0
  154. /package/dist/esm/{chunk-NMFJJOGW.mjs.map → chunk-QT3RHJP2.mjs.map} +0 -0
  155. /package/dist/esm/{chunk-CRCE7R4D.mjs.map → chunk-SZG3ZZGN.mjs.map} +0 -0
  156. /package/dist/esm/{chunk-R2G23RIY.mjs.map → chunk-ZGBIH6MJ.mjs.map} +0 -0
  157. /package/dist/esm/{chunk-NQUZ4UHR.mjs.map → chunk-ZZEJESMY.mjs.map} +0 -0
  158. /package/dist/esm/internal/{ans.mjs.map → mns.mjs.map} +0 -0
@@ -14,7 +14,7 @@ import { MovementConfig } from "../api/movementConfig";
14
14
  import { AccountAddress, AccountAddressInput } from "../core";
15
15
  import { SimpleTransaction } from "../transactions/instances/simpleTransaction";
16
16
  import { InputGenerateTransactionOptions } from "../transactions/types";
17
- import { GetANSNameResponse, MoveAddressType, OrderByArg, PaginationArgs, WhereArg } from "../types";
17
+ import { GetMNSNameResponse, MoveAddressType, OrderByArg, PaginationArgs, WhereArg } from "../types";
18
18
  import { GetNamesQuery } from "../types/generated/operations";
19
19
  import { GetNames } from "../types/generated/queries";
20
20
  import { CurrentAptosNamesBoolExp } from "../types/generated/types";
@@ -30,14 +30,14 @@ export const VALIDATION_RULES_DESCRIPTION = [
30
30
  ].join(" ");
31
31
 
32
32
  /**
33
- * Validate if a given fragment is a valid ANS segment.
34
- * This function checks the length and character constraints of the fragment to ensure it meets the ANS standards.
33
+ * Validate if a given fragment is a valid MNS segment.
34
+ * This function checks the length and character constraints of the fragment to ensure it meets the MNS standards.
35
35
  *
36
36
  * @param fragment - A fragment of a name, either the domain or subdomain.
37
37
  * @returns A boolean indicating if the fragment is a valid fragment.
38
38
  * @group Implementation
39
39
  */
40
- export function isValidANSSegment(fragment: string): boolean {
40
+ export function isValidMNSSegment(fragment: string): boolean {
41
41
  if (!fragment) return false;
42
42
  if (fragment.length < 3) return false;
43
43
  if (fragment.length > 63) return false;
@@ -47,23 +47,23 @@ export function isValidANSSegment(fragment: string): boolean {
47
47
  }
48
48
 
49
49
  /**
50
- * Checks if an ANS name is valid or not.
50
+ * Checks if an MNS name is valid or not.
51
51
  *
52
- * @param name - A string of the domain name, which can include or exclude the .apt suffix.
52
+ * @param name - A string of the domain name, which can include or exclude the .move suffix.
53
53
  * @group Implementation
54
54
  */
55
- export function isValidANSName(name: string): { domainName: string; subdomainName?: string } {
56
- const [first, second, ...rest] = name.replace(/\.apt$/, "").split(".");
55
+ export function isValidMNSName(name: string): { domainName: string; subdomainName?: string } {
56
+ const [first, second, ...rest] = name.replace(/\.move$/, "").split(".");
57
57
 
58
58
  if (rest.length > 0) {
59
59
  throw new Error(`${name} is invalid. A name can only have two parts, a domain and a subdomain separated by a "."`);
60
60
  }
61
61
 
62
- if (!isValidANSSegment(first)) {
62
+ if (!isValidMNSSegment(first)) {
63
63
  throw new Error(`${first} is not valid. ${VALIDATION_RULES_DESCRIPTION}`);
64
64
  }
65
65
 
66
- if (second && !isValidANSSegment(second)) {
66
+ if (second && !isValidMNSSegment(second)) {
67
67
  throw new Error(`${second} is not valid. ${VALIDATION_RULES_DESCRIPTION}`);
68
68
  }
69
69
 
@@ -83,16 +83,16 @@ export enum SubdomainExpirationPolicy {
83
83
  }
84
84
 
85
85
  /**
86
- * Determine if a given ANS name is considered active based on its expiration dates.
86
+ * Determine if a given MNS name is considered active based on its expiration dates.
87
87
  * Domains are active if their expiration date is in the future, while subdomains may
88
88
  * follow their parent's expiration policy (1) or expire independently (0).
89
89
  * If the subdomain is expiring independently, it can expire before their parent, but not after.
90
90
  *
91
- * @param name - An ANS name returned from one of the functions of the SDK.
91
+ * @param name - An MNS name returned from one of the functions of the SDK.
92
92
  * @returns A boolean indicating whether the contract considers the name active or not.
93
93
  * @group Implementation
94
94
  */
95
- export function isActiveANSName(name: GetANSNameResponse[0]): boolean {
95
+ export function isActiveMNSName(name: GetMNSNameResponse[0]): boolean {
96
96
  if (!name) return false;
97
97
 
98
98
  const isTLDExpired = new Date(name.domain_expiration_timestamp).getTime() < Date.now();
@@ -110,16 +110,17 @@ export function isActiveANSName(name: GetANSNameResponse[0]): boolean {
110
110
  return !isExpired;
111
111
  }
112
112
 
113
- export const LOCAL_ANS_ACCOUNT_PK =
114
- process.env.ANS_TEST_ACCOUNT_PRIVATE_KEY ??
113
+ export const LOCAL_MNS_ACCOUNT_PK =
114
+ process.env.MNS_TEST_ACCOUNT_PRIVATE_KEY ??
115
115
  "ed25519-priv-0x37368b46ce665362562c6d1d4ec01a08c8644c488690df5a17e13ba163e20221";
116
- export const LOCAL_ANS_ACCOUNT_ADDRESS =
117
- process.env.ANS_TEST_ACCOUNT_ADDRESS ?? "0x585fc9f0f0c54183b039ffc770ca282ebd87307916c215a3e692f2f8e4305e82";
118
-
119
- const NetworkToAnsContract: Record<Network, string | null> = {
120
- [Network.TESTNET]: "0x5f8fd2347449685cf41d4db97926ec3a096eaf381332be4f1318ad4d16a8497c",
121
- [Network.MAINNET]: "0x867ed1f6bf916171b1de3ee92849b8978b7d1b9e0a8cc982a3d19d535dfd9c0c",
122
- [Network.LOCAL]: LOCAL_ANS_ACCOUNT_ADDRESS,
116
+ export const LOCAL_MNS_ACCOUNT_ADDRESS =
117
+ process.env.MNS_TEST_ACCOUNT_ADDRESS ?? "0x585fc9f0f0c54183b039ffc770ca282ebd87307916c215a3e692f2f8e4305e82";
118
+
119
+ const NetworkToMnsContract: Record<Network, string | null> = {
120
+ [Network.TESTNET]: "0x67bf15b3eed0fc62deea9630bbbd1d48842550655140f913699a1ca7e6f727d8",
121
+ // TODO: Update mainnet address once Movement Name Service is deployed to mainnet
122
+ [Network.MAINNET]: null,
123
+ [Network.LOCAL]: LOCAL_MNS_ACCOUNT_ADDRESS,
123
124
  [Network.CUSTOM]: null,
124
125
  [Network.DEVNET]: null,
125
126
  [Network.SHELBYNET]: null,
@@ -127,17 +128,17 @@ const NetworkToAnsContract: Record<Network, string | null> = {
127
128
  };
128
129
 
129
130
  /**
130
- * Retrieves the address of the ANS contract based on the specified Movement network configuration.
131
+ * Retrieves the address of the MNS contract based on the specified Movement network configuration.
131
132
  *
132
133
  * @param movementConfig - The configuration object for the Movement network.
133
- * @param movementConfig.network - The network for which to retrieve the ANS contract address.
134
+ * @param movementConfig.network - The network for which to retrieve the MNS contract address.
134
135
  *
135
- * @throws Throws an error if the ANS contract is not deployed to the specified network.
136
+ * @throws Throws an error if the MNS contract is not deployed to the specified network.
136
137
  * @group Implementation
137
138
  */
138
139
  function getRouterAddress(movementConfig: MovementConfig): string {
139
- const address = NetworkToAnsContract[movementConfig.network];
140
- if (!address) throw new Error(`The ANS contract is not deployed to ${movementConfig.network}`);
140
+ const address = NetworkToMnsContract[movementConfig.network];
141
+ if (!address) throw new Error(`The MNS contract is not deployed to ${movementConfig.network}`);
141
142
  return address;
142
143
  }
143
144
 
@@ -164,7 +165,7 @@ export async function getOwnerAddress(args: {
164
165
  }): Promise<AccountAddress | undefined> {
165
166
  const { movementConfig, name } = args;
166
167
  const routerAddress = getRouterAddress(movementConfig);
167
- const { domainName, subdomainName } = isValidANSName(name);
168
+ const { domainName, subdomainName } = isValidMNSName(name);
168
169
 
169
170
  const res = await view({
170
171
  movementConfig,
@@ -229,7 +230,7 @@ export interface RegisterNameParameters {
229
230
  export async function registerName(args: RegisterNameParameters): Promise<SimpleTransaction> {
230
231
  const { movementConfig, expiration, name, sender, targetAddress, toAddress, options, transferable } = args;
231
232
  const routerAddress = getRouterAddress(movementConfig);
232
- const { domainName, subdomainName } = isValidANSName(name);
233
+ const { domainName, subdomainName } = isValidMNSName(name);
233
234
 
234
235
  const hasSubdomainPolicy =
235
236
  expiration.policy === "subdomain:independent" || expiration.policy === "subdomain:follow-domain";
@@ -271,37 +272,19 @@ export async function registerName(args: RegisterNameParameters): Promise<Simple
271
272
  throw new Error(`${expiration.policy} requires a subdomain to be provided.`);
272
273
  }
273
274
 
274
- const tldExpiration = await getExpiration({ movementConfig, name: domainName });
275
- if (!tldExpiration) {
276
- throw new Error("The domain does not exist");
277
- }
278
-
279
- const expirationDateInMillisecondsSinceEpoch =
280
- expiration.policy === "subdomain:independent" ? expiration.expirationDate : tldExpiration;
281
-
282
- if (expirationDateInMillisecondsSinceEpoch > tldExpiration) {
283
- throw new Error("The subdomain expiration time cannot be greater than the domain expiration time");
284
- }
285
-
286
- const transaction = await generateTransaction({
287
- movementConfig,
288
- sender: sender.accountAddress.toString(),
289
- data: {
290
- function: `${routerAddress}::router::register_subdomain`,
291
- functionArguments: [
292
- domainName,
293
- subdomainName,
294
- Math.round(expirationDateInMillisecondsSinceEpoch / 1000),
295
- expiration.policy === "subdomain:follow-domain" ? 1 : 0,
296
- !!transferable,
297
- targetAddress,
298
- toAddress,
299
- ],
300
- },
301
- options,
302
- });
303
-
304
- return transaction;
275
+ // Movement's MNS contract uses a key staking mechanism for subdomains instead of
276
+ // a direct register_subdomain function. The router has stake_key_for_subdomain,
277
+ // unstake_key_for_subdomain, and buy_and_stake_key_for_subdomain functions.
278
+ // This is different from Aptos ANS which has register_subdomain.
279
+ //
280
+ // For now, subdomain registration is not supported via this SDK.
281
+ // Users who need subdomain functionality should interact with the contract directly
282
+ // using the key staking functions.
283
+ throw new Error(
284
+ "Subdomain registration is not currently supported on Movement. " +
285
+ "Movement's MNS contract uses a key staking mechanism for subdomains. " +
286
+ "See the router module's stake_key_for_subdomain and buy_and_stake_key_for_subdomain functions."
287
+ );
305
288
  }
306
289
 
307
290
  /**
@@ -316,7 +299,7 @@ export async function registerName(args: RegisterNameParameters): Promise<Simple
316
299
  export async function getExpiration(args: { movementConfig: MovementConfig; name: string }): Promise<number | undefined> {
317
300
  const { movementConfig, name } = args;
318
301
  const routerAddress = getRouterAddress(movementConfig);
319
- const { domainName, subdomainName } = isValidANSName(name);
302
+ const { domainName, subdomainName } = isValidMNSName(name);
320
303
 
321
304
  try {
322
305
  const res = await view({
@@ -402,7 +385,7 @@ export async function setPrimaryName(args: {
402
385
  return transaction;
403
386
  }
404
387
 
405
- const { domainName, subdomainName } = isValidANSName(name);
388
+ const { domainName, subdomainName } = isValidMNSName(name);
406
389
 
407
390
  const transaction = await generateTransaction({
408
391
  movementConfig,
@@ -434,7 +417,7 @@ export async function getTargetAddress(args: {
434
417
  }): Promise<AccountAddress | undefined> {
435
418
  const { movementConfig, name } = args;
436
419
  const routerAddress = getRouterAddress(movementConfig);
437
- const { domainName, subdomainName } = isValidANSName(name);
420
+ const { domainName, subdomainName } = isValidMNSName(name);
438
421
 
439
422
  const res = await view({
440
423
  movementConfig,
@@ -471,7 +454,7 @@ export async function setTargetAddress(args: {
471
454
  }): Promise<SimpleTransaction> {
472
455
  const { movementConfig, sender, name, address, options } = args;
473
456
  const routerAddress = getRouterAddress(movementConfig);
474
- const { domainName, subdomainName } = isValidANSName(name);
457
+ const { domainName, subdomainName } = isValidMNSName(name);
475
458
 
476
459
  const transaction = await generateTransaction({
477
460
  movementConfig,
@@ -498,9 +481,9 @@ export async function setTargetAddress(args: {
498
481
  export async function getName(args: {
499
482
  movementConfig: MovementConfig;
500
483
  name: string;
501
- }): Promise<GetANSNameResponse[0] | undefined> {
484
+ }): Promise<GetMNSNameResponse[0] | undefined> {
502
485
  const { movementConfig, name } = args;
503
- const { domainName, subdomainName = "" } = isValidANSName(name);
486
+ const { domainName, subdomainName = "" } = isValidMNSName(name);
504
487
 
505
488
  const where: CurrentAptosNamesBoolExp = {
506
489
  domain: { _eq: domainName },
@@ -522,10 +505,10 @@ export async function getName(args: {
522
505
  // Convert the expiration_timestamp from an ISO string to milliseconds since epoch
523
506
  let res = data.current_aptos_names[0];
524
507
  if (res) {
525
- res = sanitizeANSName(res);
508
+ res = sanitizeMNSName(res);
526
509
  }
527
510
 
528
- return isActiveANSName(res) ? res : undefined;
511
+ return isActiveMNSName(res) ? res : undefined;
529
512
  }
530
513
 
531
514
  /**
@@ -535,7 +518,7 @@ export async function getName(args: {
535
518
  * @group Implementation
536
519
  */
537
520
  interface QueryNamesOptions {
538
- options?: PaginationArgs & OrderByArg<GetANSNameResponse[0]> & WhereArg<CurrentAptosNamesBoolExp>;
521
+ options?: PaginationArgs & OrderByArg<GetMNSNameResponse[0]> & WhereArg<CurrentAptosNamesBoolExp>;
539
522
  }
540
523
 
541
524
  /**
@@ -566,10 +549,10 @@ export interface GetAccountNamesArgs extends QueryNamesOptions {
566
549
  */
567
550
  export async function getAccountNames(
568
551
  args: { movementConfig: MovementConfig } & GetAccountNamesArgs,
569
- ): Promise<GetANSNameResponse> {
552
+ ): Promise<GetMNSNameResponse> {
570
553
  const { movementConfig, options, accountAddress } = args;
571
554
 
572
- const expirationDate = await getANSExpirationDate({ movementConfig });
555
+ const expirationDate = await getMNSExpirationDate({ movementConfig });
573
556
 
574
557
  const data = await queryIndexer<GetNamesQuery>({
575
558
  movementConfig,
@@ -589,7 +572,7 @@ export async function getAccountNames(
589
572
  },
590
573
  });
591
574
 
592
- return data.current_aptos_names.map(sanitizeANSName);
575
+ return data.current_aptos_names.map(sanitizeMNSName);
593
576
  }
594
577
 
595
578
  /**
@@ -622,10 +605,10 @@ export interface GetAccountDomainsArgs extends QueryNamesOptions {
622
605
  */
623
606
  export async function getAccountDomains(
624
607
  args: { movementConfig: MovementConfig } & GetAccountDomainsArgs,
625
- ): Promise<GetANSNameResponse> {
608
+ ): Promise<GetMNSNameResponse> {
626
609
  const { movementConfig, options, accountAddress } = args;
627
610
 
628
- const expirationDate = await getANSExpirationDate({ movementConfig });
611
+ const expirationDate = await getMNSExpirationDate({ movementConfig });
629
612
 
630
613
  const data = await queryIndexer<GetNamesQuery>({
631
614
  movementConfig,
@@ -646,7 +629,7 @@ export async function getAccountDomains(
646
629
  },
647
630
  });
648
631
 
649
- return data.current_aptos_names.map(sanitizeANSName);
632
+ return data.current_aptos_names.map(sanitizeMNSName);
650
633
  }
651
634
 
652
635
  /**
@@ -678,10 +661,10 @@ export interface GetAccountSubdomainsArgs extends QueryNamesOptions {
678
661
  */
679
662
  export async function getAccountSubdomains(
680
663
  args: { movementConfig: MovementConfig } & GetAccountSubdomainsArgs,
681
- ): Promise<GetANSNameResponse> {
664
+ ): Promise<GetMNSNameResponse> {
682
665
  const { movementConfig, options, accountAddress } = args;
683
666
 
684
- const expirationDate = await getANSExpirationDate({ movementConfig });
667
+ const expirationDate = await getMNSExpirationDate({ movementConfig });
685
668
 
686
669
  const data = await queryIndexer<GetNamesQuery>({
687
670
  movementConfig,
@@ -702,7 +685,7 @@ export async function getAccountSubdomains(
702
685
  },
703
686
  });
704
687
 
705
- return data.current_aptos_names.map(sanitizeANSName);
688
+ return data.current_aptos_names.map(sanitizeMNSName);
706
689
  }
707
690
 
708
691
  /**
@@ -736,7 +719,7 @@ export interface GetDomainSubdomainsArgs extends QueryNamesOptions {
736
719
  */
737
720
  export async function getDomainSubdomains(
738
721
  args: { movementConfig: MovementConfig } & GetDomainSubdomainsArgs,
739
- ): Promise<GetANSNameResponse> {
722
+ ): Promise<GetMNSNameResponse> {
740
723
  const { movementConfig, options, domain } = args;
741
724
 
742
725
  const data = await queryIndexer<GetNamesQuery>({
@@ -757,7 +740,7 @@ export async function getDomainSubdomains(
757
740
  },
758
741
  });
759
742
 
760
- return data.current_aptos_names.map(sanitizeANSName).filter(isActiveANSName);
743
+ return data.current_aptos_names.map(sanitizeMNSName).filter(isActiveMNSName);
761
744
  }
762
745
 
763
746
  /**
@@ -773,7 +756,7 @@ export async function getDomainSubdomains(
773
756
  * @returns The expiration date in ISO 8601 format.
774
757
  * @group Implementation
775
758
  */
776
- async function getANSExpirationDate(args: { movementConfig: MovementConfig }): Promise<string> {
759
+ async function getMNSExpirationDate(args: { movementConfig: MovementConfig }): Promise<string> {
777
760
  const { movementConfig } = args;
778
761
  const routerAddress = getRouterAddress(movementConfig);
779
762
 
@@ -812,7 +795,7 @@ export async function renewDomain(args: {
812
795
  const { movementConfig, sender, name, years = 1, options } = args;
813
796
  const routerAddress = getRouterAddress(movementConfig);
814
797
  const renewalDuration = years * 31536000;
815
- const { domainName, subdomainName } = isValidANSName(name);
798
+ const { domainName, subdomainName } = isValidMNSName(name);
816
799
 
817
800
  if (subdomainName) {
818
801
  throw new Error("Subdomains cannot be renewed");
@@ -841,13 +824,588 @@ export async function renewDomain(args: {
841
824
  * milliseconds. In the future, if other properties need sanitization, this can
842
825
  * be extended.
843
826
  *
844
- * @param name - The ANS name response to sanitize.
827
+ * @param name - The MNS name response to sanitize.
845
828
  * @param name.expiration_timestamp - The expiration timestamp in ISO string format.
846
829
  * @group Implementation
847
830
  */
848
- function sanitizeANSName(name: GetANSNameResponse[0]): GetANSNameResponse[0] {
831
+ function sanitizeMNSName(name: GetMNSNameResponse[0]): GetMNSNameResponse[0] {
849
832
  return {
850
833
  ...name,
851
834
  expiration_timestamp: new Date(name.expiration_timestamp).getTime(),
852
835
  };
853
836
  }
837
+
838
+ // ============================================================================
839
+ // Subdomain Key Staking Functions
840
+ // ============================================================================
841
+ // Movement MNS uses a bonding curve mechanism for subdomains where users must
842
+ // buy and stake "keys" to own subdomains. These functions provide SDK support
843
+ // for this mechanism.
844
+ // ============================================================================
845
+
846
+ /**
847
+ * Get the price to buy keys for a domain. Each domain has tradeable keys that
848
+ * follow a bonding curve - price increases as more keys are bought.
849
+ *
850
+ * @param args - The arguments for the function.
851
+ * @param args.movementConfig - The configuration object for Movement.
852
+ * @param args.domainName - The domain name to get the key price for.
853
+ * @param args.amount - The number of keys to get the price for (default: 1).
854
+ * @returns The price in octas to buy the specified number of keys.
855
+ * @group Implementation
856
+ */
857
+ export async function getKeyBuyPrice(args: {
858
+ movementConfig: MovementConfig;
859
+ domainName: string;
860
+ amount?: number;
861
+ }): Promise<bigint> {
862
+ const { movementConfig, domainName, amount = 1 } = args;
863
+ const routerAddress = getRouterAddress(movementConfig);
864
+ const { domainName: validatedDomain, subdomainName } = isValidMNSName(domainName);
865
+
866
+ if (subdomainName) {
867
+ throw new Error("Can only get key price for domains, not subdomains");
868
+ }
869
+
870
+ // Get the key address for the domain
871
+ // Response format: [{ vec: [{ metadata: { inner: "0x..." }, name, supply, symbol }] }]
872
+ const keyInfo = await view({
873
+ movementConfig,
874
+ payload: {
875
+ function: `${routerAddress}::key_manager::get_key_info_by_domain_name`,
876
+ functionArguments: [validatedDomain],
877
+ },
878
+ });
879
+
880
+ const keyData = (keyInfo[0] as any)?.vec?.[0];
881
+ const keyAddress = keyData?.metadata?.inner;
882
+ if (!keyAddress) {
883
+ throw new Error(`No key found for domain ${validatedDomain}. Keys are created when the first subdomain is registered.`);
884
+ }
885
+
886
+ const res = await view({
887
+ movementConfig,
888
+ payload: {
889
+ function: `${routerAddress}::key_manager::get_buy_price_after_fee`,
890
+ functionArguments: [keyAddress, amount],
891
+ },
892
+ });
893
+
894
+ return BigInt(res[0] as string);
895
+ }
896
+
897
+ /**
898
+ * Get the price when selling keys for a domain.
899
+ *
900
+ * @param args - The arguments for the function.
901
+ * @param args.movementConfig - The configuration object for Movement.
902
+ * @param args.domainName - The domain name to get the sell price for.
903
+ * @param args.amount - The number of keys to get the price for (default: 1).
904
+ * @returns The price in octas received when selling the specified number of keys.
905
+ * @group Implementation
906
+ */
907
+ export async function getKeySellPrice(args: {
908
+ movementConfig: MovementConfig;
909
+ domainName: string;
910
+ amount?: number;
911
+ }): Promise<bigint> {
912
+ const { movementConfig, domainName, amount = 1 } = args;
913
+ const routerAddress = getRouterAddress(movementConfig);
914
+ const { domainName: validatedDomain, subdomainName } = isValidMNSName(domainName);
915
+
916
+ if (subdomainName) {
917
+ throw new Error("Can only get key price for domains, not subdomains");
918
+ }
919
+
920
+ // Get the key address for the domain
921
+ // Response format: [{ vec: [{ metadata: { inner: "0x..." }, name, supply, symbol }] }]
922
+ const keyInfo = await view({
923
+ movementConfig,
924
+ payload: {
925
+ function: `${routerAddress}::key_manager::get_key_info_by_domain_name`,
926
+ functionArguments: [validatedDomain],
927
+ },
928
+ });
929
+
930
+ const keyData = (keyInfo[0] as any)?.vec?.[0];
931
+ const keyAddress = keyData?.metadata?.inner;
932
+ if (!keyAddress) {
933
+ throw new Error(`No key found for domain ${validatedDomain}. Keys are created when the first subdomain is registered.`);
934
+ }
935
+
936
+ const res = await view({
937
+ movementConfig,
938
+ payload: {
939
+ function: `${routerAddress}::key_manager::get_sell_price_after_fee`,
940
+ functionArguments: [keyAddress, amount],
941
+ },
942
+ });
943
+
944
+ return BigInt(res[0] as string);
945
+ }
946
+
947
+ /**
948
+ * Parameters for subdomain key staking operations.
949
+ * @group Implementation
950
+ */
951
+ export interface SubdomainKeyStakingParams {
952
+ movementConfig: MovementConfig;
953
+ sender: Account;
954
+ domainName: string;
955
+ subdomainName: string;
956
+ /** The address that this subdomain will resolve to (optional, defaults to sender) */
957
+ targetAddress?: AccountAddressInput;
958
+ /** The address that will own this subdomain (optional, defaults to sender) */
959
+ toAddress?: AccountAddressInput;
960
+ options?: InputGenerateTransactionOptions;
961
+ }
962
+
963
+ /**
964
+ * Buy a key for a domain and stake it to claim a subdomain in one transaction.
965
+ * This is the primary way to register a subdomain on Movement.
966
+ *
967
+ * The price follows a bonding curve - use getKeyBuyPrice() to check the current price.
968
+ *
969
+ * @param args - The arguments for the function.
970
+ * @returns A transaction object to be signed and submitted.
971
+ * @group Implementation
972
+ */
973
+ export async function buyAndStakeKeyForSubdomain(
974
+ args: SubdomainKeyStakingParams & { referrer?: AccountAddressInput },
975
+ ): Promise<SimpleTransaction> {
976
+ const { movementConfig, sender, domainName, subdomainName, targetAddress, toAddress, referrer, options } = args;
977
+ const routerAddress = getRouterAddress(movementConfig);
978
+
979
+ // Validate the names
980
+ const { domainName: validatedDomain } = isValidMNSName(domainName);
981
+ if (!isValidMNSSegment(subdomainName)) {
982
+ throw new Error(`Invalid subdomain name: ${subdomainName}. ${VALIDATION_RULES_DESCRIPTION}`);
983
+ }
984
+
985
+ // Optional addresses - pass null for empty options, the SDK will handle MoveOption conversion
986
+ const targetOpt = targetAddress ? AccountAddress.from(targetAddress).toString() : null;
987
+ const toOpt = toAddress ? AccountAddress.from(toAddress).toString() : null;
988
+ const referrerOpt = referrer ? AccountAddress.from(referrer).toString() : null;
989
+
990
+ const transaction = await generateTransaction({
991
+ movementConfig,
992
+ sender: sender.accountAddress.toString(),
993
+ data: {
994
+ function: `${routerAddress}::router::buy_and_stake_key_for_subdomain`,
995
+ functionArguments: [validatedDomain, subdomainName, targetOpt, toOpt, referrerOpt],
996
+ },
997
+ options,
998
+ });
999
+
1000
+ return transaction;
1001
+ }
1002
+
1003
+ /**
1004
+ * Stake an existing key to claim a subdomain. You must already own a key for
1005
+ * the parent domain (obtained via buyAndStakeKeyForSubdomain or by buying keys directly).
1006
+ *
1007
+ * @param args - The arguments for the function.
1008
+ * @returns A transaction object to be signed and submitted.
1009
+ * @group Implementation
1010
+ */
1011
+ export async function stakeKeyForSubdomain(args: SubdomainKeyStakingParams): Promise<SimpleTransaction> {
1012
+ const { movementConfig, sender, domainName, subdomainName, targetAddress, toAddress, options } = args;
1013
+ const routerAddress = getRouterAddress(movementConfig);
1014
+
1015
+ // Validate the names
1016
+ const { domainName: validatedDomain } = isValidMNSName(domainName);
1017
+ if (!isValidMNSSegment(subdomainName)) {
1018
+ throw new Error(`Invalid subdomain name: ${subdomainName}. ${VALIDATION_RULES_DESCRIPTION}`);
1019
+ }
1020
+
1021
+ // Optional addresses - pass null for empty options
1022
+ const targetOpt = targetAddress ? AccountAddress.from(targetAddress).toString() : null;
1023
+ const toOpt = toAddress ? AccountAddress.from(toAddress).toString() : null;
1024
+
1025
+ const transaction = await generateTransaction({
1026
+ movementConfig,
1027
+ sender: sender.accountAddress.toString(),
1028
+ data: {
1029
+ function: `${routerAddress}::router::stake_key_for_subdomain`,
1030
+ functionArguments: [validatedDomain, subdomainName, targetOpt, toOpt],
1031
+ },
1032
+ options,
1033
+ });
1034
+
1035
+ return transaction;
1036
+ }
1037
+
1038
+ /**
1039
+ * Unstake a key from a subdomain, giving up ownership but keeping the key.
1040
+ * You can sell the key later or stake it for a different subdomain.
1041
+ *
1042
+ * @param args - The arguments for the function.
1043
+ * @returns A transaction object to be signed and submitted.
1044
+ * @group Implementation
1045
+ */
1046
+ export async function unstakeKeyForSubdomain(args: {
1047
+ movementConfig: MovementConfig;
1048
+ sender: Account;
1049
+ domainName: string;
1050
+ subdomainName: string;
1051
+ options?: InputGenerateTransactionOptions;
1052
+ }): Promise<SimpleTransaction> {
1053
+ const { movementConfig, sender, domainName, subdomainName, options } = args;
1054
+ const routerAddress = getRouterAddress(movementConfig);
1055
+
1056
+ // Validate the names
1057
+ const { domainName: validatedDomain } = isValidMNSName(domainName);
1058
+ if (!isValidMNSSegment(subdomainName)) {
1059
+ throw new Error(`Invalid subdomain name: ${subdomainName}. ${VALIDATION_RULES_DESCRIPTION}`);
1060
+ }
1061
+
1062
+ const transaction = await generateTransaction({
1063
+ movementConfig,
1064
+ sender: sender.accountAddress.toString(),
1065
+ data: {
1066
+ function: `${routerAddress}::router::unstake_key_for_subdomain`,
1067
+ functionArguments: [validatedDomain, subdomainName],
1068
+ },
1069
+ options,
1070
+ });
1071
+
1072
+ return transaction;
1073
+ }
1074
+
1075
+ /**
1076
+ * Unstake a key from a subdomain and sell it in one transaction.
1077
+ * This gives up the subdomain and converts the key back to MOVE tokens.
1078
+ *
1079
+ * @param args - The arguments for the function.
1080
+ * @returns A transaction object to be signed and submitted.
1081
+ * @group Implementation
1082
+ */
1083
+ export async function unstakeAndSellKeyForSubdomain(args: {
1084
+ movementConfig: MovementConfig;
1085
+ sender: Account;
1086
+ domainName: string;
1087
+ subdomainName: string;
1088
+ referrer?: AccountAddressInput;
1089
+ options?: InputGenerateTransactionOptions;
1090
+ }): Promise<SimpleTransaction> {
1091
+ const { movementConfig, sender, domainName, subdomainName, referrer, options } = args;
1092
+ const routerAddress = getRouterAddress(movementConfig);
1093
+
1094
+ // Validate the names
1095
+ const { domainName: validatedDomain } = isValidMNSName(domainName);
1096
+ if (!isValidMNSSegment(subdomainName)) {
1097
+ throw new Error(`Invalid subdomain name: ${subdomainName}. ${VALIDATION_RULES_DESCRIPTION}`);
1098
+ }
1099
+
1100
+ const referrerOpt = referrer ? AccountAddress.from(referrer).toString() : null;
1101
+
1102
+ const transaction = await generateTransaction({
1103
+ movementConfig,
1104
+ sender: sender.accountAddress.toString(),
1105
+ data: {
1106
+ function: `${routerAddress}::router::unstake_and_sell_key_for_subdomain`,
1107
+ functionArguments: [validatedDomain, subdomainName, referrerOpt],
1108
+ },
1109
+ options,
1110
+ });
1111
+
1112
+ return transaction;
1113
+ }
1114
+
1115
+ // ============================================================================
1116
+ // Additional Router Functions
1117
+ // ============================================================================
1118
+
1119
+ /**
1120
+ * Check if a name (domain or subdomain) is available for registration.
1121
+ *
1122
+ * @param args - The arguments for the function.
1123
+ * @param args.movementConfig - The configuration object for Movement.
1124
+ * @param args.name - The name to check (e.g., "test" or "sub.test").
1125
+ * @param args.account - The account address that would register the name.
1126
+ * @returns True if the name can be registered by the account, false otherwise.
1127
+ * @group Implementation
1128
+ */
1129
+ export async function canRegister(args: {
1130
+ movementConfig: MovementConfig;
1131
+ name: string;
1132
+ account: AccountAddressInput;
1133
+ }): Promise<boolean> {
1134
+ const { movementConfig, name, account } = args;
1135
+ const routerAddress = getRouterAddress(movementConfig);
1136
+ const { domainName, subdomainName } = isValidMNSName(name);
1137
+
1138
+ const res = await view({
1139
+ movementConfig,
1140
+ payload: {
1141
+ function: `${routerAddress}::router::can_register`,
1142
+ functionArguments: [
1143
+ AccountAddress.from(account).toString(),
1144
+ domainName,
1145
+ subdomainName ?? null,
1146
+ ],
1147
+ },
1148
+ });
1149
+
1150
+ return res[0] as boolean;
1151
+ }
1152
+
1153
+ /**
1154
+ * Check if a specific address owns a name.
1155
+ *
1156
+ * @param args - The arguments for the function.
1157
+ * @param args.movementConfig - The configuration object for Movement.
1158
+ * @param args.name - The name to check ownership of.
1159
+ * @param args.account - The account address to check.
1160
+ * @returns True if the account owns the name, false otherwise.
1161
+ * @group Implementation
1162
+ */
1163
+ export async function isNameOwner(args: {
1164
+ movementConfig: MovementConfig;
1165
+ name: string;
1166
+ account: AccountAddressInput;
1167
+ }): Promise<boolean> {
1168
+ const { movementConfig, name, account } = args;
1169
+ const routerAddress = getRouterAddress(movementConfig);
1170
+ const { domainName, subdomainName } = isValidMNSName(name);
1171
+
1172
+ const res = await view({
1173
+ movementConfig,
1174
+ payload: {
1175
+ function: `${routerAddress}::router::is_name_owner`,
1176
+ functionArguments: [
1177
+ AccountAddress.from(account).toString(),
1178
+ domainName,
1179
+ subdomainName ?? null,
1180
+ ],
1181
+ },
1182
+ });
1183
+
1184
+ return res[0] as boolean;
1185
+ }
1186
+
1187
+ /**
1188
+ * Get the NFT token address for a domain or subdomain.
1189
+ *
1190
+ * @param args - The arguments for the function.
1191
+ * @param args.movementConfig - The configuration object for Movement.
1192
+ * @param args.name - The name to get the token address for.
1193
+ * @returns The token address, or undefined if the name doesn't exist.
1194
+ * @group Implementation
1195
+ */
1196
+ export async function getTokenAddress(args: {
1197
+ movementConfig: MovementConfig;
1198
+ name: string;
1199
+ }): Promise<AccountAddress | undefined> {
1200
+ const { movementConfig, name } = args;
1201
+ const routerAddress = getRouterAddress(movementConfig);
1202
+ const { domainName, subdomainName } = isValidMNSName(name);
1203
+
1204
+ try {
1205
+ const res = await view({
1206
+ movementConfig,
1207
+ payload: {
1208
+ function: `${routerAddress}::router::get_token_addr`,
1209
+ functionArguments: [domainName, subdomainName ?? null],
1210
+ },
1211
+ });
1212
+
1213
+ // Response is directly the address, not wrapped in Option
1214
+ const tokenAddr = res[0] as MoveAddressType;
1215
+ return tokenAddr ? AccountAddress.from(tokenAddr) : undefined;
1216
+ } catch {
1217
+ // Contract throws if name doesn't exist
1218
+ return undefined;
1219
+ }
1220
+ }
1221
+
1222
+ /**
1223
+ * Clear the target address for a domain or subdomain, stopping it from resolving.
1224
+ *
1225
+ * @param args - The arguments for the function.
1226
+ * @param args.movementConfig - The configuration object for Movement.
1227
+ * @param args.sender - The account that owns the name.
1228
+ * @param args.name - The name to clear the target address for.
1229
+ * @param args.options - Optional transaction options.
1230
+ * @returns A transaction object to be signed and submitted.
1231
+ * @group Implementation
1232
+ */
1233
+ export async function clearTargetAddress(args: {
1234
+ movementConfig: MovementConfig;
1235
+ sender: Account;
1236
+ name: string;
1237
+ options?: InputGenerateTransactionOptions;
1238
+ }): Promise<SimpleTransaction> {
1239
+ const { movementConfig, sender, name, options } = args;
1240
+ const routerAddress = getRouterAddress(movementConfig);
1241
+ const { domainName, subdomainName } = isValidMNSName(name);
1242
+
1243
+ const transaction = await generateTransaction({
1244
+ movementConfig,
1245
+ sender: sender.accountAddress.toString(),
1246
+ data: {
1247
+ function: `${routerAddress}::router::clear_target_addr`,
1248
+ functionArguments: [domainName, subdomainName ?? null],
1249
+ },
1250
+ options,
1251
+ });
1252
+
1253
+ return transaction;
1254
+ }
1255
+
1256
+ /**
1257
+ * Buy keys for a domain without staking them for a subdomain.
1258
+ * Keys can be held for speculation or staked later via stakeKeyForSubdomain.
1259
+ *
1260
+ * @param args - The arguments for the function.
1261
+ * @param args.movementConfig - The configuration object for Movement.
1262
+ * @param args.sender - The account buying the keys.
1263
+ * @param args.domainName - The domain to buy keys for.
1264
+ * @param args.amount - The number of keys to buy.
1265
+ * @param args.referrer - Optional referrer address for fee sharing.
1266
+ * @param args.options - Optional transaction options.
1267
+ * @returns A transaction object to be signed and submitted.
1268
+ * @group Implementation
1269
+ */
1270
+ export async function buyKeys(args: {
1271
+ movementConfig: MovementConfig;
1272
+ sender: Account;
1273
+ domainName: string;
1274
+ amount: number;
1275
+ referrer?: AccountAddressInput;
1276
+ options?: InputGenerateTransactionOptions;
1277
+ }): Promise<SimpleTransaction> {
1278
+ const { movementConfig, sender, domainName, amount, referrer, options } = args;
1279
+ const routerAddress = getRouterAddress(movementConfig);
1280
+ const { domainName: validatedDomain, subdomainName } = isValidMNSName(domainName);
1281
+
1282
+ if (subdomainName) {
1283
+ throw new Error("Can only buy keys for domains, not subdomains");
1284
+ }
1285
+
1286
+ // Get the key address for the domain
1287
+ const keyInfo = await view({
1288
+ movementConfig,
1289
+ payload: {
1290
+ function: `${routerAddress}::key_manager::get_key_info_by_domain_name`,
1291
+ functionArguments: [validatedDomain],
1292
+ },
1293
+ });
1294
+
1295
+ const keyData = (keyInfo[0] as any)?.vec?.[0];
1296
+ const keyAddress = keyData?.metadata?.inner;
1297
+ if (!keyAddress) {
1298
+ throw new Error(`No key found for domain ${validatedDomain}. Keys are created when the first subdomain is registered.`);
1299
+ }
1300
+
1301
+ const referrerOpt = referrer ? AccountAddress.from(referrer).toString() : null;
1302
+
1303
+ const transaction = await generateTransaction({
1304
+ movementConfig,
1305
+ sender: sender.accountAddress.toString(),
1306
+ data: {
1307
+ function: `${routerAddress}::router::buy_keys`,
1308
+ functionArguments: [keyAddress, amount, referrerOpt],
1309
+ },
1310
+ options,
1311
+ });
1312
+
1313
+ return transaction;
1314
+ }
1315
+
1316
+ /**
1317
+ * Sell keys for a domain back to the bonding curve.
1318
+ *
1319
+ * @param args - The arguments for the function.
1320
+ * @param args.movementConfig - The configuration object for Movement.
1321
+ * @param args.sender - The account selling the keys.
1322
+ * @param args.domainName - The domain to sell keys for.
1323
+ * @param args.amount - The number of keys to sell.
1324
+ * @param args.referrer - Optional referrer address for fee sharing.
1325
+ * @param args.options - Optional transaction options.
1326
+ * @returns A transaction object to be signed and submitted.
1327
+ * @group Implementation
1328
+ */
1329
+ export async function sellKeys(args: {
1330
+ movementConfig: MovementConfig;
1331
+ sender: Account;
1332
+ domainName: string;
1333
+ amount: number;
1334
+ referrer?: AccountAddressInput;
1335
+ options?: InputGenerateTransactionOptions;
1336
+ }): Promise<SimpleTransaction> {
1337
+ const { movementConfig, sender, domainName, amount, referrer, options } = args;
1338
+ const routerAddress = getRouterAddress(movementConfig);
1339
+ const { domainName: validatedDomain, subdomainName } = isValidMNSName(domainName);
1340
+
1341
+ if (subdomainName) {
1342
+ throw new Error("Can only sell keys for domains, not subdomains");
1343
+ }
1344
+
1345
+ // Get the key address for the domain
1346
+ const keyInfo = await view({
1347
+ movementConfig,
1348
+ payload: {
1349
+ function: `${routerAddress}::key_manager::get_key_info_by_domain_name`,
1350
+ functionArguments: [validatedDomain],
1351
+ },
1352
+ });
1353
+
1354
+ const keyData = (keyInfo[0] as any)?.vec?.[0];
1355
+ const keyAddress = keyData?.metadata?.inner;
1356
+ if (!keyAddress) {
1357
+ throw new Error(`No key found for domain ${validatedDomain}.`);
1358
+ }
1359
+
1360
+ const referrerOpt = referrer ? AccountAddress.from(referrer).toString() : null;
1361
+
1362
+ const transaction = await generateTransaction({
1363
+ movementConfig,
1364
+ sender: sender.accountAddress.toString(),
1365
+ data: {
1366
+ function: `${routerAddress}::router::sell_keys`,
1367
+ functionArguments: [keyAddress, amount, referrerOpt],
1368
+ },
1369
+ options,
1370
+ });
1371
+
1372
+ return transaction;
1373
+ }
1374
+
1375
+ /**
1376
+ * Get the registration price for a domain name.
1377
+ * Price varies based on domain length and registration duration.
1378
+ *
1379
+ * @param args - The arguments for the function.
1380
+ * @param args.movementConfig - The configuration object for Movement.
1381
+ * @param args.name - The domain name to get the price for.
1382
+ * @param args.years - Number of years to register (default: 1).
1383
+ * @returns The price in octas to register the domain.
1384
+ * @group Implementation
1385
+ */
1386
+ export async function getDomainPrice(args: {
1387
+ movementConfig: MovementConfig;
1388
+ name: string;
1389
+ years?: number;
1390
+ }): Promise<bigint> {
1391
+ const { movementConfig, name, years = 1 } = args;
1392
+ const routerAddress = getRouterAddress(movementConfig);
1393
+ const { domainName, subdomainName } = isValidMNSName(name);
1394
+
1395
+ if (subdomainName) {
1396
+ throw new Error("Can only get price for domains, not subdomains. Subdomains use key staking.");
1397
+ }
1398
+
1399
+ const secondsPerYear = 31536000;
1400
+ const registrationSeconds = years * secondsPerYear;
1401
+
1402
+ const res = await view({
1403
+ movementConfig,
1404
+ payload: {
1405
+ function: `${routerAddress}::price_model_v2::price_for_domain`,
1406
+ functionArguments: [domainName, registrationSeconds],
1407
+ },
1408
+ });
1409
+
1410
+ return BigInt(res[0] as string);
1411
+ }