Skip to content

Commit ddcc7e2

Browse files
authored
Merge pull request #6921 from BitGo/COIN-5352-refactors
refactor(apt): changes to validation and test cases
2 parents 5cd32b7 + 5fd49b9 commit ddcc7e2

File tree

4 files changed

+118
-61
lines changed

4 files changed

+118
-61
lines changed

modules/sdk-coin-apt/src/lib/transaction/customTransaction.ts

Lines changed: 5 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
TransactionPayloadEntryFunction,
1111
} from '@aptos-labs/ts-sdk';
1212
import { CustomTransactionParams } from '../iface';
13+
import { validateModuleName, validateFunctionName } from '../utils/validation';
1314

1415
/**
1516
* Transaction class for custom Aptos transactions with entry function payloads.
@@ -32,8 +33,8 @@ export class CustomTransaction extends Transaction {
3233
* @param {CustomTransactionParams} params - Custom transaction parameters
3334
*/
3435
setCustomTransactionParams(params: CustomTransactionParams): void {
35-
this.validateModuleName(params.moduleName);
36-
this.validateFunctionName(params.functionName);
36+
validateModuleName(params.moduleName);
37+
validateFunctionName(params.functionName);
3738
this.validateAbi(params.abi);
3839

3940
this._moduleName = params.moduleName;
@@ -84,8 +85,8 @@ export class CustomTransaction extends Transaction {
8485
const moduleName = `${moduleAddress}::${moduleIdentifier}`;
8586

8687
// Validate the extracted names using our existing validation
87-
this.validateModuleName(moduleName);
88-
this.validateFunctionName(functionIdentifier);
88+
validateModuleName(moduleName);
89+
validateFunctionName(functionIdentifier);
8990

9091
this._moduleName = moduleName;
9192
this._functionName = functionIdentifier;
@@ -128,48 +129,6 @@ export class CustomTransaction extends Transaction {
128129
};
129130
}
130131

131-
/**
132-
* Validate module name format
133-
*
134-
* @param {string} moduleName - Module name to validate
135-
* @throws {Error} If module name format is invalid
136-
*/
137-
private validateModuleName(moduleName: string): void {
138-
if (!moduleName || typeof moduleName !== 'string') {
139-
throw new Error('Module name is required and must be a non-empty string');
140-
}
141-
142-
// Aptos module name format: address::module_name
143-
// Supports both SHORT (0x1) and LONG (0x0000...0001) address formats
144-
// Also supports named addresses (resolved at deployment time)
145-
const moduleNamePattern = /^(0x[a-fA-F0-9]{1,64}|[a-zA-Z_][a-zA-Z0-9_]*)::[a-zA-Z_][a-zA-Z0-9_]*$/;
146-
if (!moduleNamePattern.test(moduleName)) {
147-
throw new Error(
148-
`Invalid module name format: "${moduleName}". Expected format: "0xaddress::module_name" or "named_address::module_name"`
149-
);
150-
}
151-
}
152-
153-
/**
154-
* Validate function name format
155-
*
156-
* @param {string} functionName - Function name to validate
157-
* @throws {Error} If function name format is invalid
158-
*/
159-
private validateFunctionName(functionName: string): void {
160-
if (!functionName || typeof functionName !== 'string') {
161-
throw new Error('Function name is required and must be a non-empty string');
162-
}
163-
164-
// Aptos function name pattern: valid identifier (letters, numbers, underscores, starting with letter/underscore)
165-
const functionNamePattern = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
166-
if (!functionNamePattern.test(functionName)) {
167-
throw new Error(
168-
`Invalid function name format: "${functionName}". Function names must be valid identifiers (letters, numbers, underscores, starting with letter or underscore)`
169-
);
170-
}
171-
}
172-
173132
/**
174133
* Override the deprecated recipient getter to handle custom transactions gracefully
175134
* Custom transactions may not have traditional recipients

modules/sdk-coin-apt/src/lib/transactionBuilder/customTransactionBuilder.ts

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Transaction } from '../transaction/transaction';
55
import { TransactionBuilder } from './transactionBuilder';
66
import { CustomTransaction } from '../transaction/customTransaction';
77
import { CustomTransactionParams } from '../iface';
8+
import { isValidModuleName, isValidFunctionName } from '../utils/validation';
89

910
/**
1011
* Builder for Aptos custom transactions.
@@ -77,23 +78,13 @@ export class CustomTransactionBuilder extends TransactionBuilder {
7778
}
7879
try {
7980
const entryFunction = payload.entryFunction;
80-
// Validate module address format
81-
const moduleAddress = entryFunction.module_name.address.toString();
82-
if (!moduleAddress.startsWith('0x')) {
83-
return false;
84-
}
8581
// Validate module and function identifiers
82+
const moduleAddress = entryFunction.module_name.address.toString();
8683
const moduleIdentifier = entryFunction.module_name.name.identifier;
8784
const functionIdentifier = entryFunction.function_name.identifier;
88-
// Check identifier format (letters, numbers, underscores, starting with letter/underscore)
89-
const identifierPattern = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
90-
if (!identifierPattern.test(moduleIdentifier) || !identifierPattern.test(functionIdentifier)) {
91-
return false;
92-
}
93-
// Validate module name format
9485
const moduleName = `${moduleAddress}::${moduleIdentifier}`;
95-
const moduleNamePattern = /^0x[a-fA-F0-9]{1,64}::[a-zA-Z_][a-zA-Z0-9_]*$/;
96-
if (!moduleNamePattern.test(moduleName)) {
86+
87+
if (!isValidModuleName(moduleName) || !isValidFunctionName(functionIdentifier)) {
9788
return false;
9889
}
9990
return true;
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/**
2+
* Validation utilities for Aptos transactions
3+
*/
4+
5+
interface ValidationResult {
6+
isValid: boolean;
7+
errorMessage?: string;
8+
}
9+
10+
/**
11+
* Core validation logic for module names
12+
*
13+
* @param {string} moduleName - Module name to validate
14+
* @returns {ValidationResult} Validation result with isValid flag and optional error message
15+
*/
16+
function validateModuleNameCore(moduleName: string): ValidationResult {
17+
if (!moduleName || typeof moduleName !== 'string') {
18+
return {
19+
isValid: false,
20+
errorMessage: 'Module name is required and must be a non-empty string',
21+
};
22+
}
23+
24+
// Aptos module name format: address::module_name
25+
// Supports both SHORT (0x1) and LONG (0x0000...0001) address formats
26+
const moduleNamePattern = /^0x[a-fA-F0-9]{1,64}::[a-zA-Z_][a-zA-Z0-9_]*$/;
27+
if (!moduleNamePattern.test(moduleName)) {
28+
return {
29+
isValid: false,
30+
errorMessage: `Invalid module name format: "${moduleName}". Expected format: "0xaddress::module_name" (hex addresses only)`,
31+
};
32+
}
33+
34+
return { isValid: true };
35+
}
36+
37+
/**
38+
* Core validation logic for function names
39+
*
40+
* @param {string} functionName - Function name to validate
41+
* @returns {ValidationResult} Validation result with isValid flag and optional error message
42+
*/
43+
function validateFunctionNameCore(functionName: string): ValidationResult {
44+
if (!functionName || typeof functionName !== 'string') {
45+
return {
46+
isValid: false,
47+
errorMessage: 'Function name is required and must be a non-empty string',
48+
};
49+
}
50+
51+
// Aptos function name pattern: valid identifier (letters, numbers, underscores, starting with letter/underscore)
52+
const functionNamePattern = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
53+
if (!functionNamePattern.test(functionName)) {
54+
return {
55+
isValid: false,
56+
errorMessage: `Invalid function name format: "${functionName}". Function names must be valid identifiers (letters, numbers, underscores, starting with letter or underscore)`,
57+
};
58+
}
59+
60+
return { isValid: true };
61+
}
62+
63+
/**
64+
* Validate module name format (throwing version)
65+
*
66+
* @param {string} moduleName - Module name to validate
67+
* @throws {Error} If module name format is invalid
68+
*/
69+
export function validateModuleName(moduleName: string): void {
70+
const result = validateModuleNameCore(moduleName);
71+
if (!result.isValid) {
72+
throw new Error(result.errorMessage);
73+
}
74+
}
75+
76+
/**
77+
* Validate function name format (throwing version)
78+
*
79+
* @param {string} functionName - Function name to validate
80+
* @throws {Error} If function name format is invalid
81+
*/
82+
export function validateFunctionName(functionName: string): void {
83+
const result = validateFunctionNameCore(functionName);
84+
if (!result.isValid) {
85+
throw new Error(result.errorMessage);
86+
}
87+
}
88+
89+
/**
90+
* Check if a module name matches the expected pattern (non-throwing version)
91+
*
92+
* @param {string} moduleName - Module name to check
93+
* @returns {boolean} True if valid, false otherwise
94+
*/
95+
export function isValidModuleName(moduleName: string): boolean {
96+
return validateModuleNameCore(moduleName).isValid;
97+
}
98+
99+
/**
100+
* Check if a function name matches the expected pattern (non-throwing version)
101+
*
102+
* @param {string} functionName - Function name to check
103+
* @returns {boolean} True if valid, false otherwise
104+
*/
105+
export function isValidFunctionName(functionName: string): boolean {
106+
return validateFunctionNameCore(functionName).isValid;
107+
}

modules/sdk-coin-apt/test/unit/transactionBuilder/customTransactionBuilder.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ describe('Apt Custom Transaction Builder', () => {
284284
).not.throw();
285285
});
286286

287-
it('should accept named addresses', async function () {
287+
it('should reject named addresses', async function () {
288288
const builder = factory.getCustomTransactionBuilder();
289289
should(() =>
290290
builder.customTransaction({
@@ -294,7 +294,7 @@ describe('Apt Custom Transaction Builder', () => {
294294
functionArguments: [],
295295
abi: basicAbi,
296296
})
297-
).not.throw();
297+
).throwError(/Invalid module name format.*hex addresses only/);
298298
});
299299

300300
it('should validate transaction payloads properly', async function () {

0 commit comments

Comments
 (0)