-
Notifications
You must be signed in to change notification settings - Fork 11
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
Experimental feature: Add @marlowe.io/marlowe-template package #184
Conversation
WalkthroughThis update introduces significant enhancements to the development environment and the core functionality of a TypeScript-based project. It focuses on improving debugging configurations, refining the handling and validation of data types, and advancing the project's architecture through the introduction of a new Changes
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (invoked as PR comments)
Additionally, you can add CodeRabbit Configration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review Status
Actionable comments generated: 1
Configuration used: CodeRabbit UI
Files ignored due to path filters (8)
package-lock.json
is excluded by:!**/*.json
package.json
is excluded by:!**/*.json
packages/blueprint/package.json
is excluded by:!**/*.json
packages/blueprint/src/tsconfig.json
is excluded by:!**/*.json
packages/blueprint/test/tsconfig.json
is excluded by:!**/*.json
packages/blueprint/typedoc.json
is excluded by:!**/*.json
tsconfig.json
is excluded by:!**/*.json
tsconfig.test.json
is excluded by:!**/*.json
Files selected for processing (15)
- .vscode/settings.json (2 hunks)
- examples/nodejs/src/marlowe-object-flow.ts (11 hunks)
- jest.unit.config.js (1 hunks)
- jsdelivr-npm-importmap.js (1 hunks)
- packages/adapter/src/bigint.ts (1 hunks)
- packages/blueprint/Readme.md (1 hunks)
- packages/blueprint/src/codecs.ts (1 hunks)
- packages/blueprint/src/index.ts (1 hunks)
- packages/blueprint/src/typed.ts (1 hunks)
- packages/blueprint/test/blueprint-basic-types.spec.ts (1 hunks)
- packages/blueprint/test/blueprint-delayed-payment.spec.ts (1 hunks)
- packages/blueprint/test/codecs.spec.ts (1 hunks)
- packages/blueprint/test/jest.unit.config.mjs (1 hunks)
- packages/runtime/core/src/address.ts (1 hunks)
- packages/runtime/core/src/metadata.ts (1 hunks)
Files skipped from review due to trivial changes (1)
- packages/blueprint/src/index.ts
Additional comments: 24
packages/blueprint/test/jest.unit.config.mjs (1)
- 1-14: Configuration in
jest.unit.config.mjs
is correctly set up for Jest testing of the blueprint package.jest.unit.config.js (1)
- 5-5: Addition of the blueprint package to Jest projects array in
jest.unit.config.js
is correctly done.packages/adapter/src/bigint.ts (1)
- 4-24: Implementation of
BigIntOrNumber
type andBigIntOrNumberGuard
inbigint.ts
is correctly done using io-ts for runtime validation.packages/blueprint/src/codecs.ts (1)
- 1-33: Custom codecs for Date and String types in
codecs.ts
are correctly implemented using io-ts, ensuring type safety and data integrity..vscode/settings.json (1)
- 4-10: > 📝 NOTE
This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [7-17]
Addition of blueprint package paths to the
outFiles
list in.vscode/settings.json
correctly enhances debugging configuration.packages/runtime/core/src/address.ts (1)
- 6-23: Update to use a lighter library for Bech32 address validation in
address.ts
potentially enhances performance and maintainability.packages/blueprint/test/codecs.spec.ts (1)
- 1-39: Tests in
codecs.spec.ts
comprehensively validate the custom codecs for Date and String types, ensuring their correctness and reliability.packages/blueprint/test/blueprint-basic-types.spec.ts (1)
- 1-94: Tests in
blueprint-basic-types.spec.ts
effectively validate the functionality of a blueprint with basic types, demonstrating its correctness and utility.packages/runtime/core/src/metadata.ts (1)
- 1-93: Refinements and updates to metadata types in
metadata.ts
correctly enhance type safety and enforce constraints on metadata values.packages/blueprint/test/blueprint-delayed-payment.spec.ts (1)
- 1-166: Tests in
blueprint-delayed-payment.spec.ts
comprehensively validate the functionality of the delayed payment blueprint, ensuring its correctness and reliability.jsdelivr-npm-importmap.js (1)
- 25-26: Addition of
@marlowe.io/blueprint
to the import map injsdelivr-npm-importmap.js
correctly enhances its accessibility and usability in web projects.packages/blueprint/src/typed.ts (1)
- 1-229: Implementation in
typed.ts
correctly establishes a comprehensive system for creating and working with typed blueprints, enhancing type safety and data integrity.examples/nodejs/src/marlowe-object-flow.ts (12)
- 18-21: The import of
Metadata
is introduced but not used within the provided code. Verify if it's necessary or remove it to avoid unused imports.- 43-43: The import of
BlueprintOf, mkBlueprint
from@marlowe.io/blueprint
is correctly added for the new functionality. Ensure that the@marlowe.io/blueprint
package is included in the project dependencies.- 145-150: The use of
addressBech32
for validating and formatting the payee address is correct. Ensure that thebech32Validator
function properly uses theC.Address.from_bech32
method fromlucid-cardano
for validation.- 184-185: Replacing
payFrom
andpayTo
withpayer
andpayee
in theDelayPaymentScheme
aligns with the updated contract logic. Ensure all references to these fields are updated accordingly.- 190-195: Using
delayPaymentBlueprint.encode(scheme)
for encoding contract details is correct. Ensure that thedelayPaymentBlueprint
is properly defined and that itsencode
method correctly handles thescheme
object structure.- 218-218: The validation result
InvalidBlueprint
is correctly handled. Ensure that thedelayPaymentBlueprint.decode
method properly identifies invalid blueprints.- 231-232: Updating console outputs to reflect the new
payer
andpayee
terminology is correct. Ensure consistency across all user-facing messages.- 383-417: The definition of
delayPaymentBlueprint
usingmkBlueprint
is correct and aligns with the new blueprint functionality. Ensure that all parameter types (address
,value
,date
) are correctly handled by the blueprint and the Marlowe runtime.- 422-422: The type alias
DelayPaymentScheme
for the blueprint parameters is correctly defined. Ensure that it is used consistently throughout the code.- 459-462: The updated contract creation logic using the new
payer
andpayee
fields is correct. Ensure that thelovelace
token type and theinto_account
structure are correctly handled by the Marlowe runtime.- 526-526: The updated console output using the new
payer
field is correct. Ensure consistency in messaging and that thescheme.amount
is correctly formatted for display.- 606-609: Handling the case where
delayPaymentBlueprint.decode
returnsnull
(indicating an invalid blueprint) is correct. Ensure that the decode function is robust and accurately identifies invalid metadata.
packages/blueprint/Readme.md
Outdated
@@ -0,0 +1,3 @@ | |||
# Marlowe Blueprint | |||
|
|||
TODO. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Readme.md file contains a "TODO" placeholder, indicating incomplete documentation.
Would you like assistance in completing the documentation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review Status
Actionable comments generated: 1
Configuration used: CodeRabbit UI
Files ignored due to path filters (3)
package-lock.json
is excluded by:!**/*.json
packages/blueprint/src/tsconfig.json
is excluded by:!**/*.json
packages/blueprint/typedoc.json
is excluded by:!**/*.json
Files selected for processing (16)
- changelog.d/20240214_142506_hrajchert_blueprints.md (1 hunks)
- examples/nodejs/src/marlowe-object-flow.ts (11 hunks)
- packages/adapter/src/bigint.ts (1 hunks)
- packages/blueprint/src/blueprint-param.ts (1 hunks)
- packages/blueprint/src/blueprint.ts (1 hunks)
- packages/blueprint/src/codecs.ts (1 hunks)
- packages/blueprint/src/index.ts (1 hunks)
- packages/blueprint/test/blueprint-basic-types.spec.ts (1 hunks)
- packages/blueprint/test/codecs.spec.ts (1 hunks)
- packages/runtime/client/rest/src/contract/details.ts (2 hunks)
- packages/runtime/client/rest/src/contract/endpoints/collection.ts (4 hunks)
- packages/runtime/client/rest/src/contract/header.ts (2 hunks)
- packages/runtime/client/rest/src/contract/transaction/details.ts (3 hunks)
- packages/runtime/client/rest/src/contract/transaction/endpoints/collection.ts (3 hunks)
- packages/runtime/client/rest/src/contract/transaction/header.ts (2 hunks)
- packages/runtime/core/src/metadata.ts (1 hunks)
Files skipped from review as they are similar to previous changes (4)
- examples/nodejs/src/marlowe-object-flow.ts
- packages/adapter/src/bigint.ts
- packages/blueprint/test/blueprint-basic-types.spec.ts
- packages/runtime/core/src/metadata.ts
Additional comments: 19
changelog.d/20240214_142506_hrajchert_blueprints.md (1)
- 1-12: LGTM!
packages/blueprint/src/codecs.ts (1)
- 1-52: LGTM!
packages/runtime/client/rest/src/contract/transaction/header.ts (2)
- 9-9: LGTM!
- 52-52: LGTM!
packages/runtime/client/rest/src/contract/header.ts (2)
- 11-11: LGTM!
- 72-72: LGTM!
packages/blueprint/test/codecs.spec.ts (1)
- 1-52: LGTM!
packages/blueprint/src/index.ts (1)
- 1-73: LGTM!
packages/runtime/client/rest/src/contract/details.ts (2)
- 6-6: LGTM!
- 16-16: LGTM!
packages/runtime/client/rest/src/contract/transaction/details.ts (2)
- 15-15: LGTM!
- 103-103: LGTM!
packages/blueprint/src/blueprint-param.ts (1)
- 1-161: LGTM!
packages/runtime/client/rest/src/contract/transaction/endpoints/collection.ts (3)
- 20-20: LGTM!
- 159-159: LGTM!
- 208-208: LGTM!
packages/blueprint/src/blueprint.ts (1)
- 1-226: LGTM!
packages/runtime/client/rest/src/contract/endpoints/collection.ts (2)
- 220-220: Replace
Metadata
withMetadataGuard
inBuildCreateContractTxRequestOptionsGuard
to ensure type safety.- 549-549: Replace
Metadata
withMetadataGuard
inPostContractsRequest
to ensure type safety.
@@ -19,6 +19,7 @@ import { | |||
Tag, | |||
Tags, | |||
TagsGuard, | |||
MetadataGuard, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicate import of Metadata
is unnecessary since MetadataGuard
is now used for type validation.
- Metadata,
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.
MetadataGuard, | |
MetadataGuard, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review Status
Actionable comments generated: 0
Configuration used: CodeRabbit UI
Files selected for processing (10)
- packages/adapter/src/io-ts.ts (2 hunks)
- packages/blueprint/src/blueprint.ts (1 hunks)
- packages/blueprint/src/codecs.ts (1 hunks)
- packages/blueprint/src/index.ts (1 hunks)
- packages/runtime/client/rest/src/pagination.ts (1 hunks)
- packages/runtime/client/rest/src/runtime/version.ts (1 hunks)
- packages/runtime/core/src/address.ts (1 hunks)
- packages/runtime/core/src/contract/id.ts (1 hunks)
- packages/runtime/core/src/metadata.ts (1 hunks)
- packages/runtime/core/src/policyId.ts (1 hunks)
Files skipped from review as they are similar to previous changes (5)
- packages/blueprint/src/blueprint.ts
- packages/blueprint/src/codecs.ts
- packages/blueprint/src/index.ts
- packages/runtime/core/src/address.ts
- packages/runtime/core/src/metadata.ts
Additional comments: 5
packages/runtime/core/src/policyId.ts (1)
- 9-9: The predicate function in
PolicyIdGuard
always returns true. Confirm if this is a placeholder for future validation logic or if more specific validation is intended but not implemented.packages/runtime/client/rest/src/runtime/version.ts (1)
- 9-9: The predicate function in
RuntimeVersionGuard
always returns true. Verify if this is intentional or if it's a placeholder for more specific future validation.packages/runtime/core/src/contract/id.ts (1)
- 13-13: The predicate function in
ContractIdGuard
always returns true. Confirm if this is by design or a placeholder for more specific validation logic.packages/runtime/client/rest/src/pagination.ts (1)
- 13-13: The predicate function in
ItemRangeGuard
always returns true. Verify if this is intentional or if it's a placeholder for more specific future validation.packages/adapter/src/io-ts.ts (1)
- 139-176: The implementation of
preservedBrand
aligns with the PR's objectives and addresses the limitations oft.brand
as described. Ensure thorough testing to validate its behavior across various use cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All Good, just the renaming we discussed to be done...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review Status
Actionable comments generated: 1
Configuration used: CodeRabbit UI
Files ignored due to path filters (8)
package-lock.json
is excluded by:!**/*.json
package.json
is excluded by:!**/*.json
packages/marlowe-template/package.json
is excluded by:!**/*.json
packages/marlowe-template/src/tsconfig.json
is excluded by:!**/*.json
packages/marlowe-template/test/tsconfig.json
is excluded by:!**/*.json
packages/marlowe-template/typedoc.json
is excluded by:!**/*.json
tsconfig.json
is excluded by:!**/*.json
tsconfig.test.json
is excluded by:!**/*.json
Files selected for processing (13)
- .vscode/settings.json (2 hunks)
- changelog.d/20240214_142506_hrajchert_marlowe_template.md (1 hunks)
- examples/nodejs/src/marlowe-object-flow.ts (13 hunks)
- jest.unit.config.js (1 hunks)
- jsdelivr-npm-importmap.js (1 hunks)
- packages/marlowe-template/src/codecs.ts (1 hunks)
- packages/marlowe-template/src/index.ts (1 hunks)
- packages/marlowe-template/src/template-param.ts (1 hunks)
- packages/marlowe-template/src/template.ts (1 hunks)
- packages/marlowe-template/test/basic-types-template.spec.ts (1 hunks)
- packages/marlowe-template/test/codecs.spec.ts (1 hunks)
- packages/marlowe-template/test/delayed-payment-template.spec.ts (1 hunks)
- packages/marlowe-template/test/jest.unit.config.mjs (1 hunks)
Files skipped from review as they are similar to previous changes (4)
- .vscode/settings.json
- examples/nodejs/src/marlowe-object-flow.ts
- jest.unit.config.js
- jsdelivr-npm-importmap.js
Additional comments: 15
packages/marlowe-template/test/jest.unit.config.mjs (1)
- 1-14: The Jest configuration for the
marlowe-template
package is correctly set up for a TypeScript project using ESM modules. It includes appropriate settings for the test environment, handling of ESM extensions, module name mapping, and transformation of TypeScript files.changelog.d/20240214_142506_hrajchert_marlowe_template.md (1)
- 3-12: The changelog entries clearly document the introduction of the new
@marlowe.io/marlowe-template
package and updates to the@marlowe.io/runtime-core
package. The inclusion of PR links provides valuable context for these changes.packages/marlowe-template/src/codecs.ts (3)
- 10-21: The
DateFromEpochMS
codec correctly handles the conversion betweenBigIntOrNumber
andDate
, ensuring proper type checking and conversion logic.- 23-37: The
StringSplitCodec
efficiently handles string splitting and joining, considering the ledger's constraints on string lengths. It ensures strings are correctly split into chunks of 64 characters or less and reassembled.- 39-52: The
TokenCodec
correctly manages the conversion between a tuple representation and theToken
type, leveraging theTokenFromTuple
intermediate codec for accurate type validation and conversion.packages/marlowe-template/test/codecs.spec.ts (3)
- 7-19: The tests for
DateFromEpochMS
codec correctly cover scenarios for decoding numbers as dates, failing to decode strings, and encoding dates as numbers. These tests ensure the codec's functionality is as expected.- 22-42: The tests for
StringSplitCodec
cover decoding small strings, merging strings, encoding small strings, splitting long strings, and failing to decode numbers. These tests comprehensively validate the codec's string handling capabilities.- 45-55: The tests for
TokenCodec
ensure that it correctly decodes and encodes valid tokens, providing comprehensive coverage for the codec's functionality in handling token representations.packages/marlowe-template/src/index.ts (1)
- 57-75: The exports related to the MarloweTemplate, including the class, creation function, parameter types, and utility types, are correctly listed and relevant to the package's functionality. This modular organization facilitates easy usage and extension of the package.
packages/marlowe-template/test/basic-types-template.spec.ts (1)
- 5-96: The unit tests for the template with basic types comprehensively cover scenarios for correct and incorrect values, encoding and decoding of valid values, and handling of long strings. These tests ensure the template's functionality is as expected and robustly handles various parameter types.
packages/marlowe-template/src/template-param.ts (1)
- 10-166: The interfaces for various template parameters (StringParam, ValueParam, AddressParam, DateParam, TokenParam) and utility types/functions (TypeOfParam, TemplateKeys, TemplateType, templateParamsCodec, templateParamsObjectGuard) are correctly defined and implemented. They provide efficient and type-safe ways to work with Marlowe templates, adhering to best practices in TypeScript.
packages/marlowe-template/test/delayed-payment-template.spec.ts (1)
- 6-168: The unit tests for the delayed payment template comprehensively cover scenarios for correct and incorrect values, encoding and decoding of valid values, and handling of invalid addresses. These tests ensure the template's functionality is as expected and robustly handles various parameter types and edge cases.
packages/marlowe-template/src/template.ts (3)
- 1-9: The imports are well-organized and clearly separated by their source. However, ensure that all imported entities are used within the file to avoid unnecessary dependencies.
- 15-25: The
DecodingTemplateError
class is a specialized error type for decoding issues. It's well-implemented, providing clear error messaging and extending the native Error class properly. Including the template and error details in the constructor enhances error traceability.- 172-226: The utility types and functions, such as
Expand
,TemplateParametersOf
, andmkMarloweTemplate
, are well-implemented, enhancing the usability and readability of the template system. ThemkMarloweTemplate
function, in particular, simplifies the creation ofMarloweTemplate
instances with type inference for template parameters. These additions follow best practices for type safety and code clarity.
} | ||
if (metadatum === null) { | ||
return t.failure(val, ctx, "Metadata entry 9041 is null"); | ||
} | ||
|
||
if ("v" in metadatum === false) { | ||
return t.failure( | ||
val, | ||
ctx, | ||
"Metadata entry 9041 doesn't have a version field" | ||
); | ||
} | ||
if ("params" in metadatum === false) { | ||
return t.failure( | ||
val, | ||
ctx, | ||
"Metadata entry 9041 doesn't have a params field" | ||
); | ||
} | ||
|
||
const version = metadatum["v" as any]; | ||
|
||
if ( | ||
!BigIntOrNumberGuard.is(version) || | ||
BigIntOrNumberGuard.encode(version) !== 1n | ||
) { | ||
return t.failure( | ||
val, | ||
ctx, | ||
"Metadata entry 9041 has an invalid version" | ||
); | ||
} | ||
|
||
const paramList = paramListCodec.decode(metadatum["params" as any]); | ||
if (paramList._tag === "Left") { | ||
return t.failure( | ||
paramList.left[0].value, | ||
paramList.left[0].context, | ||
"Invalid params" | ||
); | ||
} | ||
const result = {} as any; | ||
templateParams.forEach((param, ix) => { | ||
result[param.name] = paramList.right[ix]; | ||
}); | ||
return t.success(result as ObjectParams); | ||
}, | ||
(values) => { | ||
// FIXME: Try to type | ||
let valuesAsList: any[] = []; | ||
|
||
templateParams.forEach((param, ix) => { | ||
const value = (values as any)[param.name]; | ||
if (typeof value === "undefined") { | ||
throw new Error(`The value for ${param.name} is missing`); | ||
} | ||
valuesAsList.push(value); | ||
}); | ||
return { | ||
"9041": { | ||
v: 1, | ||
params: paramListCodec.encode(valuesAsList), | ||
}, | ||
}; | ||
} | ||
) | ||
); | ||
} | ||
|
||
/** | ||
* Type guard for the ObjectParams type. | ||
* @param value - an unknown value to check if it is a valid ObjectParams. | ||
* @returns true if the value is a valid ObjectParams, false otherwise. | ||
*/ | ||
is(value: unknown): value is ObjectParams { | ||
return this.templateCodec.is(value); | ||
} | ||
/** | ||
* Decodes a Metadata into an ObjectParams. | ||
* @param value - a Metadata to decode. | ||
* @returns the decoded ObjectParams. | ||
* @throws {@link DecodingTemplateError} - if the value is not a valid Metadata. | ||
*/ | ||
fromMetadata(value: Metadata): ObjectParams { | ||
const decoded = this.templateCodec.decode(value); | ||
if (decoded._tag === "Right") { | ||
return decoded.right; | ||
} else { | ||
throw new DecodingTemplateError(this, decoded.left); | ||
} | ||
} | ||
/** | ||
* Encodes the given value into a Metadata object. | ||
* | ||
* @param value The value to be encoded. | ||
* @returns The encoded Metadata object. | ||
*/ | ||
toMetadata(value: ObjectParams) { | ||
return this.templateCodec.encode(value); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The MarloweTemplate
class is the core of this file, encapsulating the logic for serializing and deserializing template parameters to and from Metadata. The implementation is robust, with several key points to note:
- The use of
io-ts
codecs and guards for type safety and validation is commendable. - The constructor's logic for creating codecs based on template parameters is complex but well-structured. However, the magic number
9041
used as a key in the metadata object could benefit from being defined as a constant with a descriptive name for better readability and maintainability. - The error handling in
fromMetadata
method is precise, throwing aDecodingTemplateError
with relevant information when decoding fails. - The FIXME comment on line 118 suggests an area for improvement. Attempting to type the
valuesAsList
more specifically thanany[]
would enhance type safety.
Consider defining the magic number 9041
as a constant and addressing the FIXME note by exploring ways to type valuesAsList
more accurately.
+ const METADATA_KEY = 9041; // Descriptive name for the metadata key
- const metadatum = val[9041] ?? (val["9041"] as unknown);
+ const metadatum = val[METADATA_KEY] ?? (val[String(METADATA_KEY)] as unknown);
Address the FIXME on line 118 by exploring more specific typing for valuesAsList
.
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.
export class MarloweTemplate<ObjectParams extends object> { | |
private templateCodec: t.Type<ObjectParams, Metadata, unknown>; | |
name: string; | |
description?: string; | |
/** | |
* Manual constructor for the MarloweTemplate class, you should use {@link mkMarloweTemplate} instead. | |
* @typeParam ObjectParams - The inferred type of the `options.params` as an object. | |
* @param options - The {@link MkTemplateOptions | options} to create a new MarloweTemplate. | |
*/ | |
constructor(options: MkTemplateOptions<readonly TemplateParam<any>[]>) { | |
const templateParams = options.params; | |
this.name = options.name; | |
this.description = options.description; | |
const paramListCodec = templateParamsCodec(templateParams); | |
const paramObjectCodec = templateParamsObjectGuard(templateParams); | |
this.templateCodec = MetadataGuard.pipe( | |
new t.Type<ObjectParams, Metadata, Metadata>( | |
options.name, | |
paramObjectCodec.is, | |
(val, ctx) => { | |
const metadatum = val[9041] ?? (val["9041"] as unknown); | |
if (typeof metadatum === "undefined") { | |
return t.failure(val, ctx, "Missing metadata entry 9041"); | |
} | |
if (typeof metadatum !== "object") { | |
return t.failure(val, ctx, "Metadata entry 9041 is not an object"); | |
} | |
if (metadatum === null) { | |
return t.failure(val, ctx, "Metadata entry 9041 is null"); | |
} | |
if ("v" in metadatum === false) { | |
return t.failure( | |
val, | |
ctx, | |
"Metadata entry 9041 doesn't have a version field" | |
); | |
} | |
if ("params" in metadatum === false) { | |
return t.failure( | |
val, | |
ctx, | |
"Metadata entry 9041 doesn't have a params field" | |
); | |
} | |
const version = metadatum["v" as any]; | |
if ( | |
!BigIntOrNumberGuard.is(version) || | |
BigIntOrNumberGuard.encode(version) !== 1n | |
) { | |
return t.failure( | |
val, | |
ctx, | |
"Metadata entry 9041 has an invalid version" | |
); | |
} | |
const paramList = paramListCodec.decode(metadatum["params" as any]); | |
if (paramList._tag === "Left") { | |
return t.failure( | |
paramList.left[0].value, | |
paramList.left[0].context, | |
"Invalid params" | |
); | |
} | |
const result = {} as any; | |
templateParams.forEach((param, ix) => { | |
result[param.name] = paramList.right[ix]; | |
}); | |
return t.success(result as ObjectParams); | |
}, | |
(values) => { | |
// FIXME: Try to type | |
let valuesAsList: any[] = []; | |
templateParams.forEach((param, ix) => { | |
const value = (values as any)[param.name]; | |
if (typeof value === "undefined") { | |
throw new Error(`The value for ${param.name} is missing`); | |
} | |
valuesAsList.push(value); | |
}); | |
return { | |
"9041": { | |
v: 1, | |
params: paramListCodec.encode(valuesAsList), | |
}, | |
}; | |
} | |
) | |
); | |
} | |
/** | |
* Type guard for the ObjectParams type. | |
* @param value - an unknown value to check if it is a valid ObjectParams. | |
* @returns true if the value is a valid ObjectParams, false otherwise. | |
*/ | |
is(value: unknown): value is ObjectParams { | |
return this.templateCodec.is(value); | |
} | |
/** | |
* Decodes a Metadata into an ObjectParams. | |
* @param value - a Metadata to decode. | |
* @returns the decoded ObjectParams. | |
* @throws {@link DecodingTemplateError} - if the value is not a valid Metadata. | |
*/ | |
fromMetadata(value: Metadata): ObjectParams { | |
const decoded = this.templateCodec.decode(value); | |
if (decoded._tag === "Right") { | |
return decoded.right; | |
} else { | |
throw new DecodingTemplateError(this, decoded.left); | |
} | |
} | |
/** | |
* Encodes the given value into a Metadata object. | |
* | |
* @param value The value to be encoded. | |
* @returns The encoded Metadata object. | |
*/ | |
toMetadata(value: ObjectParams) { | |
return this.templateCodec.encode(value); | |
} | |
export class MarloweTemplate<ObjectParams extends object> { | |
private templateCodec: t.Type<ObjectParams, Metadata, unknown>; | |
name: string; | |
description?: string; | |
/** | |
* Manual constructor for the MarloweTemplate class, you should use {@link mkMarloweTemplate} instead. | |
* @typeParam ObjectParams - The inferred type of the `options.params` as an object. | |
* @param options - The {@link MkTemplateOptions | options} to create a new MarloweTemplate. | |
*/ | |
constructor(options: MkTemplateOptions<readonly TemplateParam<any>[]>) { | |
const templateParams = options.params; | |
this.name = options.name; | |
this.description = options.description; | |
const paramListCodec = templateParamsCodec(templateParams); | |
const paramObjectCodec = templateParamsObjectGuard(templateParams); | |
+ const METADATA_KEY = 9041; // Descriptive name for the metadata key | |
this.templateCodec = MetadataGuard.pipe( | |
new t.Type<ObjectParams, Metadata, Metadata>( | |
options.name, | |
paramObjectCodec.is, | |
(val, ctx) => { | |
- const metadatum = val[9041] ?? (val["9041"] as unknown); | |
+ const metadatum = val[METADATA_KEY] ?? (val[String(METADATA_KEY)] as unknown); | |
if (typeof metadatum === "undefined") { | |
return t.failure(val, ctx, "Missing metadata entry 9041"); | |
} | |
if (typeof metadatum !== "object") { | |
return t.failure(val, ctx, "Metadata entry 9041 is not an object"); | |
} | |
if (metadatum === null) { | |
return t.failure(val, ctx, "Metadata entry 9041 is null"); | |
} | |
if ("v" in metadatum === false) { | |
return t.failure( | |
val, | |
ctx, | |
"Metadata entry 9041 doesn't have a version field" | |
); | |
} | |
if ("params" in metadatum === false) { | |
return t.failure( | |
val, | |
ctx, | |
"Metadata entry 9041 doesn't have a params field" | |
); | |
} | |
const version = metadatum["v" as any]; | |
if ( | |
!BigIntOrNumberGuard.is(version) || | |
BigIntOrNumberGuard.encode(version) !== 1n | |
) { | |
return t.failure( | |
val, | |
ctx, | |
"Metadata entry 9041 has an invalid version" | |
); | |
} | |
const paramList = paramListCodec.decode(metadatum["params" as any]); | |
if (paramList._tag === "Left") { | |
return t.failure( | |
paramList.left[0].value, | |
paramList.left[0].context, | |
"Invalid params" | |
); | |
} | |
const result = {} as any; | |
templateParams.forEach((param, ix) => { | |
result[param.name] = paramList.right[ix]; | |
}); | |
return t.success(result as ObjectParams); | |
}, | |
(values) => { | |
// FIXME: Try to type | |
let valuesAsList: any[] = []; | |
templateParams.forEach((param, ix) => { | |
const value = (values as any)[param.name]; | |
if (typeof value === "undefined") { | |
throw new Error(`The value for ${param.name} is missing`); | |
} | |
valuesAsList.push(value); | |
}); | |
return { | |
"9041": { | |
v: 1, | |
params: paramListCodec.encode(valuesAsList), | |
}, | |
}; | |
} | |
) | |
); | |
} | |
/** | |
* Type guard for the ObjectParams type. | |
* @param value - an unknown value to check if it is a valid ObjectParams. | |
* @returns true if the value is a valid ObjectParams, false otherwise. | |
*/ | |
is(value: unknown): value is ObjectParams { | |
return this.templateCodec.is(value); | |
} | |
/** | |
* Decodes a Metadata into an ObjectParams. | |
* @param value - a Metadata to decode. | |
* @returns the decoded ObjectParams. | |
* @throws {@link DecodingTemplateError} - if the value is not a valid Metadata. | |
*/ | |
fromMetadata(value: Metadata): ObjectParams { | |
const decoded = this.templateCodec.decode(value); | |
if (decoded._tag === "Right") { | |
return decoded.right; | |
} else { | |
throw new DecodingTemplateError(this, decoded.left); | |
} | |
} | |
/** | |
* Encodes the given value into a Metadata object. | |
* | |
* @param value The value to be encoded. | |
* @returns The encoded Metadata object. | |
*/ | |
toMetadata(value: ObjectParams) { | |
return this.templateCodec.encode(value); | |
} |
const delayPaymentTemplate = mkMarloweTemplate({ | ||
name: "Delayed payment", | ||
description: | ||
"In a delay payment, a `payer` transfer an `amount` of ADA to the `payee` which can be redeemed after a `releaseDeadline`. While the payment is held by the contract, it can be staked to the payer, to generate pasive income while the payee has the guarantees that the money will be released.", | ||
params: [ | ||
{ | ||
name: "payer", | ||
description: "Who is making the payment", | ||
type: "address", | ||
}, | ||
{ | ||
name: "payee", | ||
description: "Who is receiving the payment", | ||
type: "address", | ||
}, | ||
{ | ||
name: "amount", | ||
description: "The amount of lovelaces to be paid", | ||
type: "value", | ||
}, | ||
{ | ||
name: "depositDeadline", | ||
description: | ||
"The deadline for the payment to be made. If the payment is not made by this date, the contract can be closed", | ||
type: "date", | ||
}, | ||
{ | ||
name: "releaseDeadline", | ||
description: | ||
"A date after the payment can be released to the receiver. NOTE: An empty transaction must be done to close the contract", | ||
type: "date", | ||
}, | ||
] as const, | ||
}); | ||
|
||
/** | ||
* These are the parameters of the contract | ||
*/ | ||
interface DelayPaymentScheme { | ||
/** | ||
* Who is making the delayed payment | ||
*/ | ||
payFrom: Address; | ||
/** | ||
* Who is receiving the payment | ||
*/ | ||
payTo: Address; | ||
/** | ||
* The amount of lovelaces to be paid | ||
*/ | ||
amount: bigint; | ||
/** | ||
* The deadline for the payment to be made. If the payment is not made by this date, the contract can be closed | ||
*/ | ||
depositDeadline: Date; | ||
/** | ||
* A date after the payment can be released to the receiver. | ||
* NOTE: An empty transaction must be done to close the contract | ||
*/ | ||
releaseDeadline: Date; | ||
} | ||
type DelayPaymentParameters = TemplateParametersOf<typeof delayPaymentTemplate>; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before we manually defined the DelayPaymentScheme, now we manually define the delayPaymentTemplate
and the Scheme
is inferred (and renamed as DelayPaymentParameters)
const mkDelayPaymentTags = (schema: DelayPaymentScheme) => { | ||
const tag = "DELAY_PYMNT-1"; | ||
const tags = {} as Tags; | ||
|
||
tags[`${tag}-from-0`] = splitAddress(schema.payFrom)[0]; | ||
tags[`${tag}-from-1`] = splitAddress(schema.payFrom)[1]; | ||
tags[`${tag}-to-0`] = splitAddress(schema.payTo)[0]; | ||
tags[`${tag}-to-1`] = splitAddress(schema.payTo)[1]; | ||
tags[`${tag}-amount`] = schema.amount; | ||
tags[`${tag}-deposit`] = schema.depositDeadline; | ||
tags[`${tag}-release`] = schema.releaseDeadline; | ||
return tags; | ||
}; | ||
|
||
const extractSchemeFromTags = ( | ||
tags: unknown | ||
): DelayPaymentScheme | undefined => { | ||
const tagsGuard = t.type({ | ||
"DELAY_PYMNT-1-from-0": t.string, | ||
"DELAY_PYMNT-1-from-1": t.string, | ||
"DELAY_PYMNT-1-to-0": t.string, | ||
"DELAY_PYMNT-1-to-1": t.string, | ||
"DELAY_PYMNT-1-amount": t.bigint, | ||
"DELAY_PYMNT-1-deposit": t.string, | ||
"DELAY_PYMNT-1-release": t.string, | ||
}); | ||
|
||
if (!tagsGuard.is(tags)) { | ||
return; | ||
} | ||
|
||
return { | ||
payFrom: { | ||
address: `${tags["DELAY_PYMNT-1-from-0"]}${tags["DELAY_PYMNT-1-from-1"]}`, | ||
}, | ||
payTo: { | ||
address: `${tags["DELAY_PYMNT-1-to-0"]}${tags["DELAY_PYMNT-1-to-1"]}`, | ||
}, | ||
amount: tags["DELAY_PYMNT-1-amount"], | ||
depositDeadline: new Date(tags["DELAY_PYMNT-1-deposit"]), | ||
releaseDeadline: new Date(tags["DELAY_PYMNT-1-release"]), | ||
}; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these functions that manually encoded and decoded the parameters as tags are now provided by the toMetadata
and fromMetadata
functions of the template parameters.
When you create a Marlowe contract using the ts-sdk, it is common to have a template function that receives some Parameters/Schema/Blueprint and generates either a
Contract
orContractBundleMap
. When you want another participant to know if a given contract is an instance of your template, the other participant must have the same template function applied with the same parameters.Sharing the template function is easy and generally solved by using the same web DAPP. The template parameters, on the other hand, need to be shared differently.
It is easy to see how this PR simplifies the Parameter sharing In the
marlowe-object-flow.ts
example changes.Before, we manually defined the type
DelayPaymentScheme
and two functions (extractSchemeFromTags
andmkDelayPaymentTags
) to decode and encode the Scheme asTags
. Now, we manually define theMarloweTemplate
but we infer theDelayPaymentParameters
type and the decoding and encoding function asMetadata
are provided by the package.Summary by CodeRabbit
Summary by CodeRabbit
Metadata
type.