This repository was archived by the owner on Jun 24, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* copy state/index to custom * create sample of Operator actions * create sample of Operator reducer * add operator reducer to state * create sample of Operator hooks * changeto absolute import { AppDispatch, AppState } from 'state/' * change to absolute import store from 'state/' * minor organisational changes * from 'state/' -> from 'state' * special var for UNISWAP_REDUCERS * simplify types * fix import error * create some actions for Orders * create some reducers for Orders * hook up Orders reducer * create some hooks for Orders * differentiate orders by chainId * add clearOrders action * expand Orders types * fix orders/hooks * add useClearOrders hook * wrap hook function returns in useCallback * scaffold orders/updater * don't use const enum * fix build * remove unnecessary stuff * setup mock event watcher * extend types * use mock EventUpdater * change type names * try different decoding methods * improve event watcher * better types * normalize decoder interface * differentiate Orders by status * fullfillOrder action + reducer * hooks for different Orders by state * useFulfillOrder hook * persist state.orders in localStorage * separate pending and fulfilled orders more * prefill optional state for convenience * rename action to explicitly say pendingOrder * refactorhooks fornew state shape * rename some types * fix build * remove updater for now * move isTruthy to utils * remove Updater reference * OrderKind enum in line with api * more comments for types * OrderStatus enum values as strings,for readability when serialized * add Order.summary prop Co-authored-by: David Sato <[email protected]>
- Loading branch information
Showing
5 changed files
with
252 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { createAction } from '@reduxjs/toolkit' | ||
import { ChainId } from '@uniswap/sdk' | ||
|
||
enum OrderKind { | ||
SELL = 'sell', | ||
BUY = 'buy' | ||
} | ||
|
||
// posted to /api/v1/orders on Order creation | ||
// serializable, so no BigNumbers | ||
export interface OrderCreation { | ||
sellToken: string // address, without '0x' prefix | ||
buyToken: string // address, without '0x' prefix | ||
sellAmount: string // in atoms | ||
buyAmount: string // in atoms | ||
validTo: number // unix timestamp, seconds, use new Date(validTo * 1000) | ||
appData: number // arbitrary identifier sent along with the order | ||
tip: string // in atoms | ||
orderType: OrderKind | ||
partiallyFillable: boolean | ||
signature: string // 65 bytes encoded as hex without `0x` prefix. v + r + s from the spec | ||
} | ||
|
||
export enum OrderStatus { | ||
PENDING = 'pending', | ||
FULFILLED = 'fulfilled' | ||
} | ||
|
||
// used internally by dapp | ||
export interface Order extends OrderCreation { | ||
id: OrderID // it is special :), Unique identifier for the order: 56 bytes encoded as hex without 0x | ||
owner: string // address, without '0x' prefix | ||
status: OrderStatus | ||
fulfillmentTime?: string | ||
creationTime: string | ||
summary: string // for dapp use only, readable by user | ||
} | ||
|
||
// gotten from querying /api/v1/orders | ||
export interface OrderFromApi extends OrderCreation { | ||
creationTime: string // Creation time of the order. Encoded as ISO 8601 UTC | ||
owner: string // address, without '0x' prefix | ||
} | ||
|
||
/** | ||
* Unique identifier for the order, calculated by keccak256(orderDigest, ownerAddress, validTo), | ||
where orderDigest = keccak256(orderStruct). bytes32. | ||
*/ | ||
export type OrderID = string | ||
|
||
export const addPendingOrder = createAction<{ id: OrderID; chainId: ChainId; order: Order }>('order/addPendingOrder') | ||
export const removeOrder = createAction<{ id: OrderID; chainId: ChainId }>('order/removeOrder') | ||
// fulfillmentTime from event timestamp | ||
export const fulfillOrder = createAction<{ id: OrderID; chainId: ChainId; fulfillmentTime: string }>( | ||
'order/fulfillOrder' | ||
) | ||
export const clearOrders = createAction<{ chainId: ChainId }>('order/clearOrders') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import { ChainId } from '@uniswap/sdk' | ||
import { useCallback, useMemo } from 'react' | ||
import { useDispatch, useSelector } from 'react-redux' | ||
|
||
import { AppDispatch, AppState } from 'state' | ||
import { addPendingOrder, removeOrder, clearOrders, fulfillOrder, Order, OrderID } from './actions' | ||
import { OrdersState, PartialOrdersMap } from './reducer' | ||
import { isTruthy } from 'utils/misc' | ||
|
||
interface AddPendingOrderParams extends GetRemoveOrderParams { | ||
order: Order | ||
} | ||
|
||
interface FulfillOrderParams extends GetRemoveOrderParams { | ||
fulfillmentTime: string | ||
} | ||
interface GetRemoveOrderParams { | ||
id: OrderID | ||
chainId: ChainId | ||
} | ||
|
||
interface ClearOrdersParams { | ||
chainId: ChainId | ||
} | ||
|
||
type GetOrdersParams = Pick<GetRemoveOrderParams, 'chainId'> | ||
|
||
type AddOrderCallback = (addOrderParams: AddPendingOrderParams) => void | ||
type RemoveOrderCallback = (clearOrderParams: GetRemoveOrderParams) => void | ||
type FulfillOrderCallback = (fulfillOrderParams: FulfillOrderParams) => void | ||
type ClearOrdersCallback = (clearOrdersParams: ClearOrdersParams) => void | ||
|
||
export const useOrder = ({ id, chainId }: GetRemoveOrderParams): Order | undefined => { | ||
const state = useSelector<AppState, OrdersState[ChainId]>(state => state.orders[chainId]) | ||
|
||
return state?.fulfilled[id]?.order || state?.pending[id]?.order | ||
} | ||
|
||
export const useOrders = ({ chainId }: GetOrdersParams): Order[] => { | ||
const state = useSelector<AppState, OrdersState[ChainId]>(state => state.orders?.[chainId]) | ||
|
||
return useMemo(() => { | ||
if (!state) return [] | ||
|
||
const allOrders = Object.values(state.fulfilled) | ||
.concat(Object.values(state.pending)) | ||
.map(orderObject => orderObject?.order) | ||
.filter(isTruthy) | ||
return allOrders | ||
}, [state]) | ||
} | ||
|
||
export const usePendingOrders = ({ chainId }: GetOrdersParams): Order[] => { | ||
const state = useSelector<AppState, PartialOrdersMap | undefined>(state => state.orders?.[chainId]?.pending) | ||
|
||
return useMemo(() => { | ||
if (!state) return [] | ||
|
||
const allOrders = Object.values(state) | ||
.map(orderObject => orderObject?.order) | ||
.filter(isTruthy) | ||
return allOrders | ||
}, [state]) | ||
} | ||
|
||
export const useFulfilledOrders = ({ chainId }: GetOrdersParams): Order[] => { | ||
const state = useSelector<AppState, PartialOrdersMap | undefined>(state => state.orders?.[chainId]?.fulfilled) | ||
|
||
return useMemo(() => { | ||
if (!state) return [] | ||
|
||
const allOrders = Object.values(state) | ||
.map(orderObject => orderObject?.order) | ||
.filter(isTruthy) | ||
return allOrders | ||
}, [state]) | ||
} | ||
|
||
export const useAddPendingOrder = (): AddOrderCallback => { | ||
const dispatch = useDispatch<AppDispatch>() | ||
return useCallback((addOrderParams: AddPendingOrderParams) => dispatch(addPendingOrder(addOrderParams)), [dispatch]) | ||
} | ||
|
||
export const useFulfillOrder = (): FulfillOrderCallback => { | ||
const dispatch = useDispatch<AppDispatch>() | ||
return useCallback((fulfillOrderParams: FulfillOrderParams) => dispatch(fulfillOrder(fulfillOrderParams)), [dispatch]) | ||
} | ||
|
||
export const useRemoveOrder = (): RemoveOrderCallback => { | ||
const dispatch = useDispatch<AppDispatch>() | ||
return useCallback((removeOrderParams: GetRemoveOrderParams) => dispatch(removeOrder(removeOrderParams)), [dispatch]) | ||
} | ||
|
||
export const useClearOrders = (): ClearOrdersCallback => { | ||
const dispatch = useDispatch<AppDispatch>() | ||
return useCallback((clearOrdersParams: ClearOrdersParams) => dispatch(clearOrders(clearOrdersParams)), [dispatch]) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import { createReducer, PayloadAction } from '@reduxjs/toolkit' | ||
import { ChainId } from '@uniswap/sdk' | ||
import { addPendingOrder, removeOrder, Order, OrderID, clearOrders, fulfillOrder, OrderStatus } from './actions' | ||
|
||
export interface OrderObject { | ||
id: OrderID | ||
order: Order | ||
} | ||
|
||
// {order uuid => OrderObject} mapping | ||
type OrdersMap = Record<OrderID, OrderObject> | ||
export type PartialOrdersMap = Partial<OrdersMap> | ||
|
||
export type OrdersState = { | ||
readonly [chainId in ChainId]?: { | ||
pending: PartialOrdersMap | ||
fulfilled: PartialOrdersMap | ||
} | ||
} | ||
|
||
interface PrefillStateRequired { | ||
chainId: ChainId | ||
} | ||
|
||
type Writable<T> = { | ||
-readonly [K in keyof T]: T[K] | ||
} | ||
|
||
// makes sure there's always an object at state[chainId], state[chainId].pending | .fulfilled | ||
function prefillState( | ||
state: Writable<OrdersState>, | ||
{ payload: { chainId } }: PayloadAction<PrefillStateRequired> | ||
): asserts state is Required<OrdersState> { | ||
// asserts that state[chainId].pending | .fulfilled is ok to access | ||
const stateAtChainId = state[chainId] | ||
|
||
if (!stateAtChainId) { | ||
state[chainId] = { | ||
pending: {}, | ||
fulfilled: {} | ||
} | ||
return | ||
} | ||
|
||
if (!stateAtChainId.pending) { | ||
stateAtChainId.pending = {} | ||
} | ||
|
||
if (!stateAtChainId.fulfilled) { | ||
stateAtChainId.fulfilled = {} | ||
} | ||
} | ||
|
||
const initialState: OrdersState = {} | ||
|
||
export default createReducer(initialState, builder => | ||
builder | ||
.addCase(addPendingOrder, (state, action) => { | ||
prefillState(state, action) | ||
const { order, id, chainId } = action.payload | ||
|
||
state[chainId].pending[id] = { order, id } | ||
}) | ||
.addCase(removeOrder, (state, action) => { | ||
prefillState(state, action) | ||
const { id, chainId } = action.payload | ||
delete state[chainId].pending[id] | ||
delete state[chainId].fulfilled[id] | ||
}) | ||
.addCase(fulfillOrder, (state, action) => { | ||
prefillState(state, action) | ||
const { id, chainId, fulfillmentTime } = action.payload | ||
|
||
const orderObject = state[chainId].pending[id] | ||
|
||
if (orderObject) { | ||
delete state[chainId].pending[id] | ||
|
||
orderObject.order.status = OrderStatus.FULFILLED | ||
orderObject.order.fulfillmentTime = fulfillmentTime | ||
|
||
state[chainId].fulfilled[id] = orderObject | ||
} | ||
}) | ||
.addCase(clearOrders, (state, action) => { | ||
const { chainId } = action.payload | ||
|
||
state[chainId] = { | ||
pending: {}, | ||
fulfilled: {} | ||
} | ||
}) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const isTruthy = <T>(value: T | null | undefined | false): value is T => !!value |