diff --git a/benchmark/MockServer.ts b/benchmark/MockServer.ts index f1908af..8a09ee6 100644 --- a/benchmark/MockServer.ts +++ b/benchmark/MockServer.ts @@ -98,8 +98,8 @@ function createRPCHandler(): JsonRPC { contractAddress: '0x6dA0e6ABd44175f50C563cd8b860DD988A7C3433', decimal: 18, precision: 6, - minTradeAmount: 0.001, - maxTradeAmount: 1e4, + minTradeAmount: 0.1, + maxTradeAmount: 1e1, }, { symbol: 'UNI', diff --git a/package.json b/package.json index b96e416..06fcb39 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@consenlabs/tokenlon-mmsk", - "version": "5.2.0-alpha.1", + "version": "5.2.0-alpha.2", "description": "Tokenlon market maker server kit", "author": "imToken PTE. LTD.", "license": "MIT", diff --git a/src/handler/newOrder.ts b/src/handler/newOrder.ts index 28be766..a521228 100644 --- a/src/handler/newOrder.ts +++ b/src/handler/newOrder.ts @@ -9,6 +9,7 @@ import { assetDataUtils } from '0x-v2-order-utils' import { buildSignedOrder as buildRFQV1SignedOrder } from '../signer/rfqv1' import { buildSignedOrder } from '../signer/pmmv5' import { buildSignedOrder as buildAMMV1Order } from '../signer/ammv1' +import { buildSignedOrder as buildAMMV2Order } from '../signer/ammv2' import { FEE_RECIPIENT_ADDRESS } from '../constants' import { BigNumber, @@ -84,6 +85,7 @@ async function requestMarketMaker(quoter: Quoter, query: QueryInterface) { const rateBody = { ...constructQuoteResponse(priceResult, side), quoteId: addQuoteIdPrefix(priceResult.quoteId), + payload: priceResult.payload, } return { simpleOrder, rateBody } } @@ -223,6 +225,22 @@ export const newOrder = async (ctx) => { } resp.order = buildAMMV1Order(order, rateBody.makerAddress, config.wethContractAddress) break + case Protocol.AMMV2: + { + const tokenSymbol = simpleOrder.base + const tokenConfig = tokenList.find( + (token) => token.symbol.toUpperCase() === tokenSymbol.toUpperCase() + ) + resp.minAmount = tokenConfig.minTradeAmount + resp.maxAmount = tokenConfig.maxTradeAmount + } + resp.order = buildAMMV2Order( + order, + rateBody.payload, + rateBody.makerAddress, + config.wethContractAddress + ) + break case Protocol.PMMV5: resp.order = await buildSignedOrder( signer, diff --git a/src/handler/version.ts b/src/handler/version.ts index d41e929..f102e68 100644 --- a/src/handler/version.ts +++ b/src/handler/version.ts @@ -1,6 +1,6 @@ export const version = (ctx) => { ctx.body = { result: true, - version: '5.2.0-alpha.1', + version: '5.2.0-alpha.2', } } diff --git a/src/request/_request.ts b/src/request/_request.ts index 394e45b..47d27fa 100644 --- a/src/request/_request.ts +++ b/src/request/_request.ts @@ -20,22 +20,23 @@ const getHeaders = () => { } } -const newError = (message, url: string) => { +const newError = (message: any | string, url: string) => { + let responseMessage = '' if (_.isObject(message) && message.message) { - const error = message + const error: any = message if (_.isObject(error.response) && _.isObject(error.response.data)) { if (error.response.data.error) { message = error.response.data.error.message } } else { - message = `${url}: ${message.message}` + responseMessage = `${url}: ${message.message}` } } else { - message = `${url}: ${message}` + responseMessage = `${url}: ${message}` } - const error = new Error(message) - error.message = message - error.toString = () => message + const error = new Error(responseMessage) + error.message = responseMessage + error.toString = () => responseMessage return error } @@ -59,7 +60,7 @@ export const sendRequest = (config): Promise => { console.log('request error', error) reject(newError(error, config.url)) }) - }) as Promise<{ error: object; result: any }> + }) as Promise<{ error: unknown; result: any }> } export const jsonrpc = { diff --git a/src/request/marketMaker/types.ts b/src/request/marketMaker/types.ts index 10ff5ad..ca50717 100644 --- a/src/request/marketMaker/types.ts +++ b/src/request/marketMaker/types.ts @@ -35,6 +35,7 @@ export interface PriceApiParams extends IndicativePriceApiParams { export interface PriceApiResult extends IndicativePriceApiResult { quoteId: string + payload?: string } export interface NotifyOrderResult { diff --git a/src/signer/ammv1.ts b/src/signer/ammv1.ts index d7b1b28..21a1b05 100644 --- a/src/signer/ammv1.ts +++ b/src/signer/ammv1.ts @@ -4,7 +4,7 @@ import * as cryptoRandomString from 'crypto-random-string' import { orderBNToString } from '../utils' import { NULL_ADDRESS } from '../constants' -export const buildSignedOrder = (order, makerAddress, wethAddress) => { +export const buildSignedOrder = (order, makerAddress, wethAddress): any => { const makerAssetAddress = order.makerAssetAddress.toLowerCase() const takerAssetAddress = order.takerAssetAddress.toLowerCase() // = Rewrite order fields diff --git a/src/signer/ammv2.ts b/src/signer/ammv2.ts new file mode 100644 index 0000000..a282a08 --- /dev/null +++ b/src/signer/ammv2.ts @@ -0,0 +1,31 @@ +// sign order from custom quoter no need to put it in MMSK +import { assetDataUtils, generatePseudoRandomSalt } from '0x-v2-order-utils' +import * as cryptoRandomString from 'crypto-random-string' +import { orderBNToString } from '../utils' +import { NULL_ADDRESS } from '../constants' + +export const buildSignedOrder = (order, payload, makerAddress, wethAddress): any => { + const makerAssetAddress = order.makerAssetAddress.toLowerCase() + const takerAssetAddress = order.takerAssetAddress.toLowerCase() + // = Rewrite order fields + // 1. change maker address to LP pool address + order.makerAddress = makerAddress + // 2. convert weth to eth + if (makerAssetAddress === wethAddress.toLowerCase()) { + order.makerAssetAddress = NULL_ADDRESS + order.makerAssetData = assetDataUtils.encodeERC20AssetData(NULL_ADDRESS) + } + + if (takerAssetAddress === wethAddress.toLowerCase()) { + order.takerAssetAddress = NULL_ADDRESS + order.takerAssetData = assetDataUtils.encodeERC20AssetData(NULL_ADDRESS) + } + // NOTE: for AMM order we don't do signing here + const signedOrder = { + ...order, + payload: payload, + salt: generatePseudoRandomSalt(), + makerWalletSignature: cryptoRandomString({ length: 40 }), + } + return orderBNToString(signedOrder) +} diff --git a/src/signer/pmmv5.ts b/src/signer/pmmv5.ts index 6697ee0..a9da7e4 100644 --- a/src/signer/pmmv5.ts +++ b/src/signer/pmmv5.ts @@ -86,7 +86,7 @@ function signByMMPSigner( } // Move fee factor to salt field -export const buildSignedOrder = async (signer: Wallet, order, userAddr, pmm) => { +export const buildSignedOrder = async (signer: Wallet, order, userAddr, pmm): Promise => { const feeFactor = order.feeFactor order.takerAddress = pmm.toLowerCase() order.senderAddress = pmm.toLowerCase() diff --git a/src/signer/rfqv1.ts b/src/signer/rfqv1.ts index 38eaf5e..4dcb75f 100644 --- a/src/signer/rfqv1.ts +++ b/src/signer/rfqv1.ts @@ -65,7 +65,7 @@ export const buildSignedOrder = async ( userAddr: string, chainId: number, rfqAddr: string -) => { +): Promise => { // inject fee factor to salt const feeFactor = order.feeFactor order.takerAddress = userAddr.toLowerCase() diff --git a/src/types/index.ts b/src/types/index.ts index 17fbdb3..3d6b4a7 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -97,6 +97,7 @@ export interface QueryInterface { export enum Protocol { AMMV1 = 'AMMV1', + AMMV2 = 'AMMV2', PMMV5 = 'PMMV5', RFQV1 = 'RFQV1', } diff --git a/test/new_order.spec.ts b/test/new_order.spec.ts index 0ede2d7..8b7d8c0 100644 --- a/test/new_order.spec.ts +++ b/test/new_order.spec.ts @@ -145,6 +145,78 @@ describe('NewOrder', function () { assert.isTrue(Number(order.expirationTimeSeconds) > 0) }) + it('should signed ammv2 order by uniswap v2', async function () { + const ammAddr = '0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852' + const payload = Buffer.from( + JSON.stringify({ + path: [ + '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + '0xdac17f958d2ee523a2206206994597c13d831ec7', + ], + }) + ).toString('base64') + const signedOrderResp = await newOrder({ + signer: signer, + quoter: { + getPrice: () => { + return Promise.resolve({ + result: true, + exchangeable: true, + minAmount: 0, + maxAmount: 1000, + price: 1, + makerAddress: ammAddr, + quoteId: 'echo-testing-8888', + payload: payload, + }) + }, + }, + query: { + base: 'ETH', + quote: 'USDT', + side: 'SELL', + amount: 0.1, + uniqId: 'testing-1111', + userAddr: Wallet.createRandom().address.toLowerCase(), + protocol: Protocol.AMMV2, + }, + }) + + assert(signedOrderResp) + + // verify data object + const order = signedOrderResp.order + assert(order) + assert.equal(order.protocol, Protocol.AMMV2) + assert.equal(order.quoteId, '1--echo-testing-8888') + assert.equal(order.makerAddress, '0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852') + assert.equal(order.makerAssetAmount, '100000') + assert.equal(order.makerAssetAddress, '0xdac17f958d2ee523a2206206994597c13d831ec7') + assert.equal( + order.makerAssetData, + '0xf47261b0000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7' + ) + assert.equal(order.takerAddress, '0x25657705a6be20511687d483f2fccfb2d92f6033') + assert.equal(order.takerAssetAmount, '100000000000000000') + assert.equal(order.takerAssetAddress, '0x0000000000000000000000000000000000000000') + assert.equal( + order.takerAssetData, + '0xf47261b00000000000000000000000000000000000000000000000000000000000000000' + ) + assert.equal(order.senderAddress, '0xd489f1684cf5e78d933e254bd7ac8a9a6a70d491') + assert.equal(order.feeRecipientAddress, '0xb9e29984fe50602e7a619662ebed4f90d93824c7') + assert.equal(order.exchangeAddress, '0x30589010550762d2f0d06f650d8e8b6ade6dbf4b') + // The following fields are to be compatible `Order` struct. + assert.equal(order.makerFee, '0') + assert.equal(order.takerFee, '0') + // verify signature length, the signature is generated ramdonly. + assert.equal(order.makerWalletSignature.length, 40) + // verify random values + assert.isTrue(order.salt.length > 0) + assert.isTrue(Number(order.expirationTimeSeconds) > 0) + assert.equal(order.payload, payload) + }) + it('should raise error for pmmv4 order', async function () { assert.equal( await newOrder({