From 3290751c7301bded09343cfd0eea0046e2099604 Mon Sep 17 00:00:00 2001 From: Mateo-mro <160488334+Mateo-mro@users.noreply.github.com> Date: Wed, 28 Feb 2024 15:34:35 +0100 Subject: [PATCH] Add replacedOrder to metadata (#60) * Add replacedOrder to metadata --- src/generatedTypes/index.ts | 10 +- src/generatedTypes/v1.1.0.ts | 160 ++++++++++++++++++++++++++ src/schemas/definitions.json | 7 ++ src/schemas/replacedOrder/v0.1.0.json | 15 +++ src/schemas/v1.1.0.json | 68 +++++++++++ src/scripts/compile.ts | 4 +- test/schema.spec.ts | 60 ++++++++++ 7 files changed, 320 insertions(+), 4 deletions(-) create mode 100644 src/generatedTypes/v1.1.0.ts create mode 100644 src/schemas/replacedOrder/v0.1.0.json create mode 100644 src/schemas/v1.1.0.json diff --git a/src/generatedTypes/index.ts b/src/generatedTypes/index.ts index f70ec71..d095d5a 100644 --- a/src/generatedTypes/index.ts +++ b/src/generatedTypes/index.ts @@ -12,10 +12,11 @@ import * as v0_7_0 from './v0.7.0' import * as v0_8_0 from './v0.8.0' import * as v0_9_0 from './v0.9.0' import * as v1_0_0 from './v1.0.0' +import * as v1_1_0 from './v1.1.0' -export * as latest from './v1.0.0' +export * as latest from './v1.1.0' -export const LATEST_APP_DATA_VERSION = '1.0.0' +export const LATEST_APP_DATA_VERSION = '1.1.0' export const LATEST_QUOTE_METADATA_VERSION = '1.0.0' export const LATEST_REFERRER_METADATA_VERSION = '0.2.0' export const LATEST_ORDER_CLASS_METADATA_VERSION = '0.3.0' @@ -24,9 +25,11 @@ export const LATEST_HOOKS_METADATA_VERSION = '0.1.0' export const LATEST_SIGNER_METADATA_VERSION = '0.1.0' export const LATEST_WIDGET_METADATA_VERSION = '0.1.0' export const LATEST_PARTNER_FEE_METADATA_VERSION = '0.1.0' +export const LATEST_REPLACED_ORDER_METADATA_VERSION = '0.1.0' -export type LatestAppDataDocVersion = v1_0_0.AppDataRootSchema +export type LatestAppDataDocVersion = v1_1_0.AppDataRootSchema export type AnyAppDataDocVersion = + | v1_1_0.AppDataRootSchema | v1_0_0.AppDataRootSchema | v0_11_0.AppDataRootSchema | v0_10_0.AppDataRootSchema @@ -41,6 +44,7 @@ export type AnyAppDataDocVersion = | v0_1_0.AppDataRootSchema export { + v1_1_0, v1_0_0, v0_11_0, v0_10_0, diff --git a/src/generatedTypes/v1.1.0.ts b/src/generatedTypes/v1.1.0.ts new file mode 100644 index 0000000..e6f91cb --- /dev/null +++ b/src/generatedTypes/v1.1.0.ts @@ -0,0 +1,160 @@ +/* tslint:disable */ +/** + * This file was automatically generated by json-schema-to-typescript. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run json-schema-to-typescript to regenerate this file. + */ + +/** + * Semantic versioning of document. + */ +export type Version = string; +/** + * The code identifying the CLI, UI, service generating the order. + */ +export type AppCode = string; +/** + * Environment from which the order came from. + */ +export type Environment = string; +/** + * The address of the trader who signs the CoW Swap order. This field should normally be omitted; it is recommended to use it if the signer is a smart-contract wallet using EIP-1271 signatures. + */ +export type Signer = string; +export type ReferrerAddress = string; +/** + * Tracks in which medium the traffic originated from (twitter, facebook, etc.) + */ +export type UTMSource = string; +/** + * Tracks in which medium the traffic originated from (mail, CPC, social, etc.) + */ +export type UTMMedium = string; +/** + * Track the performance of a specific campaign + */ +export type UTMCampaign = string; +/** + * Track which link was clicked + */ +export type UTMContent = string; +/** + * Track which keyword term a website visitor came from + */ +export type UTMKeywordTerm = string; +/** + * Slippage tolerance that was applied to the order to get the limit price. Expressed in Basis Points (BPS). One basis point is equivalent to 0.01% (1/100th of a percent) + */ +export type SlippageBips = number; +/** + * Indicator of the order class. + */ +export type OrderClass1 = "market" | "limit" | "liquidity" | "twap"; +/** + * Semantic versioning of document. + */ +export type Version1 = string; +/** + * The contract to call for the hook + */ +export type HookTarget = string; +/** + * The calldata to use when calling the hook + */ +export type HookCallData = string; +/** + * The gas limit (in gas units) for the hook + */ +export type HookGasLimit = string; +/** + * CoW Hooks to call before an order executes + */ +export type PreHooks = CoWHook[]; +/** + * CoW Hooks to call after an order executes + */ +export type PostHooks = CoWHook[]; +/** + * The code identifying the UI powering the widget + */ +export type AppCode1 = string; +/** + * Environment from which the order came from. + */ +export type Environment1 = string; +/** + * The fee in basis points (BPS) to be paid to the partner. One basis point is equivalent to 0.01% (1/100th of a percent) + */ +export type BasisPointBPS = number; +/** + * The Ethereum address of the partner to receive the fee. + */ +export type PartnerAccount = string; +/** + * The replaced order UID. + */ +export type ReplacedOrderUID = string; + +/** + * Metadata JSON document for adding information to orders. + */ +export interface AppDataRootSchema { + version: Version; + appCode?: AppCode; + environment?: Environment; + metadata: Metadata; +} +/** + * Each metadata will specify one aspect of the order. + */ +export interface Metadata { + signer?: Signer; + referrer?: Referrer; + utm?: UTMCodes; + quote?: Quote; + orderClass?: OrderClass; + hooks?: OrderInteractionHooks; + widget?: Widget; + partnerFee?: PartnerFee; + replacedOrder?: ReplacedOrder; +} +export interface Referrer { + address: ReferrerAddress; +} +export interface UTMCodes { + utmSource?: UTMSource; + utmMedium?: UTMMedium; + utmCampaign?: UTMCampaign; + utmContent?: UTMContent; + utmTerm?: UTMKeywordTerm; +} +export interface Quote { + slippageBips: SlippageBips; +} +export interface OrderClass { + orderClass: OrderClass1; +} +/** + * Optional Pre and Post order interaction hooks attached to a single order + */ +export interface OrderInteractionHooks { + version?: Version1; + pre?: PreHooks; + post?: PostHooks; +} +export interface CoWHook { + target: HookTarget; + callData: HookCallData; + gasLimit: HookGasLimit; +} +export interface Widget { + appCode: AppCode1; + environment?: Environment1; +} +export interface PartnerFee { + bps: BasisPointBPS; + recipient: PartnerAccount; +} +export interface ReplacedOrder { + uid: ReplacedOrderUID; +} diff --git a/src/schemas/definitions.json b/src/schemas/definitions.json index 78bf010..bdefabf 100644 --- a/src/schemas/definitions.json +++ b/src/schemas/definitions.json @@ -33,6 +33,13 @@ "type": "integer", "maximum": 10000, "minimum": 0 + }, + "orderUid": { + "$id": "#/definitions/orderUid", + "pattern": "^0x[a-fA-F0-9]{112}$", + "title": "Order UID", + "examples": ["0xff2e2e54d178997f173266817c1e9ed6fee1a1aae4b43971c53b543cffcc2969845c6f5599fbb25dbdd1b9b013daf85c03f3c63763e4bc4a"], + "type": "string" } } } diff --git a/src/schemas/replacedOrder/v0.1.0.json b/src/schemas/replacedOrder/v0.1.0.json new file mode 100644 index 0000000..ecba827 --- /dev/null +++ b/src/schemas/replacedOrder/v0.1.0.json @@ -0,0 +1,15 @@ +{ + "$id": "#replacedOrder/v0.1.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "required": ["uid"], + "title": "Replaced order", + "type": "object", + "additionalProperties": false, + "properties": { + "uid": { + "title": "Replaced order UID", + "description": "The replaced order UID.", + "$ref": "../definitions.json#/definitions/orderUid" + } + } +} diff --git a/src/schemas/v1.1.0.json b/src/schemas/v1.1.0.json new file mode 100644 index 0000000..3192276 --- /dev/null +++ b/src/schemas/v1.1.0.json @@ -0,0 +1,68 @@ +{ + "$id": "https://cowswap.exchange/schemas/app-data/v1.1.0.json", + "$schema": "http://json-schema.org/draft-07/schema", + "description": "Metadata JSON document for adding information to orders.", + "required": ["version", "metadata"], + "title": "AppData Root Schema", + "type": "object", + "additionalProperties": false, + "properties": { + "version": { + "$ref": "definitions.json#/definitions/version", + "readOnly": true, + "default": "0.11.0" + }, + "appCode": { + "$id": "#/properties/appCode", + "description": "The code identifying the CLI, UI, service generating the order.", + "examples": ["CoW Swap"], + "title": "App Code", + "type": "string" + }, + "environment": { + "$id": "#/properties/environment", + "description": "Environment from which the order came from.", + "title": "Environment", + "type": "string", + "examples": ["production", "development", "staging", "ens"] + }, + "metadata": { + "$id": "#/properties/metadata", + "default": {}, + "description": "Each metadata will specify one aspect of the order.", + "required": [], + "title": "Metadata", + "type": "object", + "additionalProperties": false, + "properties": { + "signer": { + "$ref": "signer/v0.1.0.json#" + }, + "referrer": { + "$ref": "referrer/v0.2.0.json#" + }, + "utm": { + "$ref": "utm/v0.2.0.json#" + }, + "quote": { + "$ref": "quote/v1.0.0.json#" + }, + "orderClass": { + "$ref": "orderClass/v0.3.0.json#" + }, + "hooks": { + "$ref": "hooks/v0.1.0.json#" + }, + "widget": { + "$ref": "widget/v0.1.0.json#" + }, + "partnerFee": { + "$ref": "partnerFee/v0.1.0.json#" + }, + "replacedOrder": { + "$ref": "replacedOrder/v0.1.0.json#" + } + } + } + } +} diff --git a/src/scripts/compile.ts b/src/scripts/compile.ts index 4cd3b30..8dc03d6 100644 --- a/src/scripts/compile.ts +++ b/src/scripts/compile.ts @@ -65,6 +65,7 @@ async function compile(): Promise { const latestSignerVersion = await getLatestMetadataDocVersion('signer') const latestWidgetVersion = await getLatestMetadataDocVersion('widget') const latestPartnerFeeVersion = await getLatestMetadataDocVersion('partnerFee') + const latestReplacedOrderVersion = await getLatestMetadataDocVersion('replacedOrder') const additionalTypesExport = ` export * as latest from './${latest}' @@ -78,6 +79,7 @@ export const LATEST_HOOKS_METADATA_VERSION = '${extractSemver(latestHooksVersion export const LATEST_SIGNER_METADATA_VERSION = '${extractSemver(latestSignerVersion)}' export const LATEST_WIDGET_METADATA_VERSION = '${extractSemver(latestWidgetVersion)}' export const LATEST_PARTNER_FEE_METADATA_VERSION = '${extractSemver(latestPartnerFeeVersion)}' +export const LATEST_REPLACED_ORDER_METADATA_VERSION = '${extractSemver(latestReplacedOrderVersion)}' export type LatestAppDataDocVersion = ${latestExport}.AppDataRootSchema export type AnyAppDataDocVersion = ${allVersions} @@ -102,7 +104,7 @@ function extractSemver(name: string): string { } async function getLatestMetadataDocVersion( - metadataDocName: 'quote' | 'referrer' | 'orderClass' | 'utm' | 'hooks' | 'signer' | 'widget' | 'partnerFee' + metadataDocName: 'quote' | 'referrer' | 'orderClass' | 'utm' | 'hooks' | 'signer' | 'widget' | 'partnerFee' | 'replacedOrder' ): Promise { const metadataPath = path.join(SCHEMAS_SRC_PATH, metadataDocName) const versions = await fs.promises.readdir(metadataPath) diff --git a/test/schema.spec.ts b/test/schema.spec.ts index bf9487c..9e3c1f6 100644 --- a/test/schema.spec.ts +++ b/test/schema.spec.ts @@ -10,6 +10,7 @@ import schemaV0_9_0 from '../schemas/v0.9.0.json' import schemaV0_10_0 from '../schemas/v0.10.0.json' import schemaV0_11_0 from '../schemas/v0.11.0.json' import schemaV1_0_0 from '../schemas/v1.0.0.json' +import schemaV1_1_0 from '../schemas/v1.1.0.json' const ADDRESS = '0xb6BAd41ae76A11D10f7b0E664C5007b908bC77C9' const REFERRER_V0_1_0 = { address: ADDRESS, version: '0.1.0' } @@ -886,6 +887,65 @@ describe('Schema v1.0.0: Update quote definition', () => { ) }) +describe('Schema v1.1.0: Add replaced order', () => { + const ajv = new Ajv() + const validator = ajv.compile(schemaV1_1_0) + + const BASE_DOCUMENT = { + version: '1.1.0', + metadata: {}, + } + + test( + 'Valid order id', + _buildAssertValidFn(validator, { + ...BASE_DOCUMENT, + metadata: { replacedOrder: { uid: "0xff2e2e54d178997f173266817c1e9ed6fee1a1aae4b43971c53b543cffcc2969845c6f5599fbb25dbdd1b9b013daf85c03f3c63763e4bc4a" } }, + }) + ) + + + test( + 'Invalid order id length', + _buildAssertInvalidFn( + validator, + { + ...BASE_DOCUMENT, + metadata: { replacedOrder: { uid: "0xgogogog" } }, + }, + [ + { + instancePath: '/metadata/replacedOrder/uid', + keyword: "pattern", + message: "must match pattern \"^0x[a-fA-F0-9]{112}$\"", + params: { pattern: "^0x[a-fA-F0-9]{112}$" }, + schemaPath: '#/properties/metadata/properties/replacedOrder/properties/uid/pattern', + }, + ] + ) + ) + + test( + 'Invalid order id length', + _buildAssertInvalidFn( + validator, + { + ...BASE_DOCUMENT, + metadata: { replacedOrder: { } }, + }, + [ + { + instancePath: '/metadata/replacedOrder', + keyword: "required", + message: "must have required property 'uid'", + params: { missingProperty: "uid" }, + schemaPath: '#/properties/metadata/properties/replacedOrder/required', + }, + ] + ) + ) +}) + function _buildAssertValidFn(validator: ValidateFunction, doc: any) { return () => { // when