Skip to content

Commit

Permalink
Merge pull request #24 from consenlabs/support_ammv2
Browse files Browse the repository at this point in the history
Support AMMV2 protocol
  • Loading branch information
tailingchen authored Jul 6, 2021
2 parents faf4919 + ba2534e commit 6dcac64
Show file tree
Hide file tree
Showing 12 changed files with 139 additions and 15 deletions.
4 changes: 2 additions & 2 deletions benchmark/MockServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
18 changes: 18 additions & 0 deletions src/handler/newOrder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 }
}
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/handler/version.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export const version = (ctx) => {
ctx.body = {
result: true,
version: '5.2.0-alpha.1',
version: '5.2.0-alpha.2',
}
}
17 changes: 9 additions & 8 deletions src/request/_request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -59,7 +60,7 @@ export const sendRequest = (config): Promise<any> => {
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 = {
Expand Down
1 change: 1 addition & 0 deletions src/request/marketMaker/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export interface PriceApiParams extends IndicativePriceApiParams {

export interface PriceApiResult extends IndicativePriceApiResult {
quoteId: string
payload?: string
}

export interface NotifyOrderResult {
Expand Down
2 changes: 1 addition & 1 deletion src/signer/ammv1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
31 changes: 31 additions & 0 deletions src/signer/ammv2.ts
Original file line number Diff line number Diff line change
@@ -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)
}
2 changes: 1 addition & 1 deletion src/signer/pmmv5.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<any> => {
const feeFactor = order.feeFactor
order.takerAddress = pmm.toLowerCase()
order.senderAddress = pmm.toLowerCase()
Expand Down
2 changes: 1 addition & 1 deletion src/signer/rfqv1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export const buildSignedOrder = async (
userAddr: string,
chainId: number,
rfqAddr: string
) => {
): Promise<any> => {
// inject fee factor to salt
const feeFactor = order.feeFactor
order.takerAddress = userAddr.toLowerCase()
Expand Down
1 change: 1 addition & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export interface QueryInterface {

export enum Protocol {
AMMV1 = 'AMMV1',
AMMV2 = 'AMMV2',
PMMV5 = 'PMMV5',
RFQV1 = 'RFQV1',
}
72 changes: 72 additions & 0 deletions test/new_order.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down

0 comments on commit 6dcac64

Please sign in to comment.