Skip to content

Commit

Permalink
Merge pull request #17 from algorandfoundation/feat/ENG-306-algo-mode…
Browse files Browse the repository at this point in the history
…ls-add-the-remaining-transaction-type-models

feat: add the remaining transaction type models
  • Loading branch information
ehanoc authored Oct 31, 2024
2 parents ef195da + b431685 commit 2004c89
Show file tree
Hide file tree
Showing 26 changed files with 2,376 additions and 1,816 deletions.
150 changes: 150 additions & 0 deletions lib/algorand.asset.params.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import {AlgorandEncoder} from "./algorand.encoder.js";

/**
* @category Common
*/
export class AssetParams {
/**
* Total
*
* The total number of base units of the asset to create. This number cannot be changed.
*/
t?: number | 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
/**
* Default Frozen
*
* True to freeze holdings for this asset by default.
*/
df?: boolean
/**
* Unit Name
*
* The name of a unit of this asset. Supplied on creation. Max size is 8 bytes. Example: USDT
*/
un?: string
/**
* Asset Name
*
* The name of the asset. Supplied on creation. Max size is 32 bytes. Example: Tether
*/
an?: string
/**
* URL
*
* Specifies a URL where more information about the asset can be retrieved. Max size is 96 bytes.
*/
au?: string
/**
* MetaDataHash
*
* This field is intended to be a 32-byte hash of some metadata that is relevant to your asset and/or asset holders. The format of this metadata is up to the application. This field can only be specified upon creation.
* An example might be the hash of some certificate that acknowledges the digitized asset as the official representation of a particular real-world asset.
*/
am?: Uint8Array
/**
* Manager Address
*
* The address of the account that can manage the configuration of the asset and destroy it.
*/
m?: Uint8Array
/**
* Reserve Address
*
* The address of the account that holds the reserve (non-minted) units of the asset. This address has no specific authority in the protocol itself.
* It is used in the case where you want to signal to holders of your asset that the non-minted units of the asset reside in an account that is different from the default creator account (the sender).
*/
r?: Uint8Array
/**
* Freeze Address
* The address of the account used to freeze holdings of this asset. If empty, freezing is not permitted.
*/
f?: Uint8Array
/**
* Clawback Address
*
* The address of the account that can clawback holdings of this asset. If empty, clawback is not permitted.
*/
c?: Uint8Array
}

/**
* @category Builders
* @internal
*/
export interface IAssetParamsBuilder {
addTotal(total: number): IAssetParamsBuilder
addDecimals(decimals: number): IAssetParamsBuilder
addDefaultFrozen(frozen: boolean): IAssetParamsBuilder
addUnitName(unitName: string): IAssetParamsBuilder
addAssetName(assetName: string): IAssetParamsBuilder
addMetadataHash(hash: Uint8Array): IAssetParamsBuilder
addManagerAddress(address: string): IAssetParamsBuilder
addReserveAddress(address: string): IAssetParamsBuilder
addFreezeAddress(address: string): IAssetParamsBuilder
addClawbackAddress(address: string): IAssetParamsBuilder
get(): AssetParams
}

/**
* @category Builders
*
*/
export class AssetParamsBuilder implements IAssetParamsBuilder {
private params: AssetParams
private readonly encoder: AlgorandEncoder
constructor() {
this.params = new AssetParams()
this.encoder = new AlgorandEncoder()
}

addTotal(total: number): IAssetParamsBuilder {
this.params.t = total
return this
}
addDecimals(decimals: number): IAssetParamsBuilder {
this.params.dc = decimals
return this
}
addDefaultFrozen(frozen: boolean): IAssetParamsBuilder {
this.params.df = frozen
return this
}
addUnitName(unitName: string): IAssetParamsBuilder {
this.params.un = unitName
return this
}
addAssetName(assetName: string): IAssetParamsBuilder {
this.params.an = assetName
return this
}
addMetadataHash(hash: Uint8Array): IAssetParamsBuilder {
this.params.am = hash
return this
}
addManagerAddress(address: string): IAssetParamsBuilder {
this.params.m = this.encoder.decodeAddress(address)
return this
}
addReserveAddress(address: string): IAssetParamsBuilder {
this.params.r = this.encoder.decodeAddress(address)
return this
}
addFreezeAddress(address: string): IAssetParamsBuilder {
this.params.f = this.encoder.decodeAddress(address)
return this
}
addClawbackAddress(address: string): IAssetParamsBuilder {
this.params.c = this.encoder.decodeAddress(address)
return this
}
get() {
return this.params
}
}
91 changes: 91 additions & 0 deletions lib/algorand.encoder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import * as msgpack from "algo-msgpack-with-bigint"
import { PayTransaction } from "./algorand.transaction.pay"
import { KeyregTransaction } from "./algorand.transaction.keyreg"
import { AlgorandTransactionCrafter } from "./algorand.transaction.crafter"
import {AssetParamsBuilder} from "./algorand.asset.params";
import {AssetConfigTransaction} from "./algorand.transaction.acfg";
import {AssetFreezeTransaction} from "./algorand.transaction.afrz";
import {AssetTransferTransaction} from "./algorand.transaction.axfer";

export function concatArrays(...arrs: ArrayLike<number>[]) {
const size = arrs.reduce((sum, arr) => sum + arr.length, 0)
Expand Down Expand Up @@ -135,7 +139,94 @@ describe("Algorand Encoding", () => {
const encoded: Uint8Array = txn.encode()
expect(encoded).toEqual(algoEncoder.encodeTransaction(txn))
})
it("(OK) Encoding of asset config transaction", async () => {
// from algorand address
const from: string = algoEncoder.encodeAddress(Buffer.from(randomBytes(32)))

// note
const note: string = Buffer.from(randomBytes(32)).toString("base64")
const grp = randomBytes(32)
const lx = randomBytes(32)
const params = new AssetParamsBuilder()
.addTotal(1)
.addDecimals(1)
.addDefaultFrozen(false)
.addAssetName("Big Yeetus")
.addUnitName("YEET")
.addMetadataHash(randomBytes(32))
.addClawbackAddress(from)
.addFreezeAddress(from)
.addManagerAddress(from)
.addReserveAddress(from)
.get()

// create keyreg transaction
const txn: AssetConfigTransaction = algorandCrafter
.createAsset(from, params)
.addFirstValidRound(1000)
.addLastValidRound(2000)
.addNote(note, "base64")
.addFee(1000)
.addGroup(grp)
.addRekey(from)
.addLease(lx)
.get()

const encoded: Uint8Array = txn.encode()
expect(encoded).toEqual(algoEncoder.encodeTransaction(txn))
})
it("(OK) Encoding of asset freeze transaction", async () => {
// from algorand address
const from: string = algoEncoder.encodeAddress(Buffer.from(randomBytes(32)))

// note
const note: string = Buffer.from(randomBytes(32)).toString("base64")
const grp = randomBytes(32)
const lx = randomBytes(32)

// create keyreg transaction
const txn: AssetFreezeTransaction = algorandCrafter
.freezeAsset(from, 1, true)
.addSender(from)
.addFirstValidRound(1000)
.addLastValidRound(2000)
.addNote(note, "base64")
.addFee(1000)
.addGroup(grp)
.addRekey(from)
.addLease(lx)
.get()

const encoded: Uint8Array = txn.encode()
expect(encoded).toEqual(algoEncoder.encodeTransaction(txn))
})
it("(OK) Encoding of asset transfer transaction", async () => {
// from algorand address
const from: string = algoEncoder.encodeAddress(Buffer.from(randomBytes(32)))
const to: string = algoEncoder.encodeAddress(Buffer.from(randomBytes(32)))

// note
const note: string = Buffer.from(randomBytes(32)).toString("base64")
const grp = randomBytes(32)
const lx = randomBytes(32)

// create keyreg transaction
const txn: AssetTransferTransaction = algorandCrafter
.transferAsset(from, 1, to, 1)
.addAssetCloseTo(from)
.addAssetSender(from)
.addFirstValidRound(1000)
.addLastValidRound(2000)
.addNote(note, "base64")
.addFee(1000)
.addGroup(grp)
.addRekey(from)
.addLease(lx)
.get()

const encoded: Uint8Array = txn.encode()
expect(encoded).toEqual(algoEncoder.encodeTransaction(txn))
})
it("(OK) Encode & Decode Address ", async () => {
const keyPair: SignKeyPair = {
publicKey: Uint8Array.from([
Expand Down
9 changes: 9 additions & 0 deletions lib/algorand.encoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,18 @@ const ALGORAND_ADDRESS_BYTE_LENGTH = 36
const ALGORAND_CHECKSUM_BYTE_LENGTH = 4
const ALGORAND_ADDRESS_LENGTH = 58
const HASH_BYTES_LENGTH = 32
/**
* @internal
*/
export const MALFORMED_ADDRESS_ERROR_MSG = "Malformed address"
/**
* @internal
*/
export const ALGORAND_ADDRESS_BAD_CHECKSUM_ERROR_MSG = "Bad checksum"

/**
* @category Encoding
*/
export class AlgorandEncoder extends Encoder{
/**
* decodeAddress takes an Algorand address in string form and decodes it into a Uint8Array.
Expand Down
Loading

0 comments on commit 2004c89

Please sign in to comment.