From aadb064c80c634303f0df3530bc1f5ca5760ce5a Mon Sep 17 00:00:00 2001 From: Michael Kim Date: Mon, 6 Nov 2023 14:30:06 +0900 Subject: [PATCH] [Relay] Improve payment information using royalties to be stored in the database --- .github/workflows/main.yml | 12 + packages/relay/config/config.example.yaml | 31 --- packages/relay/config/config.yaml | 39 ++- packages/relay/config/config_test.yaml | 39 ++- packages/relay/docker-compose.yml | 27 +- packages/relay/env/.env.sample | 7 + packages/relay/package.json | 8 +- packages/relay/src/DefaultServer.ts | 13 +- packages/relay/src/common/Config.ts | 212 ++++++++++++++++ packages/relay/src/main.ts | 38 +-- packages/relay/src/routers/DefaultRouter.ts | 6 +- packages/relay/src/routers/LedgerRouter.ts | 6 +- packages/relay/src/routers/PaymentRouter.ts | 54 ++-- packages/relay/src/routers/ShopRouter.ts | 6 +- packages/relay/src/storage/RelayStorage.ts | 144 +++++++++++ packages/relay/src/storage/Storage.ts | 235 ++++++++++++++++++ packages/relay/src/storage/mapper/payment.xml | 57 +++++ packages/relay/src/storage/mapper/table.xml | 42 ++++ packages/relay/src/types/index.ts | 16 ++ packages/relay/test/Endpoints.test.ts | 10 +- packages/relay/test/Payment.test.ts | 19 +- packages/relay/test/Shop.test.ts | 6 +- packages/relay/test/ShopWithdraw.test.ts | 6 +- packages/relay/tsconfig.json | 2 +- yarn.lock | 136 +++++++++- 25 files changed, 1038 insertions(+), 133 deletions(-) delete mode 100644 packages/relay/config/config.example.yaml create mode 100644 packages/relay/src/storage/RelayStorage.ts create mode 100644 packages/relay/src/storage/Storage.ts create mode 100644 packages/relay/src/storage/mapper/payment.xml create mode 100644 packages/relay/src/storage/mapper/table.xml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 499c6d83..c4245ce2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,6 +20,18 @@ jobs: with: cache: 'yarn' node-version: ${{ matrix.node-version }} + + - name: Setup MySQL 8.0 + uses: ankane/setup-mysql@v1 + with: + mysql-version: 8.0 + database: relay + + - name: Config MySQL + run: | + mysqladmin -u root password '12345678' + exit; + - name: Install dependencies run: yarn install --pure-lockfile - name: Check formatting of Contract diff --git a/packages/relay/config/config.example.yaml b/packages/relay/config/config.example.yaml deleted file mode 100644 index b62d9eb0..00000000 --- a/packages/relay/config/config.example.yaml +++ /dev/null @@ -1,31 +0,0 @@ -################################################################################ -## Network interface ## -################################################################################ -server: - # Address to which we bind (default: 127.0.0.1) - address: "0.0.0.0" - # Port on which we bind (default: 3000) - port: 3000 - -################################################################################ -## Logging options ## -################################################################################ -logging: - # The name of the folder where the logs are stored. - folder: logs - # Values: error, warn, info, http, verbose, debug, silly - level: http - # Default to false - console: true - -relay: - managerKeys: - - ${MANAGER_KEY1} - accessKey: ${ACCESS_KEY} - certifier: ${CERTIFIER_KEY} - -contracts: - tokenAddress : ${TOKEN_CONTRACT_ADDRESS} - ledgerAddress : ${LEDGER_CONTRACT_ADDRESS} - phoneLinkerAddress : ${PHONE_LINKER_CONTRACT_ADDRESS} - diff --git a/packages/relay/config/config.yaml b/packages/relay/config/config.yaml index 8c793c7e..a2248c74 100644 --- a/packages/relay/config/config.yaml +++ b/packages/relay/config/config.yaml @@ -3,9 +3,22 @@ ################################################################################ server: # Address to which we bind (default: 127.0.0.1) - address: ${HOST_IP} + address: "${HOST_IP}" # Port on which we bind (default: 3000) - port: ${HOST_PORT} + port: "${HOST_PORT}" + +################################################################################ +## Database options ## +################################################################################ +database: + host: "${DATABASE_HOST}" + user: "${DATABASE_USER}" + database: "${DATABASE_NAME}" + password: "${DATABASE_PASSWORD}" + port: "${DATABASE_PORT}" + waitForConnections: true + connectionLimit: 30 + queueLimit: 0 ################################################################################ ## Logging options ## @@ -20,16 +33,16 @@ logging: relay: managerKeys: - - ${MANAGER_KEY1} - - ${MANAGER_KEY2} - - ${MANAGER_KEY3} - - ${MANAGER_KEY4} - - ${MANAGER_KEY5} - accessKey: ${ACCESS_KEY} - certifier: ${CERTIFIER_KEY} + - "${MANAGER_KEY1}" + - "${MANAGER_KEY2}" + - "${MANAGER_KEY3}" + - "${MANAGER_KEY4}" + - "${MANAGER_KEY5}" + accessKey: "${ACCESS_KEY}" + certifier: "${CERTIFIER_KEY}" contracts: - tokenAddress : ${TOKEN_CONTRACT_ADDRESS} - ledgerAddress : ${LEDGER_CONTRACT_ADDRESS} - phoneLinkerAddress : ${PHONE_LINKER_CONTRACT_ADDRESS} - shopAddress : ${SHOP_CONTRACT_ADDRESS} + tokenAddress : "${TOKEN_CONTRACT_ADDRESS}" + ledgerAddress : "${LEDGER_CONTRACT_ADDRESS}" + phoneLinkerAddress : "${PHONE_LINKER_CONTRACT_ADDRESS}" + shopAddress : "${SHOP_CONTRACT_ADDRESS}" diff --git a/packages/relay/config/config_test.yaml b/packages/relay/config/config_test.yaml index 05ff434a..a2248c74 100644 --- a/packages/relay/config/config_test.yaml +++ b/packages/relay/config/config_test.yaml @@ -3,9 +3,22 @@ ################################################################################ server: # Address to which we bind (default: 127.0.0.1) - address: "0.0.0.0" + address: "${HOST_IP}" # Port on which we bind (default: 3000) - port: 3001 + port: "${HOST_PORT}" + +################################################################################ +## Database options ## +################################################################################ +database: + host: "${DATABASE_HOST}" + user: "${DATABASE_USER}" + database: "${DATABASE_NAME}" + password: "${DATABASE_PASSWORD}" + port: "${DATABASE_PORT}" + waitForConnections: true + connectionLimit: 30 + queueLimit: 0 ################################################################################ ## Logging options ## @@ -20,16 +33,16 @@ logging: relay: managerKeys: - - ${MANAGER_KEY1} - - ${MANAGER_KEY2} - - ${MANAGER_KEY3} - - ${MANAGER_KEY4} - - ${MANAGER_KEY5} - accessKey: ${ACCESS_KEY} - certifier: ${CERTIFIER_KEY} + - "${MANAGER_KEY1}" + - "${MANAGER_KEY2}" + - "${MANAGER_KEY3}" + - "${MANAGER_KEY4}" + - "${MANAGER_KEY5}" + accessKey: "${ACCESS_KEY}" + certifier: "${CERTIFIER_KEY}" contracts: - tokenAddress : ${TOKEN_CONTRACT_ADDRESS} - ledgerAddress : ${LEDGER_CONTRACT_ADDRESS} - phoneLinkerAddress : ${PHONE_LINKER_CONTRACT_ADDRESS} - + tokenAddress : "${TOKEN_CONTRACT_ADDRESS}" + ledgerAddress : "${LEDGER_CONTRACT_ADDRESS}" + phoneLinkerAddress : "${PHONE_LINKER_CONTRACT_ADDRESS}" + shopAddress : "${SHOP_CONTRACT_ADDRESS}" diff --git a/packages/relay/docker-compose.yml b/packages/relay/docker-compose.yml index 62855531..33a53ed9 100644 --- a/packages/relay/docker-compose.yml +++ b/packages/relay/docker-compose.yml @@ -1,21 +1,18 @@ version: '3.3' + services: - dms-osx-relay: - image: bosagora/dms-osx-relay:latest - container_name: dms-osx-relay + mysql: + image: "mysql:8.0" + cap_add: + - SYS_NICE ports: - - '3000:3000' + - "3306:3306" + command: --default-authentication-plugin=mysql_native_password restart: always - extra_hosts: - - host.docker.internal:host-gateway + volumes: + - mysql_db:/var/lib/mysql environment: - PRODUCTION_NET_URL: http://host.docker.internal:8545 - PRODUCTION_CHAIN_ID: 24680 - HOST_IP: 0.0.0.0 - HOST_PORT: 3000 - networks: - - default_network + MYSQL_ROOT_PASSWORD: "12345678" -networks: - default_network: - driver: bridge +volumes: + mysql_db: diff --git a/packages/relay/env/.env.sample b/packages/relay/env/.env.sample index de784486..11cec4f2 100644 --- a/packages/relay/env/.env.sample +++ b/packages/relay/env/.env.sample @@ -8,6 +8,13 @@ TEST_NET_URL=https://testnet.bosagora.org PRODUCTION_NET_URL=http://localhost:8545 PRODUCTION_CHAIN_ID=24680 +# DATABASE INFO +DATABASE_HOST=127.0.0.1 +DATABASE_USER=root +DATABASE_NAME=relay +DATABASE_PASSWORD=12345678 +DATABASE_PORT=3306 + # 0xDc245797409fb79446523Fa1A4ca97294eef22EE DEPLOYER=0x2b5d5cc406b66c0398d0b8327d340cb4f6e30540621802e115506fe001398ba3 diff --git a/packages/relay/package.json b/packages/relay/package.json index 994bf518..b4d1adda 100644 --- a/packages/relay/package.json +++ b/packages/relay/package.json @@ -51,7 +51,9 @@ "@types/mocha": "^10.0.0", "@types/node": "^12.20.43", "@types/node-cron": "^3.0.1", - "@types/urijs": "^1.19.12", + "@types/urijs": "^1.19.12" + }, + "dependencies": { "argparse": "^2.0.1", "assert": "^2.0.0", "axios": "^0.26.0", @@ -59,6 +61,7 @@ "chai": "^4.3.7", "chai-http": "^4.3.7", "cors": "^2.8.5", + "del-osx-artifacts": "^1.1.1", "dms-osx-artifacts": "^1.1.7", "dotenv": "^10.0.0", "ethereum-waffle": "^4.0.10", @@ -69,8 +72,9 @@ "hardhat": "^2.12.7", "hardhat-gas-reporter": "^1.0.7", "ip": "^1.1.5", - "del-osx-artifacts": "^1.1.1", "moment": "^2.29.1", + "mybatis-mapper": "^0.7.1", + "mysql2": "^3.6.3", "prettier": "^2.5.1", "prettier-plugin-solidity": "^1.1.1", "smart-buffer": "^4.1.0", diff --git a/packages/relay/src/DefaultServer.ts b/packages/relay/src/DefaultServer.ts index 441dcd1d..464b96f0 100644 --- a/packages/relay/src/DefaultServer.ts +++ b/packages/relay/src/DefaultServer.ts @@ -9,6 +9,7 @@ import { ShopRouter } from "./routers/ShopRouter"; import { WebService } from "./service/WebService"; import { RelaySigners } from "./contract/Signers"; +import { RelayStorage } from "./storage/RelayStorage"; export class DefaultServer extends WebService { /** @@ -22,20 +23,22 @@ export class DefaultServer extends WebService { public readonly shopRouter: ShopRouter; public readonly paymentRouter: PaymentRouter; public readonly relaySigners: RelaySigners; + public readonly storage: RelayStorage; /** * Constructor * @param config Configuration */ - constructor(config: Config) { + constructor(config: Config, storage: RelayStorage) { super(config.server.port, config.server.address); this.config = config; + this.storage = storage; this.relaySigners = new RelaySigners(this.config); - this.defaultRouter = new DefaultRouter(this, this.config, this.relaySigners); - this.ledgerRouter = new LedgerRouter(this, this.config, this.relaySigners); - this.shopRouter = new ShopRouter(this, this.config, this.relaySigners); - this.paymentRouter = new PaymentRouter(this, this.config, this.relaySigners); + this.defaultRouter = new DefaultRouter(this, this.config, this.storage, this.relaySigners); + this.ledgerRouter = new LedgerRouter(this, this.config, this.storage, this.relaySigners); + this.shopRouter = new ShopRouter(this, this.config, this.storage, this.relaySigners); + this.paymentRouter = new PaymentRouter(this, this.config, this.storage, this.relaySigners); } /** diff --git a/packages/relay/src/common/Config.ts b/packages/relay/src/common/Config.ts index 64f7e568..099cb937 100644 --- a/packages/relay/src/common/Config.ts +++ b/packages/relay/src/common/Config.ts @@ -25,6 +25,11 @@ export class Config implements IConfig { */ public server: ServerConfig; + /** + * Database config + */ + public database: DatabaseConfig; + /** * Logging config */ @@ -39,6 +44,7 @@ export class Config implements IConfig { */ constructor() { this.server = new ServerConfig(); + this.database = new DatabaseConfig(); this.logging = new LoggingConfig(); this.relay = new RelayConfig(); this.contracts = new ContractsConfig(); @@ -85,6 +91,7 @@ export class Config implements IConfig { return (process.env || {})[key]; }) as IConfig; this.server.readFromObject(cfg.server); + this.database.readFromObject(cfg.database); this.logging.readFromObject(cfg.logging); this.relay.readFromObject(cfg.relay); this.contracts.readFromObject(cfg.contracts); @@ -150,6 +157,149 @@ export class ServerConfig implements IServerConfig { } } +/** + * Database config + */ +export class DatabaseConfig implements IDatabaseConfig { + /** + * The host of mysql + */ + host: string; + + /** + * The user of mysql + */ + user: string; + + /** + * The password of mysql + */ + password: string; + + /** + * The database name + */ + database?: string; + + /** + * The host database port + */ + port: number; + + /** + * multiple Statements exec config + */ + multipleStatements: boolean; + + /** + * Determines the pool's action when no connections are available + * and the limit has been reached. + * If true, the pool will queue the connection request and call + * it when one becomes available. + * If false, the pool will immediately call back with an error. + */ + waitForConnections: boolean; + + /** + * The maximum number of connections to create at once. + */ + connectionLimit: number; + + /** + * The maximum number of connection requests the pool + * will queue before returning an error from getConnection. + * If set to 0, there is no limit to the number of queued connection requests. + */ + queueLimit: number; + + /** + * Constructor + * @param host Mysql database host + * @param user Mysql database user + * @param password Mysql database password + * @param database Mysql database name + * @param port Mysql database port + * @param multipleStatements Mysql allow multiple statement to execute (true / false) + * @param waitForConnections Determines the pool's action when no connections are available + * and the limit has been reached. + * If true, the pool will queue the connection request and call + * it when one becomes available. + * If false, the pool will immediately call back with an error. + * @param connectionLimit The maximum number of connections to create at once. + * @param queueLimit The maximum number of connection requests the pool + * will queue before returning an error from getConnection. + * If set to 0, there is no limit to the number of queued connection requests. + */ + constructor( + host?: string, + user?: string, + password?: string, + database?: string, + port?: number, + multipleStatements?: boolean, + waitForConnections?: boolean, + connectionLimit?: number, + queueLimit?: number + ) { + const conf = extend(true, {}, DatabaseConfig.defaultValue()); + extend(true, conf, { + host, + user, + password, + database, + port, + multipleStatements, + waitForConnections, + connectionLimit, + queueLimit, + }); + this.host = conf.host; + this.user = conf.user; + this.password = conf.password; + this.database = conf.database; + this.port = conf.port; + this.multipleStatements = conf.multipleStatements; + this.waitForConnections = conf.waitForConnections; + this.connectionLimit = conf.connectionLimit; + this.queueLimit = conf.queueLimit; + } + + /** + * Returns default value + */ + public static defaultValue(): IDatabaseConfig { + return { + host: "localhost", + user: "root", + password: "12345678", + database: "boascan", + port: 3306, + multipleStatements: true, + waitForConnections: true, + connectionLimit: 10, + queueLimit: 0, + }; + } + + /** + * Reads from Object + * @param config The object of IDatabaseConfig + */ + public readFromObject(config: IDatabaseConfig) { + const conf = extend(true, {}, DatabaseConfig.defaultValue()); + extend(true, conf, config); + this.host = conf.host; + this.user = conf.user; + this.password = conf.password; + this.database = conf.database; + this.port = conf.port; + this.multipleStatements = conf.multipleStatements; + this.waitForConnections = conf.waitForConnections; + this.connectionLimit = conf.connectionLimit; + this.queueLimit = conf.queueLimit; + } +} + /** * Logging config */ @@ -314,6 +464,62 @@ export interface IServerConfig { port: number; } +/** + * The interface of database config + */ +export interface IDatabaseConfig { + /** + * The host of mysql + */ + host: string; + + /** + * The user of mysql + */ + user: string; + + /** + * The password of mysql + */ + password: string; + + /** + * The database name + */ + database?: string; + + /** + * The host database port + */ + port: number; + + /** + * Multiple Statements execution statement Option + */ + multipleStatements: boolean; + + /** + * Determines the pool's action when no connections are available + * and the limit has been reached. + * If true, the pool will queue the connection request and call + * it when one becomes available. + * If false, the pool will immediately call back with an error. + */ + waitForConnections: boolean; + + /** + * The maximum number of connections to create at once. + */ + connectionLimit: number; + + /** + * The maximum number of connection requests the pool + * will queue before returning an error from getConnection. + * If set to 0, there is no limit to the number of queued connection requests. + */ + queueLimit: number; +} + /** * The interface of logging config */ @@ -347,6 +553,7 @@ export interface IContractsConfig { shopAddress: string; currencyRateAddress: string; } + /** * The interface of main config */ @@ -356,6 +563,11 @@ export interface IConfig { */ server: IServerConfig; + /** + * Database config + */ + database: IDatabaseConfig; + /** * Logging config */ diff --git a/packages/relay/src/main.ts b/packages/relay/src/main.ts index 812e5744..3de6d78d 100644 --- a/packages/relay/src/main.ts +++ b/packages/relay/src/main.ts @@ -1,6 +1,8 @@ import { Config } from "./common/Config"; import { logger, Logger } from "./common/Logger"; import { DefaultServer } from "./DefaultServer"; +import { RelayStorage } from "./storage/RelayStorage"; +import { Storage } from "./storage/Storage"; let server: DefaultServer; @@ -32,21 +34,27 @@ async function main() { logger.info(`address: ${config.server.address}`); logger.info(`port: ${config.server.port}`); - server = new DefaultServer(config); - return server.start().catch((error: any) => { - // handle specific listen errors with friendly messages - switch (error.code) { - case "EACCES": - logger.error(`${config.server.port} requires elevated privileges`); - break; - case "EADDRINUSE": - logger.error(`Port ${config.server.port} is already in use`); - break; - default: - logger.error(`An error occurred while starting the server: ${error.stack}`); - } - process.exit(1); - }); + Storage.waiteForConnection(config.database) + .then(() => { + return RelayStorage.make(config.database); + }) + .then(async (storage) => { + server = new DefaultServer(config, storage); + return server.start().catch((error: any) => { + // handle specific listen errors with friendly messages + switch (error.code) { + case "EACCES": + logger.error(`${config.server.port} requires elevated privileges`); + break; + case "EADDRINUSE": + logger.error(`Port ${config.server.port} is already in use`); + break; + default: + logger.error(`An error occurred while starting the server: ${error.stack}`); + } + process.exit(1); + }); + }); } // We recommend this pattern to be able to use async/await everywhere diff --git a/packages/relay/src/routers/DefaultRouter.ts b/packages/relay/src/routers/DefaultRouter.ts index 76d206ef..37c75ff5 100644 --- a/packages/relay/src/routers/DefaultRouter.ts +++ b/packages/relay/src/routers/DefaultRouter.ts @@ -6,6 +6,7 @@ import * as hre from "hardhat"; import express from "express"; import { ISignerItem, RelaySigners } from "../contract/Signers"; +import { RelayStorage } from "../storage/RelayStorage"; export class DefaultRouter { /** @@ -52,15 +53,18 @@ export class DefaultRouter { */ private _currencyRateContract: CurrencyRate | undefined; + private _storage: RelayStorage; + /** * * @param service WebService * @param config Configuration */ - constructor(service: WebService, config: Config, relaySigners: RelaySigners) { + constructor(service: WebService, config: Config, storage: RelayStorage, relaySigners: RelaySigners) { this._web_service = service; this._config = config; + this._storage = storage; this._relaySigners = relaySigners; } diff --git a/packages/relay/src/routers/LedgerRouter.ts b/packages/relay/src/routers/LedgerRouter.ts index b5fdb6ae..1fceed31 100644 --- a/packages/relay/src/routers/LedgerRouter.ts +++ b/packages/relay/src/routers/LedgerRouter.ts @@ -10,6 +10,7 @@ import * as hre from "hardhat"; import express from "express"; import { ISignerItem, RelaySigners } from "../contract/Signers"; +import { RelayStorage } from "../storage/RelayStorage"; export class LedgerRouter { /** @@ -56,15 +57,18 @@ export class LedgerRouter { */ private _currencyRateContract: CurrencyRate | undefined; + private _storage: RelayStorage; + /** * * @param service WebService * @param config Configuration */ - constructor(service: WebService, config: Config, relaySigners: RelaySigners) { + constructor(service: WebService, config: Config, storage: RelayStorage, relaySigners: RelaySigners) { this._web_service = service; this._config = config; + this._storage = storage; this._relaySigners = relaySigners; } diff --git a/packages/relay/src/routers/PaymentRouter.ts b/packages/relay/src/routers/PaymentRouter.ts index 81fdf3db..38763026 100644 --- a/packages/relay/src/routers/PaymentRouter.ts +++ b/packages/relay/src/routers/PaymentRouter.ts @@ -12,20 +12,7 @@ import * as hre from "hardhat"; import express from "express"; import { ISignerItem, RelaySigners } from "../contract/Signers"; - -export interface LoyaltyPaymentInputData { - paymentId: string; - purchaseId: string; - amount: BigNumber; - currency: string; - shopId: string; - account: string; - loyaltyType: LoyaltyType; - purchaseAmount: BigNumber; - feeAmount: BigNumber; - totalAmount: BigNumber; - paymentStatus: LoyaltyPaymentInputDataStatus; -} +import { RelayStorage } from "../storage/RelayStorage"; export class PaymentRouter { /** @@ -72,19 +59,19 @@ export class PaymentRouter { */ private _currencyRateContract: CurrencyRate | undefined; - private payments: Map; + private _storage: RelayStorage; /** * * @param service WebService * @param config Configuration */ - constructor(service: WebService, config: Config, relaySigners: RelaySigners) { + constructor(service: WebService, config: Config, storage: RelayStorage, relaySigners: RelaySigners) { this._web_service = service; this._config = config; + this._storage = storage; this._relaySigners = relaySigners; - this.payments = new Map(); } private get app(): express.Application { @@ -585,7 +572,19 @@ export class PaymentRouter { totalAmount, paymentStatus: LoyaltyPaymentInputDataStatus.CREATED, }; - this.payments.set(paymentId, item); + await this._storage.postPayment( + item.paymentId, + item.purchaseId, + item.amount, + item.currency, + item.shopId, + item.account, + item.loyaltyType, + item.purchaseAmount, + item.feeAmount, + item.totalAmount, + item.paymentStatus + ); /// 사용자에게 푸쉬 메세지 발송 후 서명을 확인함 @@ -635,7 +634,7 @@ export class PaymentRouter { try { const paymentId: string = String(req.body.paymentId).trim(); - const item = this.payments.get(paymentId); + const item = await this._storage.getPayment(paymentId); if (item === undefined) { return res.status(200).json( this.makeResponseData(402, undefined, { @@ -691,7 +690,7 @@ export class PaymentRouter { try { const paymentId: string = String(req.body.paymentId).trim(); const signature: string = String(req.body.signature).trim(); - const item = this.payments.get(paymentId); + const item = await this._storage.getPayment(paymentId); if (item === undefined) { res.status(200).json( this.makeResponseData(401, undefined, { @@ -738,7 +737,9 @@ export class PaymentRouter { account: item.account, signature, }); + item.paymentStatus = LoyaltyPaymentInputDataStatus.CREATE_CONFIRMED; + await this._storage.updatePaymentStatus(item.paymentId, item.paymentStatus); return res.status(200).json( this.makeResponseData(200, { paymentId: item.paymentId, @@ -790,7 +791,7 @@ export class PaymentRouter { try { const paymentId: string = String(req.body.paymentId).trim(); const signature: string = String(req.body.signature).trim(); - const item = this.payments.get(paymentId); + const item = await this._storage.getPayment(paymentId); if (item === undefined) { return res.status(200).json( this.makeResponseData(401, undefined, { @@ -828,6 +829,7 @@ export class PaymentRouter { } item.paymentStatus = LoyaltyPaymentInputDataStatus.CREATE_DENIED; + await this._storage.updatePaymentStatus(item.paymentId, item.paymentStatus); return res.status(200).json( this.makeResponseData(200, { paymentId: item.paymentId, @@ -885,7 +887,7 @@ export class PaymentRouter { } const paymentId: string = String(req.body.paymentId).trim(); - const item = this.payments.get(paymentId); + const item = await this._storage.getPayment(paymentId); if (item === undefined) { return res.status(200).json( this.makeResponseData(401, undefined, { @@ -903,6 +905,7 @@ export class PaymentRouter { } item.paymentStatus = LoyaltyPaymentInputDataStatus.CANCELED; + await this._storage.updatePaymentStatus(item.paymentId, item.paymentStatus); return res.status(200).json( this.makeResponseData(200, { paymentId: item.paymentId, @@ -952,7 +955,7 @@ export class PaymentRouter { try { const paymentId: string = String(req.body.paymentId).trim(); const signature: string = String(req.body.signature).trim(); - const item = this.payments.get(paymentId); + const item = await this._storage.getPayment(paymentId); if (item === undefined) { res.status(200).json( this.makeResponseData(401, undefined, { @@ -1002,7 +1005,9 @@ export class PaymentRouter { signature, certifierSignature, }); + item.paymentStatus = LoyaltyPaymentInputDataStatus.CANCEL_CONFIRMED; + await this._storage.updatePaymentStatus(item.paymentId, item.paymentStatus); return res.status(200).json( this.makeResponseData(200, { paymentId: item.paymentId, @@ -1054,7 +1059,7 @@ export class PaymentRouter { try { const paymentId: string = String(req.body.paymentId).trim(); const signature: string = String(req.body.signature).trim(); - const item = this.payments.get(paymentId); + const item = await this._storage.getPayment(paymentId); if (item === undefined) { return res.status(200).json( this.makeResponseData(401, undefined, { @@ -1089,6 +1094,7 @@ export class PaymentRouter { } item.paymentStatus = LoyaltyPaymentInputDataStatus.CANCEL_DENIED; + await this._storage.updatePaymentStatus(item.paymentId, item.paymentStatus); return res.status(200).json( this.makeResponseData(200, { paymentId: item.paymentId, diff --git a/packages/relay/src/routers/ShopRouter.ts b/packages/relay/src/routers/ShopRouter.ts index d35f18a8..00bb50e4 100644 --- a/packages/relay/src/routers/ShopRouter.ts +++ b/packages/relay/src/routers/ShopRouter.ts @@ -10,6 +10,7 @@ import * as hre from "hardhat"; import express from "express"; import { ISignerItem, RelaySigners } from "../contract/Signers"; +import { RelayStorage } from "../storage/RelayStorage"; export class ShopRouter { /** @@ -56,15 +57,18 @@ export class ShopRouter { */ private _currencyRateContract: CurrencyRate | undefined; + private _storage: RelayStorage; + /** * * @param service WebService * @param config Configuration */ - constructor(service: WebService, config: Config, relaySigners: RelaySigners) { + constructor(service: WebService, config: Config, storage: RelayStorage, relaySigners: RelaySigners) { this._web_service = service; this._config = config; + this._storage = storage; this._relaySigners = relaySigners; } diff --git a/packages/relay/src/storage/RelayStorage.ts b/packages/relay/src/storage/RelayStorage.ts new file mode 100644 index 00000000..7e8635c9 --- /dev/null +++ b/packages/relay/src/storage/RelayStorage.ts @@ -0,0 +1,144 @@ +import { IDatabaseConfig } from "../common/Config"; +import { Utils } from "../utils/Utils"; +import { Storage } from "./Storage"; + +import { BigNumber } from "ethers"; +import MybatisMapper from "mybatis-mapper"; +// tslint:disable-next-line:no-submodule-imports +import * as mysql from "mysql2/promise"; +import path from "path"; +import { LoyaltyPaymentInputData, LoyaltyPaymentInputDataStatus, LoyaltyType } from "../types"; + +/** + * The class that inserts and reads the ledger into the database. + */ +export class RelayStorage extends Storage { + constructor(databaseConfig: IDatabaseConfig, callback: (err: Error | null) => void) { + super(databaseConfig, callback); + MybatisMapper.createMapper([path.resolve(Utils.getInitCWD(), "src/storage/mapper/table.xml")]); + MybatisMapper.createMapper([path.resolve(Utils.getInitCWD(), "src/storage/mapper/payment.xml")]); + } + + /** + * Construct an instance of `Storage` using `Promise` API. + * + * @param databaseConfig + */ + public static make(databaseConfig: IDatabaseConfig): Promise { + return new Promise((resolve, reject) => { + const result = new RelayStorage(databaseConfig, (err: Error | null) => { + if (err) reject(err); + else resolve(result); + }); + return result; + }); + } + + /** + * Creates tables related to the ledger. + * @returns Returns the Promise. If it is finished successfully the `.then` + * of the returned Promise is called and if an error occurs the `.catch` + * is called with an error. + */ + public createTables(): Promise { + return this.exec(MybatisMapper.getStatement("table", "create_table")); + } + + /** + * Drop Database + * Use this only in the test code. + * @param database The name of database + */ + public async dropTestDB(database: any): Promise { + return this.exec(MybatisMapper.getStatement("table", "drop_table", { database })); + } + + public postPayment( + paymentId: string, + purchaseId: string, + amount: BigNumber, + currency: string, + shopId: string, + account: string, + loyaltyType: LoyaltyType, + purchaseAmount: BigNumber, + feeAmount: BigNumber, + totalAmount: BigNumber, + paymentStatus: LoyaltyPaymentInputDataStatus, + conn?: mysql.PoolConnection + ): Promise { + return new Promise(async (resolve, reject) => { + this.queryForMapper( + "payment", + "postPayment", + { + paymentId, + purchaseId, + amount: amount.toString(), + currency, + shopId, + account, + loyaltyType, + purchaseAmount: purchaseAmount.toString(), + feeAmount: feeAmount.toString(), + totalAmount: totalAmount.toString(), + paymentStatus, + }, + conn + ) + .then(() => { + return resolve(); + }) + .catch((reason) => { + if (reason instanceof Error) return reject(reason); + return reject(new Error(reason)); + }); + }); + } + + public getPayment(paymentId: string, conn?: mysql.PoolConnection): Promise { + return new Promise(async (resolve, reject) => { + this.queryForMapper("payment", "getPayment", { paymentId }, conn) + .then((rows: any[]) => { + if (rows.length > 0) { + const m = rows[0]; + return resolve({ + paymentId: m.paymentId, + purchaseId: m.purchaseId, + amount: m.amount, + currency: m.currency, + shopId: m.shopId, + account: m.account, + loyaltyType: m.loyaltyType, + purchaseAmount: m.purchaseAmount, + feeAmount: m.feeAmount, + totalAmount: m.totalAmount, + paymentStatus: m.paymentStatus, + }); + } else { + return resolve(undefined); + } + }) + .catch((reason) => { + if (reason instanceof Error) return reject(reason); + return reject(new Error(reason)); + }); + }); + } + + public updatePaymentStatus( + paymentId: string, + paymentStatus: LoyaltyPaymentInputDataStatus, + conn?: mysql.PoolConnection + ): Promise { + return this.queryForMapper( + "payment", + "updateStatus", + { + paymentId, + paymentStatus, + }, + conn + ); + } +} diff --git a/packages/relay/src/storage/Storage.ts b/packages/relay/src/storage/Storage.ts new file mode 100644 index 00000000..ea303761 --- /dev/null +++ b/packages/relay/src/storage/Storage.ts @@ -0,0 +1,235 @@ +import MybatisMapper, { Params } from "mybatis-mapper"; +// tslint:disable-next-line:no-submodule-imports +import * as mysql from "mysql2/promise"; +import { IDatabaseConfig } from "../common/Config"; +import { logger } from "../common/Logger"; + +export class Storage { + /** + * The instance of mysql Connection Pool. + */ + protected pool: mysql.Pool; + + /** + * Constructor + * @param databaseConfig Valid value is of type IDatabaseConfig, + * @param callback If provided, this function will be called when + * the database was opened successfully or when an error occurred. + * The first argument is an error object. If there is no error, this value is null. + */ + constructor(databaseConfig: IDatabaseConfig, callback: (err: Error | null) => void) { + const dbconfig: IDatabaseConfig = { + host: databaseConfig.host, + user: databaseConfig.user, + password: databaseConfig.password, + multipleStatements: databaseConfig.multipleStatements, + port: Number(databaseConfig.port), + waitForConnections: databaseConfig.waitForConnections, + connectionLimit: Number(databaseConfig.connectionLimit), + queueLimit: Number(databaseConfig.queueLimit), + }; + this.pool = mysql.createPool(dbconfig); + + this.query(`CREATE DATABASE IF NOT EXISTS \`${databaseConfig.database}\`;`, []) + .then(async (result) => { + dbconfig.database = databaseConfig.database; + this.pool = mysql.createPool(dbconfig); + this.createTables() + .then(() => { + if (callback != null) callback(null); + }) + .catch((err: any) => { + if (callback != null) callback(err); + }); + }) + .catch((err) => { + if (callback != null) callback(err); + }); + } + + /** + * The main thread waits until the database is accessed. + * The maximum waiting time is about 50 seconds. + * @param databaseConfig Valid value is of type IDatabaseConfig, + */ + public static waiteForConnection(databaseConfig: IDatabaseConfig): Promise { + const connection_config: mysql.ConnectionOptions = { + host: databaseConfig.host, + user: databaseConfig.user, + password: databaseConfig.password, + multipleStatements: databaseConfig.multipleStatements, + port: Number(databaseConfig.port), + }; + + return new Promise((resolve) => { + let try_count = 0; + const check_connection = async () => { + try_count++; + const connection = await mysql.createConnection(connection_config); + connection + .connect() + .then(() => { + return resolve(); + }) + .catch((err) => { + if (try_count < 10) { + setTimeout(() => { + check_connection(); + }, 5000); + } + }); + }; + check_connection(); + }); + } + + /** + * Creates tables. + * @returns Returns the Promise. If it is finished successfully the `.then` + * of the returned Promise is called and if an error occurs the `.catch` + * is called with an error. + */ + public createTables(): Promise { + return new Promise((resolve, reject) => { + resolve(); + }); + } + + /** + * Returns the DB connection of the Connection Pool. + * @returns Returns the Promise. If it is finished successfully the `.then` + * of the returned Promise is called and if an error occurs the `.catch` + * is called with an error. + */ + + public getConnection(): Promise { + return this.pool.getConnection(); + } + + /** + * Close the database + */ + public close(): Promise { + return this.pool.end(); + } + + /** + * Execute SQL to query the database for data. + * @param sql The SQL query to run. + * @param params When the SQL statement contains placeholders, + * you can pass them in here. + * @param conn Use this if it is providing a db connection. + * @returns Returns the Promise. If it is finished successfully the `.then` + * of the returned Promise is called with the records + * and if an error occurs the `.catch` is called with an error. + */ + public query(sql: string, params: any, conn?: mysql.PoolConnection): Promise { + return new Promise(async (resolve, reject) => { + let connection: mysql.PoolConnection | null = null; + try { + if (!conn) connection = await this.getConnection(); + else connection = conn; + + const [rows] = await connection.query(sql, params); + if (!conn) connection.release(); + resolve(rows as any); + } catch (err) { + if (!conn && connection) connection.release(); + return reject(err); + } + }); + } + + /** + * Execute SQL to query the database for data with MybatisMapper. + * @param namespace namespace of the query defined in the mapper file + * @param sql_id ID of the query defined in the mapper file + * @param param When the SQL statement contains placeholders, + * you can pass them in here. + * @param conn Use this if it is providing a db connection. + */ + public queryForMapper( + namespace: string, + sql_id: string, + param?: Params, + conn?: mysql.PoolConnection + ): Promise { + return new Promise(async (resolve, reject) => { + let connection: mysql.PoolConnection | null = null; + try { + if (!conn) connection = await this.getConnection(); + else connection = conn; + const sql = MybatisMapper.getStatement(namespace, sql_id, param, { language: "sql", indent: " " }); + const [rows] = await connection.query(sql); + if (!conn) connection.release(); + resolve(rows as any); + } catch (err) { + if (!conn && connection) connection.release(); + return reject(err); + } + }); + } + + /** + * Executes the SQL query + * @param sql The SQL query to run. + * @param conn Use this if it is providing a db connection. + * @returns Returns the Promise. If it is finished successfully the `.then` + * of the returned Promise is called and if an error occurs the `.catch` + * is called with an error. + */ + protected exec(sql: string, conn?: mysql.PoolConnection): Promise { + return new Promise(async (resolve, reject) => { + let connection: mysql.PoolConnection | null = null; + try { + if (!conn) connection = await this.getConnection(); + else connection = conn; + await connection.query(sql); + if (!conn) connection.release(); + resolve(); + } catch (err) { + if (!conn && connection) connection.release(); + return reject(err); + } + }); + } + + /** + * Mysql transaction statement + * To start a transaction explicitly, + * Open a transaction by issuing the beginning function + * the transaction is open until it is explicitly + * committed or rolled back. + * @param conn Use this if it is providing a db connection must be released after transaction end. + * @returns Returns the Promise. If it is finished successfully the `.then` + * of the returned Promise is called and if an error occurs the `.catch` + * is called with an error. + */ + public begin(conn: mysql.PoolConnection): Promise { + return conn.beginTransaction(); + } + + /** + * Mysql transaction statement + * Commit the changes to the database by using this. + * @returns Returns the Promise. If it is finished successfully the `.then` + * of the returned Promise is called and if an error occurs the `.catch` + * is called with an error. + */ + public commit(conn: mysql.PoolConnection): Promise { + return conn.commit(); + } + + /** + * Mysql transaction statement + * If it does not want to save the changes, + * it can roll back using this. + * @param conn Use this if it is providing a db connection must be released after transaction end. + * @returns Returns the Promise. If it is finished successfully the `.then` + * of the returned Promise is called and if an error occurs the `.catch` + * is called with an error. + */ + public rollback(conn: mysql.PoolConnection): Promise { + return conn.rollback(); + } +} diff --git a/packages/relay/src/storage/mapper/payment.xml b/packages/relay/src/storage/mapper/payment.xml new file mode 100644 index 00000000..73170b2d --- /dev/null +++ b/packages/relay/src/storage/mapper/payment.xml @@ -0,0 +1,57 @@ + + + + + + INSERT INTO payments + ( + paymentId , + purchaseId , + amount , + currency , + shopId , + account , + loyaltyType , + purchaseAmount , + feeAmount , + totalAmount , + paymentStatus + ) + VALUES + ( + #{paymentId}, + #{purchaseId}, + #{amount}, + #{currency}, + #{shopId}, + #{account}, + ${loyaltyType}, + #{purchaseAmount}, + #{feeAmount}, + #{totalAmount}, + ${paymentStatus} + ) + ON DUPLICATE KEY UPDATE + purchaseId = VALUES(purchaseId), + amount = VALUES(amount), + currency = VALUES(currency), + shopId = VALUES(shopId), + account = VALUES(account), + loyaltyType = VALUES(loyaltyType), + purchaseAmount = VALUES(purchaseAmount), + feeAmount = VALUES(feeAmount), + totalAmount = VALUES(totalAmount), + paymentStatus = VALUES(paymentStatus); + + + + + + UPDATE payments + SET + paymentStatus = #{paymentStatus} + WHERE paymentId = #{paymentId} + + diff --git a/packages/relay/src/storage/mapper/table.xml b/packages/relay/src/storage/mapper/table.xml new file mode 100644 index 00000000..65063eae --- /dev/null +++ b/packages/relay/src/storage/mapper/table.xml @@ -0,0 +1,42 @@ + + + + + paymentId: string; + purchaseId: string; + amount: BigNumber; + currency: string; + shopId: string; + account: string; + loyaltyType: LoyaltyType; + purchaseAmount: BigNumber; + feeAmount: BigNumber; + totalAmount: BigNumber; + paymentStatus: LoyaltyPaymentInputDataStatus; + + CREATE TABLE IF NOT EXISTS payments + ( + paymentId VARCHAR(66) NOT NULL, + purchaseId VARCHAR(66) NOT NULL, + amount VARCHAR(32) NOT NULL, + currency VARCHAR(12) NOT NULL, + shopId VARCHAR(66) NOT NULL, + account VARCHAR(42) NOT NULL, + loyaltyType INTEGER, + purchaseAmount VARCHAR(32) NOT NULL, + feeAmount VARCHAR(32) NOT NULL, + totalAmount VARCHAR(32) NOT NULL, + paymentStatus INTEGER, + PRIMARY KEY (paymentId) + ) ENGINE = InnoDB DEFAULT CHARSET = utf8; + + + + + + + diff --git a/packages/relay/src/types/index.ts b/packages/relay/src/types/index.ts index cd5a9336..e6fef502 100644 --- a/packages/relay/src/types/index.ts +++ b/packages/relay/src/types/index.ts @@ -1,3 +1,5 @@ +import { BigNumber } from "ethers"; + export enum LoyaltyType { POINT, TOKEN, @@ -16,3 +18,17 @@ export enum LoyaltyPaymentInputDataStatus { CANCEL_CONFIRMED, CANCEL_DENIED, } + +export interface LoyaltyPaymentInputData { + paymentId: string; + purchaseId: string; + amount: BigNumber; + currency: string; + shopId: string; + account: string; + loyaltyType: LoyaltyType; + purchaseAmount: BigNumber; + feeAmount: BigNumber; + totalAmount: BigNumber; + paymentStatus: LoyaltyPaymentInputDataStatus; +} diff --git a/packages/relay/test/Endpoints.test.ts b/packages/relay/test/Endpoints.test.ts index 97eddbdd..ca448398 100644 --- a/packages/relay/test/Endpoints.test.ts +++ b/packages/relay/test/Endpoints.test.ts @@ -10,6 +10,7 @@ import { ValidatorCollection, } from "../typechain-types"; import { TestClient, TestServer } from "./helper/Utility"; +import { RelayStorage } from "../src/storage/RelayStorage"; import chai, { expect } from "chai"; import { solidity } from "ethereum-waffle"; @@ -166,6 +167,7 @@ describe("Test of Server", function () { const client = new TestClient(); let server: TestServer; + let storage: RelayStorage; let serverURL: URL; let config: Config; @@ -289,7 +291,8 @@ describe("Test of Server", function () { before("Create TestServer", async () => { serverURL = new URL(`http://127.0.0.1:${config.server.port}`); - server = new TestServer(config); + storage = await RelayStorage.make(config.database); + server = new TestServer(config, storage); }); before("Start TestServer", async () => { @@ -298,6 +301,7 @@ describe("Test of Server", function () { after("Stop TestServer", async () => { await server.stop(); + await storage.dropTestDB(config.database.database); }); context("Prepare shop data", () => { @@ -411,7 +415,8 @@ describe("Test of Server", function () { before("Create TestServer", async () => { serverURL = new URL(`http://127.0.0.1:${config.server.port}`); - server = new TestServer(config); + storage = await RelayStorage.make(config.database); + server = new TestServer(config, storage); }); before("Start TestServer", async () => { @@ -420,6 +425,7 @@ describe("Test of Server", function () { after("Stop TestServer", async () => { await server.stop(); + await storage.dropTestDB(config.database.database); }); context("Prepare shop data", () => { diff --git a/packages/relay/test/Payment.test.ts b/packages/relay/test/Payment.test.ts index 80a10dd9..6da87db6 100644 --- a/packages/relay/test/Payment.test.ts +++ b/packages/relay/test/Payment.test.ts @@ -25,6 +25,8 @@ import { BigNumber, Wallet } from "ethers"; import { AddressZero } from "@ethersproject/constants"; import { LoyaltyPaymentInputDataStatus, LoyaltyType } from "../src/types"; +import { RelayStorage } from "../src/storage/RelayStorage"; + // tslint:disable-next-line:no-var-requires const URI = require("urijs"); @@ -169,6 +171,7 @@ describe("Test of Server", function () { }; const client = new TestClient(); + let storage: RelayStorage; let server: TestServer; let serverURL: URL; let config: Config; @@ -295,7 +298,8 @@ describe("Test of Server", function () { before("Create TestServer", async () => { serverURL = new URL(`http://127.0.0.1:${config.server.port}`); - server = new TestServer(config); + storage = await RelayStorage.make(config.database); + server = new TestServer(config, storage); }); before("Start TestServer", async () => { @@ -304,6 +308,7 @@ describe("Test of Server", function () { after("Stop TestServer", async () => { await server.stop(); + await storage.dropTestDB(config.database.database); }); context("Prepare shop data", () => { @@ -756,7 +761,8 @@ describe("Test of Server", function () { before("Create TestServer", async () => { serverURL = new URL(`http://127.0.0.1:${config.server.port}`); - server = new TestServer(config); + storage = await RelayStorage.make(config.database); + server = new TestServer(config, storage); }); before("Start TestServer", async () => { @@ -765,6 +771,7 @@ describe("Test of Server", function () { after("Stop TestServer", async () => { await server.stop(); + await storage.dropTestDB(config.database.database); }); context("Prepare shop data", () => { @@ -1085,7 +1092,8 @@ describe("Test of Server", function () { before("Create TestServer", async () => { serverURL = new URL(`http://127.0.0.1:${config.server.port}`); - server = new TestServer(config); + storage = await RelayStorage.make(config.database); + server = new TestServer(config, storage); }); before("Start TestServer", async () => { @@ -1094,6 +1102,7 @@ describe("Test of Server", function () { after("Stop TestServer", async () => { await server.stop(); + await storage.dropTestDB(config.database.database); }); context("Prepare shop data", () => { @@ -1397,7 +1406,8 @@ describe("Test of Server", function () { before("Create TestServer", async () => { serverURL = new URL(`http://127.0.0.1:${config.server.port}`); - server = new TestServer(config); + storage = await RelayStorage.make(config.database); + server = new TestServer(config, storage); }); before("Start TestServer", async () => { @@ -1406,6 +1416,7 @@ describe("Test of Server", function () { after("Stop TestServer", async () => { await server.stop(); + await storage.dropTestDB(config.database.database); }); context("Prepare shop data", () => { diff --git a/packages/relay/test/Shop.test.ts b/packages/relay/test/Shop.test.ts index c1bcbdf0..e7f5ba76 100644 --- a/packages/relay/test/Shop.test.ts +++ b/packages/relay/test/Shop.test.ts @@ -1,4 +1,5 @@ import { Amount } from "../src/common/Amount"; +import { RelayStorage } from "../src/storage/RelayStorage"; import { ContractUtils } from "../src/utils/ContractUtils"; import { CurrencyRate, @@ -162,6 +163,7 @@ describe("Test for ShopCollection", () => { const client = new TestClient(); let server: TestServer; + let storage: RelayStorage; let serverURL: URL; let config: Config; @@ -249,7 +251,8 @@ describe("Test for ShopCollection", () => { before("Create TestServer", async () => { serverURL = new URL(`http://127.0.0.1:${config.server.port}`); - server = new TestServer(config); + storage = await RelayStorage.make(config.database); + server = new TestServer(config, storage); }); before("Start TestServer", async () => { @@ -258,6 +261,7 @@ describe("Test for ShopCollection", () => { after("Stop TestServer", async () => { await server.stop(); + await storage.dropTestDB(config.database.database); }); it("Add", async () => { diff --git a/packages/relay/test/ShopWithdraw.test.ts b/packages/relay/test/ShopWithdraw.test.ts index 6823def1..cfc37d4f 100644 --- a/packages/relay/test/ShopWithdraw.test.ts +++ b/packages/relay/test/ShopWithdraw.test.ts @@ -29,6 +29,7 @@ import path from "path"; // tslint:disable-next-line:no-implicit-dependencies import { AddressZero } from "@ethersproject/constants"; +import { RelayStorage } from "../src/storage/RelayStorage"; chai.use(solidity); @@ -190,6 +191,7 @@ describe("Test for ShopCollection", () => { const client = new TestClient(); let server: TestServer; + let storage: RelayStorage; let serverURL: URL; let config: Config; @@ -347,7 +349,8 @@ describe("Test for ShopCollection", () => { before("Create TestServer", async () => { serverURL = new URL(`http://127.0.0.1:${config.server.port}`); - server = new TestServer(config); + storage = await RelayStorage.make(config.database); + server = new TestServer(config, storage); }); before("Start TestServer", async () => { @@ -356,6 +359,7 @@ describe("Test for ShopCollection", () => { after("Stop TestServer", async () => { await server.stop(); + await storage.dropTestDB(config.database.database); }); before("Prepare Token", async () => { diff --git a/packages/relay/tsconfig.json b/packages/relay/tsconfig.json index 3c09a9a0..9a063a01 100644 --- a/packages/relay/tsconfig.json +++ b/packages/relay/tsconfig.json @@ -8,6 +8,6 @@ "declaration": true, "resolveJsonModule": true }, - "include": ["./scripts", "./test", "./typechain-types"], + "include": ["./scripts", "./src", "./test", "./typechain-types"], "files": ["./hardhat.config.ts"] } diff --git a/yarn.lock b/yarn.lock index 9f8465d1..05ce266b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2916,7 +2916,7 @@ commander@^10.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== -commander@^2.12.1, commander@^2.20.3: +commander@^2.12.1, commander@^2.19.0, commander@^2.20.3: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -3222,6 +3222,11 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== +denque@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" + integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== + depd@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" @@ -3277,6 +3282,11 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +discontinuous-range@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a" + integrity sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ== + dns-over-http-resolver@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/dns-over-http-resolver/-/dns-over-http-resolver-1.2.3.tgz#194d5e140a42153f55bb79ac5a64dd2768c36af9" @@ -4336,6 +4346,13 @@ ganache@7.4.3: bufferutil "4.0.5" utf-8-validate "5.0.7" +generate-function@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" + integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== + dependencies: + is-property "^1.0.2" + get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -4371,6 +4388,11 @@ get-port@^3.1.0: resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" integrity sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg== +get-stdin@=8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" + integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== + get-stream@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" @@ -4875,6 +4897,13 @@ hosted-git-info@^2.6.0: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== +html-parse-stringify@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2" + integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg== + dependencies: + void-elements "3.1.0" + http-basic@^8.1.1: version "8.1.3" resolved "https://registry.yarnpkg.com/http-basic/-/http-basic-8.1.3.tgz#a7cabee7526869b9b710136970805b1004261bbf" @@ -4963,7 +4992,7 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@^0.6.2: +iconv-lite@^0.6.2, iconv-lite@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -5359,6 +5388,11 @@ is-plain-obj@^2.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== +is-property@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + integrity sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g== + is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -5981,7 +6015,7 @@ long@^4.0.0: resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== -long@^5.2.0: +long@^5.2.0, long@^5.2.1: version "5.2.3" resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== @@ -6017,6 +6051,16 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-cache@^7.14.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + +lru-cache@^8.0.0: + version "8.0.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-8.0.5.tgz#983fe337f3e176667f8e567cfcce7cb064ea214e" + integrity sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA== + "lru-cache@^9.1.1 || ^10.0.0": version "10.0.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.1.tgz#0a3be479df549cca0e5d693ac402ff19537a6b7a" @@ -6467,6 +6511,11 @@ moment@^2.29.1: resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== +moo@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.2.tgz#f9fe82473bc7c184b0d32e2215d3f6e67278733c" + integrity sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -6565,6 +6614,35 @@ mustache@^4.2.0: resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== +mybatis-mapper@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/mybatis-mapper/-/mybatis-mapper-0.7.1.tgz#f4c1c3e5ba2af590e4d8dbe49b5b9b0151e4af10" + integrity sha512-th02VMccSBASUsx9tDS4y9lNfCIUZHzX7UPudvuh6heuyJ37UEeFDIHxPNWcqkmt0TPAX/+nK6jaNH9WBnAnKQ== + dependencies: + html-parse-stringify "3.0.1" + sql-formatter "12.2.4" + +mysql2@^3.6.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-3.6.3.tgz#d478be7aa97ea675d92f079d072f5a45cb772902" + integrity sha512-qYd/1CDuW1KYZjD4tzg2O8YS3X/UWuGH8ZMHyMeggMTXL3yOdMisbwZ5SNkHzDGlZXKYLAvV8tMrEH+NUMz3fw== + dependencies: + denque "^2.1.0" + generate-function "^2.3.1" + iconv-lite "^0.6.3" + long "^5.2.1" + lru-cache "^8.0.0" + named-placeholders "^1.1.3" + seq-queue "^0.0.5" + sqlstring "^2.3.2" + +named-placeholders@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/named-placeholders/-/named-placeholders-1.1.3.tgz#df595799a36654da55dda6152ba7a137ad1d9351" + integrity sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w== + dependencies: + lru-cache "^7.14.1" + nano-json-stream-parser@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" @@ -6605,6 +6683,16 @@ natural-orderby@^2.0.3: resolved "https://registry.yarnpkg.com/natural-orderby/-/natural-orderby-2.0.3.tgz#8623bc518ba162f8ff1cdb8941d74deb0fdcc016" integrity sha512-p7KTHxU0CUrcOXe62Zfrb5Z13nLvPhSWR/so3kFulUQU0sgUll2Z0LwpsLN351eOOD+hRGu/F1g+6xDfPeD++Q== +nearley@^2.20.1: + version "2.20.1" + resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.20.1.tgz#246cd33eff0d012faf197ff6774d7ac78acdd474" + integrity sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ== + dependencies: + commander "^2.19.0" + moo "^0.5.0" + railroad-diagrams "^1.0.0" + randexp "0.4.6" + negotiator@0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" @@ -7205,6 +7293,19 @@ quick-lru@^5.1.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== +railroad-diagrams@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e" + integrity sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A== + +randexp@0.4.6: + version "0.4.6" + resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3" + integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ== + dependencies: + discontinuous-range "1.0.0" + ret "~0.1.10" + randombytes@^2.0.1, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -7454,6 +7555,11 @@ restore-cursor@^3.1.0: onetime "^5.1.0" signal-exit "^3.0.2" +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + retimer@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/retimer/-/retimer-3.0.0.tgz#98b751b1feaf1af13eb0228f8ea68b8f9da530df" @@ -7673,6 +7779,11 @@ send@0.18.0: range-parser "~1.2.1" statuses "2.0.1" +seq-queue@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" + integrity sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q== + serialize-javascript@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" @@ -7944,6 +8055,20 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== +sql-formatter@12.2.4: + version "12.2.4" + resolved "https://registry.yarnpkg.com/sql-formatter/-/sql-formatter-12.2.4.tgz#1671b2a5c40a87414281e2483108a24f98e8a556" + integrity sha512-Qj45LEHSfgrdYDOrAtIkR8SdS10SWcqCIM2WZwQwMKF2v9sM0K2dlThWPS7eYCUrhttZIrU1WwuIwHk7MjsWOw== + dependencies: + argparse "^2.0.1" + get-stdin "=8.0.0" + nearley "^2.20.1" + +sqlstring@^2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.3.tgz#2ddc21f03bce2c387ed60680e739922c65751d0c" + integrity sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg== + sshpk@^1.7.0: version "1.17.0" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" @@ -8783,6 +8908,11 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +void-elements@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" + integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w== + wabt@1.0.24: version "1.0.24" resolved "https://registry.yarnpkg.com/wabt/-/wabt-1.0.24.tgz#c02e0b5b4503b94feaf4a30a426ef01c1bea7c6c"