@puppet.fund/operator 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -47
- package/dist/{core-DC0-qJhv.d.ts → core-DlV1H6H-.d.ts} +41 -23
- package/dist/gmx/index.d.ts +4 -5
- package/dist/gmx/index.js +24 -13
- package/dist/{gmx-DwTiknYm.js → gmx-C0vgbHXM.js} +8317 -117
- package/dist/index.d.ts +9 -10
- package/dist/index.js +283 -8025
- package/package.json +1 -5
package/README.md
CHANGED
|
@@ -24,36 +24,39 @@ The package is split by operator, so you choose yours:
|
|
|
24
24
|
|
|
25
25
|
- **`@puppet.fund/operator/gmx`** — the ready-made GMX V2 venue (`gmxOperator(core)`), wrapping a
|
|
26
26
|
core you build. What the scaffolder sets up, and what most users want.
|
|
27
|
-
- **`@puppet.fund/operator`** — the generic, venue-agnostic core. `
|
|
28
|
-
gives you
|
|
27
|
+
- **`@puppet.fund/operator`** — the generic, venue-agnostic core. `pairOverBrowser()` →
|
|
28
|
+
`createOperatorCore(session)` gives you the RPC client, account + fund prediction and
|
|
29
29
|
deploy checks, the matchmaker compact, the generic `operate(callList)` dispatch,
|
|
30
|
-
`getFundBalance()`, and connection lifecycle (`
|
|
30
|
+
`getFundBalance()`, and connection lifecycle (`onStatus()` / `isOpen()` / `close()`). Compose it
|
|
31
31
|
with your own venue call shapes to build an extended operator — a different venue, base token,
|
|
32
32
|
or surface. The GMX venue is built on exactly this. Also here: `runOperator`,
|
|
33
|
-
`pairOverBrowser`, `createCompact`, `TOKEN_ID`, and the compact error helpers. Type your
|
|
33
|
+
`pairOverBrowser`, `buildSession`, `createCompact`, `TOKEN_ID`, and the compact error helpers. Type your
|
|
34
34
|
operator against `IOperatorCore`.
|
|
35
35
|
|
|
36
36
|
## Usage
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
the public Arbitrum endpoint (set `rpcUrl`
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
`pairOverBrowser()` returns the **session** — the whole sealed identity (account params, the fund
|
|
39
|
+
and its base token, the matchmaker endpoint, the token registry). Pass it to `createOperatorCore`
|
|
40
|
+
to build the **core** (signing, the matchmaker compact, your account + fund, lifecycle), then wrap
|
|
41
|
+
it in the GMX venue. The core does the async setup and owns the connection; `gmxOperator(core)` is
|
|
42
|
+
synchronous and adds only GMX execution + reads — so you call connection/account actions on `core`,
|
|
43
|
+
GMX actions on `gmx`. RPC defaults to the public Arbitrum endpoint (set `rpcUrl` beyond a first
|
|
44
|
+
run). The agent trades whatever base the paired fund settles in (the session names it) — WETH is
|
|
45
|
+
the simplest (GMX's keeper fee is the same token, carved straight from the collateral, so the fund
|
|
46
|
+
needs no native ETH); USDC or native ETH work too, but a non-WETH base also needs a little ETH in
|
|
47
|
+
the fund for the GMX execution fee.
|
|
46
48
|
|
|
47
49
|
```ts
|
|
48
|
-
import { createOperatorCore, runOperator } from '@puppet.fund/operator'
|
|
49
|
-
import {
|
|
50
|
+
import { createOperatorCore, pairOverBrowser, runOperator } from '@puppet.fund/operator'
|
|
51
|
+
import { gmxOperator } from '@puppet.fund/operator/gmx'
|
|
50
52
|
|
|
51
|
-
//
|
|
52
|
-
const
|
|
53
|
+
// Pairing carries everything: identity, fund + base token, matchmaker endpoint, registry.
|
|
54
|
+
const session = await pairOverBrowser() // a PAIR_URL for a local site, else https://puppet.fund
|
|
55
|
+
const core = await createOperatorCore(session, { rpcUrl: Bun.env.ARBITRUM_RPC_URL })
|
|
53
56
|
const gmx = gmxOperator(core)
|
|
54
57
|
|
|
55
|
-
//
|
|
56
|
-
//
|
|
58
|
+
// Headless (no browser): build the session yourself —
|
|
59
|
+
// buildSession({ signerKey, user, baseTokenId, name }) — then createOperatorCore(session).
|
|
57
60
|
|
|
58
61
|
const deployable = await core.getFundBalance() // core: account + connection
|
|
59
62
|
const positions = await gmx.getPositions() // gmx: venue reads
|
|
@@ -80,25 +83,30 @@ Two addresses matter, both deterministic and printed at startup:
|
|
|
80
83
|
|
|
81
84
|
## Surface
|
|
82
85
|
|
|
83
|
-
- `
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
`
|
|
92
|
-
`
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
86
|
+
- `pairOverBrowser(pairUrl?)` (async, package root) — opens a one-time `127.0.0.1` listener and
|
|
87
|
+
prints a link to open on the site; returns the sealed `IPairedSession`: `params` ({user,
|
|
88
|
+
signer}), `share` ({master, baseTokenId, name}), `matchmakerUrl`, and the token registry. For
|
|
89
|
+
unattended runs build the same shape with `buildSession({ signerKey, user, baseTokenId, name })`.
|
|
90
|
+
- `createOperatorCore(session, config?)` (async, package root) — takes a session, sets up the
|
|
91
|
+
matchmaker compact, predicts + checks the account and the fund (asserting the session's
|
|
92
|
+
identity is internally consistent), and returns the connection + account surface you call
|
|
93
|
+
directly: `getFundBalance()` (live base in the fund), `getFundSignedBalance()` (the signed
|
|
94
|
+
portion, on-chain), `isOpen()` (is the matchmaker connected — gate a tick on it), `onStatus()`,
|
|
95
|
+
`operate(callList, transferList?)` (the generic signed-call dispatch under the venue surface —
|
|
96
|
+
declare value the calls move as transfer legs; the default 0/0 leg fits value-neutral
|
|
97
|
+
dispatches, and the protocol attestor co-signs only calls inside the venue perimeter it can
|
|
98
|
+
screen, so arbitrary targets are rejected), and `close()`; plus `params`, `share`, `account`,
|
|
99
|
+
`fund`, `signer`, `user`, `token`, `baseTokenId`, `publicClient`, `compact`. Type it as
|
|
100
|
+
`IOperatorCore`. The base token is the paired fund's (`share.baseTokenId`); the matchmaker
|
|
101
|
+
endpoint and registry come from the session too. The operator needs no indexer: intent block
|
|
102
|
+
anchors arrive as head frames on the relay socket, balances and settlement are read from the
|
|
103
|
+
chain. `config.rpcUrl` defaults to the public Arbitrum endpoint. `config.dryRun: true` runs
|
|
104
|
+
every dispatch through the identical local verification + venue screen the matchmaker applies,
|
|
105
|
+
logs what would be sent, and skips the dispatch — the way to watch a strategy decide without
|
|
106
|
+
risking funds. The session signer stays private to the core — you get signed intents, not the key.
|
|
107
|
+
- `gmxOperator(core, opts?)` (sync, `@puppet.fund/operator/gmx`) — the GMX venue over a core whose
|
|
108
|
+
fund settles in a GMX-tradable base (WETH is simplest; `GMX_BASE_TOKEN_ID` is its id). Adds
|
|
109
|
+
GMX-only surface. Reads: `getMarket(indexToken)` (the canonical perp for the base token;
|
|
102
110
|
throws only if still ambiguous), `getPositions(account?)` (defaults to your fund; pass any GMX
|
|
103
111
|
account to read someone else's), `getOrders(account?)`, `markets`. Writes: `createOrder(...)` —
|
|
104
112
|
the one primitive for every increase/decrease order type (market/limit/stop/take-profit) via
|
|
@@ -110,19 +118,18 @@ Two addresses matter, both deterministic and printed at startup:
|
|
|
110
118
|
(size/collateral/margin incl. unrealized PnL, valuing WETH or USDC collateral correctly).
|
|
111
119
|
`opts.executionFeeBufferBps` (default 2000 = 20%) pads the auto-quote. It does NOT re-expose
|
|
112
120
|
core methods — call those on `core`.
|
|
113
|
-
- Lifecycle + building blocks (`runOperator`, `pairOverBrowser`, `
|
|
114
|
-
types + error helpers) live at the package root — see **Entrypoints** above.
|
|
115
|
-
`
|
|
116
|
-
`runOperator(core, body)`.
|
|
121
|
+
- Lifecycle + building blocks (`runOperator`, `pairOverBrowser`, `buildSession`, `createCompact`,
|
|
122
|
+
`TOKEN_ID`, compact types + error helpers) live at the package root — see **Entrypoints** above.
|
|
123
|
+
A GMX operator is `pairOverBrowser()` → `createOperatorCore(session)` → `gmxOperator(core)`, run
|
|
124
|
+
under `runOperator(core, body)`.
|
|
117
125
|
|
|
118
126
|
## Pairing
|
|
119
127
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
memory only, never written to disk
|
|
124
|
-
|
|
125
|
-
first).
|
|
128
|
+
`pairOverBrowser()` opens a one-time `127.0.0.1` listener and prints a link you open on the site.
|
|
129
|
+
The browser seals the session — your session key, the fund identity (`params` + `share`), the
|
|
130
|
+
matchmaker endpoint, and the token registry — to an ephemeral key in that link and posts it back.
|
|
131
|
+
The key reaches the operator in memory only, never written to disk. Your account and fund must
|
|
132
|
+
already exist (create the account and allocate the fund on the site first).
|
|
126
133
|
|
|
127
134
|
The browser seals the *derived* session key, never your wallet's bind signature, so a
|
|
128
135
|
paired operator can sign operate intents but cannot deploy accounts under you.
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { Address, Hex, PublicClient } from "viem";
|
|
2
|
-
import { IStream } from "aelea/stream";
|
|
3
|
-
import { Client } from "graphql-ws";
|
|
4
2
|
//#region ../contracts/dist/types/types/index.d.ts
|
|
5
3
|
interface IAccount__CreatePuppetAccountIntent {
|
|
6
4
|
params: IAccountLib__AccountInitParams;
|
|
@@ -181,16 +179,28 @@ interface ISubscribe__SubscribeIntent {
|
|
|
181
179
|
//#endregion
|
|
182
180
|
//#region ../contracts/dist/types/const/index.d.ts
|
|
183
181
|
declare const TOKEN_ID: {
|
|
182
|
+
readonly ETH: '0xaaaebeba3810b1e6b70781f14b2d72c1cb89c0b2b320c43bb67ff79f562f5ff4';
|
|
184
183
|
readonly USDC: '0xd6aca1be9729c13d677335161321649cccae6a591554772516700f986f942eaa';
|
|
185
184
|
readonly WETH: '0x0f8a193ff464434486c0daf7db2a895884365d2bc84ba47a68fcf89c1b14b5b8';
|
|
186
185
|
};
|
|
187
186
|
//#endregion
|
|
188
|
-
//#region
|
|
189
|
-
interface
|
|
190
|
-
|
|
191
|
-
|
|
187
|
+
//#region ../../indexer/script/__generated/entities.d.ts
|
|
188
|
+
interface IRegisterToken__RegisterToken {
|
|
189
|
+
id: string;
|
|
190
|
+
chainId: bigint;
|
|
191
|
+
tokenId: Hex;
|
|
192
|
+
token: Address;
|
|
193
|
+
cap: bigint;
|
|
194
|
+
hubToken: Address;
|
|
195
|
+
blockTimestamp: number;
|
|
196
|
+
blockNumber: bigint;
|
|
197
|
+
logIndex: number;
|
|
198
|
+
transactionHash: Hex;
|
|
192
199
|
}
|
|
193
200
|
//#endregion
|
|
201
|
+
//#region ../sdk/dist/types/state/tokenRegistry.d.ts
|
|
202
|
+
type ITokenInfo = IRegisterToken__RegisterToken;
|
|
203
|
+
//#endregion
|
|
194
204
|
//#region ../sdk/dist/types/attestation/shared.d.ts
|
|
195
205
|
interface IIntentByKind {
|
|
196
206
|
subscribe: ISubscribe__SubscribeIntent;
|
|
@@ -433,21 +443,24 @@ type IRelayRequest<K extends IActionKind = IActionKind> = K extends IActionKind
|
|
|
433
443
|
intent: IIntentByKind[K];
|
|
434
444
|
signature: Hex;
|
|
435
445
|
} : never;
|
|
436
|
-
interface
|
|
446
|
+
interface IDispatchedFrame {
|
|
447
|
+
chainId: bigint;
|
|
448
|
+
account: Address;
|
|
449
|
+
nonce: bigint;
|
|
437
450
|
txHash: Hex;
|
|
438
451
|
actualRelayFee: bigint;
|
|
439
452
|
}
|
|
440
453
|
type IMatchmakerStatus = 'open' | 'connecting' | 'closed';
|
|
441
454
|
interface ICompactOpts {
|
|
442
455
|
matchmakerUrl: string;
|
|
443
|
-
sql: IIndexerClient;
|
|
444
456
|
defaultTimeoutMs?: number;
|
|
445
|
-
settlementTimeoutMs?: number;
|
|
446
457
|
}
|
|
447
458
|
interface ICompact {
|
|
448
|
-
attest(request: IRelayRequest, timeoutMs?: number): Promise<
|
|
449
|
-
status:
|
|
459
|
+
attest(request: IRelayRequest, timeoutMs?: number): Promise<IDispatchedFrame>;
|
|
460
|
+
onStatus(cb: (status: IMatchmakerStatus) => void): () => void;
|
|
450
461
|
isOpen(): boolean;
|
|
462
|
+
head(network: string): bigint | undefined;
|
|
463
|
+
awaitHead(network: string, timeoutMs?: number): Promise<bigint>;
|
|
451
464
|
close(): void;
|
|
452
465
|
}
|
|
453
466
|
declare function createCompact(opts: ICompactOpts): ICompact;
|
|
@@ -2952,19 +2965,23 @@ declare function humanizeErrorCode(code: string): string;
|
|
|
2952
2965
|
declare function humanizeContractError(code: string, args: readonly unknown[]): string;
|
|
2953
2966
|
declare function formatThrownError(err: unknown): string;
|
|
2954
2967
|
//#endregion
|
|
2968
|
+
//#region ../sdk/dist/types/account/pairing.d.ts
|
|
2969
|
+
interface IPairedSession {
|
|
2970
|
+
signerKey: Hex;
|
|
2971
|
+
params: IAccountLib__AccountInitParams;
|
|
2972
|
+
share: IShareLib__ShareInitParams;
|
|
2973
|
+
matchmakerUrl: string;
|
|
2974
|
+
tokenRegistry?: ITokenInfo[];
|
|
2975
|
+
}
|
|
2976
|
+
//#endregion
|
|
2955
2977
|
//#region src/core.d.ts
|
|
2956
2978
|
interface IOperatorConfig {
|
|
2957
|
-
baseTokenId: Hex;
|
|
2958
|
-
matchmakerUrl?: string;
|
|
2959
|
-
indexerUrl?: string;
|
|
2960
2979
|
rpcUrl?: string;
|
|
2961
|
-
user?: Address;
|
|
2962
|
-
signerKey?: Hex;
|
|
2963
|
-
siteUrl?: string;
|
|
2964
|
-
pairPort?: number;
|
|
2965
2980
|
dryRun?: boolean;
|
|
2966
2981
|
}
|
|
2967
2982
|
interface IOperatorCore {
|
|
2983
|
+
params: IAccountLib__AccountInitParams;
|
|
2984
|
+
share: IShareLib__ShareInitParams;
|
|
2968
2985
|
user: Address;
|
|
2969
2986
|
signer: Address;
|
|
2970
2987
|
account: Address;
|
|
@@ -2972,15 +2989,16 @@ interface IOperatorCore {
|
|
|
2972
2989
|
baseTokenId: Hex;
|
|
2973
2990
|
token: Address;
|
|
2974
2991
|
publicClient: PublicClient;
|
|
2975
|
-
sql: IIndexerClient;
|
|
2976
2992
|
compact: ICompact;
|
|
2977
|
-
|
|
2993
|
+
onStatus: ICompact['onStatus'];
|
|
2994
|
+
tokenIdFor: (token: Address) => Hex;
|
|
2995
|
+
readSignedBalance: (tokenId: Hex) => Promise<bigint>;
|
|
2978
2996
|
isOpen: () => boolean;
|
|
2979
2997
|
getFundBalance: () => Promise<bigint>;
|
|
2980
2998
|
getFundSignedBalance: () => Promise<bigint>;
|
|
2981
|
-
operate: (callList: IIAccount__Call[], transferList?: IIAccount__SignTransfer[]) => Promise<
|
|
2999
|
+
operate: (callList: IIAccount__Call[], transferList?: IIAccount__SignTransfer[]) => Promise<IDispatchedFrame>;
|
|
2982
3000
|
close: () => void;
|
|
2983
3001
|
}
|
|
2984
|
-
declare function createOperatorCore(
|
|
3002
|
+
declare function createOperatorCore(session: IPairedSession, config?: IOperatorConfig): Promise<IOperatorCore>;
|
|
2985
3003
|
//#endregion
|
|
2986
|
-
export {
|
|
3004
|
+
export { CompactContractError as a, humanizeContractError as c, ICompactOpts as d, IDispatchedFrame as f, TOKEN_ID as g, ITokenInfo as h, IPairedSession as i, humanizeErrorCode as l, createCompact as m, IOperatorCore as n, CompactError as o, IMatchmakerStatus as p, createOperatorCore as r, formatThrownError as s, IOperatorConfig as t, ICompact as u };
|
package/dist/gmx/index.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { f as IDispatchedFrame, n as IOperatorCore } from "../core-DlV1H6H-.js";
|
|
2
2
|
import { Address, Hex, PublicClient } from "viem";
|
|
3
|
-
import { IStream } from "aelea/stream";
|
|
4
3
|
//#region ../sdk/dist/types/venue/gmx.d.ts
|
|
5
4
|
declare const GMX_ORDER_TYPE: {
|
|
6
5
|
readonly MarketSwap: 0;
|
|
@@ -994,9 +993,9 @@ declare function gmxOperator(core: IOperatorCore, opts?: IGmxOptions): {
|
|
|
994
993
|
_dataList: readonly `0x${string}`[];
|
|
995
994
|
};
|
|
996
995
|
}[]>;
|
|
997
|
-
createOrder: (p: IGmxOrder) => Promise<
|
|
998
|
-
cancelOrder(key: Hex): Promise<
|
|
999
|
-
updateOrder(p: IUpdateOrder): Promise<
|
|
996
|
+
createOrder: (p: IGmxOrder) => Promise<IDispatchedFrame>;
|
|
997
|
+
cancelOrder(key: Hex): Promise<IDispatchedFrame>;
|
|
998
|
+
updateOrder(p: IUpdateOrder): Promise<IDispatchedFrame>;
|
|
1000
999
|
};
|
|
1001
1000
|
//#endregion
|
|
1002
1001
|
export { GMX_BASE_TOKEN_ID, GMX_DECREASE_SWAP_TYPE, GMX_ORDER_TYPE, type IGasLimitsConfig, IGmxOptions, type IGmxOrder, IGmxPositionMetrics, IGmxPositionView, IUpdateOrder, acceptablePrice, calculateExecutionFee, dominantPosition, formatUsd, formatWeth, getGasLimitsConfig, getPositionPnlUsd, gmxOperator, gmxPrice, positionMetrics, usd, weth };
|
package/dist/gmx/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { encodeFunctionData, formatUnits, isAddressEqual, parseUnits } from "viem";
|
|
1
|
+
import { S as symbolForBaseTokenId, d as selectGmxMarket, f as getPositionPnlUsd, h as GMX_V2_CONTRACT_MAP, k as TOKEN_ID, l as buildGmxOrderCalls, m as ARBITRUM_MARKET_LIST, n as GMX_INCREASE_TYPES, p as applyFactor, r as GMX_ORDER_TYPE, t as GMX_DECREASE_SWAP_TYPE } from "../gmx-C0vgbHXM.js";
|
|
2
|
+
import { encodeFunctionData, erc20Abi, formatUnits, getAddress, isAddressEqual, parseUnits, zeroAddress } from "viem";
|
|
3
3
|
import { encodeAbiParameters as encodeAbiParameters$1, keccak256 as keccak256$1, parseAbiParameters } from "viem/utils";
|
|
4
4
|
//#region ../sdk/dist/esm/gmx/gmxUtils.js
|
|
5
5
|
function hashData(types, values) {
|
|
@@ -80,7 +80,6 @@ function positionMetrics(p, markPrice, longToken, shortTokenDecimals = 6) {
|
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
82
|
function gmxOperator(core, opts = {}) {
|
|
83
|
-
if (!isAddressEqual(core.token, CHAIN_TOKEN_MAP[42161].WETH)) throw new Error("gmxOperator needs a WETH-based core — create it with baseTokenId GMX_BASE_TOKEN_ID");
|
|
84
83
|
const { operate, publicClient, token, fund } = core;
|
|
85
84
|
const executionFeeBufferBps = opts.executionFeeBufferBps ?? 2000n;
|
|
86
85
|
let gasLimitsConfig = null;
|
|
@@ -91,20 +90,32 @@ function gmxOperator(core, opts = {}) {
|
|
|
91
90
|
return calculateExecutionFee(gasLimitsConfig, gasPrice, actionGasLimit) * (10000n + executionFeeBufferBps) / 10000n;
|
|
92
91
|
}
|
|
93
92
|
async function createOrder(p) {
|
|
94
|
-
const { callList,
|
|
93
|
+
const { callList, outflows } = buildGmxOrderCalls(p, {
|
|
95
94
|
master: fund,
|
|
96
95
|
baseToken: token,
|
|
97
96
|
executionFee: p.executionFee ?? await quoteExecutionFee(p.orderType)
|
|
98
97
|
});
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
98
|
+
return operate(callList, await Promise.all(outflows.map(async (o) => {
|
|
99
|
+
const outToken = getAddress(o.token);
|
|
100
|
+
const tokenId = core.tokenIdFor(outToken);
|
|
101
|
+
const isNative = isAddressEqual(outToken, zeroAddress);
|
|
102
|
+
const [balance, signed] = await Promise.all([isNative ? publicClient.getBalance({ address: fund }) : publicClient.readContract({
|
|
103
|
+
address: outToken,
|
|
104
|
+
abi: erc20Abi,
|
|
105
|
+
functionName: "balanceOf",
|
|
106
|
+
args: [fund]
|
|
107
|
+
}), core.readSignedBalance(tokenId)]);
|
|
108
|
+
if (o.amountOut > balance) {
|
|
109
|
+
const sym = symbolForBaseTokenId(tokenId) ?? outToken;
|
|
110
|
+
throw new Error(`insufficient ${sym}: fund holds ${balance}, order needs ${o.amountOut} — allocate more on the site or size down`);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
tokenId,
|
|
114
|
+
token: outToken,
|
|
115
|
+
amountIn: balance > signed ? balance - signed : 0n,
|
|
116
|
+
amountOut: o.amountOut
|
|
117
|
+
};
|
|
118
|
+
})));
|
|
108
119
|
}
|
|
109
120
|
return {
|
|
110
121
|
GMX_ORDER_TYPE,
|