Skip to content

Commit

Permalink
Merge pull request #19 from algorandfoundation/bigint-schemas-update
Browse files Browse the repository at this point in the history
BIGINTs and Schema validation updates
  • Loading branch information
ehanoc authored Feb 10, 2025
2 parents 1a3e847 + ab048d4 commit 3866931
Show file tree
Hide file tree
Showing 20 changed files with 146 additions and 124 deletions.
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 (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
}
}
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

0 comments on commit 3866931

Please sign in to comment.