diff --git a/apps/web/src/components/specification/decoder.ts b/apps/web/src/components/specification/decoder.ts index 70df7109..77e3554e 100644 --- a/apps/web/src/components/specification/decoder.ts +++ b/apps/web/src/components/specification/decoder.ts @@ -21,6 +21,7 @@ import { decodeFunctionData, parseAbiParameters, slice, + SliceOffsetOutOfBoundsError, } from "viem"; import SpecificationModeNotSupportedError from "./errors/SpecificationModeNotSupported"; import { ABI_PARAMS, JSON_ABI, Specification, specModes } from "./types"; @@ -70,8 +71,28 @@ const addPiecesToEnvelope = (e: Envelope): Envelope => { try { if (e.spec.sliceInstructions?.length) { e.spec.sliceInstructions.forEach((instruction, index) => { - const { from, to, name, type } = instruction; - const part = slice(e.input, from, to); + const { + from, + to, + name, + type, + optional = false, + } = instruction; + let part; + + try { + part = slice(e.input, from, to); + } catch (err: any) { + if ( + err instanceof SliceOffsetOutOfBoundsError && + optional + ) { + part = "0x" as Hex; + } else { + throw err; + } + } + const decodedPart = !isNil(type) && !isEmpty(type) ? head(decodeAbiParameters([{ type, name }], part)) diff --git a/apps/web/src/components/specification/systemSpecs.ts b/apps/web/src/components/specification/systemSpecs.ts index 619cf848..2d4f8fc0 100644 --- a/apps/web/src/components/specification/systemSpecs.ts +++ b/apps/web/src/components/specification/systemSpecs.ts @@ -85,6 +85,7 @@ const ERC20PortalSpec: Specification = { { from: 1, to: 21, name: "tokenAddress" }, { from: 21, to: 41, name: "from" }, { from: 41, to: 73, name: "amount", type: "uint" }, + { from: 73, name: "execLayerData", optional: true }, ], abiParams: [], conditionals: [ @@ -135,6 +136,7 @@ const EtherPortalSpec: Specification = { sliceInstructions: [ { from: 0, to: 20, name: "sender" }, { from: 20, to: 52, name: "amount", type: "uint256" }, + { from: 52, name: "execLayerData", optional: true }, ], abiParams: [], conditionals: [ diff --git a/apps/web/src/components/specification/types.ts b/apps/web/src/components/specification/types.ts index 641dd8a3..1309a9bf 100644 --- a/apps/web/src/components/specification/types.ts +++ b/apps/web/src/components/specification/types.ts @@ -46,6 +46,8 @@ export interface SliceInstruction { name?: string; /** The type to decode e.g. uint. Leaving empty will default to the raw sliced return; useful for Address for example.*/ type?: AbiType; + /** A flag to signal to the decoder not to throw an exception caused by slicing the defined byte range.*/ + optional?: boolean; } type Commons = { diff --git a/apps/web/test/components/specification/decoder.test.ts b/apps/web/test/components/specification/decoder.test.ts index 408ca660..5dc9c4d6 100644 --- a/apps/web/test/components/specification/decoder.test.ts +++ b/apps/web/test/components/specification/decoder.test.ts @@ -229,8 +229,44 @@ Slice name: "from" (Is it the right one?)`, expect(envelope.result).toEqual({ amount: 10000000000000000n, sender: "0x0c70e9a737aa92055c8c1217bf887a65cb2292f4", + execLayerData: "0x", }); }); + + it("should decode ether-portal data with the exec-layer information", () => { + const envelope = decodePayload( + systemSpecification.EtherPortalSpec, + encodedDataSamples.etherPortalSampleWithExecLayer, + ); + + expect(envelope.result).toEqual({ + amount: 300n, + execLayerData: + "0x33303020657468206465706f736974656420746f20307833613134363931353532376264313532383032366235633134333335303938646666323730306361", + sender: "0x3a146915527bd1528026b5c14335098dff2700ca", + }); + }); + + it("should fail to decode ether-portal data with required but missing exec-layer information", () => { + const envelope = decodePayload( + { + ...systemSpecification.EtherPortalSpec, + sliceInstructions: + systemSpecification.EtherPortalSpec.sliceInstructions?.map( + (instruction) => + instruction.name === "execLayerData" + ? { + ...instruction, + optional: false, + } + : instruction, + ), + }, + encodedDataSamples.etherPortalSampleWithoutExecLayer, + ); + + expect(envelope.result).toEqual({}); + }); }); describe("Struct definition example cases", () => {