diff --git a/assembly/__tests__/runtime/runtime.spec.ts b/assembly/__tests__/runtime/runtime.spec.ts index e6fb0f24..dcb2b093 100644 --- a/assembly/__tests__/runtime/runtime.spec.ts +++ b/assembly/__tests__/runtime/runtime.spec.ts @@ -65,6 +65,80 @@ describe("Encodings", () => { }); }); +describe("Account env util", () => { + it("isValidAccountID", () => { + var accounts = [ + "aa", + "a-a", + "a-aa", + "100", + "0o", + "com", + "near", + "bowen", + "b-o_w_e-n", + "b.owen", + "bro.wen", + "a.ha", + "a.b-a.ra", + "system", + "over.9000", + "google.com", + "illia.cheapaccounts.near", + "0o0ooo00oo00o", + "alex-skidanov", + "10-4.8-2", + "b-o_w_e-n", + "no_lols", + "0123456789012345678901234567890123456789012345678901234567890123", + // Valid, but can't be created + "near.a", + "a.a", + ]; + for (var i = 0; i < accounts.length; i++) { + expect(env.isValidAccountID(accounts[i])).toBe( + true, + "valid accountId not recognized " + accounts[i] + ); + } + + accounts = [ + "", + "a", + "A", + "Abc", + "-near", + "near-", + "-near-", + "near.", + ".near", + "near@", + "@near", + "неар", + "@@@@@", + "0__0", + "0_-_0", + "0_-_0", + "..", + "a..near", + "nEar", + "_bowen", + "hello world", + "abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz", + "01234567890123456789012345678901234567890123456789012345678901234", + // `@` separators are banned now + "some-complex-address@gmail.com", + "sub.buy_d1gitz@atata@b0-rg.c_0_m", + ]; + for (i = 0; i < accounts.length; i++) { + expect(env.isValidAccountID(accounts[i])).toBe( + false, + "invalid accountId not detected " + accounts[i] + ); + } + }); +}); + let outcome: Outcome; describe("outcome", () => { afterAll(() => { diff --git a/assembly/sdk/env/env.ts b/assembly/sdk/env/env.ts index 7857011f..25d74cae 100644 --- a/assembly/sdk/env/env.ts +++ b/assembly/sdk/env/env.ts @@ -62,6 +62,41 @@ export namespace env { return _storage_usage(); } + // ############ + // # Accounts # + // ############ + export function isValidAccountID(accountId: string): boolean { + const MIN_ACCOUNT_ID_LEN = 2; + const MAX_ACCOUNT_ID_LEN = 64; + if ( + accountId.length < MIN_ACCOUNT_ID_LEN || + accountId.length > MAX_ACCOUNT_ID_LEN + ) { + return false; + } + + // The valid account ID regex is /^(([a-z\d]+[-_])*[a-z\d]+\.)*([a-z\d]+[-_])*[a-z\d]+$/ + + // We can safely assume that last char was a separator. + var last_char_is_separator = true; + + for (let n = 0; n < accountId.length; n++) { + let c = accountId.charAt(n); + let current_char_is_separator = c == "-" || c == "_" || c == "."; + if ( + !current_char_is_separator && + !((c >= "a" && c <= "z") || (c >= "0" && c <= "9")) + ) + return false; //only 0..9 a..z and separators are valid chars + if (current_char_is_separator && last_char_is_separator) { + return false; //do not allow 2 separs together + } + last_char_is_separator = current_char_is_separator; + } + // The account can't end as separator. + return !last_char_is_separator; + } + // ################# // # Economics API # // #################