diff --git a/modules/sdk-coin-xlm/src/stellarToken.ts b/modules/sdk-coin-xlm/src/stellarToken.ts index 9c2c06eda0..31f302b2ad 100644 --- a/modules/sdk-coin-xlm/src/stellarToken.ts +++ b/modules/sdk-coin-xlm/src/stellarToken.ts @@ -10,7 +10,11 @@ import { StellarTokenConfig, tokens } from '@bitgo/statics'; export { StellarTokenConfig }; export class StellarToken extends Xlm { - static readonly tokenPattern: RegExp = /[A-Z]{1,12}-G[A-Z0-9]{55}/; + // Stellar asset codes are alphanumeric (a-z, A-Z, 0-9) and 1-12 characters long — see + // stellar-core `isAsciiAlphaNumeric`/`isAssetValid` and the `AssetCode4`/`AssetCode12` + // XDR definitions. Lowercase is legal and already in use (e.g. xlm:gBENJI-...), so the + // code class must not be uppercase-only. Anchored so the whole token is validated. + static readonly tokenPattern: RegExp = /^[a-zA-Z0-9]{1,12}-G[A-Z0-9]{55}$/; public readonly tokenConfig: StellarTokenConfig; private readonly _code: string; private readonly _issuer: string; diff --git a/modules/sdk-coin-xlm/test/unit/xlm.ts b/modules/sdk-coin-xlm/test/unit/xlm.ts index e817112804..6e60115f4d 100644 --- a/modules/sdk-coin-xlm/test/unit/xlm.ts +++ b/modules/sdk-coin-xlm/test/unit/xlm.ts @@ -12,7 +12,7 @@ import { Wallet, } from '@bitgo/sdk-core'; import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test'; -import { Txlm, getStellarKeys } from '../../src'; +import { StellarToken, Txlm, getStellarKeys, register } from '../../src'; import { KeyPair } from '../../src/lib/keyPair'; import * as assert from 'assert'; @@ -1982,4 +1982,54 @@ describe('XLM:', function () { clawbackOperation.amount.should.equal('100000000'); }); }); + + describe('StellarToken:', () => { + const usdm1Issuer = 'GDM5QWWXCMDTQMZAKMYTCI52LA7FWBHAZMU5NJLMIFHDJISJRP2ZWPKC'; + + it('should register every Stellar token in statics, including alphanumeric asset codes', () => { + const tokenSdk = TestBitGo.decorate(BitGoAPI, { env: 'test' }); + register(tokenSdk); + + for (const { name } of StellarToken.createTokenConstructors()) { + tokenSdk.coin(name).type.should.equal(name); + } + }); + + it('should accept an asset code containing digits', () => { + const usdm1 = new StellarToken(bitgo, { + coin: 'xlm', + network: 'Mainnet', + name: 'USDM1', + type: `xlm:USDM1-${usdm1Issuer}`, + decimalPlaces: 7, + }); + + usdm1.code.should.equal('USDM1'); + usdm1.issuer.should.equal(usdm1Issuer); + }); + + it('should reject an asset code with a non-alphanumeric character', () => { + (() => { + new StellarToken(bitgo, { + coin: 'xlm', + network: 'Mainnet', + name: 'Invalid', + type: `xlm:USD!1-${usdm1Issuer}`, + decimalPlaces: 7, + }); + }).should.throw(/invalid token:/); + }); + + it('should reject an asset code longer than 12 characters', () => { + (() => { + new StellarToken(bitgo, { + coin: 'xlm', + network: 'Mainnet', + name: 'Invalid', + type: `xlm:TOOLONGASSETCODE-${usdm1Issuer}`, + decimalPlaces: 7, + }); + }).should.throw(/invalid token:/); + }); + }); });