diff --git a/src/asset.ts b/src/asset.ts index 65210168..f7e09d4e 100644 --- a/src/asset.ts +++ b/src/asset.ts @@ -192,10 +192,10 @@ export class Asset { * * - `native`, * - `credit_alphanum4`, - * - `credit_alphanum12`, or - * - `unknown` as the error case (which should never occur) + * - `credit_alphanum12` + * @throws {Error} Throws `Error` if asset type is unsupported. */ - getAssetType(): AssetType | "unknown" { + getAssetType(): AssetType { switch (this.getRawAssetType().value) { case xdr.AssetType.assetTypeNative().value: return AssetType.native; @@ -204,7 +204,9 @@ export class Asset { case xdr.AssetType.assetTypeCreditAlphanum12().value: return AssetType.credit12; default: - return "unknown"; + throw new Error( + "Supported asset types are: native, credit_alphanum4, credit_alphanum12", + ); } } diff --git a/src/fee_bump_transaction.ts b/src/fee_bump_transaction.ts index 7432e24c..bd02da5f 100644 --- a/src/fee_bump_transaction.ts +++ b/src/fee_bump_transaction.ts @@ -15,8 +15,8 @@ import { encodeMuxedAccountToAddress } from "./util/decode_encode_muxed_account. * submitting to the network or forwarding on to additional signers. */ export class FeeBumpTransaction extends TransactionBase { - _feeSource: string; - _innerTransaction: Transaction; + private _feeSource: string; + private _innerTransaction: Transaction; /** * @param envelope - transaction envelope object or base64 encoded string. diff --git a/src/numbers/index.ts b/src/numbers/index.ts index 01f4c730..23fbca25 100644 --- a/src/numbers/index.ts +++ b/src/numbers/index.ts @@ -44,15 +44,21 @@ export function scValToBigInt(scv: xdr.ScVal): bigint { case "scvI64": case "scvTimepoint": case "scvDuration": + if (scIntType === undefined) { + throw TypeError(`invalid integer type for ${switchName}`); + } return new XdrLargeInt( - scIntType as ScIntType, + scIntType, value as xdr.Int64 | xdr.Uint64, ).toBigInt(); case "scvU128": case "scvI128": { + if (scIntType === undefined) { + throw TypeError(`invalid integer type for ${switchName}`); + } const int128Value = value as xdr.Int128Parts | xdr.UInt128Parts; - return new XdrLargeInt(scIntType as ScIntType, [ + return new XdrLargeInt(scIntType, [ int128Value.lo(), int128Value.hi(), ]).toBigInt(); @@ -60,8 +66,11 @@ export function scValToBigInt(scv: xdr.ScVal): bigint { case "scvU256": case "scvI256": { + if (scIntType === undefined) { + throw TypeError(`invalid integer type for ${switchName}`); + } const int256Value = value as xdr.Int256Parts | xdr.UInt256Parts; - return new XdrLargeInt(scIntType as ScIntType, [ + return new XdrLargeInt(scIntType, [ int256Value.loLo(), int256Value.loHi(), int256Value.hiLo(), diff --git a/src/numbers/xdr_large_int.ts b/src/numbers/xdr_large_int.ts index c068cc0f..80d57bc8 100644 --- a/src/numbers/xdr_large_int.ts +++ b/src/numbers/xdr_large_int.ts @@ -34,7 +34,7 @@ export type ScIntType = */ export class XdrLargeInt { int: LargeInt; - type: string; + type: ScIntType; /** * @param type - specifies a data type to use to represent the integer, one @@ -257,7 +257,10 @@ export class XdrLargeInt { case "duration": return this.toDuration(); default: - throw TypeError(`invalid type: ${this.type}`); + // This should be unreachable if the compiler enforces valid types + // This serves as a runtime check if the type is somehow invalid + // (e.g. from user input or a future extension) + throw TypeError(`invalid type: ${this.type as string}`); } } @@ -286,7 +289,7 @@ export class XdrLargeInt { } /** Returns true if the given string is a valid XDR large integer type name. */ - static isType(type: string): boolean { + static isType(type: string): type is ScIntType { switch (type) { case "i64": case "i128": @@ -307,8 +310,15 @@ export class XdrLargeInt { * to a type description for {@link XdrLargeInt} construction (e.g. 'i128') * * @param scvType - the `xdr.ScValType` as a string + * @returns the corresponding {@link ScIntType} if it's an integer type, or + * `undefined` if it's not an integer type */ - static getType(scvType: string): string { - return scvType.slice(3).toLowerCase(); + static getType(scvType: string): ScIntType | undefined { + const type = scvType.slice(3).toLowerCase(); + if (this.isType(type)) { + return type; + } + + return undefined; } } diff --git a/src/operations/set_options.ts b/src/operations/set_options.ts index 1b9fd8f1..32fb75e1 100644 --- a/src/operations/set_options.ts +++ b/src/operations/set_options.ts @@ -165,11 +165,16 @@ export function setOptions( if (setValues !== 1) { throw new Error( - "Signer object must contain exactly one of signer.ed25519PublicKey, signer.sha256Hash, signer.preAuthTx.", + "Signer object must contain exactly one of signer.ed25519PublicKey, signer.sha256Hash, signer.preAuthTx, or signer.ed25519SignedPayload.", ); } - - signer = new xdr.Signer({ key: key!, weight: weight! }); + if (weight === undefined) { + throw new Error("signer weight is required."); + } + if (key === undefined) { + throw new Error("signer key is required."); + } + signer = new xdr.Signer({ key: key, weight: weight }); } const setOptionsOp = new xdr.SetOptionsOp({ diff --git a/src/operations/types.ts b/src/operations/types.ts index f5358a2f..32a07267 100644 --- a/src/operations/types.ts +++ b/src/operations/types.ts @@ -73,7 +73,7 @@ export interface LiquidityPoolWithdrawOpts { export interface AllowTrustOpts { trustor: string; assetCode: string; - authorize?: boolean | number; + authorize?: TrustLineFlag | boolean; source?: string; } @@ -105,8 +105,8 @@ export interface SignerOpts { export interface SetOptionsOpts { inflationDest?: string; - clearFlags?: number | string; - setFlags?: number | string; + clearFlags?: AuthFlags; + setFlags?: AuthFlags; masterWeight?: number | string; lowThreshold?: number | string; medThreshold?: number | string; @@ -411,20 +411,18 @@ export type OperationType = // Literal types matching the AuthRequiredFlag/AuthRevocableFlag/AuthImmutableFlag/AuthClawbackEnabledFlag // constants exported from src/operation.ts. -// TODO: Once src/index.js is migrated to src/index.ts, replace these literals with -// `typeof AuthRequiredFlag` etc. to avoid duplication with the runtime constants. -export namespace AuthFlag { - export type required = 1; - export type revocable = 2; - export type immutable = 4; - export type clawbackEnabled = 8; -} -export type AuthFlag = - | AuthFlag.clawbackEnabled - | AuthFlag.immutable - | AuthFlag.required - | AuthFlag.revocable; +export const AuthFlag = { + required: 1, + revocable: 2, + immutable: 4, + clawbackEnabled: 8, +} as const; +export type AuthFlag = (typeof AuthFlag)[keyof typeof AuthFlag]; +/** + * A single {@link AuthFlag} or multiple flags combined with `|` (e.g. `AuthRequiredFlag | AuthRevocableFlag`). + */ +export type AuthFlags = AuthFlag | (number & {}); export namespace TrustLineFlag { export type deauthorize = 0; export type authorize = 1; @@ -525,8 +523,8 @@ export interface SetOptionsResult< // hold raw uint32 bitmasks, so combined values (e.g. AuthRequired | AuthRevocable = 3) // are valid but not expressible as AuthFlag. Use bitwise AND to test individual flags: // if (result.clearFlags & AuthRequiredFlag) { ... } - clearFlags?: AuthFlag; - setFlags?: AuthFlag; + clearFlags?: AuthFlags; + setFlags?: AuthFlags; masterWeight?: number; lowThreshold?: number; medThreshold?: number; diff --git a/src/scval.ts b/src/scval.ts index 7f703ec7..e3df1ef9 100644 --- a/src/scval.ts +++ b/src/scval.ts @@ -299,7 +299,7 @@ export function nativeToScVal( default: if (XdrLargeInt.isType(optType)) { - return new XdrLargeInt(optType as ScIntType, val).toScVal(); + return new XdrLargeInt(optType, val).toScVal(); } throw new TypeError( diff --git a/src/transaction.ts b/src/transaction.ts index b80099dc..ea3fc933 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -25,20 +25,20 @@ import { OperationRecord } from "./operations/types.js"; export class Transaction extends TransactionBase< xdr.Transaction | xdr.TransactionV0 > { - _envelopeType: xdr.EnvelopeType; - _source: string = ""; - _memo: xdr.Memo; - _sequence: string; - _operations: OperationRecord[]; - _timeBounds?: { minTime: string; maxTime: string }; - _ledgerBounds?: { minLedger: number; maxLedger: number }; - _minAccountSequence?: string; + private _envelopeType: xdr.EnvelopeType; + private _source: string = ""; + private _memo: xdr.Memo; + private _sequence: string; + private _operations: OperationRecord[]; + private _timeBounds?: { minTime: string; maxTime: string }; + private _ledgerBounds?: { minLedger: number; maxLedger: number }; + private _minAccountSequence?: string; // TODO: types/index.d.ts declares this as `number`, but the XDR type is // Duration = Uint64 (64-bit), which can exceed Number.MAX_SAFE_INTEGER. // This is a breaking type change that should be surfaced when types/index.d.ts is updated. - _minAccountSequenceAge?: xdr.Uint64; - _minAccountSequenceLedgerGap?: number; - _extraSigners?: xdr.SignerKey[]; + private _minAccountSequenceAge?: xdr.Uint64; + private _minAccountSequenceLedgerGap?: number; + private _extraSigners?: xdr.SignerKey[]; /** * @param envelope - transaction envelope object or base64 encoded string diff --git a/src/transaction_base.ts b/src/transaction_base.ts index 380d5dbd..abaea4d3 100644 --- a/src/transaction_base.ts +++ b/src/transaction_base.ts @@ -8,10 +8,10 @@ import { Keypair } from "./keypair.js"; export class TransactionBase< TTx extends xdr.FeeBumpTransaction | xdr.Transaction | xdr.TransactionV0, > { - _tx: TTx; - _signatures: xdr.DecoratedSignature[]; - _fee: string; - _networkPassphrase: string; + private _tx: TTx; + private _signatures: xdr.DecoratedSignature[]; + private _fee: string; + private _networkPassphrase: string; constructor( tx: TTx, diff --git a/src/transaction_builder.ts b/src/transaction_builder.ts index ea3d4c57..d751866b 100644 --- a/src/transaction_builder.ts +++ b/src/transaction_builder.ts @@ -275,7 +275,7 @@ export class TransactionBuilder { const builder = new TransactionBuilder(source, builderOpts); - tx._tx.operations().forEach((op) => builder.addOperation(op)); + tx.tx.operations().forEach((op) => builder.addOperation(op)); return builder; }