Skip to content

Commit

Permalink
Implement addOrdersAsync method
Browse files Browse the repository at this point in the history
  • Loading branch information
albrow committed Aug 22, 2019
1 parent 91d2533 commit 733dee3
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 7 deletions.
52 changes: 51 additions & 1 deletion browser/go/main.go
Expand Up @@ -4,6 +4,7 @@ package main

import (
"context"
"encoding/json"
"errors"
"syscall/js"
"time"
Expand Down Expand Up @@ -53,6 +54,7 @@ type MeshWrapper struct {
app *core.App
ctx context.Context
cancel context.CancelFunc
errChan chan error
orderEvents chan []*zeroex.OrderEvent
orderEventsSubscription event.Subscription
orderEventHandler js.Value
Expand Down Expand Up @@ -119,9 +121,33 @@ func NewMeshWrapper(config core.Config) (*MeshWrapper, error) {
func (cw *MeshWrapper) Start() error {
cw.orderEvents = make(chan []*zeroex.OrderEvent, orderEventsBufferSize)
cw.orderEventsSubscription = cw.app.SubscribeToOrderEvents(cw.orderEvents)
cw.errChan = make(chan error, 1)

// cw.app.Start blocks until there is an error or the app is closed, so we
// need to start it in a goroutine.
go func() {
cw.errChan <- cw.app.Start(cw.ctx)
}()

// Wait up to 1 second to see if cw.app.Start returns an error right away.
// If it does, it probably indicates a configuration error which we should
// return immediately.
startTimeout := 1 * time.Second
select {
case err := <-cw.errChan:
return err
case <-time.After(startTimeout):
break
}

// Otherwise listen for future events in a goroutine and return nil.
go func() {
for {
select {
case err := <-cw.errChan:
// core.App exited with an error.
// TODO(albrow): Handle this better.
panic(err)
case <-cw.ctx.Done():
return
case events := <-cw.orderEvents:
Expand All @@ -135,7 +161,25 @@ func (cw *MeshWrapper) Start() error {
}
}
}()
return cw.app.Start(cw.ctx)

return nil
}

func (cw *MeshWrapper) AddOrders(rawOrders js.Value) (js.Value, error) {
// HACK(albrow): There is a more effecient way to do this, but for now,
// just use JSON to convert to the Go type.
encodedOrders := js.Global().Get("JSON").Call("stringify", rawOrders).String()
var rawMessages []*json.RawMessage
if err := json.Unmarshal([]byte(encodedOrders), &rawMessages); err != nil {
return js.Undefined(), err
}
results, err := cw.app.AddOrders(rawMessages)
if err != nil {
return js.Undefined(), err
}
encodedResults, err := json.Marshal(results)
resultsJS := js.Global().Get("JSON").Call("parse", string(encodedResults))
return resultsJS, nil
}

func (cw *MeshWrapper) JSValue() js.Value {
Expand All @@ -152,6 +196,12 @@ func (cw *MeshWrapper) JSValue() js.Value {
cw.orderEventHandler = handler
return nil
}),
// addOrderAsync(orders: Array<SignedOrder>): Promise<ValidationResults>
"addOrdersAsync": js.FuncOf(func(this js.Value, args []js.Value) interface{} {
return wrapInPromise(func() (interface{}, error) {
return cw.AddOrders(args[0])
})
}),
})
}

Expand Down
73 changes: 68 additions & 5 deletions browser/ts/index.ts
Expand Up @@ -3,6 +3,7 @@ import { wasmBuffer } from './generated/wasm_buffer';
// Side-effect import
// Side-effects include adding an `fs` and `Go` property on the global object.
import './wasm_exec';
import { promises } from 'dns';

// The Go code sets certain global values and this is our only way of
// interacting with it. Define those values and their types here.
Expand Down Expand Up @@ -33,16 +34,67 @@ interface ZeroExMesh {
interface MeshWrapper {
startAsync(): Promise<void>;
setOrderEventsHandler(handler: (events: Array<OrderEvent>) => void): void;
addOrdersAsync(orders: Array<SignedOrder>): Promise<ValidationResults>;
}

export interface OrderEvent {
orderHash: string;
signedOrder: any;
signedOrder: SignedOrder;
kind: string;
fillableTakerAssetAmount: string;
txHashes: Array<string>;
}

// TODO(albrow): Use existing 0x types where possible instead of creating new
// ones.
export interface SignedOrder {
makerAddress: string;
makerAssetData: string;
makerAssetAmount: string;
makerFee: string;
takerAddress: string;
takerAssetData: string;
takerAssetAmount: string;
takerFee: string;
senderAddress: string;
exchangeAddress: string;
feeRecipientAddress: string;
expirationTimeSeconds: string;
salt: string;
signature: string;
}

export interface ValidationResults {
accepted: Array<AcceptedOrderInfo>;
rejected: Array<RejectedOrderInfo>;
}

export interface AcceptedOrderInfo {
orderHash: string;
signedOrder: SignedOrder;
fillableTakerAssetAmount: string;
isNew: boolean;
}

export interface RejectedOrderInfo {
orderHash: string;
signedOrder: SignedOrder;
kind: RejectedOrderKind;
status: RejectedOrderStatus;
}

export enum RejectedOrderKind {
ZeroExValidation = 'ZEROEX_VALIDATION',
MeshError = 'MESH_ERROR',
MeshValidation = 'MESH_VALIDATION',
CoordinatorError = 'COORDINATOR_ERROR',
}

export interface RejectedOrderStatus {
code: string;
message: string;
}

var isWasmLoaded = false;
const loadEventName = '0xmeshload';
window.addEventListener(loadEventName, () => {
Expand Down Expand Up @@ -75,6 +127,13 @@ export class Mesh {
}
}

setOrderEventsHandler(handler: (events: Array<OrderEvent>) => void) {
this._orderEventHandler = handler;
if (this._wrapper != undefined) {
this._wrapper.setOrderEventsHandler(this._orderEventHandler);
}
}

async startAsync(): Promise<void> {
await this._waitForLoadAsync();
this._wrapper = await zeroExMesh.newWrapperAsync(this._config);
Expand All @@ -84,11 +143,15 @@ export class Mesh {
return this._wrapper.startAsync();
}

setOrderEventsHandler(handler: (events: Array<OrderEvent>) => void) {
this._orderEventHandler = handler;
if (this._wrapper != undefined) {
this._wrapper.setOrderEventsHandler(this._orderEventHandler);
async addOrdersAsync(orders: Array<SignedOrder>): Promise<ValidationResults> {
await this._waitForLoadAsync();
if (this._wrapper == undefined) {
// If this is called after startAsync, this._wrapper is always
// defined. This check is here just in case and satisfies the
// compiler.
return Promise.reject(new Error('Mesh is still loading. Try again soon.'));
}
return this._wrapper.addOrdersAsync(orders);
}
}

Expand Down
24 changes: 23 additions & 1 deletion examples/browser/src/index.ts
@@ -1,4 +1,4 @@
import { Mesh, OrderEvent } from '@0x/mesh-browser';
import { Mesh, OrderEvent, SignedOrder } from '@0x/mesh-browser';

(async () => {
const mesh = new Mesh({
Expand All @@ -11,6 +11,28 @@ import { Mesh, OrderEvent } from '@0x/mesh-browser';
}
});
await mesh.startAsync();

// This order is for demonstration purposes only and is invalid. It will be
// rejected by Mesh. You can replace it with a valid order.
const order: SignedOrder = {
makerAddress: '0xa3eCE5D5B6319Fa785EfC10D3112769a46C6E149',
makerAssetData: '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498',
makerAssetAmount: '1000000000000000000',
makerFee: '0',
takerAddress: '0x0000000000000000000000000000000000000000',
takerAssetData: '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
takerAssetAmount: '10000000000000000000000',
takerFee: '0',
senderAddress: '0x0000000000000000000000000000000000000000',
exchangeAddress: '0x080bf510FCbF18b91105470639e9561022937712',
feeRecipientAddress: '0x0000000000000000000000000000000000000000',
expirationTimeSeconds: '1586340602',
salt: '41253767178111694375645046549067933145709740457131351457334397888365956743955',
signature:
'0x1c0827552a3bde2c72560362950a69f581ae7a1e6fa8c160bb437f3a61002bb96c22b646edd3b103b976db4aa4840a11c13306b2a02a0bb6ce647806c858c238ec03',
};
const result = await mesh.addOrdersAsync([order]);
console.log(result);
})().catch(err => {
console.error(err);
});

0 comments on commit 733dee3

Please sign in to comment.