From 5ff1562884efa7db92147b4d3871151be722c678 Mon Sep 17 00:00:00 2001 From: James Heal <790364833@qq.com> Date: Sat, 23 Sep 2023 13:32:28 +0800 Subject: [PATCH] Feat/order hash (#25) * add function hash order * add orderHash in post order return value * feat: order hash * fix: need result * fix: type error * fix: order id nessesary --------- Co-authored-by: night-scholar --- __test__/bulk-listing.test.ts | 2 +- __test__/common/config.ts | 4 +- __test__/common/create-env-test-skd.ts | 2 +- __test__/create-offer.test.ts | 20 +- __test__/go-trading.test.ts | 2 +- __test__/order-fetcher.test.ts | 71 +++++++ package.json | 2 +- pnpm-lock.yaml | 68 +++---- src/http/client.ts | 7 +- src/modules/aggregator/index.ts | 25 +-- src/modules/utils/action/processor/index.ts | 10 +- src/modules/utils/action/task/pass-through.ts | 3 +- src/modules/utils/action/task/transaction.ts | 4 +- src/modules/utils/post-order/handler.ts | 174 ++++++++++++------ src/modules/utils/post-order/index.ts | 23 +-- src/modules/utils/post-order/utils/index.ts | 3 +- src/types/action/action.ts | 4 +- src/types/action/processor.ts | 2 +- src/types/aggregator/cancel.ts | 2 +- src/types/aggregator/create.ts | 5 +- src/types/aggregator/fulfill.ts | 10 +- src/types/aggregator/response.ts | 4 + src/types/config.ts | 4 +- src/types/order-fetcher.ts | 2 + src/types/safe-any.ts | 1 + tsconfig.json | 1 - 26 files changed, 299 insertions(+), 156 deletions(-) create mode 100644 __test__/order-fetcher.test.ts diff --git a/__test__/bulk-listing.test.ts b/__test__/bulk-listing.test.ts index 948a2a5..0d01941 100644 --- a/__test__/bulk-listing.test.ts +++ b/__test__/bulk-listing.test.ts @@ -439,7 +439,7 @@ describe('fulfill listing main process', () => { const sdk = init({ apiKey, - baseUrl: 'https://data-api.nftgo.dev', + baseUrl: process.env.BASE_URL, web3Provider, walletConfig: { address, diff --git a/__test__/common/config.ts b/__test__/common/config.ts index 5a1e4f7..ae20c01 100644 --- a/__test__/common/config.ts +++ b/__test__/common/config.ts @@ -26,8 +26,8 @@ export function initConfig() { const provider = initWeb3Provider(); const config: Config = { apiKey: process.env.API_KEY || '', // Replace with your own API Key. - baseUrl: 'https://data-api.nftgo.dev', - chain: EVMChain.ETH, + baseUrl: process.env.BASE_URL || '', + chain: EVMChain.ETHEREUM, web3Provider: provider, walletConfig: { address: process.env.ADDRESS || '', diff --git a/__test__/common/create-env-test-skd.ts b/__test__/common/create-env-test-skd.ts index 28d1850..298ea19 100644 --- a/__test__/common/create-env-test-skd.ts +++ b/__test__/common/create-env-test-skd.ts @@ -10,7 +10,7 @@ export function createEnvTestSdk() { const sdk = init({ apiKey, - baseUrl: 'https://data-api.nftgo.dev', + baseUrl: process.env.BASE_URL, web3Provider, walletConfig: { address, diff --git a/__test__/create-offer.test.ts b/__test__/create-offer.test.ts index e2f53e2..d44ef48 100644 --- a/__test__/create-offer.test.ts +++ b/__test__/create-offer.test.ts @@ -1,5 +1,4 @@ import { OrderKind, Orderbook, init } from '../src'; -import { HttpsProxyAgent } from 'https-proxy-agent'; import Web3 from 'web3'; describe('create offer main process', () => { @@ -7,16 +6,17 @@ describe('create offer main process', () => { address = process.env.ADDRESS!, privateKey = process.env.PRIVATE_KEY!, apiKey = process.env.API_KEY!, + baseUrl = process.env.BASE_URL!, web3Provider = new Web3.providers.HttpProvider(endpoint); const sdk = init({ apiKey, web3Provider, + baseUrl, walletConfig: { address, privateKey, }, - agent: new HttpsProxyAgent('http://127.0.0.1:7890'), }); test('should return buy actions', async () => { @@ -26,18 +26,24 @@ describe('create offer main process', () => { { collection: '0xed5af388653567af2f388e6224dc7c4b3241c544', quantity: 1, - weiPrice: '10000000000000', + weiPrice: '10000000000000000', orderKind: OrderKind.SeaportV15, orderbook: Orderbook.Opensea, - listingTime: '1692605611', - expirationTime: '1692615611', + listingTime: (Date.now() / 1000).toFixed(0), + expirationTime: (Date.now() / 1000 + 3 * 60 * 60).toFixed(0), automatedRoyalties: true, + currency: 'WETH', }, ], }); - const { actions } = res; - console.log({ actions }); + const { actions, executeActions } = res; + console.log(actions); + await executeActions({ + onTaskExecuted: task => { + console.log(task.action, task.result); + }, + }); expect(Array.isArray(actions)).toBe(true); }); }); diff --git a/__test__/go-trading.test.ts b/__test__/go-trading.test.ts index d25ca05..8a355ad 100644 --- a/__test__/go-trading.test.ts +++ b/__test__/go-trading.test.ts @@ -15,7 +15,7 @@ describe('Test go-trading sdk', () => { const sdk = init({ apiKey, - baseUrl: 'https://data-api.nftgo.dev', + baseUrl: process.env.BASE_URL, web3Provider, walletConfig: { address, diff --git a/__test__/order-fetcher.test.ts b/__test__/order-fetcher.test.ts new file mode 100644 index 0000000..593eb23 --- /dev/null +++ b/__test__/order-fetcher.test.ts @@ -0,0 +1,71 @@ +require('dotenv').config(); + +import { OrderType } from '@/types'; +import { init } from '../src'; +import Web3 from 'web3'; + +describe('Test order fetcher sdk', () => { + const endpoint = process.env.PROVIDER_URL!, + // address = process.env.ADDRESS!, + // privateKey = process.env.PRIVATE_KEY!, + apiKey = process.env.API_KEY!, + web3Provider = new Web3.providers.HttpProvider(endpoint); + + const contractAddress = '0xED5AF388653567Af2F388E6224dC7C4b3241C544'; + + const sdk = init({ + apiKey, + baseUrl: process.env.BASE_URL, + web3Provider, + // agent: + // walletConfig: { + // address, + // privateKey, + // }, + }); + + it('fetch Azuki Listing NFT', async () => { + const orders = await sdk.orderFetcher.getOrdersByContract({ + contractAddress, + limit: 1, + offset: 0, + orderType: OrderType.Listing, + }); + expect(orders.listingDTOs.length).toBe(1); + expect(typeof orders.listingDTOs?.[0].orderHash).toBe('string'); + }); + + it('fetch orders by ids', async () => { + const orders = await sdk.orderFetcher.getOrdersByIds({ + orders: [ + { + orderId: '650d79013a36395019514b9d', + orderType: OrderType.Listing, + }, + ], + }); + expect(orders.listingDTOs.length).toBe(1); + expect(typeof orders.listingDTOs?.[0].orderHash).toBe('string'); + }); + + it('fetch orders by maker', async () => { + const orders = await sdk.orderFetcher.getOrdersByMaker({ + maker: '0x08c1ae7e46d4a13b766566033b5c47c735e19f6f', + orderType: OrderType.Offer, + includePrivate: true, + }); + console.log(orders, orders.offerDTOs?.[0]?.orderHash); + expect(orders.offerDTOs.length > 0).toBe(true); + expect(typeof orders.offerDTOs?.[0].orderHash).toBe('string'); + }); + + it('fetch orders by NFT', async () => { + const orders = await sdk.orderFetcher.getOrdersByNFT({ + contractAddress: '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D', + tokenId: '2632', + orderType: OrderType.Offer, + }); + expect(orders.offerDTOs.length > 0).toBe(true); + expect(typeof orders.offerDTOs?.[0].orderHash).toBe('string'); + }); +}); diff --git a/package.json b/package.json index 0b31764..07eb627 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@nftgo/gotrading", - "version": "1.0.8", + "version": "1.0.9", "main": "dist/index.js", "module": "dist/index.esm.js", "types": "dist/index.d.ts", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1b4acbf..e7d2cc0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -63,7 +63,7 @@ devDependencies: version: registry.npmmirror.com/@types/node@14.18.53 '@typescript-eslint/eslint-plugin': specifier: latest - version: 6.6.0(@typescript-eslint/parser@5.61.0)(eslint@8.44.0)(typescript@5.1.6) + version: 6.7.2(@typescript-eslint/parser@5.61.0)(eslint@8.44.0)(typescript@5.1.6) '@typescript-eslint/parser': specifier: ^5.46.1 version: registry.npmmirror.com/@typescript-eslint/parser@5.61.0(eslint@8.44.0)(typescript@5.1.6) @@ -81,7 +81,7 @@ devDependencies: version: registry.npmmirror.com/eslint@8.44.0 eslint-plugin-jest: specifier: latest - version: 27.2.3(@typescript-eslint/eslint-plugin@6.6.0)(eslint@8.44.0)(jest@29.6.0)(typescript@5.1.6) + version: 27.4.0(@typescript-eslint/eslint-plugin@6.7.2)(eslint@8.44.0)(jest@29.6.0)(typescript@5.1.6) gts: specifier: ^3.1.1 version: registry.npmmirror.com/gts@3.1.1(typescript@5.1.6) @@ -203,8 +203,8 @@ packages: - supports-color dev: true - /@typescript-eslint/eslint-plugin@6.6.0(@typescript-eslint/parser@5.61.0)(eslint@8.44.0)(typescript@5.1.6): - resolution: {integrity: sha512-CW9YDGTQnNYMIo5lMeuiIG08p4E0cXrXTbcZ2saT/ETE7dWUrNxlijsQeU04qAAKkILiLzdQz+cGFxCJjaZUmA==} + /@typescript-eslint/eslint-plugin@6.7.2(@typescript-eslint/parser@5.61.0)(eslint@8.44.0)(typescript@5.1.6): + resolution: {integrity: sha512-ooaHxlmSgZTM6CHYAFRlifqh1OAr3PAQEwi7lhYhaegbnXrnh7CDcHmc3+ihhbQC7H0i4JF0psI5ehzkF6Yl6Q==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha @@ -216,10 +216,10 @@ packages: dependencies: '@eslint-community/regexpp': 4.5.1 '@typescript-eslint/parser': registry.npmmirror.com/@typescript-eslint/parser@5.61.0(eslint@8.44.0)(typescript@5.1.6) - '@typescript-eslint/scope-manager': 6.6.0 - '@typescript-eslint/type-utils': 6.6.0(eslint@8.44.0)(typescript@5.1.6) - '@typescript-eslint/utils': 6.6.0(eslint@8.44.0)(typescript@5.1.6) - '@typescript-eslint/visitor-keys': 6.6.0 + '@typescript-eslint/scope-manager': 6.7.2 + '@typescript-eslint/type-utils': 6.7.2(eslint@8.44.0)(typescript@5.1.6) + '@typescript-eslint/utils': 6.7.2(eslint@8.44.0)(typescript@5.1.6) + '@typescript-eslint/visitor-keys': 6.7.2 debug: 4.3.4 eslint: registry.npmmirror.com/eslint@8.44.0 graphemer: 1.4.0 @@ -248,16 +248,16 @@ packages: '@typescript-eslint/visitor-keys': 5.61.0 dev: true - /@typescript-eslint/scope-manager@6.6.0: - resolution: {integrity: sha512-pT08u5W/GT4KjPUmEtc2kSYvrH8x89cVzkA0Sy2aaOUIw6YxOIjA8ilwLr/1fLjOedX1QAuBpG9XggWqIIfERw==} + /@typescript-eslint/scope-manager@6.7.2: + resolution: {integrity: sha512-bgi6plgyZjEqapr7u2mhxGR6E8WCzKNUFWNh6fkpVe9+yzRZeYtDTbsIBzKbcxI+r1qVWt6VIoMSNZ4r2A+6Yw==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.6.0 - '@typescript-eslint/visitor-keys': 6.6.0 + '@typescript-eslint/types': 6.7.2 + '@typescript-eslint/visitor-keys': 6.7.2 dev: true - /@typescript-eslint/type-utils@6.6.0(eslint@8.44.0)(typescript@5.1.6): - resolution: {integrity: sha512-8m16fwAcEnQc69IpeDyokNO+D5spo0w1jepWWY2Q6y5ZKNuj5EhVQXjtVAeDDqvW6Yg7dhclbsz6rTtOvcwpHg==} + /@typescript-eslint/type-utils@6.7.2(eslint@8.44.0)(typescript@5.1.6): + resolution: {integrity: sha512-36F4fOYIROYRl0qj95dYKx6kybddLtsbmPIYNK0OBeXv2j9L5nZ17j9jmfy+bIDHKQgn2EZX+cofsqi8NPATBQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -266,8 +266,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 6.6.0(typescript@5.1.6) - '@typescript-eslint/utils': 6.6.0(eslint@8.44.0)(typescript@5.1.6) + '@typescript-eslint/typescript-estree': 6.7.2(typescript@5.1.6) + '@typescript-eslint/utils': 6.7.2(eslint@8.44.0)(typescript@5.1.6) debug: 4.3.4 eslint: registry.npmmirror.com/eslint@8.44.0 ts-api-utils: 1.0.2(typescript@5.1.6) @@ -286,8 +286,8 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/types@6.6.0: - resolution: {integrity: sha512-CB6QpJQ6BAHlJXdwUmiaXDBmTqIE2bzGTDLADgvqtHWuhfNP3rAOK7kAgRMAET5rDRr9Utt+qAzRBdu3AhR3sg==} + /@typescript-eslint/types@6.7.2: + resolution: {integrity: sha512-flJYwMYgnUNDAN9/GAI3l8+wTmvTYdv64fcH8aoJK76Y+1FCZ08RtI5zDerM/FYT5DMkAc+19E4aLmd5KqdFyg==} engines: {node: ^16.0.0 || >=18.0.0} dev: true @@ -333,8 +333,8 @@ packages: - supports-color dev: true - /@typescript-eslint/typescript-estree@6.6.0(typescript@5.1.6): - resolution: {integrity: sha512-hMcTQ6Al8MP2E6JKBAaSxSVw5bDhdmbCEhGW/V8QXkb9oNsFkA4SBuOMYVPxD3jbtQ4R/vSODBsr76R6fP3tbA==} + /@typescript-eslint/typescript-estree@6.7.2(typescript@5.1.6): + resolution: {integrity: sha512-kiJKVMLkoSciGyFU0TOY0fRxnp9qq1AzVOHNeN1+B9erKFCJ4Z8WdjAkKQPP+b1pWStGFqezMLltxO+308dJTQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: typescript: '*' @@ -342,8 +342,8 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 6.6.0 - '@typescript-eslint/visitor-keys': 6.6.0 + '@typescript-eslint/types': 6.7.2 + '@typescript-eslint/visitor-keys': 6.7.2 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 @@ -374,8 +374,8 @@ packages: - typescript dev: true - /@typescript-eslint/utils@6.6.0(eslint@8.44.0)(typescript@5.1.6): - resolution: {integrity: sha512-mPHFoNa2bPIWWglWYdR0QfY9GN0CfvvXX1Sv6DlSTive3jlMTUy+an67//Gysc+0Me9pjitrq0LJp0nGtLgftw==} + /@typescript-eslint/utils@6.7.2(eslint@8.44.0)(typescript@5.1.6): + resolution: {integrity: sha512-ZCcBJug/TS6fXRTsoTkgnsvyWSiXwMNiPzBUani7hDidBdj1779qwM1FIAmpH4lvlOZNF3EScsxxuGifjpLSWQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -383,9 +383,9 @@ packages: '@eslint-community/eslint-utils': 4.4.0(eslint@8.44.0) '@types/json-schema': 7.0.12 '@types/semver': 7.5.0 - '@typescript-eslint/scope-manager': 6.6.0 - '@typescript-eslint/types': 6.6.0 - '@typescript-eslint/typescript-estree': 6.6.0(typescript@5.1.6) + '@typescript-eslint/scope-manager': 6.7.2 + '@typescript-eslint/types': 6.7.2 + '@typescript-eslint/typescript-estree': 6.7.2(typescript@5.1.6) eslint: registry.npmmirror.com/eslint@8.44.0 semver: 7.5.4 transitivePeerDependencies: @@ -409,11 +409,11 @@ packages: eslint-visitor-keys: 3.4.1 dev: true - /@typescript-eslint/visitor-keys@6.6.0: - resolution: {integrity: sha512-L61uJT26cMOfFQ+lMZKoJNbAEckLe539VhTxiGHrWl5XSKQgA0RTBZJW2HFPy5T0ZvPVSD93QsrTKDkfNwJGyQ==} + /@typescript-eslint/visitor-keys@6.7.2: + resolution: {integrity: sha512-uVw9VIMFBUTz8rIeaUT3fFe8xIUx8r4ywAdlQv1ifH+6acn/XF8Y6rwJ7XNmkNMDrTW+7+vxFFPIF40nJCVsMQ==} engines: {node: ^16.0.0 || >=18.0.0} dependencies: - '@typescript-eslint/types': 6.6.0 + '@typescript-eslint/types': 6.7.2 eslint-visitor-keys: 3.4.1 dev: true @@ -467,8 +467,8 @@ packages: path-type: 4.0.0 dev: true - /eslint-plugin-jest@27.2.3(@typescript-eslint/eslint-plugin@6.6.0)(eslint@8.44.0)(jest@29.6.0)(typescript@5.1.6): - resolution: {integrity: sha512-sRLlSCpICzWuje66Gl9zvdF6mwD5X86I4u55hJyFBsxYOsBCmT5+kSUjf+fkFWVMMgpzNEupjW8WzUqi83hJAQ==} + /eslint-plugin-jest@27.4.0(@typescript-eslint/eslint-plugin@6.7.2)(eslint@8.44.0)(jest@29.6.0)(typescript@5.1.6): + resolution: {integrity: sha512-ukVeKmMPAUA5SWjHenvyyXnirKfHKMdOsTZdn5tZx5EW05HGVQwBohigjFZGGj3zuv1cV6hc82FvWv6LdIbkgg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@typescript-eslint/eslint-plugin': ^5.0.0 || ^6.0.0 @@ -480,7 +480,7 @@ packages: jest: optional: true dependencies: - '@typescript-eslint/eslint-plugin': 6.6.0(@typescript-eslint/parser@5.61.0)(eslint@8.44.0)(typescript@5.1.6) + '@typescript-eslint/eslint-plugin': 6.7.2(@typescript-eslint/parser@5.61.0)(eslint@8.44.0)(typescript@5.1.6) '@typescript-eslint/utils': 5.61.0(eslint@8.44.0)(typescript@5.1.6) eslint: registry.npmmirror.com/eslint@8.44.0 jest: registry.npmmirror.com/jest@29.6.0(@types/node@14.18.53)(ts-node@10.9.1) @@ -4552,7 +4552,7 @@ packages: fast-deep-equal: registry.npmmirror.com/fast-deep-equal@3.1.3 file-entry-cache: registry.npmmirror.com/file-entry-cache@6.0.1 functional-red-black-tree: registry.npmmirror.com/functional-red-black-tree@1.0.1 - glob-parent: registry.npmmirror.com/glob-parent@5.1.2 + glob-parent: 5.1.2 globals: registry.npmmirror.com/globals@13.20.0 ignore: 4.0.6 import-fresh: registry.npmmirror.com/import-fresh@3.3.0 diff --git a/src/http/client.ts b/src/http/client.ts index bdf1e2d..524a2bc 100644 --- a/src/http/client.ts +++ b/src/http/client.ts @@ -1,10 +1,11 @@ -import { HttpsProxyAgent } from 'https-proxy-agent'; import { AggregatorApiException } from '@/exceptions'; import { HTTPClient } from '@/types'; +import { Agent } from 'https'; +import { SafeAny } from 'src/types/safe-any'; export class HTTPClientStable implements HTTPClient { - constructor(private agent?: HttpsProxyAgent) {} + constructor(private agent?: Agent) {} fetch(input: RequestInfo | URL, init?: RequestInit | undefined) { const agentOption = this.agent ? { agent: this.agent } : {}; return new Promise((resolve, reject) => { @@ -39,7 +40,7 @@ export class HTTPClientStable implements HTTPClient { let actualUrl = url; for (const key in query) { if (query[key] instanceof Array) { - for (const value of query[key] as unknown as Array) { + for (const value of query[key] as unknown as Array) { value !== null && value !== undefined && params.push(`${key}=${value}`); } } else { diff --git a/src/modules/aggregator/index.ts b/src/modules/aggregator/index.ts index 1043b76..fd47a6d 100644 --- a/src/modules/aggregator/index.ts +++ b/src/modules/aggregator/index.ts @@ -12,8 +12,6 @@ import { CreateOffersReq, FulfillListingsReq, FulfillOffersReq, - AggregatorAction, - ActionKind, } from '@/types'; export class Aggregator implements AggregatorInterface { @@ -27,9 +25,7 @@ export class Aggregator implements AggregatorInterface { */ createOffers = async (params: CreateOffersReq): Promise => { const res = await this.post('/create-offers', params); - const { actions } = res; - - return this.response(actions); + return this.response(res); }; /** @@ -40,8 +36,7 @@ export class Aggregator implements AggregatorInterface { */ fulfillOffers = async (params: FulfillOffersReq): Promise => { const res = await this.post('/fulfill-offers', params); - const { actions } = res; - return this.response(actions); + return this.response(res); }; /** @@ -52,9 +47,8 @@ export class Aggregator implements AggregatorInterface { */ cancelOrders = async (params: CancelOrdersReq): Promise => { const res = await this.post('/cancel-orders', params); - const { actions } = res; - return this.response(actions); + return this.response(res); }; /** @@ -65,9 +59,8 @@ export class Aggregator implements AggregatorInterface { */ createListings = async (params: CreateListingsReq): Promise => { const data = await this.post('/create-listings', params); - const { actions } = data; - return this.response(actions); + return this.response(data); }; /** @@ -78,9 +71,7 @@ export class Aggregator implements AggregatorInterface { */ fulfillListings = async (params: FulfillListingsReq): Promise => { const data = await this.post('/fulfill-listings', params); - const { actions } = data; - - return this.response(actions); + return this.response(data); }; private get headers(): Record { @@ -102,11 +93,11 @@ export class Aggregator implements AggregatorInterface { } } - private response(actions: AggregatorAction[]): AggregatorResponse { - const executor = this.utils.createActionExecutor(actions); + private response(data: AggregatorApiResponse): AggregatorResponse { + const executor = this.utils.createActionExecutor(data.actions); const executeActions = executor.execute; const response: AggregatorResponse = { - actions, + ...data, executeActions, }; diff --git a/src/modules/utils/action/processor/index.ts b/src/modules/utils/action/processor/index.ts index 7855ac3..ee1b953 100644 --- a/src/modules/utils/action/processor/index.ts +++ b/src/modules/utils/action/processor/index.ts @@ -39,16 +39,16 @@ export class AggregateActionProcessor implements ActionProcessor { throw new Error('txData is required'); } if (name === 'nft-approval') { - await signInfo(txData, this.utils.sendTransaction); + return await signInfo(txData, this.utils.sendTransaction); } else if (name === 'accept-listing') { if (safeMode) { - await signInfo(txData, this.utils.sendSafeModeTransaction); + return await signInfo(txData, this.utils.sendSafeModeTransaction); } else { - await signInfo(txData, this.utils.sendTransaction); + return await signInfo(txData, this.utils.sendTransaction); } // other name case: currency-wrapping currency-approval } else { - await signInfo(txData, this.utils.sendTransaction); + return await signInfo(txData, this.utils.sendTransaction); } } @@ -63,7 +63,7 @@ export class AggregateActionProcessor implements ActionProcessor { throw new Error('action signature is required'); } const { payload, endpoint } = data; - const postOrderResult = this.postOrderHandler.handle(payload as any, params.signature, endpoint); + const postOrderResult = await this.postOrderHandler.handle(payload, params.signature, endpoint); return postOrderResult; } return Promise.resolve({ diff --git a/src/modules/utils/action/task/pass-through.ts b/src/modules/utils/action/task/pass-through.ts index b149569..41f0469 100644 --- a/src/modules/utils/action/task/pass-through.ts +++ b/src/modules/utils/action/task/pass-through.ts @@ -13,8 +13,7 @@ export class PassThroughActionTask extends ActionTaskTemplate orderIndexes.includes(index)); if (needThisSignature) { - await this.processor.processPassThroughAction(this.action, result); - return null; + return await this.processor.processPassThroughAction(this.action, result); } } pre = pre.pre; diff --git a/src/modules/utils/action/task/transaction.ts b/src/modules/utils/action/task/transaction.ts index 9d7de46..3e1a5ec 100644 --- a/src/modules/utils/action/task/transaction.ts +++ b/src/modules/utils/action/task/transaction.ts @@ -3,8 +3,6 @@ import { ActionTaskTemplate } from './template'; export class TransactionActionTask extends ActionTaskTemplate { protected run = async () => { - await this.processor.processTransactionAction(this.action); - - return null; + return await this.processor.processTransactionAction(this.action); }; } diff --git a/src/modules/utils/post-order/handler.ts b/src/modules/utils/post-order/handler.ts index 2763759..56c84ab 100644 --- a/src/modules/utils/post-order/handler.ts +++ b/src/modules/utils/post-order/handler.ts @@ -4,8 +4,9 @@ import * as Models from './utils'; import { ExternalServiceRateLimiter } from '@/common'; import { RateLimiter } from 'limiter'; import { BaseException } from '@/exceptions'; -import { defaultAbiCoder } from 'ethers/lib/utils'; +import { _TypedDataEncoder, defaultAbiCoder } from 'ethers/lib/utils'; import { IPostOrderHandler } from './utils'; +import { SafeAny } from 'src/types/safe-any'; export class SeaportV1D5Handler implements IPostOrderHandler { protocol = OrderKind.SeaportV15; @@ -22,7 +23,7 @@ export class SeaportV1D5Handler implements IPostOrderHandler { }) ); } - async handle(payload: any): Promise { + async handle(payload: SafeAny): Promise { const order = payload.order; const orderbook = payload.orderbook; const orderType = payload.orderType; @@ -44,39 +45,39 @@ export class SeaportV1D5Handler implements IPostOrderHandler { counter: order.data.counter, signature: order.data.signature, }; + const apiKey = await this.rateLimiter.getAPIKeyWithRateLimiter(); if (orderType === OrderType.Listing) { - try { - const result = await this.client.post( - this.listingUrl, - { - parameters: { - ...seaportOrder, - totalOriginalConsiderationItems: order.data.consideration.length, - }, - signature: order.data.signature, - protocol_address: Models.SeaportV1D5.Addresses.Exchange[Models.Utils.Network.Ethereum], + const result = await this.client.post( + this.listingUrl, + { + parameters: { + ...seaportOrder, + totalOriginalConsiderationItems: order.data.consideration.length, }, - { 'X-Api-Key': apiKey }, - true - ); - return result; - } catch (error) { - throw error; - } + signature: order.data.signature, + protocol_address: Models.SeaportV1D5.Addresses.Exchange[Models.Utils.Network.Ethereum], + }, + { 'X-Api-Key': apiKey }, + true + ); + const orderHash = this.hash(seaportOrder); + return { + orderHash, + result, + }; } else { // OrderType.Offer // We'll always have only one of the below cases: // Only relevant/present for attribute bids const attribute = payload.attribute; // Only relevant for collection bids - const collection = payload.collection; const slug = payload.slug; // Only relevant for token set bids const tokenSetId = payload.tokenSetId; const isNonFlagged = payload.isNonFlagged; - let schema: any; + let schema; if (attribute) { schema = { kind: 'attribute', @@ -118,31 +119,32 @@ export class SeaportV1D5Handler implements IPostOrderHandler { if (schema && ['collection', 'collection-non-flagged', 'attribute'].includes(schema.kind)) { // post collection/trait offer - try { - const result = await this.client.post( - this.offerCollectionUrl, - { - criteria: schema.data, - protocol_data: { - parameters: { - ...seaportOrder, - totalOriginalConsiderationItems: order.data.consideration.length, - }, - signature: order.data.signature, + const result = await this.client.post( + this.offerCollectionUrl, + { + criteria: schema.data, + protocol_data: { + parameters: { + ...seaportOrder, + totalOriginalConsiderationItems: order.data.consideration.length, }, - protocol_address: Models.SeaportV1D5.Addresses.Exchange[Models.Utils.Network.Ethereum], + signature: order.data.signature, }, - { 'X-Api-Key': apiKey }, - true - ); - return result; - } catch (error) { - throw error; - } + protocol_address: Models.SeaportV1D5.Addresses.Exchange[Models.Utils.Network.Ethereum], + }, + { 'X-Api-Key': apiKey }, + true + ); + const orderHash = this.hash(seaportOrder); + return { + orderHash, + result, + }; } else { // post token offer - try { - const result = await this.client.post(this.offerTokenUrl, { + const result = await this.client.post( + this.offerTokenUrl, + { parameters: { ...seaportOrder, totalOriginalConsiderationItems: order.data.consideration.length, @@ -151,14 +153,50 @@ export class SeaportV1D5Handler implements IPostOrderHandler { protocol_address: Models.SeaportV1D5.Addresses.Exchange[Models.Utils.Network.Ethereum], }, { 'X-Api-Key': apiKey }, - true); - return result; - } catch (error) { - throw error; - } + true + ); + const orderHash = this.hash(seaportOrder); + return { + orderHash, + result, + }; } } } + + hash(order: Models.SeaportV1D5.Types.OrderComponents) { + const ORDER_EIP712_TYPES = { + OrderComponents: [ + { name: 'offerer', type: 'address' }, + { name: 'zone', type: 'address' }, + { name: 'offer', type: 'OfferItem[]' }, + { name: 'consideration', type: 'ConsiderationItem[]' }, + { name: 'orderType', type: 'uint8' }, + { name: 'startTime', type: 'uint256' }, + { name: 'endTime', type: 'uint256' }, + { name: 'zoneHash', type: 'bytes32' }, + { name: 'salt', type: 'uint256' }, + { name: 'conduitKey', type: 'bytes32' }, + { name: 'counter', type: 'uint256' }, + ], + OfferItem: [ + { name: 'itemType', type: 'uint8' }, + { name: 'token', type: 'address' }, + { name: 'identifierOrCriteria', type: 'uint256' }, + { name: 'startAmount', type: 'uint256' }, + { name: 'endAmount', type: 'uint256' }, + ], + ConsiderationItem: [ + { name: 'itemType', type: 'uint8' }, + { name: 'token', type: 'address' }, + { name: 'identifierOrCriteria', type: 'uint256' }, + { name: 'startAmount', type: 'uint256' }, + { name: 'endAmount', type: 'uint256' }, + { name: 'recipient', type: 'address' }, + ], + }; + return _TypedDataEncoder.hashStruct('OrderComponents', ORDER_EIP712_TYPES, order); + } } export class LooksRareV2Handler implements IPostOrderHandler { @@ -174,7 +212,7 @@ export class LooksRareV2Handler implements IPostOrderHandler { }) ); } - async handle(payload: any): Promise { + async handle(payload: SafeAny): Promise { const order = payload.order; const orderbook = payload.orderbook; @@ -184,8 +222,7 @@ export class LooksRareV2Handler implements IPostOrderHandler { const looksrareOrder: Models.LooksRareV2.Types.MakerOrderParams = order.data; const apiKey = await this.rateLimiter.getAPIKeyWithRateLimiter(); - - return this.client.post( + const result = this.client.post( this.url, { ...looksrareOrder, @@ -193,6 +230,33 @@ export class LooksRareV2Handler implements IPostOrderHandler { { 'X-Api-Key': apiKey }, true ); + const orderHash = this.hash(looksrareOrder); + return { + orderHash, + result, + }; + } + hash(order: Models.LooksRareV2.Types.MakerOrderParams) { + const EIP712_TYPES = { + Maker: [ + { name: 'quoteType', type: 'uint8' }, + { name: 'globalNonce', type: 'uint256' }, + { name: 'subsetNonce', type: 'uint256' }, + { name: 'orderNonce', type: 'uint256' }, + { name: 'strategyId', type: 'uint256' }, + { name: 'collectionType', type: 'uint8' }, + { name: 'collection', type: 'address' }, + { name: 'currency', type: 'address' }, + { name: 'signer', type: 'address' }, + { name: 'startTime', type: 'uint256' }, + { name: 'endTime', type: 'uint256' }, + { name: 'price', type: 'uint256' }, + { name: 'itemIds', type: 'uint256[]' }, + { name: 'amounts', type: 'uint256[]' }, + { name: 'additionalParameters', type: 'bytes' }, + ], + }; + return _TypedDataEncoder.hashStruct('Maker', EIP712_TYPES, order); } } @@ -211,7 +275,7 @@ export class X2Y2Handler implements IPostOrderHandler { ); } - async handle(payload: any): Promise { + async handle(payload: SafeAny): Promise { const order = payload.order; const orderbook = payload.orderbook; @@ -250,7 +314,11 @@ export class X2Y2Handler implements IPostOrderHandler { changePrice: false, isCollection: order.data.dataMask !== '0x', }; - - return this.client.post(this.url, orderParams, { 'X-Api-Key': apiKey }, true); + const result = this.client.post(this.url, orderParams, { 'X-Api-Key': apiKey }, true); + const orderHash = x2y2Order.itemHash; + return { + orderHash, + result, + }; } } diff --git a/src/modules/utils/post-order/index.ts b/src/modules/utils/post-order/index.ts index 888ae00..0f83dfc 100644 --- a/src/modules/utils/post-order/index.ts +++ b/src/modules/utils/post-order/index.ts @@ -15,6 +15,7 @@ import { } from '@/types'; import { SeaportV1D5Handler, LooksRareV2Handler, X2Y2Handler } from './handler'; import { HTTPClientStable } from '@/http'; +import { SafeAny } from 'src/types/safe-any'; export class PostOrderHandler { private handlers = new Map(); private client: HTTPClient = new HTTPClientStable(); @@ -34,14 +35,14 @@ export class PostOrderHandler { } } - async handle(params: PostOrderReq, signature: string, endpoint: string): Promise { + async handle(params: PostOrderReq, signature: string, endpoint: string): Promise { // given the orderKind, invoke NFTGo developer API or directly post order to marketplace if (params.order.kind === OrderKind.Blur) { const res = await this.post(endpoint, { ...params, signature, }); - return { message: 'blur' }; + return res; } else { const handler = this.handlers.get(params.order.kind); if (!handler) { @@ -49,7 +50,7 @@ export class PostOrderHandler { } switch (params.extraArgs.version) { - case 'v3': + case 'v3': { try { const { v, r, s } = splitSignature(signature); params.order.data = { @@ -63,13 +64,12 @@ export class PostOrderHandler { } catch (e) { throw BaseException.invalidParamError('signature', 'invalid signature ' + signature); } - - handler.handle(params); - break; - case 'v4': + const res = await handler.handle(params); + return res; + } + case 'v4': { try { const { v, r, s } = splitSignature(signature); - if (params.bulkData?.kind === 'seaport-v1.5') { // Encode the merkle proof of inclusion together with the signature params.order.data.signature = Models.SeaportV1D5.Utils.encodeBulkOrderProofAndSignature( @@ -90,11 +90,12 @@ export class PostOrderHandler { s, }; } - } catch (e: any) { + } catch { throw BaseException.invalidParamError('signature', 'invalid signature ' + signature); } - handler.handle(params); - break; + const res = await handler.handle(params); + return res; + } default: throw BaseException.invalidParamError('extraArgs.version', 'unsupported version ' + params.extraArgs.version); } diff --git a/src/modules/utils/post-order/utils/index.ts b/src/modules/utils/post-order/utils/index.ts index d4eb9c8..ca519cd 100644 --- a/src/modules/utils/post-order/utils/index.ts +++ b/src/modules/utils/post-order/utils/index.ts @@ -6,11 +6,12 @@ import * as Utils from './utils'; import { OrderKind } from '@/types'; import { ExternalServiceRateLimiter } from '@/common'; +import { SafeAny } from 'src/types/safe-any'; export interface IPostOrderHandler { protocol: OrderKind; rateLimiter: ExternalServiceRateLimiter; - handle: (payload: any) => Promise; + handle: (payload: SafeAny) => Promise; } export { SeaportV1D5, LooksRare, LooksRareV2, X2Y2, Utils }; diff --git a/src/types/action/action.ts b/src/types/action/action.ts index 7469eac..fee75f6 100644 --- a/src/types/action/action.ts +++ b/src/types/action/action.ts @@ -50,8 +50,8 @@ export interface SignData { verifyingContract: string; }; signatureKind: 'eip191' | 'eip712'; - types: Record; - value: Record; + types: Record; + value: Record; message?: string; } diff --git a/src/types/action/processor.ts b/src/types/action/processor.ts index 910df23..e3ae1b9 100644 --- a/src/types/action/processor.ts +++ b/src/types/action/processor.ts @@ -9,7 +9,7 @@ export interface ActionProcessor { processPassThroughAction: ( action: AggregatorAction, params: ProcessPassThroughActionParams - ) => Promise; + ) => Promise; processControllerAction(action: AggregatorAction): Promise[]>; } diff --git a/src/types/aggregator/cancel.ts b/src/types/aggregator/cancel.ts index 8e720ef..37f29af 100644 --- a/src/types/aggregator/cancel.ts +++ b/src/types/aggregator/cancel.ts @@ -10,5 +10,5 @@ export interface CancelOrdersReq { sign?: string; signMessage?: string; }; - orders: Order[]; + orders: (Order & { orderHash?: string })[]; } diff --git a/src/types/aggregator/create.ts b/src/types/aggregator/create.ts index e9bdcfe..d198e73 100644 --- a/src/types/aggregator/create.ts +++ b/src/types/aggregator/create.ts @@ -1,5 +1,4 @@ import { OrderKind, Orderbook } from '../order'; -import { SafeAny } from '../safe-any'; /** * create listings @@ -15,7 +14,7 @@ export interface CreateListingsReq { params: CreateListingInput[]; extraArgs?: { blurAuth?: string; - } + }; } export interface CreateListingInput { @@ -74,7 +73,7 @@ export interface CreateOffersReq { params: CreateOfferInput[]; extraArgs?: { blurAuth?: string; - } + }; } export interface CreateOfferInput { diff --git a/src/types/aggregator/fulfill.ts b/src/types/aggregator/fulfill.ts index 21acd6f..c2686bf 100644 --- a/src/types/aggregator/fulfill.ts +++ b/src/types/aggregator/fulfill.ts @@ -4,11 +4,12 @@ export interface FulfillListingsReq { buyer: string; noDirect?: boolean; - orderIds: string[]; + orderIds?: string[]; + orderHashes?: string[]; safeMode: boolean; extraArgs?: { blurAuth?: string; - } + }; } /** @@ -19,12 +20,13 @@ export interface FulfillOffersReq { sellerAddress: string; extraArgs?: { blurAuth?: string; - } + }; } export interface OfferFulfillmentIntention { contractAddress: string; - orderId: string; + orderId?: string; + orderHash?: string; quantity: number; tokenId: string; } diff --git a/src/types/aggregator/response.ts b/src/types/aggregator/response.ts index 22afaf5..a135917 100644 --- a/src/types/aggregator/response.ts +++ b/src/types/aggregator/response.ts @@ -2,10 +2,14 @@ import { ActionKind, ActionTaskExecutor, AggregatorAction } from '../action'; export interface AggregatorApiResponse { actions: AggregatorAction[]; + invalidOrderHashes?: string[]; + invalidOrderIds?: string[]; } export interface AggregatorResponse { actions: AggregatorAction[]; + invalidOrderHashes?: string[]; + invalidOrderIds?: string[]; executeActions: ActionTaskExecutor['execute']; } diff --git a/src/types/config.ts b/src/types/config.ts index 1e461f3..0fbaaad 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -1,4 +1,4 @@ -import { HttpsProxyAgent } from 'https-proxy-agent'; +import { Agent } from 'https'; import { provider } from 'web3-core'; // # user-land interface , core should implement this @@ -21,7 +21,7 @@ export interface Config { openSeaApiKeyConfig?: ApiKeyConfig; looksRareApiKeyConfig?: ApiKeyConfig; x2y2ApiKeyConfig?: ApiKeyConfig; - agent?: HttpsProxyAgent; + agent?: Agent; } export type ApiKeyConfig = { diff --git a/src/types/order-fetcher.ts b/src/types/order-fetcher.ts index 207d4bf..60b0620 100644 --- a/src/types/order-fetcher.ts +++ b/src/types/order-fetcher.ts @@ -74,6 +74,7 @@ export interface ListingOrderDTO { orderExpirationTime?: number; orderGeneratedTime: number; orderId: string; + orderHash: string; price: TokenPrice; quantityFilled: number; quantityRemaining: number; @@ -96,6 +97,7 @@ export interface OfferDTO { orderCreateTime: number; orderExpirationTime: number; orderId: string; + orderHash: string; price: TokenPrice; quantityRemaining: number; rawData?: string; diff --git a/src/types/safe-any.ts b/src/types/safe-any.ts index 063cefa..9ffcdf1 100644 --- a/src/types/safe-any.ts +++ b/src/types/safe-any.ts @@ -1 +1,2 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export type SafeAny = any; diff --git a/tsconfig.json b/tsconfig.json index 516fd87..e971885 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -37,6 +37,5 @@ "__test__", "./node_modules/types-bn/index.d.ts", "./node_modules/types-ethereumjs-util/index.d.ts", - "./node_modules/https-proxy-agent/index.d.ts" ] }