@aptos-labs/ts-sdk 0.0.3 → 0.0.4

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 (56) hide show
  1. package/README.md +1 -0
  2. package/dist/browser/index.global.js +26 -25
  3. package/dist/browser/index.global.js.map +1 -1
  4. package/dist/cjs/index.d.ts +413 -227
  5. package/dist/cjs/index.js +451 -181
  6. package/dist/cjs/index.js.map +1 -1
  7. package/dist/esm/index.d.ts +413 -227
  8. package/dist/esm/index.mjs +440 -178
  9. package/dist/esm/index.mjs.map +1 -1
  10. package/dist/types/index.d.ts +11 -17
  11. package/dist/types/index.js.map +1 -1
  12. package/package.json +3 -2
  13. package/src/api/account.ts +17 -18
  14. package/src/api/ans.ts +58 -0
  15. package/src/api/aptos.ts +7 -1
  16. package/src/api/coin.ts +3 -3
  17. package/src/api/digitalAsset.ts +9 -8
  18. package/src/api/event.ts +4 -3
  19. package/src/api/faucet.ts +3 -2
  20. package/src/api/general.ts +2 -2
  21. package/src/api/staking.ts +5 -4
  22. package/src/api/transactionSubmission.ts +27 -18
  23. package/src/bcs/deserializer.ts +4 -4
  24. package/src/bcs/serializable/moveStructs.ts +5 -9
  25. package/src/bcs/serializer.ts +4 -4
  26. package/src/client/core.ts +14 -6
  27. package/src/core/account.ts +83 -29
  28. package/src/core/accountAddress.ts +34 -30
  29. package/src/core/authenticationKey.ts +11 -9
  30. package/src/core/crypto/ed25519.ts +48 -1
  31. package/src/core/crypto/hdKey.ts +105 -0
  32. package/src/core/crypto/index.ts +1 -0
  33. package/src/core/crypto/secp256k1.ts +36 -0
  34. package/src/index.ts +0 -1
  35. package/src/internal/account.ts +80 -58
  36. package/src/internal/ans.ts +177 -0
  37. package/src/internal/coin.ts +5 -5
  38. package/src/internal/digitalAsset.ts +16 -17
  39. package/src/internal/event.ts +7 -7
  40. package/src/internal/faucet.ts +4 -4
  41. package/src/internal/general.ts +3 -3
  42. package/src/internal/staking.ts +8 -8
  43. package/src/internal/transactionSubmission.ts +71 -18
  44. package/src/transactions/instances/index.ts +1 -0
  45. package/src/transactions/instances/moduleId.ts +1 -1
  46. package/src/transactions/instances/rotationProofChallenge.ts +58 -0
  47. package/src/transactions/transactionBuilder/helpers.ts +5 -1
  48. package/src/transactions/transactionBuilder/remoteAbi.ts +3 -3
  49. package/src/transactions/transactionBuilder/transactionBuilder.ts +31 -45
  50. package/src/transactions/typeTag/index.ts +10 -10
  51. package/src/transactions/typeTag/parser.ts +1 -1
  52. package/src/transactions/types.ts +28 -17
  53. package/src/types/index.ts +11 -16
  54. package/src/utils/apiEndpoints.ts +8 -0
  55. package/src/version.ts +1 -1
  56. package/src/utils/hdKey.ts +0 -113
@@ -2,7 +2,7 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import { AptosConfig } from "./aptosConfig";
5
- import { Account } from "../core";
5
+ import { Account, AccountAddressInput, PrivateKey } from "../core";
6
6
  import { AccountAuthenticator } from "../transactions/authenticator/account";
7
7
  import {
8
8
  AnyRawTransaction,
@@ -15,11 +15,13 @@ import {
15
15
  InputSingleSignerTransaction,
16
16
  InputSimulateTransactionData,
17
17
  InputGenerateTransactionOptions,
18
+ InputSubmitTransactionData,
18
19
  } from "../transactions/types";
19
- import { UserTransactionResponse, PendingTransactionResponse, HexInput } from "../types";
20
+ import { UserTransactionResponse, PendingTransactionResponse, HexInput, TransactionResponse } from "../types";
20
21
  import {
21
22
  generateTransaction,
22
23
  publicPackageTransaction,
24
+ rotateAuthKey,
23
25
  signAndSubmitTransaction,
24
26
  signTransaction,
25
27
  simulateTransaction,
@@ -47,7 +49,7 @@ export class TransactionSubmission {
47
49
  /**
48
50
  * Generates any transaction by passing in the required arguments
49
51
  *
50
- * @param args.sender The transaction sender's account address as a HexInput
52
+ * @param args.sender The transaction sender's account address as a AccountAddressInput
51
53
  * @param args.data EntryFunctionData | ScriptData | MultiSigData
52
54
  * @param args.feePayerAddress optional. For a fee payer (aka sponsored) transaction
53
55
  * @param args.secondarySignerAddresses optional. For a multi-agent or fee payer (aka sponsored) transactions
@@ -75,10 +77,10 @@ export class TransactionSubmission {
75
77
  * }
76
78
  * ```
77
79
  *
78
- * @return A raw transaction type (note that it holds the raw transaction as a bcs serialized data)
80
+ * @return An instance of a RawTransaction, plus optional secondary/fee payer addresses
79
81
  * ```
80
82
  * {
81
- * rawTransaction: Uint8Array,
83
+ * rawTransaction: RawTransaction,
82
84
  * secondarySignerAddresses? : Array<AccountAddress>,
83
85
  * feePayerAddress?: AccountAddress
84
86
  * }
@@ -92,10 +94,10 @@ export class TransactionSubmission {
92
94
  * Sign a transaction that can later be submitted to chain
93
95
  *
94
96
  * @param args.signer The signer account to sign the transaction
95
- * @param args.transaction A raw transaction type (note that it holds the raw transaction as a bcs serialized data)
97
+ * @param args.transaction An instance of a RawTransaction, plus optional secondary/fee payer addresses
96
98
  * ```
97
99
  * {
98
- * rawTransaction: Uint8Array,
100
+ * rawTransaction: RawTransaction,
99
101
  * secondarySignerAddresses? : Array<AccountAddress>,
100
102
  * feePayerAddress?: AccountAddress
101
103
  * }
@@ -130,14 +132,7 @@ export class TransactionSubmission {
130
132
  *
131
133
  * @return PendingTransactionResponse
132
134
  */
133
- async submitTransaction(args: {
134
- transaction: AnyRawTransaction;
135
- senderAuthenticator: AccountAuthenticator;
136
- secondarySignerAuthenticators?: {
137
- feePayerAuthenticator?: AccountAuthenticator;
138
- additionalSignersAuthenticators?: Array<AccountAuthenticator>;
139
- };
140
- }): Promise<PendingTransactionResponse> {
135
+ async submitTransaction(args: InputSubmitTransactionData): Promise<PendingTransactionResponse> {
141
136
  return submitTransaction({ aptosConfig: this.config, ...args });
142
137
  }
143
138
 
@@ -145,10 +140,10 @@ export class TransactionSubmission {
145
140
  * Sign and submit a single signer transaction to chain
146
141
  *
147
142
  * @param args.signer The signer account to sign the transaction
148
- * @param args.transaction A raw transaction type (note that it holds the raw transaction as a bcs serialized data)
143
+ * @param args.transaction An instance of a RawTransaction, plus optional secondary/fee payer addresses
149
144
  * ```
150
145
  * {
151
- * rawTransaction: Uint8Array,
146
+ * rawTransaction: RawTransaction,
152
147
  * secondarySignerAddresses? : Array<AccountAddress>,
153
148
  * feePayerAddress?: AccountAddress
154
149
  * }
@@ -182,11 +177,25 @@ export class TransactionSubmission {
182
177
  * @returns A SingleSignerTransaction that can be simulated or submitted to chain
183
178
  */
184
179
  async publishPackageTransaction(args: {
185
- account: HexInput;
180
+ account: AccountAddressInput;
186
181
  metadataBytes: HexInput;
187
182
  moduleBytecode: Array<HexInput>;
188
183
  options?: InputGenerateTransactionOptions;
189
184
  }): Promise<InputSingleSignerTransaction> {
190
185
  return publicPackageTransaction({ aptosConfig: this.config, ...args });
191
186
  }
187
+
188
+ /**
189
+ * Rotate an account's auth key. After rotation, only the new private key can be used to sign txns for
190
+ * the account.
191
+ * Note: Only legacy Ed25519 scheme is supported for now.
192
+ * More info: {@link https://aptos.dev/guides/account-management/key-rotation/}
193
+ * @param args.fromAccount The account to rotate the auth key for
194
+ * @param args.toNewPrivateKey The new private key to rotate to
195
+ *
196
+ * @returns PendingTransactionResponse
197
+ */
198
+ async rotateAuthKey(args: { fromAccount: Account; toNewPrivateKey: PrivateKey }): Promise<TransactionResponse> {
199
+ return rotateAuthKey({ aptosConfig: this.config, ...args });
200
+ }
192
201
  }
@@ -222,10 +222,10 @@ export class Deserializer {
222
222
  * @example
223
223
  * // serialize a vector of addresses
224
224
  * const addresses = new Array<AccountAddress>(
225
- * AccountAddress.fromHexInputRelaxed("0x1"),
226
- * AccountAddress.fromHexInputRelaxed("0x2"),
227
- * AccountAddress.fromHexInputRelaxed("0xa"),
228
- * AccountAddress.fromHexInputRelaxed("0xb"),
225
+ * AccountAddress.fromRelaxed("0x1"),
226
+ * AccountAddress.fromRelaxed("0x2"),
227
+ * AccountAddress.fromRelaxed("0xa"),
228
+ * AccountAddress.fromRelaxed("0xb"),
229
229
  * );
230
230
  * const serializer = new Serializer();
231
231
  * serializer.serializeVector(addresses);
@@ -42,11 +42,6 @@ import { EntryFunctionArgument, TransactionArgument } from "../../transactions/i
42
42
  * const vecOfStrings = new MoveVector([new MoveString("hello"), new MoveString("world")]);
43
43
  * const vecOfStrings2 = MoveVector.MoveString(["hello", "world"]);
44
44
  *
45
- * // where MySerializableStruct is a class you've made that implements Serializable
46
- * const vecOfSerializableValues = new MoveVector<MySerializableStruct>([
47
- * new MySerializableStruct("hello", "world"),
48
- * new MySerializableStruct("foo", "bar"),
49
- * ]);
50
45
  * @params
51
46
  * values: an Array<T> of values where T is a class that implements Serializable
52
47
  * @returns a `MoveVector<T>` with the values `values`
@@ -172,7 +167,7 @@ export class MoveVector<T extends Serializable & EntryFunctionArgument>
172
167
  *
173
168
  * @example
174
169
  * const v = MoveVector.Bool([true, false, true, false]);
175
- * @params values: an array of `numbers` to convert to Bools
170
+ * @params values: an array of `bools` to convert to Bools
176
171
  * @returns a `MoveVector<Bool>`
177
172
  */
178
173
  static Bool(values: Array<boolean>): MoveVector<Bool> {
@@ -184,7 +179,7 @@ export class MoveVector<T extends Serializable & EntryFunctionArgument>
184
179
  *
185
180
  * @example
186
181
  * const v = MoveVector.MoveString(["hello", "world"]);
187
- * @params values: an array of `numbers` to convert to MoveStrings
182
+ * @params values: an array of `strings` to convert to MoveStrings
188
183
  * @returns a `MoveVector<MoveString>`
189
184
  */
190
185
  static MoveString(values: Array<string>): MoveVector<MoveString> {
@@ -202,8 +197,9 @@ export class MoveVector<T extends Serializable & EntryFunctionArgument>
202
197
  *
203
198
  * NOTE: This will not work with types that aren't of the Serializable class.
204
199
  *
205
- * If you want to use types that merely implement Deserializable,
206
- * please use the deserializeVector function in the Deserializer class.
200
+ * If you're looking for a more flexible deserialization function, you can use the deserializeVector function
201
+ * in the Deserializer class.
202
+ *
207
203
  * @example
208
204
  * const vec = MoveVector.deserialize(deserializer, U64);
209
205
  * @params deserializer: the Deserializer instance to use, with bytes loaded into it already.
@@ -298,10 +298,10 @@ export class Serializer {
298
298
  * @param values The array of BCS Serializable values
299
299
  * @example
300
300
  * const addresses = new Array<AccountAddress>(
301
- * AccountAddress.fromHexInputRelaxed("0x1"),
302
- * AccountAddress.fromHexInputRelaxed("0x2"),
303
- * AccountAddress.fromHexInputRelaxed("0xa"),
304
- * AccountAddress.fromHexInputRelaxed("0xb"),
301
+ * AccountAddress.fromRelaxed("0x1"),
302
+ * AccountAddress.fromRelaxed("0x2"),
303
+ * AccountAddress.fromRelaxed("0xa"),
304
+ * AccountAddress.fromRelaxed("0xb"),
305
305
  * );
306
306
  * const serializer = new Serializer();
307
307
  * serializer.serializeVector(addresses);
@@ -94,11 +94,19 @@ export async function aptosRequest<Req, Res>(
94
94
  return result;
95
95
  }
96
96
 
97
- const errorMessage = errors[result.status];
97
+ let errorMessage: string;
98
98
 
99
- throw new AptosApiError(
100
- options,
101
- result,
102
- errorMessage ?? `Unhandled Error ${response.status} : ${response.statusText}`,
103
- );
99
+ // If it is the shape of an AptosApiError, convert it properly
100
+ if ("message" in response.data && "error_code" in response.data) {
101
+ const data = response.data as { message: string; error_code: string; vm_error_code?: string };
102
+ errorMessage = JSON.stringify(data);
103
+ } else if (result.status in errors) {
104
+ // If it's not an API type, it must come form infra, these are prehandled
105
+ errorMessage = errors[result.status];
106
+ } else {
107
+ // Everything else is unhandled
108
+ errorMessage = `Unhandled Error ${response.status} : ${response.statusText}`;
109
+ }
110
+
111
+ throw new AptosApiError(options, result, errorMessage);
104
112
  }
@@ -9,7 +9,6 @@ import { MultiEd25519PublicKey } from "./crypto/multiEd25519";
9
9
  import { Secp256k1PrivateKey, Secp256k1PublicKey } from "./crypto/secp256k1";
10
10
  import { Hex } from "./hex";
11
11
  import { GenerateAccount, HexInput, SigningScheme, SigningSchemeInput } from "../types";
12
- import { derivePrivateKeyFromMnemonic, KeyType } from "../utils/hdKey";
13
12
  import { AnyPublicKey } from "./crypto/anyPublicKey";
14
13
 
15
14
  /**
@@ -70,13 +69,14 @@ export class Account {
70
69
  */
71
70
  private constructor(args: { privateKey: PrivateKey; address: AccountAddress; legacy?: boolean }) {
72
71
  const { privateKey, address, legacy } = args;
72
+ const useLegacy = legacy ?? true;
73
73
 
74
74
  // Derive the public key from the private key
75
75
  this.publicKey = privateKey.publicKey();
76
76
 
77
77
  // Derive the signing scheme from the public key
78
78
  if (this.publicKey instanceof Ed25519PublicKey) {
79
- if (legacy) {
79
+ if (useLegacy) {
80
80
  this.signingScheme = SigningScheme.Ed25519;
81
81
  } else {
82
82
  this.publicKey = new AnyPublicKey(this.publicKey);
@@ -97,7 +97,8 @@ export class Account {
97
97
 
98
98
  /**
99
99
  * Derives an account with random private key and address.
100
- * Default generation is using the Unified flow with ED25519 key
100
+ *
101
+ * Default generation is using the Legacy ED25519 key
101
102
  *
102
103
  * @param args optional. Unify GenerateAccount type for Legacy and Unified keys
103
104
  *
@@ -123,37 +124,75 @@ export class Account {
123
124
  * @returns Account with the given signing scheme
124
125
  */
125
126
  static generate(args?: GenerateAccount): Account {
127
+ const useLegacy = args?.legacy ?? true;
128
+
126
129
  let privateKey: PrivateKey;
130
+ let publicKey: PublicKey;
127
131
 
128
132
  switch (args?.scheme) {
129
133
  case SigningSchemeInput.Secp256k1Ecdsa:
130
134
  privateKey = Secp256k1PrivateKey.generate();
135
+ publicKey = new AnyPublicKey(privateKey.publicKey());
131
136
  break;
132
137
  default:
133
138
  privateKey = Ed25519PrivateKey.generate();
139
+ if (useLegacy === false) {
140
+ publicKey = new AnyPublicKey(privateKey.publicKey());
141
+ } else {
142
+ publicKey = privateKey.publicKey();
143
+ }
134
144
  }
145
+ const authKey = AuthenticationKey.fromPublicKey({ publicKey });
135
146
 
136
- let publicKey = privateKey.publicKey();
137
- if (!args?.legacy) {
147
+ const address = authKey.derivedAddress();
148
+ return new Account({ privateKey, address, legacy: args?.legacy });
149
+ }
150
+
151
+ /**
152
+ * Instantiates an account given a private key.
153
+ *
154
+ * This is used as a local calculation and therefore is used to instantiate an `Account`
155
+ * that has not had its authentication key rotated.
156
+ *
157
+ * @param privateKey PrivateKey - private key of the account
158
+ * @param args.legacy optional. If set to false, the keypair generated is a Unified keypair. Defaults
159
+ * to generating a Legacy Ed25519 keypair
160
+ *
161
+ * @returns Account
162
+ */
163
+ static fromPrivateKey(args: { privateKey: PrivateKey; legacy?: boolean }): Account {
164
+ const { privateKey, legacy } = args;
165
+ const useLegacy = legacy ?? true;
166
+
167
+ let publicKey;
168
+ if (privateKey instanceof Secp256k1PrivateKey) {
169
+ // Secp256k1 single sender
138
170
  publicKey = new AnyPublicKey(privateKey.publicKey());
171
+ } else if (privateKey instanceof Ed25519PrivateKey) {
172
+ // legacy Ed25519
173
+ if (useLegacy) {
174
+ publicKey = privateKey.publicKey();
175
+ } else {
176
+ // Ed25519 single sender
177
+ publicKey = new AnyPublicKey(privateKey.publicKey());
178
+ }
179
+ } else {
180
+ throw new Error(`Unsupported private key ${privateKey}`);
139
181
  }
140
182
 
141
- const address = new AccountAddress({
142
- data: Account.authKey({
143
- publicKey,
144
- }).toUint8Array(),
145
- });
146
- return new Account({ privateKey, address, legacy: args?.legacy });
183
+ const authKey = AuthenticationKey.fromPublicKey({ publicKey });
184
+ const address = authKey.derivedAddress();
185
+ return new Account({ privateKey, address, legacy: useLegacy });
147
186
  }
148
187
 
149
188
  /**
150
189
  * Instantiates an account given a private key and a specified account address.
151
190
  * This is primarily used to instantiate an `Account` that has had its authentication key rotated.
152
191
  *
153
- * @param privateKey PrivateKey - private key of the account
154
- * @param address The account address
155
- * @param args.legacy optional. If set to true, the keypair authentication keys will be derived with a Legacy scheme.
156
- * Defaults to deriving an authentication key with a Unified scheme
192
+ * @param args.privateKey PrivateKey - the underlying private key for the account
193
+ * @param args.address AccountAddress - The account address the `Account` will sign for
194
+ * @param args.legacy optional. If set to false, the keypair generated is a Unified keypair. Defaults
195
+ * to generating a Legacy Ed25519 keypair
157
196
  *
158
197
  * @returns Account
159
198
  */
@@ -169,19 +208,35 @@ export class Account {
169
208
  /**
170
209
  * Derives an account with bip44 path and mnemonics,
171
210
  *
172
- * @param args.path the BIP44 derive path (e.g. m/44'/637'/0'/0'/0')
211
+ * @param args.scheme The signing scheme to derive with
212
+ * @param args.path the BIP44 derive hardened path (e.g. m/44'/637'/0'/0'/0') for Ed25519,
213
+ * or non-hardened path (e.g. m/44'/637'/0'/0/0) for secp256k1
173
214
  * Detailed description: {@link https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki}
174
215
  * @param args.mnemonic the mnemonic seed phrase of the account
175
- * @returns AptosAccount
216
+ * @param args.legacy optional. If set to false, the keypair generated is a Unified keypair. Defaults
217
+ * to generating a Legacy Ed25519 keypair
218
+ *
219
+ * @returns Account
176
220
  */
177
- static fromDerivationPath(args: { path: string; mnemonic: string }): Account {
178
- const { path, mnemonic } = args;
179
- const { key } = derivePrivateKeyFromMnemonic(KeyType.ED25519, path, mnemonic);
180
- const privateKey = new Ed25519PrivateKey(key);
181
- const publicKey = privateKey.publicKey();
182
- const authKey = Account.authKey({ publicKey });
183
- const address = new AccountAddress({ data: authKey.toUint8Array() });
184
- return new Account({ privateKey, address, legacy: true });
221
+ static fromDerivationPath(args: {
222
+ scheme: SigningSchemeInput;
223
+ path: string;
224
+ mnemonic: string;
225
+ legacy?: boolean;
226
+ }): Account {
227
+ const { path, mnemonic, scheme, legacy } = args;
228
+ let privateKey: PrivateKey;
229
+ switch (scheme) {
230
+ case SigningSchemeInput.Secp256k1Ecdsa:
231
+ privateKey = Secp256k1PrivateKey.fromDerivationPath(path, mnemonic);
232
+ break;
233
+ case SigningSchemeInput.Ed25519:
234
+ privateKey = Ed25519PrivateKey.fromDerivationPath(path, mnemonic);
235
+ break;
236
+ default:
237
+ throw new Error(`Unsupported scheme ${scheme}`);
238
+ }
239
+ return Account.fromPrivateKey({ privateKey, legacy });
185
240
  }
186
241
 
187
242
  /**
@@ -190,12 +245,11 @@ export class Account {
190
245
  * See here for more info: {@link https://aptos.dev/concepts/accounts#single-signer-authentication}
191
246
  *
192
247
  * @param args.publicKey PublicKey - public key of the account
193
- * @returns Authentication key for the associated account
248
+ * @returns The authentication key for the associated account
194
249
  */
195
- static authKey(args: { publicKey: PublicKey }): Hex {
250
+ static authKey(args: { publicKey: PublicKey }): AuthenticationKey {
196
251
  const { publicKey } = args;
197
- const authKey = AuthenticationKey.fromPublicKey({ publicKey });
198
- return authKey.data;
252
+ return AuthenticationKey.fromPublicKey({ publicKey });
199
253
  }
200
254
 
201
255
  /**
@@ -21,6 +21,8 @@ export enum AddressInvalidReason {
21
21
  INVALID_PADDING_ZEROES = "INVALID_PADDING_ZEROES",
22
22
  }
23
23
 
24
+ export type AccountAddressInput = HexInput | AccountAddress;
25
+
24
26
  /**
25
27
  * NOTE: Only use this class for account addresses. For other hex data, e.g. transaction
26
28
  * hashes, use the Hex class.
@@ -53,30 +55,30 @@ export class AccountAddress extends Serializable implements TransactionArgument
53
55
  */
54
56
  static readonly LONG_STRING_LENGTH: number = 64;
55
57
 
56
- static ZERO: AccountAddress = AccountAddress.fromString("0x0");
58
+ static ZERO: AccountAddress = AccountAddress.from("0x0");
57
59
 
58
- static ONE: AccountAddress = AccountAddress.fromString("0x1");
60
+ static ONE: AccountAddress = AccountAddress.from("0x1");
59
61
 
60
- static TWO: AccountAddress = AccountAddress.fromString("0x2");
62
+ static TWO: AccountAddress = AccountAddress.from("0x2");
61
63
 
62
- static THREE: AccountAddress = AccountAddress.fromString("0x3");
64
+ static THREE: AccountAddress = AccountAddress.from("0x3");
63
65
 
64
- static FOUR: AccountAddress = AccountAddress.fromString("0x4");
66
+ static FOUR: AccountAddress = AccountAddress.from("0x4");
65
67
 
66
68
  /**
67
69
  * Creates an instance of AccountAddress from a Uint8Array.
68
70
  *
69
71
  * @param args.data A Uint8Array representing an account address.
70
72
  */
71
- constructor(args: { data: Uint8Array }) {
73
+ constructor(input: Uint8Array) {
72
74
  super();
73
- if (args.data.length !== AccountAddress.LENGTH) {
75
+ if (input.length !== AccountAddress.LENGTH) {
74
76
  throw new ParsingError(
75
77
  "AccountAddress data should be exactly 32 bytes long",
76
78
  AddressInvalidReason.INCORRECT_NUMBER_OF_BYTES,
77
79
  );
78
80
  }
79
- this.data = args.data;
81
+ this.data = input;
80
82
  }
81
83
 
82
84
  /**
@@ -207,7 +209,7 @@ export class AccountAddress extends Serializable implements TransactionArgument
207
209
  */
208
210
  static deserialize(deserializer: Deserializer): AccountAddress {
209
211
  const bytes = deserializer.deserializeFixedBytes(AccountAddress.LENGTH);
210
- return new AccountAddress({ data: bytes });
212
+ return new AccountAddress(bytes);
211
213
  }
212
214
 
213
215
  // ===
@@ -331,37 +333,39 @@ export class AccountAddress extends Serializable implements TransactionArgument
331
333
  throw new ParsingError(`Hex characters are invalid: ${error.message}`, AddressInvalidReason.INVALID_HEX_CHARS);
332
334
  }
333
335
 
334
- return new AccountAddress({ data: addressBytes });
336
+ return new AccountAddress(addressBytes);
335
337
  }
336
338
 
337
339
  /**
338
- * Convenience method for creating an AccountAddress from HexInput. For
339
- * more information on how this works, see the constructor and fromString.
340
- *
341
- * @param input A hex string or Uint8Array representing an account address.
340
+ * Convenience method for creating an AccountAddress from all known inputs.
342
341
  *
343
- * @returns An instance of AccountAddress.
342
+ * This handles, Uint8array, string, and AccountAddress itself
343
+ * @param input
344
344
  */
345
- static fromHexInput(input: HexInput): AccountAddress {
345
+ static fromRelaxed(input: AccountAddressInput): AccountAddress {
346
+ if (input instanceof AccountAddress) {
347
+ return input;
348
+ }
346
349
  if (input instanceof Uint8Array) {
347
- return new AccountAddress({ data: input });
350
+ return new AccountAddress(input);
348
351
  }
349
- return AccountAddress.fromString(input);
352
+ return AccountAddress.fromStringRelaxed(input);
350
353
  }
351
354
 
352
355
  /**
353
- * Convenience method for creating an AccountAddress from HexInput. For
354
- * more information on how this works, see the constructor and fromStringRelaxed.
356
+ * Convenience method for creating an AccountAddress from all known inputs.
355
357
  *
356
- * @param hexInput A hex string or Uint8Array representing an account address.
357
- *
358
- * @returns An instance of AccountAddress.
358
+ * This handles, Uint8array, string, and AccountAddress itself
359
+ * @param input
359
360
  */
360
- static fromHexInputRelaxed(hexInput: HexInput): AccountAddress {
361
- if (hexInput instanceof Uint8Array) {
362
- return new AccountAddress({ data: hexInput });
361
+ static from(input: AccountAddressInput): AccountAddress {
362
+ if (input instanceof AccountAddress) {
363
+ return input;
363
364
  }
364
- return AccountAddress.fromStringRelaxed(hexInput);
365
+ if (input instanceof Uint8Array) {
366
+ return new AccountAddress(input);
367
+ }
368
+ return AccountAddress.fromString(input);
365
369
  }
366
370
 
367
371
  // ===
@@ -377,12 +381,12 @@ export class AccountAddress extends Serializable implements TransactionArgument
377
381
  * @returns valid = true if the string is valid, valid = false if not. If the string
378
382
  * is not valid, invalidReason will be set explaining why it is invalid.
379
383
  */
380
- static isValid(args: { input: string; relaxed?: boolean }): ParsingResult<AddressInvalidReason> {
384
+ static isValid(args: { input: AccountAddressInput; relaxed?: boolean }): ParsingResult<AddressInvalidReason> {
381
385
  try {
382
386
  if (args.relaxed) {
383
- AccountAddress.fromStringRelaxed(args.input);
387
+ AccountAddress.fromRelaxed(args.input);
384
388
  } else {
385
- AccountAddress.fromString(args.input);
389
+ AccountAddress.from(args.input);
386
390
  }
387
391
  return { valid: true };
388
392
  } catch (e) {
@@ -18,13 +18,13 @@ import { Deserializer } from "../bcs/deserializer";
18
18
  * their private key(s) associated with the account without changing the address that hosts their account.
19
19
  * @see {@link https://aptos.dev/concepts/accounts | Account Basics}
20
20
  *
21
- * Note: AuthenticationKey only supports Ed25519 and MultiEd25519 public keys for now.
22
- *
23
21
  * Account addresses can be derived from AuthenticationKey
24
22
  */
25
23
  export class AuthenticationKey extends Serializable {
26
24
  /**
27
25
  * An authentication key is always a SHA3-256 hash of data, and is always 32 bytes.
26
+ *
27
+ * The data to hash depends on the underlying public key type and the derivation scheme.
28
28
  */
29
29
  static readonly LENGTH: number = 32;
30
30
 
@@ -66,10 +66,11 @@ export class AuthenticationKey extends Serializable {
66
66
  }
67
67
 
68
68
  /**
69
- * Creates an AuthenticationKey from seed bytes and a scheme
69
+ * Derives an AuthenticationKey from the public key seed bytes and an explicit derivation scheme.
70
+ *
71
+ * This facilitates targeting a specific scheme for deriving an authentication key from a public key.
70
72
  *
71
- * This allows for the creation of AuthenticationKeys that are not derived from Public Keys directly
72
- * @param args
73
+ * @param args - the public key and scheme to use for the derivation
73
74
  */
74
75
  public static fromPublicKeyAndScheme(args: { publicKey: PublicKey; scheme: AuthenticationKeyScheme }) {
75
76
  const { publicKey, scheme } = args;
@@ -102,7 +103,8 @@ export class AuthenticationKey extends Serializable {
102
103
  }
103
104
 
104
105
  /**
105
- * Converts a PublicKey(s) to AuthenticationKey
106
+ * Converts a PublicKey(s) to an AuthenticationKey, using the derivation scheme inferred from the
107
+ * instance of the PublicKey type passed in.
106
108
  *
107
109
  * @param args.publicKey
108
110
  * @returns AuthenticationKey
@@ -129,12 +131,12 @@ export class AuthenticationKey extends Serializable {
129
131
  }
130
132
 
131
133
  /**
132
- * Derives an account address from AuthenticationKey. Since current AccountAddress is 32 bytes,
133
- * AuthenticationKey bytes are directly translated to AccountAddress.
134
+ * Derives an account address from an AuthenticationKey. Since an AccountAddress is also 32 bytes,
135
+ * the AuthenticationKey bytes are directly translated to an AccountAddress.
134
136
  *
135
137
  * @returns AccountAddress
136
138
  */
137
139
  derivedAddress(): AccountAddress {
138
- return new AccountAddress({ data: this.data.toUint8Array() });
140
+ return new AccountAddress(this.data.toUint8Array());
139
141
  }
140
142
  }
@@ -7,6 +7,7 @@ import { Deserializer } from "../../bcs/deserializer";
7
7
  import { Serializer } from "../../bcs/serializer";
8
8
  import { Hex } from "../hex";
9
9
  import { HexInput } from "../../types";
10
+ import { CKDPriv, deriveKey, HARDENED_OFFSET, isValidHardenedPath, mnemonicToSeed, splitPath } from "./hdKey";
10
11
 
11
12
  /**
12
13
  * Represents the public key of an Ed25519 key pair.
@@ -70,7 +71,7 @@ export class Ed25519PublicKey extends PublicKey {
70
71
  verifySignature(args: { message: HexInput; signature: Ed25519Signature }): boolean {
71
72
  const { message, signature } = args;
72
73
  const rawMessage = Hex.fromHexInput(message).toUint8Array();
73
- const rawSignature = Hex.fromHexInput(signature.toUint8Array()).toUint8Array();
74
+ const rawSignature = signature.toUint8Array();
74
75
  return nacl.sign.detached.verify(rawMessage, rawSignature, this.key.toUint8Array());
75
76
  }
76
77
 
@@ -98,6 +99,12 @@ export class Ed25519PrivateKey extends PrivateKey {
98
99
  */
99
100
  static readonly LENGTH: number = 32;
100
101
 
102
+ /**
103
+ * The Ed25519 key seed to use for BIP-32 compatibility
104
+ * See more {@link https://github.com/satoshilabs/slips/blob/master/slip-0010.md}
105
+ */
106
+ static readonly SLIP_0010_SEED = "ed25519 seed";
107
+
101
108
  /**
102
109
  * The Ed25519 signing key
103
110
  * @private
@@ -179,6 +186,46 @@ export class Ed25519PrivateKey extends PrivateKey {
179
186
  const bytes = this.signingKeyPair.publicKey;
180
187
  return new Ed25519PublicKey(bytes);
181
188
  }
189
+
190
+ /**
191
+ * Derives a private key from a mnemonic seed phrase.
192
+ *
193
+ * To derive multiple keys from the same phrase, change the path
194
+ *
195
+ * IMPORTANT: Ed25519 supports hardened derivation only (since it lacks a key homomorphism,
196
+ * so non-hardened derivation cannot work)
197
+ *
198
+ * @param path the BIP44 path
199
+ * @param mnemonics the mnemonic seed phrase
200
+ */
201
+ static fromDerivationPath(path: string, mnemonics: string): Ed25519PrivateKey {
202
+ if (!isValidHardenedPath(path)) {
203
+ throw new Error(`Invalid derivation path ${path}`);
204
+ }
205
+ return Ed25519PrivateKey.fromDerivationPathInner(path, mnemonicToSeed(mnemonics));
206
+ }
207
+
208
+ /**
209
+ * A private inner function so we can separate from the main fromDerivationPath() method
210
+ * to add tests to verify we create the keys correctly.
211
+ *
212
+ * @param path the BIP44 path
213
+ * @param seed the seed phrase created by the mnemonics
214
+ * @param offset the offset used for key derivation, defaults to 0x80000000
215
+ * @returns
216
+ */
217
+ private static fromDerivationPathInner(path: string, seed: Uint8Array, offset = HARDENED_OFFSET): Ed25519PrivateKey {
218
+ const { key, chainCode } = deriveKey(Ed25519PrivateKey.SLIP_0010_SEED, seed);
219
+
220
+ const segments = splitPath(path).map((el) => parseInt(el, 10));
221
+
222
+ // Derive the child key based on the path
223
+ const { key: privateKey } = segments.reduce((parentKeys, segment) => CKDPriv(parentKeys, segment + offset), {
224
+ key,
225
+ chainCode,
226
+ });
227
+ return new Ed25519PrivateKey(privateKey);
228
+ }
182
229
  }
183
230
 
184
231
  /**