diff --git a/lib/algorand.asset.params.ts b/lib/algorand.asset.params.ts index 184d1ff..d6f4200 100644 --- a/lib/algorand.asset.params.ts +++ b/lib/algorand.asset.params.ts @@ -9,14 +9,14 @@ export class AssetParams { * * The total number of base units of the asset to create. This number cannot be changed. */ - t?: number | bigint | undefined + t?: bigint | undefined /** * Decimals * * The number of digits to use after the decimal point when displaying the asset. If 0, the asset is not divisible. If 1, the base unit of the asset is in tenths. * If 2, the base unit of the asset is in hundredths, if 3, the base unit of the asset is in thousandths, and so on up to 19 decimal places */ - dc?: number | bigint + dc?: bigint /** * Default Frozen * @@ -79,8 +79,8 @@ export class AssetParams { * @internal */ export interface IAssetParamsBuilder { - addTotal(total: number): IAssetParamsBuilder - addDecimals(decimals: number): IAssetParamsBuilder + addTotal(total: number | bigint): IAssetParamsBuilder + addDecimals(decimals: number | bigint): IAssetParamsBuilder addDefaultFrozen(frozen: boolean): IAssetParamsBuilder addUnitName(unitName: string): IAssetParamsBuilder addAssetName(assetName: string): IAssetParamsBuilder @@ -104,12 +104,12 @@ export class AssetParamsBuilder implements IAssetParamsBuilder { this.encoder = new AlgorandEncoder() } - addTotal(total: number): IAssetParamsBuilder { - this.params.t = total + addTotal(total: number | bigint): IAssetParamsBuilder { + this.params.t = AlgorandEncoder.safeCastBigInt(total) return this } - addDecimals(decimals: number): IAssetParamsBuilder { - this.params.dc = decimals + addDecimals(decimals: number | bigint): IAssetParamsBuilder { + this.params.dc = AlgorandEncoder.safeCastBigInt(decimals) return this } addDefaultFrozen(frozen: boolean): IAssetParamsBuilder { diff --git a/lib/algorand.encoder.spec.ts b/lib/algorand.encoder.spec.ts index de6d39a..83bf4b0 100644 --- a/lib/algorand.encoder.spec.ts +++ b/lib/algorand.encoder.spec.ts @@ -79,7 +79,7 @@ describe("Algorand Encoding", () => { txn: { rcv: algoEncoder.decodeAddress(to), snd: algoEncoder.decodeAddress(from), - amt: 1000, + amt: 1000n, fv: 1000, lv: 2000, fee: 1000, diff --git a/lib/algorand.encoder.ts b/lib/algorand.encoder.ts index 7d51d17..89cc26a 100644 --- a/lib/algorand.encoder.ts +++ b/lib/algorand.encoder.ts @@ -110,4 +110,18 @@ export class AlgorandEncoder extends Encoder{ const decoded: object = msgpack.decode(encoded) as object return decoded as object } + + /** + * Casts a number or bigint to BigInt and checks if it's within the safe integer range. + * @param value - The number or bigint to be casted. + * @returns The value as a BigInt. + * @throws Error if the value is not within the safe integer range. + */ + static safeCastBigInt(value: number | bigint): bigint { + const bigIntValue = BigInt(value) +if (typeof value === "number" && (value < Number.MIN_SAFE_INTEGER || value > Number.MAX_SAFE_INTEGER)) { + throw new Error("Value is not within the safe integer range") + } + return bigIntValue + } } diff --git a/lib/algorand.transaction.acfg.ts b/lib/algorand.transaction.acfg.ts index e518d77..de1fa3f 100644 --- a/lib/algorand.transaction.acfg.ts +++ b/lib/algorand.transaction.acfg.ts @@ -15,9 +15,9 @@ export class AssetConfigTransaction extends TransactionHeader { declare type: "acfg" /** * For re-configure or destroy transactions, this is the unique asset ID. On asset creation, the ID is set to zero. - * @type {number | bigint} + * @type {bigint} */ - caid?: number | bigint + caid?: bigint /** * See {@link AssetParams} for all available fields. * @type {AssetParams} @@ -78,10 +78,10 @@ export class AssetConfigTxBuilder implements IAssetConfigTxBuilder { this.tx.gen = genesisId this.tx.gh = new Uint8Array(Buffer.from(genesisHash, "base64")) this.tx.type = "acfg" - this.tx.fee = 1000 + this.tx.fee = 1000n } addAssetId(caid: number | bigint): IAssetConfigTxBuilder { - this.tx.caid = caid + this.tx.caid = AlgorandEncoder.safeCastBigInt(caid) return this } addAssetParams(params: AssetParams): IAssetConfigTxBuilder{ @@ -92,16 +92,16 @@ export class AssetConfigTxBuilder implements IAssetConfigTxBuilder { this.tx.snd = this.encoder.decodeAddress(sender) return this } - addFee(fee: number): IAssetConfigTxBuilder { - this.tx.fee = fee + addFee(fee: number | bigint): IAssetConfigTxBuilder { + this.tx.fee = AlgorandEncoder.safeCastBigInt(fee) return this } - addFirstValidRound(fv: number): IAssetConfigTxBuilder { - this.tx.fv = fv + addFirstValidRound(fv: number | bigint): IAssetConfigTxBuilder { + this.tx.fv = AlgorandEncoder.safeCastBigInt(fv) return this } - addLastValidRound(lv: number): IAssetConfigTxBuilder { - this.tx.lv = lv + addLastValidRound(lv: number | bigint): IAssetConfigTxBuilder { + this.tx.lv = AlgorandEncoder.safeCastBigInt(lv) return this } addNote(note: string, encoding: BufferEncoding = "utf8"): IAssetConfigTxBuilder { diff --git a/lib/algorand.transaction.afrz.ts b/lib/algorand.transaction.afrz.ts index 33e8ea7..bde3421 100644 --- a/lib/algorand.transaction.afrz.ts +++ b/lib/algorand.transaction.afrz.ts @@ -19,7 +19,7 @@ export class AssetFreezeTransaction extends TransactionHeader { /** * The asset ID being frozen or unfrozen. */ - faid: number | bigint + faid: bigint /** * True to freeze the asset. */ @@ -48,7 +48,7 @@ export interface IAssetFreezeTxBuilder extends ITransactionHeaderBuilder { let algorandCrafter: AlgorandTransactionCrafter let algoEncoder: AlgorandEncoder @@ -59,6 +65,7 @@ describe("Algorand Transaction Crafter", () => { .addRekey(algoEncoder.encodeAddress(Buffer.from(snd))) .addLease(lx!!) } + beforeEach(async () => { algorandCrafter = new AlgorandTransactionCrafter(genesisId, genesisHash) algoEncoder = new AlgorandEncoder() @@ -71,9 +78,9 @@ describe("Algorand Transaction Crafter", () => { lx: randomBytes(32), gen: genesisId, gh: new Uint8Array(Buffer.from(genesisHash, "base64")), - fee: 1000, - fv: 1000, - lv: 2000, + fee: 1000n, + fv: 1000n, + lv: 2000n, rekey: sender } }) @@ -124,7 +131,7 @@ describe("Algorand Transaction Crafter", () => { expect(txn).toEqual({ rcv: algoEncoder.decodeAddress(to), type: "pay", - amt: 1000, + amt: 1000n, close: algoEncoder.decodeAddress(from), ...transactionHeader, }) @@ -166,9 +173,9 @@ describe("Algorand Transaction Crafter", () => { votekey: new Uint8Array(Buffer.from(voteKey, "base64")), selkey: new Uint8Array(Buffer.from(selectionKey, "base64")), sprfkey: new Uint8Array(Buffer.from(stateProofKey, "base64")), - votefst: 1000, - votelst: 2000, - votekd: 32, + votefst: 1000n, + votelst: 2000n, + votekd: 32n, type: "keyreg", ...transactionHeader, }) @@ -289,7 +296,7 @@ describe("Algorand Transaction Crafter", () => { expect(txn).toEqual({ type: "acfg", apar: undefined, - caid: 1, + caid: 1n, ...transactionHeader, }) @@ -318,7 +325,7 @@ describe("Algorand Transaction Crafter", () => { expect(txn).toEqual({ type: "afrz", fadd: algoEncoder.decodeAddress(from), - faid: 1, + faid: 1n, afrz: true, ...transactionHeader, }) @@ -349,8 +356,8 @@ describe("Algorand Transaction Crafter", () => { expect(txn).toBeInstanceOf(AssetTransferTransaction) expect(txn).toEqual({ type: "axfer", - xaid: 1, - aamt: 1, + xaid: 1n, + aamt: 1n, arcv: algoEncoder.decodeAddress(from), aclose: algoEncoder.decodeAddress(from), asnd: algoEncoder.decodeAddress(from), diff --git a/lib/algorand.transaction.crafter.ts b/lib/algorand.transaction.crafter.ts index a14cb2f..c7d5140 100644 --- a/lib/algorand.transaction.crafter.ts +++ b/lib/algorand.transaction.crafter.ts @@ -22,7 +22,7 @@ export class AlgorandTransactionCrafter extends Crafter { * @param from The address of the account that pays the fee and amount. * @param to The address of the account that receives the amount. */ - pay(amount: number, from: string, to: string): IPayTxBuilder { + pay(amount: number | bigint, from: string, to: string): IPayTxBuilder { return new PayTxBuilder(this.genesisId, this.genesisHash).addAmount(amount).addSender(from).addReceiver(to) } /** @@ -36,7 +36,7 @@ export class AlgorandTransactionCrafter extends Crafter { * @param voteLast The last round that the participation key is valid. * @param voteKeyDilution This is the dilution for the 2-level participation key. */ - changeOnline(from: string, voteKey: string, selectionKey: string, stateProofKey: string, voteFirst: number, voteLast: number, voteKeyDilution: number): IKeyregTxBuilder { + changeOnline(from: string, voteKey: string, selectionKey: string, stateProofKey: string, voteFirst: number | bigint, voteLast: number | bigint, voteKeyDilution: number | bigint): IKeyregTxBuilder { return new KeyregTxBuilder(this.genesisId, this.genesisHash) .addSender(from) .addVoteKey(voteKey) @@ -94,7 +94,7 @@ export class AlgorandTransactionCrafter extends Crafter { * @param faid The asset ID being frozen or unfrozen. * @param afrz True to freeze the asset. */ - freezeAsset(fadd: string, faid: number, afrz: boolean): IAssetFreezeTxBuilder { + freezeAsset(fadd: string, faid: number | bigint, afrz: boolean): IAssetFreezeTxBuilder { return new AssetFreezeTxBuilder(this.genesisId, this.genesisHash) .addFreezeAccount(fadd) .addFreezeAsset(faid) @@ -108,7 +108,7 @@ export class AlgorandTransactionCrafter extends Crafter { * @param arcv The recipient of the asset transfer. * @param aamt The amount of the asset to be transferred. */ - transferAsset(from: string, xaid: number, arcv: string, aamt: number | bigint): IAssetTransferTxBuilder { + transferAsset(from: string, xaid: number | bigint, arcv: string, aamt: number | bigint): IAssetTransferTxBuilder { return new AssetTransferTxBuilder(this.genesisId, this.genesisHash) .addSender(from) .addAssetId(xaid) diff --git a/lib/algorand.transaction.header.ts b/lib/algorand.transaction.header.ts index 75405b0..f9103ab 100644 --- a/lib/algorand.transaction.header.ts +++ b/lib/algorand.transaction.header.ts @@ -30,19 +30,19 @@ export abstract class TransactionHeader { * * Paid by the sender to the FeeSink to prevent denial-of-service. The minimum fee on Algorand is currently 1000 microAlgos. */ - fee: number + fee: bigint /** * First Valid * * The first round for when the transaction is valid. If the transaction is sent prior to this round it will be rejected by the network. */ - fv: number + fv: bigint /** * Last Valid * * The ending round for which the transaction is valid. After this round, the transaction will be rejected by the network. */ - lv: number + lv: bigint /** * Genesis Hash * @@ -108,21 +108,21 @@ export interface ITransactionHeaderBuilder { * * @param fee Paid by the sender to the FeeSink to prevent denial-of-service. The minimum fee on Algorand is currently 1000 microAlgos. */ - addFee(fee: number): T + addFee(fee: number | bigint): T /** * Add First Valid Round * * @param fv The first round for when the transaction is valid. If the transaction is sent prior to this round it will be rejected by the network. */ - addFirstValidRound(fv: number): T + addFirstValidRound(fv: number | bigint): T /** * Add Last Valid Round * * @param lv The ending round for which the transaction is valid. After this round, the transaction will be rejected by the network. */ - addLastValidRound(lv: number): T + addLastValidRound(lv: number | bigint): T /** * Add Note diff --git a/lib/algorand.transaction.keyreg.ts b/lib/algorand.transaction.keyreg.ts index e18aa0c..c06750d 100644 --- a/lib/algorand.transaction.keyreg.ts +++ b/lib/algorand.transaction.keyreg.ts @@ -33,21 +33,21 @@ export class KeyregTransaction extends TransactionHeader { * * The first round that the participation key is valid. Not to be confused with the FirstValid round of the keyreg transaction. */ - votefst?: number + votefst?: bigint /** * Vote Last * * The last round that the participation key is valid. * Not to be confused with the LastValid round of the keyreg transaction. */ - votelst?: number + votelst?: bigint /** * Vote Key Dilution * * This is the dilution for the 2-level participation key. * It determines the interval (number of rounds) for generating new ephemeral keys. */ - votekd?: number + votekd?: bigint /** * Nonparticipating * @@ -95,19 +95,19 @@ export interface IKeyregTxBuilder extends ITransactionHeaderBuilder{ * * @param amount The total amount to be sent in microAlgos. */ - addAmount(amount: number): IPayTxBuilder + addAmount(amount: number | bigint): IPayTxBuilder /** * Add Close Remainder To * @@ -77,14 +77,14 @@ export class PayTxBuilder implements IPayTxBuilder { this.tx.gen = genesisId this.tx.gh = new Uint8Array(Buffer.from(genesisHash, "base64")) this.tx.type = "pay" - this.tx.fee = 1000 + this.tx.fee = 1000n } addReceiver(receiver: string): IPayTxBuilder { this.tx.rcv = this.encoder.decodeAddress(receiver) return this } - addAmount(amount: number): IPayTxBuilder { - this.tx.amt = amount + addAmount(amount: number | bigint): IPayTxBuilder { + this.tx.amt = AlgorandEncoder.safeCastBigInt(amount) return this } addCloseTo(close: string): IPayTxBuilder { @@ -95,16 +95,16 @@ export class PayTxBuilder implements IPayTxBuilder { this.tx.snd = this.encoder.decodeAddress(sender) return this } - addFee(fee: number): IPayTxBuilder { - this.tx.fee = fee + addFee(fee: number | bigint): IPayTxBuilder { + this.tx.fee = AlgorandEncoder.safeCastBigInt(fee) return this } - addFirstValidRound(fv: number): IPayTxBuilder { - this.tx.fv = fv + addFirstValidRound(fv: number | bigint): IPayTxBuilder { + this.tx.fv = AlgorandEncoder.safeCastBigInt(fv) return this } - addLastValidRound(lv: number): IPayTxBuilder { - this.tx.lv = lv + addLastValidRound(lv: number | bigint): IPayTxBuilder { + this.tx.lv = AlgorandEncoder.safeCastBigInt(lv) return this } addNote(note: string, encoding: BufferEncoding = "utf8"): IPayTxBuilder { diff --git a/lib/schemas/acfg.transaction.json b/lib/schemas/acfg.transaction.json index 09a552e..6dcb6f6 100644 --- a/lib/schemas/acfg.transaction.json +++ b/lib/schemas/acfg.transaction.json @@ -12,7 +12,7 @@ "const": "acfg" }, "caid": { - "type": "integer", + "typeof": "bigint", "description": "For re-configure or destroy transactions, this is the unique asset ID. On asset creation, the ID is set to zero." }, "apar": { diff --git a/lib/schemas/afrz.transaction.json b/lib/schemas/afrz.transaction.json index 3d733e4..f067f44 100644 --- a/lib/schemas/afrz.transaction.json +++ b/lib/schemas/afrz.transaction.json @@ -16,7 +16,7 @@ "description": "The address of the account whose asset is being frozen or unfrozen." }, "faid": { - "type": "integer", + "typeof": "bigint", "description": "The asset ID being frozen or unfrozen." }, "afrz": { diff --git a/lib/schemas/asset.params.json b/lib/schemas/asset.params.json index b5a3751..4be51e3 100644 --- a/lib/schemas/asset.params.json +++ b/lib/schemas/asset.params.json @@ -5,11 +5,11 @@ "type": "object", "properties": { "t": { - "type": "integer", + "typeof": "bigint", "description": "The total number of base units of the asset to create. This number cannot be changed." }, "dc": { - "type": "integer", + "typeof": "bigint", "description": "The number of digits to use after the decimal point when displaying the asset. If 0, the asset is not divisible. If 1, the base unit of the asset is in tenths. If 2, the base unit of the asset is in hundredths, if 3, the base unit of the asset is in thousandths, and so on up to 19 decimal places" }, "df": { diff --git a/lib/schemas/axfer.transaction.json b/lib/schemas/axfer.transaction.json index 6f98588..db96215 100644 --- a/lib/schemas/axfer.transaction.json +++ b/lib/schemas/axfer.transaction.json @@ -12,11 +12,11 @@ "const": "axfer" }, "xaid": { - "type": "integer", + "typeof": "bigint", "description": "The unique ID of the asset to be transferred." }, "aamt": { - "type": "integer", + "typeof": "bigint", "description": "The amount of the asset to be transferred. A zero amount transferred to self allocates that asset in the account's Asset map." }, "asnd": { diff --git a/lib/schemas/keyreg.transaction.online.json b/lib/schemas/keyreg.transaction.online.json index 4a98c46..4c73e7d 100644 --- a/lib/schemas/keyreg.transaction.online.json +++ b/lib/schemas/keyreg.transaction.online.json @@ -29,15 +29,15 @@ "description": "The 64 byte state proof public key commitment." }, "votefst": { - "type": "integer", + "typeof": "bigint", "description": "The first round that the participation key is valid. Not to be confused with the FirstValid round of the keyreg transaction." }, "votelst": { - "type": "integer", + "typeof": "bigint", "description": "The last round that the participation key is valid. Not to be confused with the LastValid round of the keyreg transaction." }, "votekd": { - "type": "integer", + "typeof": "bigint", "description": "This is the dilution for the 2-level participation key. It determines the interval (number of rounds) for generating new ephemeral keys." } }, diff --git a/lib/schemas/pay.transaction.json b/lib/schemas/pay.transaction.json index 2043cd1..19d762f 100644 --- a/lib/schemas/pay.transaction.json +++ b/lib/schemas/pay.transaction.json @@ -12,7 +12,7 @@ "const": "pay" }, "amt": { - "type": "integer", + "typeof": "bigint", "description": "The total amount to be sent in microAlgos." }, "rcv": { diff --git a/lib/schemas/transaction.header.json b/lib/schemas/transaction.header.json index 19c4335..e9f6922 100644 --- a/lib/schemas/transaction.header.json +++ b/lib/schemas/transaction.header.json @@ -5,12 +5,11 @@ "type": "object", "properties": { "fee": { - "type": "integer", + "typeof": "bigint", "description": "Paid by the sender to the FeeSink to prevent denial-of-service. The minimum fee on Algorand is currently 1000 microAlgos." }, "fv": { - "type": "integer", - "minimum": 0, + "typeof": "bigint", "description": "The first round for when the transaction is valid. If the transaction is sent prior to this round it will be rejected by the network." }, "gh": { @@ -18,8 +17,7 @@ "description": "The hash of the genesis block of the network for which the transaction is valid. See the genesis hash for MainNet, TestNet, and BetaNet." }, "lv": { - "type": "integer", - "minimum": 0, + "typeof": "bigint", "description": "The ending round for which the transaction is valid. After this round, the transaction will be rejected by the network." }, "snd": { diff --git a/package.json b/package.json index 56d86e7..5c83b32 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,8 @@ }, "scripts": { "build": "tsc -p tsconfig.esm.json && tsc -p tsconfig.cjs.json", - "test": "jest lib --verbose", - "test:cov": "jest lib --coverage", + "test": "jest lib --verbose --maxWorkers=1", + "test:cov": "jest lib --coverage --maxWorkers=1", "typedoc": "typedoc" }, "author": "", @@ -23,6 +23,7 @@ "devDependencies": { "@types/jest": "^29.5.5", "@types/node": "^20.7.1", + "jest": "^29.7.0", "ts-jest": "^29.1.1", "tweetnacl": "^1.0.3", "typedoc": "^0.26.7", @@ -30,6 +31,8 @@ }, "dependencies": { "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "ajv-keywords": "^5.1.0", "algo-msgpack-with-bigint": "^2.1.1", "hi-base32": "^0.5.1", "js-sha512": "^0.8.0" @@ -61,7 +64,7 @@ ], "testEnvironment": "node", "moduleNameMapper": { - "(.+)\\.js": "$1" + "^((?!bignumber).+)\\.js$": "$1" }, "extensionsToTreatAsEsm": [ ".ts" diff --git a/tsconfig.cjs.json b/tsconfig.cjs.json index c0554c9..2c582e0 100644 --- a/tsconfig.cjs.json +++ b/tsconfig.cjs.json @@ -2,7 +2,7 @@ "compilerOptions": { "outDir": "./dist/cjs", "module": "commonjs", - "target": "es5", + "target": "ES2020", "declaration": false }, "extends": "./tsconfig.json"