Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BIGINTs and Schema validation updates #19

Merged
merged 6 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions lib/algorand.asset.params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion lib/algorand.encoder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
14 changes: 14 additions & 0 deletions lib/algorand.encoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 (bigIntValue < Number.MIN_SAFE_INTEGER || bigIntValue > Number.MAX_SAFE_INTEGER) {
throw new Error("Value is not within the safe integer range")
}
return bigIntValue
}
}
20 changes: 10 additions & 10 deletions lib/algorand.transaction.acfg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -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{
Expand All @@ -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 {
Expand Down
22 changes: 11 additions & 11 deletions lib/algorand.transaction.afrz.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -48,7 +48,7 @@ export interface IAssetFreezeTxBuilder extends ITransactionHeaderBuilder<IAssetF
*
* @param faid The asset ID being frozen or unfrozen.
*/
addFreezeAsset(faid: number): IAssetFreezeTxBuilder
addFreezeAsset(faid: number | bigint): IAssetFreezeTxBuilder
/**
* Add Asset Frozen
*
Expand All @@ -75,14 +75,14 @@ export class AssetFreezeTxBuilder implements IAssetFreezeTxBuilder {
this.tx.gen = genesisId
this.tx.gh = new Uint8Array(Buffer.from(genesisHash, "base64"))
this.tx.type = "afrz"
this.tx.fee = 1000
this.tx.fee = 1000n
}
addFreezeAccount(fadd: string): IAssetFreezeTxBuilder {
this.tx.fadd = this.encoder.decodeAddress(fadd)
return this
}
addFreezeAsset(faid: number): IAssetFreezeTxBuilder {
this.tx.faid = faid
addFreezeAsset(faid: number | bigint): IAssetFreezeTxBuilder {
this.tx.faid = AlgorandEncoder.safeCastBigInt(faid)
return this
}
addAssetFrozen(afrz: boolean): IAssetFreezeTxBuilder {
Expand All @@ -93,16 +93,16 @@ export class AssetFreezeTxBuilder implements IAssetFreezeTxBuilder {
this.tx.snd = this.encoder.decodeAddress(sender)
return this
}
addFee(fee: number): IAssetFreezeTxBuilder {
this.tx.fee = fee
addFee(fee: number | bigint): IAssetFreezeTxBuilder {
this.tx.fee = AlgorandEncoder.safeCastBigInt(fee)
return this
}
addFirstValidRound(firstValid: number): IAssetFreezeTxBuilder {
this.tx.fv = firstValid
addFirstValidRound(firstValid: number | bigint): IAssetFreezeTxBuilder {
this.tx.fv = AlgorandEncoder.safeCastBigInt(firstValid)
return this
}
addLastValidRound(lastValid: number): IAssetFreezeTxBuilder {
this.tx.lv = lastValid
addLastValidRound(lastValid: number | bigint): IAssetFreezeTxBuilder {
this.tx.lv = AlgorandEncoder.safeCastBigInt(lastValid)
return this
}
addNote(note: string, encoding: BufferEncoding = "utf8"): IAssetFreezeTxBuilder {
Expand Down
20 changes: 10 additions & 10 deletions lib/algorand.transaction.axfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export interface IAssetTransferTxBuilder extends ITransactionHeaderBuilder<IAsse
*
* @param xaid The unique ID of the asset to be transferred.
*/
addAssetId(xaid: number): IAssetTransferTxBuilder
addAssetId(xaid: number | bigint): IAssetTransferTxBuilder
/**
* Add Asset Amount
*
Expand Down Expand Up @@ -103,14 +103,14 @@ export class AssetTransferTxBuilder implements IAssetTransferTxBuilder {
this.tx.gen = genesisId
this.tx.gh = new Uint8Array(Buffer.from(genesisHash, "base64"))
this.tx.type = "axfer"
this.tx.fee = 1000
this.tx.fee = 1000n
}
addAssetId(xaid: number | bigint): IAssetTransferTxBuilder {
this.tx.xaid = xaid
this.tx.xaid = AlgorandEncoder.safeCastBigInt(xaid)
return this
}
addAssetAmount(aamt: number | bigint): IAssetTransferTxBuilder {
this.tx.aamt = aamt
this.tx.aamt = AlgorandEncoder.safeCastBigInt(aamt)
return this
}
addAssetSender(asnd: string): IAssetTransferTxBuilder {
Expand All @@ -129,16 +129,16 @@ export class AssetTransferTxBuilder implements IAssetTransferTxBuilder {
this.tx.snd = this.encoder.decodeAddress(sender)
return this
}
addFee(fee: number): IAssetTransferTxBuilder {
this.tx.fee = fee
addFee(fee: number | bigint): IAssetTransferTxBuilder {
this.tx.fee = AlgorandEncoder.safeCastBigInt(fee)
return this
}
addFirstValidRound(firstValid: number): IAssetTransferTxBuilder {
this.tx.fv = firstValid
addFirstValidRound(firstValid: number | bigint): IAssetTransferTxBuilder {
this.tx.fv = AlgorandEncoder.safeCastBigInt(firstValid)
return this
}
addLastValidRound(lastValid: number): IAssetTransferTxBuilder {
this.tx.lv = lastValid
addLastValidRound(lastValid: number | bigint): IAssetTransferTxBuilder {
this.tx.lv = AlgorandEncoder.safeCastBigInt(lastValid)
return this
}
addNote(note: string, encoding: BufferEncoding = "utf8"): IAssetTransferTxBuilder {
Expand Down
57 changes: 32 additions & 25 deletions lib/algorand.transaction.crafter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { AlgorandTransactionCrafter } from "./algorand.transaction.crafter"
import { PayTransaction } from "./algorand.transaction.pay"
import { KeyregTransaction } from "./algorand.transaction.keyreg"
import Ajv, {type JSONSchemaType} from "ajv"
import addFormat from 'ajv-formats'
import addKeywords from 'ajv-keywords'
import path from "path"
import fs from 'fs'
import {AssetConfigTransaction} from "./algorand.transaction.acfg";
Expand All @@ -16,26 +18,30 @@ import {ITransactionHeaderBuilder, TransactionHeader} from "./algorand.transacti

// Setup Validator
const ajv = new Ajv()
addFormat(ajv)

// Define the custom keyword 'typeof'
ajv.addKeyword({
keyword: 'typeof',
validate: function(schema: string, data: any) {
if (schema === 'bigint') {
return typeof data === 'bigint';
}

console.log("Unknown type: ", schema)

// Add more types as needed
return false;
},
errors: false
});

ajv.addSchema(JSON.parse(fs.readFileSync(path.resolve(__dirname, "schemas/bytes32.json"), "utf8")))
ajv.addSchema(JSON.parse(fs.readFileSync(path.resolve(__dirname, "schemas/bytes64.json"), "utf8")))
ajv.addSchema(JSON.parse(fs.readFileSync(path.resolve(__dirname, "schemas/transaction.header.json"), "utf8")))
ajv.addSchema(JSON.parse(fs.readFileSync(path.resolve(__dirname, "schemas/asset.params.json"), "utf8")))


type TestTransactionHeader = {
snd: Uint8Array,
note: Uint8Array,
grp: Uint8Array,
lx: Uint8Array,

gen: string
gh: Uint8Array
fee: number
fv: number
lv: number
rekey: Uint8Array
}

describe("Algorand Transaction Crafter", () => {
let algorandCrafter: AlgorandTransactionCrafter
let algoEncoder: AlgorandEncoder
Expand All @@ -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()
Expand All @@ -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
}
})
Expand Down Expand Up @@ -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,
})
Expand Down Expand Up @@ -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,
})
Expand Down Expand Up @@ -289,7 +296,7 @@ describe("Algorand Transaction Crafter", () => {
expect(txn).toEqual({
type: "acfg",
apar: undefined,
caid: 1,
caid: 1n,
...transactionHeader,
})

Expand Down Expand Up @@ -318,7 +325,7 @@ describe("Algorand Transaction Crafter", () => {
expect(txn).toEqual({
type: "afrz",
fadd: algoEncoder.decodeAddress(from),
faid: 1,
faid: 1n,
afrz: true,
...transactionHeader,
})
Expand Down Expand Up @@ -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),
Expand Down
8 changes: 4 additions & 4 deletions lib/algorand.transaction.crafter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
/**
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down
Loading