Skip to content

Commit

Permalink
Small nit suggestions (#147)
Browse files Browse the repository at this point in the history
* Rename file

* Move types and add some nitpics

* Add smal nitpics for TWAP

* Add small nitpicks

* Change file names

* Change file names back to original

* Renamings and moving static utils

* Add encode decode params utils

* Add isValidAbi tests

* Change jsdoc

Co-authored-by: mfw78 <53399572+mfw78@users.noreply.github.com>

---------

Co-authored-by: mfw78 <53399572+mfw78@users.noreply.github.com>
  • Loading branch information
anxolin and mfw78 authored Aug 24, 2023
1 parent 0aad903 commit f878bdd
Show file tree
Hide file tree
Showing 12 changed files with 423 additions and 326 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BigNumber } from 'ethers'
import { BaseConditionalOrder } from './conditionalorder'
import { TWAP } from './types/twap'
import { ConditionalOrder } from './ConditionalOrder'
import { Twap } from './types/Twap'
import { encodeParams } from './utils'

const TWAP_SERIALIZED = (salt?: string, handler?: string): string => {
return (
Expand Down Expand Up @@ -49,10 +50,7 @@ describe('ConditionalOrder', () => {

test('Serialize: Fails if invalid params', () => {
const order = new TestConditionalOrder('0x910d00a310f7Dc5B29FE73458F47f519be547D3d')
expect(() => order.testEncodeStaticInput()).toThrow('SerializationFailed')
expect(() => BaseConditionalOrder.encodeParams({ handler: '0xdeadbeef', salt: '0x', staticInput: '0x' })).toThrow(
'SerializationFailed'
)
expect(() => order.testEncodeStaticInput()).toThrow()
})

test('id: Returns correct id', () => {
Expand All @@ -68,21 +66,25 @@ describe('ConditionalOrder', () => {
'0x910d00a310f7Dc5B29FE73458F47f519be547D3d',
'0x9379a0bf532ff9a66ffde940f94b1a025d6f18803054c1aef52dc94b15255bbe'
)
expect(BaseConditionalOrder.leafToId(order.leaf)).toEqual(
expect(ConditionalOrder.leafToId(order.leaf)).toEqual(
'0x88ca0698d8c5500b31015d84fa0166272e1812320d9af8b60e29ae00153363b3'
)
})

test('Deserialize: Fails if handler mismatch', () => {
expect(() => TWAP.deserialize(TWAP_SERIALIZED(undefined, '0x9008D19f58AAbD9eD0D60971565AA8510560ab41'))).toThrow(
expect(() => Twap.deserialize(TWAP_SERIALIZED(undefined, '0x9008D19f58AAbD9eD0D60971565AA8510560ab41'))).toThrow(
'HandlerMismatch'
)
})
})

class TestConditionalOrder extends BaseConditionalOrder<string, string> {
class TestConditionalOrder extends ConditionalOrder<string, string> {
constructor(address: string, salt?: string, staticInput = '0x') {
super(address, salt, staticInput)
super({
handler: address,
salt,
staticInput,
})
}

get orderType(): string {
Expand All @@ -97,13 +99,14 @@ class TestConditionalOrder extends BaseConditionalOrder<string, string> {
return super.encodeStaticInputHelper(['uint256'], this.staticInput)
}

transformParamsToData(params: string): string {
return params
}

isValid(_o: unknown): boolean {
throw new Error('Method not implemented.')
}
serialize(): string {
return BaseConditionalOrder.encodeParams(this.leaf)
}
toString(_tokenFormatter?: (address: string, amount: BigNumber) => string): string {
throw new Error('Method not implemented.')
return encodeParams(this.leaf)
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
import { BigNumber, ethers, utils } from 'ethers'
import { ContextFactory } from './multiplexer'
import { keccak256 } from 'ethers/lib/utils'
import { ComposableCoW__factory } from './generated'
import { IConditionalOrder } from './generated/ComposableCoW'

// Define the ABI tuple for the TWAPData struct
export const CONDITIONAL_ORDER_PARAMS_ABI = ['tuple(address handler, bytes32 salt, bytes staticInput)']

export type ConditionalOrderParams = {
readonly handler: string
readonly salt: string
readonly staticInput: string
}
import { ComposableCoW__factory } from './generated'
import { decodeParams, encodeParams } from './utils'
import { ConditionalOrderArguments, ConditionalOrderParams, ContextFactory } from './types'

/**
* An abstract base class from which all conditional orders should inherit.
Expand All @@ -27,16 +19,16 @@ export type ConditionalOrderParams = {
* **NOTE**: Instances of conditional orders have an `id` property that is a `keccak256` hash of
* the serialized conditional order.
*/
export abstract class BaseConditionalOrder<T, P> {
export abstract class ConditionalOrder<Data, Params> {
public readonly handler: string
public readonly salt: string
public readonly staticInput: T
public readonly staticInput: Data
public readonly hasOffChainInput: boolean

/**
* A constructor that provides some basic validation for the conditional order.
*
* This constructor **MUST** be called by any class that inherits from `BaseConditionalOrder`.
* This constructor **MUST** be called by any class that inherits from `ConditionalOrder`.
*
* **NOTE**: The salt is optional and will be randomly generated if not provided.
* @param handler The address of the handler for the conditional order.
Expand All @@ -46,12 +38,8 @@ export abstract class BaseConditionalOrder<T, P> {
* @throws If the handler is not a valid ethereum address.
* @throws If the salt is not a valid 32-byte string.
*/
constructor(
handler: string,
salt: string = keccak256(ethers.utils.randomBytes(32)),
staticInput: P,
hasOffChainInput = false
) {
constructor(params: ConditionalOrderArguments<Params>) {
const { handler, salt = utils.keccak256(utils.randomBytes(32)), staticInput, hasOffChainInput = false } = params
// Verify input to the constructor
// 1. Verify that the handler is a valid ethereum address
if (!ethers.utils.isAddress(handler)) {
Expand All @@ -70,7 +58,8 @@ export abstract class BaseConditionalOrder<T, P> {
}

/**
* Get the concrete type of the conditional order.
* Get a descriptive name for the type of the conditional order (i.e twap, dca, etc).
*
* @returns {string} The concrete type of the conditional order.
*/
abstract get orderType(): string
Expand Down Expand Up @@ -104,6 +93,7 @@ export abstract class BaseConditionalOrder<T, P> {
}

if (context) {
// Create (with context)
const contextArgsAbi = context.factoryArgs
? utils.defaultAbiCoder.encode(context.factoryArgs.argsType, context.factoryArgs.args)
: '0x'
Expand All @@ -114,6 +104,7 @@ export abstract class BaseConditionalOrder<T, P> {
true,
])
} else {
// Create
return composableCow.encodeFunctionData('create', [paramsStruct, true])
}
}
Expand All @@ -128,7 +119,7 @@ export abstract class BaseConditionalOrder<T, P> {
}

/**
* Calculate the id of the conditional order.
* Calculate the id of the conditional order (which also happens to be the key used for `ctx` in the ComposableCoW contract).
*
* This is a `keccak256` hash of the serialized conditional order.
* @returns The id of the conditional order.
Expand Down Expand Up @@ -159,7 +150,7 @@ export abstract class BaseConditionalOrder<T, P> {
* @see ConditionalOrderParams
*/
static leafToId(leaf: ConditionalOrderParams): string {
return utils.keccak256(BaseConditionalOrder.encodeParams(leaf))
return utils.keccak256(encodeParams(leaf))
}

/**
Expand All @@ -172,23 +163,9 @@ export abstract class BaseConditionalOrder<T, P> {
return '0x'
}

/**
* Helper method for validating ABI types.
* @param types ABI types to validate against.
* @param values The values to validate.
* @returns {boolean} Whether the values are valid ABI for the given types.
*/
protected static isValidAbi(types: readonly (string | ethers.utils.ParamType)[], values: any[]): boolean {
try {
ethers.utils.defaultAbiCoder.encode(types, values)
} catch (e) {
return false
}
return true
}

/**
* Create a human-readable string representation of the conditional order.
*
* @param tokenFormatter An optional function that takes an address and an amount and returns a human-readable string.
*/
abstract toString(tokenFormatter?: (address: string, amount: BigNumber) => string): string
Expand All @@ -202,63 +179,33 @@ export abstract class BaseConditionalOrder<T, P> {

/**
* Encode the `staticInput` for the conditional order.
*
* @returns The ABI-encoded `staticInput` for the conditional order.
* @see ConditionalOrderParams
*/
abstract encodeStaticInput(): string

/**
* A helper function for generically serializing a conditional order's static input.
*
* @param orderDataTypes ABI types for the order's data struct.
* @param staticInput The order's data struct.
* @returns An ABI-encoded representation of the order's data struct.
*/
protected encodeStaticInputHelper(orderDataTypes: string[], staticInput: T): string {
try {
return utils.defaultAbiCoder.encode(orderDataTypes, [staticInput])
} catch (e) {
throw new Error('SerializationFailed')
}
protected encodeStaticInputHelper(orderDataTypes: string[], staticInput: Data): string {
return utils.defaultAbiCoder.encode(orderDataTypes, [staticInput])
}

/**
* Apply any transformations to the parameters that are passed in to the constructor.
*
* **NOTE**: This should be overridden by any conditional order that requires transformations.
* This implementation is a no-op.
* @param params {P} Parameters that are passed in to the constructor.
* @returns {T} The static input for the conditional order.
*/
protected transformParamsToData(params: P): T {
return params as unknown as T
}

/**
* Encode the `ConditionalOrderParams` for the conditional order.
* @param leaf The `ConditionalOrderParams` struct representing the conditional order as taken from a merkle tree.
* @returns The ABI-encoded conditional order.
* @see ConditionalOrderParams
*/
static encodeParams(leaf: ConditionalOrderParams): string {
try {
return utils.defaultAbiCoder.encode(CONDITIONAL_ORDER_PARAMS_ABI, [leaf])
} catch (e) {
throw new Error('SerializationFailed')
}
}

/**
* Decode the `ConditionalOrderParams` for the conditional order.
* @param encoded The encoded conditional order.
* @returns The decoded conditional order.
*
* @param params {Params} Parameters that are passed in to the constructor.
* @returns {Data} The static input for the conditional order.
*/
static decodeParams(encoded: string): ConditionalOrderParams {
try {
return utils.defaultAbiCoder.decode(CONDITIONAL_ORDER_PARAMS_ABI, encoded)[0]
} catch (e) {
throw new Error('DeserializationFailed')
}
}
abstract transformParamsToData(params: Params): Data

/**
* A helper function for generically deserializing a conditional order.
Expand All @@ -276,7 +223,7 @@ export abstract class BaseConditionalOrder<T, P> {
): T {
try {
// First, decode the `IConditionalOrder.Params` struct
const { handler: recoveredHandler, salt, staticInput } = BaseConditionalOrder.decodeParams(s)
const { handler: recoveredHandler, salt, staticInput } = decodeParams(s)

// Second, verify that the recovered handler is the correct handler
if (!(recoveredHandler == handler)) throw new Error('HandlerMismatch')
Expand Down
Loading

0 comments on commit f878bdd

Please sign in to comment.