Skip to content

Commit

Permalink
Merge pull request #145 from fleet-sdk/arobsn/i123
Browse files Browse the repository at this point in the history
Make `decode` function return `SConstant` by default instead of the decoded data
  • Loading branch information
arobsn authored Sep 17, 2024
2 parents 4638b65 + 19962ee commit 6f93d00
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 31 deletions.
5 changes: 5 additions & 0 deletions .changeset/itchy-cars-move.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fleet-sdk/serializer": minor
---

**🚨 BREAKING CHANGE**: `decode` function now returns `SConstant` instead of data directly
13 changes: 8 additions & 5 deletions packages/mock-chain/src/mockChain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
some
} from "@fleet-sdk/common";
import type { ErgoUnsignedTransaction } from "@fleet-sdk/core";
import { utf8 } from "@fleet-sdk/crypto";
import { type ByteInput, utf8 } from "@fleet-sdk/crypto";
import { decode } from "@fleet-sdk/serializer";
import pc from "picocolors";
import type { BlockchainParameters } from "sigmastate-js/main";
Expand Down Expand Up @@ -216,8 +216,8 @@ export class MockChain {
);
if (!box) return;

const name = decode(box.additionalRegisters.R4, safeUtf8Encode);
const decimals = decode(box.additionalRegisters.R6, safeUtf8Encode);
const name = decodeString(box.additionalRegisters.R4);
const decimals = decodeString(box.additionalRegisters.R6);
if (name) {
this.#metadataMap.set(firstInputId, {
name,
Expand All @@ -232,5 +232,8 @@ function log(str: string) {
console.log(str);
}

const safeUtf8Encode = (v: unknown) =>
v instanceof Uint8Array ? utf8.encode(v) : undefined;
function decodeString(bytes?: ByteInput): string | undefined {
const c = decode<Uint8Array>(bytes);
if (!c || c.type.toString() !== "SColl[SByte]") return;
return utf8.encode(c.data);
}
46 changes: 33 additions & 13 deletions packages/serializer/src/sigmaConstant.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,15 +144,36 @@ describe("SColl serialization and parsing", () => {
});
});

describe("Data only decoding", () => {
it("Should decode only data", () => {
expect(decode("40050002")).to.deep.equal([0, 1n]);
expect(decode("0101")).to.deep.equal(true);
describe("Safe decoding", () => {
const validVectors = [
{ hex: "40050002", data: [0, 1n], type: "(SInt, SLong)" },
{ hex: "0101", data: true, type: "SBool" }
];

test.each(validVectors)("Should decode valid bytes", (tv) => {
let c: SConstant | undefined;
expect(() => {
c = decode(tv.hex);
}).not.to.throw();

expect(c?.data).to.deep.equal(tv.data);
expect(c?.type.toString()).to.be.equal(tv.type);
expect(c?.toHex()).to.be.equal(tv.hex);
});

it("Should decode and encode using custom coder", () => {
expect(decode("0e0a46656d616c6520233035", utf8.encode)).to.be.equal("Female #05");
expect(decode(SInt(1).toHex(), (v: number) => v.toString())).to.be.equal("1");
it("Should not throw but return undefined for invalid inputs", () => {
expect(() => decode("deadbeef")).not.to.throw();
expect(decode("deadbeef")).to.be.undefined;
expect(decode(undefined)).to.be.undefined;
expect(decode("")).to.be.undefined;
});
});

describe("Data only decoding", () => {
it("Should decode only data", () => {
expect(parse("40050002")).to.deep.equal([0, 1n]);
expect(parse("0101")).to.deep.equal(true);
expect(parse("0101"), "safe").to.deep.equal(true);
});

it("Should throw with invalid bytes in 'strict' parsing mode", () => {
Expand All @@ -162,12 +183,11 @@ describe("Data only decoding", () => {
expect(() => parse(undefined as unknown as string, "strict")).to.throw();
});

it("Should not throw but return undefined with invalid bytes", () => {
expect(() => decode("deadbeef")).not.to.throw();
expect(decode("deadbeef")).to.be.undefined;
expect(decode(undefined)).to.be.undefined;
expect(decode("")).to.be.undefined;
expect(decode("0102")).to.be.equal(false);
it("Should return undefined with invalid bytes in 'safe' parsing mode", () => {
expect(() => parse("deadbeef", "safe")).not.to.throw();
expect(parse("deadbeef", "safe")).to.be.undefined;
expect(parse("", "safe")).to.be.undefined;
expect(parse(undefined as unknown as string, "safe")).to.be.undefined;
});
});

Expand Down
30 changes: 17 additions & 13 deletions packages/serializer/src/sigmaConstant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,24 @@ export class SConstant<D = unknown, T extends SType = SType> {
}
}

export function decode<T>(value: ByteInput | undefined): T | undefined;
export function decode<T, K>(
value: ByteInput | undefined,
coder: (input: T) => K
): K | undefined;
export function decode<T, K>(
value: ByteInput | undefined,
coder?: (input: T) => K
): T | K | undefined {
if (isUndefined(value)) return;
const data = parse<T>(value, "safe");
if (isUndefined(data)) return;
/**
* Decodes a byte input into a Sigma constant of type `SConstant<D, T>`.
*
* @template D - The data type of the constant.
* @template T - The type of the constant.
* @param value - The value to decode.
* @returns The decoded constant or `undefined` if the value is `undefined` or decoding fails.
*/
export function decode<D = unknown, T extends SType = SType>(
value?: ByteInput
): SConstant<D, T> | undefined {
if (value === undefined) return;

return coder ? coder(data) : data;
try {
return SConstant.from<D, T>(value);
} catch {
return;
}
}

/** @deprecated use `decode` instead */
Expand Down

0 comments on commit 6f93d00

Please sign in to comment.