Remove testnet-faucet from monorepo

This commit is contained in:
fabioberger 2019-07-24 19:57:30 +02:00
parent 35099d9b2f
commit 4990c4903d
21 changed files with 3 additions and 842 deletions

View File

@ -32,5 +32,3 @@ contracts: ['contracts']
@0x/json-schemas: ['packages/json-schemas'] @0x/json-schemas: ['packages/json-schemas']
@0x/ethereum-types: ['ethereum-types'] @0x/ethereum-types: ['ethereum-types']
@0x/connect: ['packages/connect'] @0x/connect: ['packages/connect']
@0x/testnet-faucets: ['packages/testnet-faucets']
@0x/monorepo-scripts: ['packages/monorepo-scripts']

3
.gitignore vendored
View File

@ -78,9 +78,6 @@ TODO.md
# VSCode file # VSCode file
.vscode .vscode
# server cli
packages/testnet-faucets/server/
# generated contract artifacts/ # generated contract artifacts/
contracts/integrations/generated-artifacts/ contracts/integrations/generated-artifacts/
contracts/staking/generated-artifacts/ contracts/staking/generated-artifacts/

View File

@ -96,10 +96,9 @@ These packages are all under development. See [/contracts/README.md](/contracts/
#### Private Packages #### Private Packages
| Package | Description | | Package | Description |
| -------------------------------------------------- | -------------------------------------------------------------------------------- | | ---------------------------------- | -------------------------------------------------------------------------------- |
| [`@0x/instant`](/packages/instant) | A free and flexible way to offer simple crypto purchasing in any app or website. | | [`@0x/instant`](/packages/instant) | A free and flexible way to offer simple crypto purchasing in any app or website. |
| [`@0x/testnet-faucets`](/packages/testnet-faucets) | A faucet micro-service that dispenses test ERC20 tokens or Ether |
## Usage ## Usage

View File

@ -1,13 +0,0 @@
FROM node
WORKDIR /src
COPY package.json .
RUN npm i
RUN npm install forever -g
COPY . .
EXPOSE 3000
CMD ["forever", "./server/server.js"]

View File

@ -1,153 +0,0 @@
## @0x/testnet-faucets
This faucet dispenses 0.1 test ether to one recipient per second and 0.1 test ZRX every 5 seconds. It has a max queue size of 1000.
## Installation
This is a private package and therefore is not published to npm. In order to build and run this package locally, see the contributing instructions below.
## Contributing
We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository.
Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
### Install dependencies
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
```bash
yarn config set workspaces-experimental true
```
Then install dependencies
```bash
yarn install
```
### Build
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
```bash
PKG=@0x/testnet-faucets yarn build
```
Or continuously rebuild on change:
```bash
PKG=@0x/testnet-faucets yarn watch
```
### Clean
```bash
yarn clean
```
### Lint
```bash
yarn lint
```
### Start
Set the following environment variables:
```bash
export DISPENSER_ADDRESS=0x5409ed021d9299bf6814279a6a1411a7e866a631
export DISPENSER_PRIVATE_KEY=f2f48ee19680706196e2e339e5da3491186e0c4c5030670656b0e0164837257d
export FAUCET_ROLLBAR_ACCESS_KEY={GET_THIS_FROM_ROLLBAR_ACCOUNT_SETTINGS}
export INFURA_API_KEY={GET_THIS_FROM_INFURA}
```
If you want to talk to testrpc, set the following environment variable:
```bash
export FAUCET_ENVIRONMENT=development
```
Infura API Key can be requested here: https://infura.io/signup
Note: The above public/private keys exist when running `testrpc` with the following option `--mnemonic concert load couple harbor equip island argue ramp clarify fence smart topic`.
```bash
PKG=0x.js yarn watch
```
### Endpoints
`GET /ping`
Returns `pong`
`GET /info`
Returns a JSON payload describing the state of the queues for each network. For example:
```json
{
"3": {
"ether": {
"full": false,
"size": 4
},
"zrx": {
"full": false,
"size": 6
}
},
"42": {
"ether": {
"full": false,
"size": 8
},
"zrx": {
"full": false,
"size": 20
}
}
}
```
`GET /ether/:recipient?networkId=:networkId`
Schedules a transaction that sends 0.1 ETH to the `recipient` on the network specified by `networkId` where `recipient` is a hex encoded Ethereum address prefixed with `0x`. If no `networkId` is provided via query parameters the faucet will default to network 42 (Kovan).
`GET /zrx/:recipient?networkId=:networkId`
Schedules a transaction that sends 0.1 ZRX to the `recipient` on the network specified by `networkId` where `recipient` is a hex encoded Ethereum address prefixed with `0x`. If no `networkId` is provided via query parameters the faucet will default to network 42 (Kovan).
`GET /order/weth/:recipient?networkId=:networkId`
Returns a JSON payload describing an order for 0.1 WETH in exchange for 0.1 ZRX signed by the dispenser address on the network specified by `networkId`. The taker is specified by `recipient` where `recipient` is a hex encoded Ethereum address prefixed with `0x`. If no `networkId` is provided via query parameters the faucet will default to network 42 (Kovan).
`GET /order/zrx/:recipient?networkId=:networkId`
Returns a JSON payload describing an order for 0.1 ZRX in exchange for 0.1 WETH signed by the dispenser address on the network specified by `networkId`. The taker is specified by `recipient` where `recipient` is a hex encoded Ethereum address prefixed with `0x`. If no `networkId` is provided via query parameters the faucet will default to network 42 (Kovan).
#### Example request
```bash
curl -i http://localhost:3000/ether/0x14e2F1F157E7DD4057D02817436D628A37120FD1\?networkId=3
```
This command will request the local server to initiate a transfer of 0.1 ETH from the dispensing address to `0x14e2F1F157E7DD4057D02817436D628A37120FD1` on the Ropsten testnet.
### Docker configs
```
docker run -d \
-p 80:3000 \
--name testnet-faucets \
--log-opt max-size=100m \
--log-opt max-file=20 \
-e DISPENSER_ADDRESS=$DISPENSER_ADDRESS \
-e DISPENSER_PRIVATE_KEY=$DISPENSER_PRIVATE_KEY \
-e FAUCET_ROLLBAR_ACCESS_KEY=$FAUCET_ROLLBAR_ACCESS_KEY \
-e FAUCET_ENVIRONMENT=production \
-e INFURA_API_KEY=$INFURA_API_KEY \
testnet-faucets
```

View File

@ -1,91 +0,0 @@
const gulp = require('gulp');
const nodemon = require('nodemon');
const path = require('path');
const webpack = require('webpack');
const fs = require('fs');
const nodeExternals = require('webpack-node-externals');
const config = {
target: 'node',
entry: [path.join(__dirname, '/src/ts/server.ts')],
output: {
path: path.join(__dirname, '/server'),
filename: 'server.js',
},
devtool: 'source-map',
resolve: {
modules: [path.join(__dirname, '/src/ts'), 'node_modules'],
extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
alias: {
ts: path.join(__dirname, '/src/ts'),
contract_artifacts: path.join(__dirname, '/src/contract_artifacts'),
},
},
module: {
rules: [
{
test: /\.js$/,
loader: 'source-map-loader',
},
{
test: /\.tsx?$/,
loader: 'awesome-typescript-loader',
},
],
},
plugins: [
new webpack.BannerPlugin({
banner: 'require("source-map-support").install();',
raw: true,
entryOnly: false,
}),
],
externals: nodeExternals({
modulesDir: path.join(__dirname, '../../node_modules'),
}),
watchOptions: {
ignored: /server|node_modules|transpiled/,
},
};
gulp.task('build', function(done) {
webpack(config).run(onBuild(done));
});
gulp.task('watch', function() {
webpack(config).watch(100, function(err, stats) {
onBuild()(err, stats);
nodemon.restart();
});
});
gulp.task('run', ['watch'], function() {
nodemon({
execMap: {
js: 'node',
},
script: path.join(__dirname, 'server/server'),
ignore: ['*'],
watch: ['foo/'],
ext: 'noop',
}).on('restart', function() {
console.log('Restarted!');
});
});
function onBuild(done) {
return function(err, stats) {
if (err) {
console.log('Error', err);
process.exit(1);
} else {
console.log(stats.toString());
}
if (done) {
if (stats.compilation.errors && stats.compilation.errors.length > 0) {
process.exit(1);
}
done();
}
};
}

View File

@ -1,53 +0,0 @@
{
"private": true,
"name": "@0x/testnet-faucets",
"version": "1.0.89",
"engines": {
"node": ">=6.12"
},
"description": "A faucet micro-service that dispenses test ERC20 tokens or Ether",
"main": "server.js",
"scripts": {
"build": "node ../../node_modules/gulp/bin/gulp.js build",
"build:ci": "yarn build",
"dev": "node ../../node_modules/gulp/bin/gulp.js run",
"start": "node ./server/server.js",
"lint": "tslint --format stylish --project .",
"fix": "tslint --fix --format stylish --project .",
"clean": "shx rm -rf server"
},
"author": "Fabio Berger",
"license": "Apache-2.0",
"dependencies": {
"0x.js": "^8.0.0-beta.0",
"@0x/contract-addresses": "^3.3.0-beta.2",
"@0x/contract-wrappers": "^12.2.0-beta.1",
"@0x/subproviders": "^5.1.0-beta.1",
"@0x/typescript-typings": "^4.4.0-beta.1",
"@0x/utils": "^4.6.0-beta.1",
"@0x/web3-wrapper": "^6.1.0-beta.1",
"body-parser": "^1.17.1",
"ethereum-types": "^2.2.0-beta.1",
"ethereumjs-tx": "^1.3.5",
"ethereumjs-util": "^5.1.1",
"express": "^4.15.2",
"lodash": "^4.17.11",
"rollbar": "^2.5.0"
},
"devDependencies": {
"@0x/tslint-config": "^3.1.0-beta.1",
"@types/body-parser": "^1.16.1",
"@types/express": "^4.0.35",
"@types/lodash": "4.14.104",
"awesome-typescript-loader": "^5.2.1",
"gulp": "^3.9.1",
"make-promises-safe": "^1.1.0",
"nodemon": "^1.11.0",
"shx": "^0.2.2",
"source-map-loader": "^0.2.4",
"tslint": "5.11.0",
"typescript": "3.0.1",
"webpack": "^4.20.2",
"webpack-node-externals": "^1.6.0"
}
}

View File

@ -1,7 +0,0 @@
export const configs = {
DISPENSER_ADDRESS: (process.env.DISPENSER_ADDRESS as string).toLowerCase(),
DISPENSER_PRIVATE_KEY: process.env.DISPENSER_PRIVATE_KEY,
ENVIRONMENT: process.env.FAUCET_ENVIRONMENT,
INFURA_API_KEY: process.env.INFURA_API_KEY,
ROLLBAR_ACCESS_KEY: process.env.FAUCET_ROLLBAR_ACCESS_KEY,
};

View File

@ -1,5 +0,0 @@
export const constants = {
SUCCESS_STATUS: 200,
SERVICE_UNAVAILABLE_STATUS: 503,
BAD_REQUEST_STATUS: 400,
};

View File

@ -1,52 +0,0 @@
import { intervalUtils, logUtils } from '@0x/utils';
import { errorReporter } from './error_reporter';
const MAX_QUEUE_SIZE = 500;
const DEFAULT_QUEUE_INTERVAL_MS = 1000;
export class DispatchQueue {
private readonly _queueIntervalMs: number;
private readonly _queue: Array<() => Promise<void>>;
private _queueIntervalIdIfExists?: NodeJS.Timer;
constructor() {
this._queueIntervalMs = DEFAULT_QUEUE_INTERVAL_MS;
this._queue = [];
this._start();
}
public add(taskAsync: () => Promise<void>): boolean {
if (this.isFull()) {
return false;
}
this._queue.push(taskAsync);
return true;
}
public size(): number {
return this._queue.length;
}
public isFull(): boolean {
return this.size() >= MAX_QUEUE_SIZE;
}
public stop(): void {
if (this._queueIntervalIdIfExists !== undefined) {
intervalUtils.clearAsyncExcludingInterval(this._queueIntervalIdIfExists);
}
}
private _start(): void {
this._queueIntervalIdIfExists = intervalUtils.setAsyncExcludingInterval(
async () => {
const taskAsync = this._queue.shift();
if (taskAsync === undefined) {
return Promise.resolve();
}
await taskAsync();
},
this._queueIntervalMs,
(err: Error) => {
logUtils.log(`Unexpected err: ${err} - ${JSON.stringify(err)}`);
// tslint:disable-next-line:no-floating-promises
errorReporter.reportAsync(err);
},
);
}
}

View File

@ -1,68 +0,0 @@
import { ERC20TokenContract, SupportedProvider } from '0x.js';
import { BigNumber, logUtils } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as _ from 'lodash';
import { configs } from './configs';
import { TOKENS_BY_CHAIN } from './tokens';
const DISPENSE_AMOUNT_ETHER = 0.1;
const DISPENSE_AMOUNT_TOKEN = 1;
const DISPENSE_MAX_AMOUNT_TOKEN = 100;
const DISPENSE_MAX_AMOUNT_ETHER = 2;
type AsyncTask = () => Promise<void>;
export const dispenseAssetTasks = {
dispenseEtherTask(recipientAddress: string, web3Wrapper: Web3Wrapper): AsyncTask {
return async () => {
logUtils.log(`Processing ETH ${recipientAddress}`);
const userBalance = await web3Wrapper.getBalanceInWeiAsync(recipientAddress);
const maxAmountInWei = Web3Wrapper.toWei(new BigNumber(DISPENSE_MAX_AMOUNT_ETHER));
if (userBalance.isGreaterThanOrEqualTo(maxAmountInWei)) {
logUtils.log(
`User exceeded ETH balance maximum (${maxAmountInWei}) ${recipientAddress} ${userBalance} `,
);
return;
}
const txHash = await web3Wrapper.sendTransactionAsync({
from: configs.DISPENSER_ADDRESS,
to: recipientAddress,
value: Web3Wrapper.toWei(new BigNumber(DISPENSE_AMOUNT_ETHER)),
});
logUtils.log(`Sent ${DISPENSE_AMOUNT_ETHER} ETH to ${recipientAddress} tx: ${txHash}`);
};
},
dispenseTokenTask(
recipientAddress: string,
tokenSymbol: string,
chainId: number,
provider: SupportedProvider,
): AsyncTask {
return async () => {
logUtils.log(`Processing ${tokenSymbol} ${recipientAddress}`);
const amountToDispense = new BigNumber(DISPENSE_AMOUNT_TOKEN);
const tokenIfExists = _.get(TOKENS_BY_CHAIN, [chainId, tokenSymbol]);
if (tokenIfExists === undefined) {
throw new Error(`Unsupported asset type: ${tokenSymbol}`);
}
const baseUnitAmount = Web3Wrapper.toBaseUnitAmount(amountToDispense, tokenIfExists.decimals);
const erc20Token = new ERC20TokenContract(tokenIfExists.address, provider);
const userBalanceBaseUnits = await erc20Token.balanceOf.callAsync(recipientAddress);
const maxAmountBaseUnits = Web3Wrapper.toBaseUnitAmount(
new BigNumber(DISPENSE_MAX_AMOUNT_TOKEN),
tokenIfExists.decimals,
);
if (userBalanceBaseUnits.isGreaterThanOrEqualTo(maxAmountBaseUnits)) {
logUtils.log(
`User exceeded token balance maximum (${maxAmountBaseUnits}) ${recipientAddress} ${userBalanceBaseUnits} `,
);
return;
}
const txHash = await erc20Token.transfer.sendTransactionAsync(recipientAddress, baseUnitAmount, {
from: configs.DISPENSER_ADDRESS,
});
logUtils.log(`Sent ${amountToDispense} ${tokenSymbol} to ${recipientAddress} tx: ${txHash}`);
};
},
};

View File

@ -1,37 +0,0 @@
import { logUtils } from '@0x/utils';
import * as express from 'express';
import rollbar = require('rollbar');
import { configs } from './configs';
export const errorReporter = {
setup(): void {
rollbar.init(configs.ROLLBAR_ACCESS_KEY, {
environment: configs.ENVIRONMENT,
});
rollbar.handleUncaughtExceptions(configs.ROLLBAR_ACCESS_KEY);
process.on('unhandledRejection', async (err: Error) => {
logUtils.log(`Uncaught exception ${err}. Stack: ${err.stack}`);
await errorReporter.reportAsync(err);
process.exit(1);
});
},
async reportAsync(err: Error, req?: express.Request): Promise<any> {
if (configs.ENVIRONMENT === 'development') {
return; // Do not log development environment errors
}
return new Promise<any>((resolve, reject) => {
rollbar.handleError(err, req, (rollbarErr: Error) => {
if (rollbarErr) {
logUtils.log(`Error reporting to rollbar, ignoring: ${rollbarErr}`);
reject(rollbarErr);
} else {
resolve();
}
});
});
},
errorHandler(): any {
return rollbar.errorHandler(configs.ROLLBAR_ACCESS_KEY);
},
};

View File

@ -1,6 +0,0 @@
declare module '*.json' {
const json: any;
/* tslint:disable */
export default json;
/* tslint:enable */
}

View File

@ -1,199 +0,0 @@
import {
assetDataUtils,
BigNumber,
generatePseudoRandomSalt,
Order,
orderHashUtils,
RPCSubprovider,
signatureUtils,
SignedOrder,
Web3ProviderEngine,
} from '0x.js';
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
import { NonceTrackerSubprovider, PrivateKeyWalletSubprovider } from '@0x/subproviders';
import { logUtils } from '@0x/utils';
import { SupportedProvider, Web3Wrapper } from '@0x/web3-wrapper';
import * as express from 'express';
import * as _ from 'lodash';
import { configs } from './configs';
import { constants } from './constants';
import { DispatchQueue } from './dispatch_queue';
import { dispenseAssetTasks } from './dispense_asset_tasks';
import { rpcUrls } from './rpc_urls';
import { TOKENS_BY_CHAIN } from './tokens';
interface ChainConfig {
dispatchQueue: DispatchQueue;
web3Wrapper: Web3Wrapper;
provider: SupportedProvider;
chainId: number;
}
interface ItemByChainId<T> {
[chainId: string]: T;
}
enum RequestedAssetType {
ETH = 'ETH', // tslint:disable-line:enum-naming
WETH = 'WETH', // tslint:disable-line:enum-naming
ZRX = 'ZRX', // tslint:disable-line:enum-naming
}
const FIVE_DAYS_IN_MS = 4.32e8; // TODO: make this configurable
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
const ZERO = new BigNumber(0);
const ASSET_AMOUNT = new BigNumber(0.1);
export class Handler {
private readonly _chainConfigByChainId: ItemByChainId<ChainConfig> = {};
private static _createProviderEngine(rpcUrl: string): Web3ProviderEngine {
if (configs.DISPENSER_PRIVATE_KEY === undefined) {
throw new Error('Dispenser Private key not found');
}
const engine = new Web3ProviderEngine();
engine.addProvider(new NonceTrackerSubprovider());
engine.addProvider(new PrivateKeyWalletSubprovider(configs.DISPENSER_PRIVATE_KEY));
engine.addProvider(new RPCSubprovider(rpcUrl));
engine.start();
return engine;
}
constructor() {
_.forIn(rpcUrls, (rpcUrl: string, chainIdString: string) => {
const providerObj = Handler._createProviderEngine(rpcUrl);
const web3Wrapper = new Web3Wrapper(providerObj);
// tslint:disable-next-line:custom-no-magic-numbers
const chainId = parseInt(chainIdString, 10);
const dispatchQueue = new DispatchQueue();
this._chainConfigByChainId[chainId] = {
dispatchQueue,
web3Wrapper,
provider: providerObj,
chainId,
};
});
}
public getQueueInfo(_req: express.Request, res: express.Response): void {
res.setHeader('Content-Type', 'application/json');
const queueInfo = _.mapValues(rpcUrls, (_rpcUrl: string, chainId: string) => {
const dispatchQueue = this._chainConfigByChainId[chainId].dispatchQueue;
return {
full: dispatchQueue.isFull(),
size: dispatchQueue.size(),
};
});
const payload = JSON.stringify(queueInfo);
res.status(constants.SUCCESS_STATUS).send(payload);
}
public dispenseEther(req: express.Request, res: express.Response): void {
this._dispenseAsset(req, res, RequestedAssetType.ETH);
}
public dispenseZRX(req: express.Request, res: express.Response): void {
this._dispenseAsset(req, res, RequestedAssetType.ZRX);
}
public async dispenseWETHOrderAsync(req: express.Request, res: express.Response): Promise<void> {
await this._dispenseOrderAsync(req, res, RequestedAssetType.WETH);
}
public async dispenseZRXOrderAsync(
req: express.Request,
res: express.Response,
_next: express.NextFunction,
): Promise<void> {
await this._dispenseOrderAsync(req, res, RequestedAssetType.ZRX);
}
private _dispenseAsset(req: express.Request, res: express.Response, requestedAssetType: RequestedAssetType): void {
const chainId = req.params.chainId;
const recipient = req.params.recipient;
const chainConfig = _.get(this._chainConfigByChainId, chainId);
if (chainConfig === undefined) {
res.status(constants.BAD_REQUEST_STATUS).send('UNSUPPORTED_CHAIN_ID');
return;
}
let dispenserTask;
switch (requestedAssetType) {
case RequestedAssetType.ETH:
dispenserTask = dispenseAssetTasks.dispenseEtherTask(recipient, chainConfig.web3Wrapper);
break;
case RequestedAssetType.WETH:
case RequestedAssetType.ZRX:
dispenserTask = dispenseAssetTasks.dispenseTokenTask(
recipient,
requestedAssetType,
chainConfig.chainId,
chainConfig.provider,
);
break;
default:
throw new Error(`Unsupported asset type: ${requestedAssetType}`);
}
const didAddToQueue = chainConfig.dispatchQueue.add(dispenserTask);
if (!didAddToQueue) {
res.status(constants.SERVICE_UNAVAILABLE_STATUS).send('QUEUE_IS_FULL');
return;
}
logUtils.log(`Added ${recipient} to queue: ${requestedAssetType} chainId: ${chainId}`);
res.status(constants.SUCCESS_STATUS).end();
}
private async _dispenseOrderAsync(
req: express.Request,
res: express.Response,
requestedAssetType: RequestedAssetType,
): Promise<void> {
const chainConfig = _.get(this._chainConfigByChainId, req.params.chainId);
if (chainConfig === undefined) {
res.status(constants.BAD_REQUEST_STATUS).send('UNSUPPORTED_CHAIN_ID');
return;
}
res.setHeader('Content-Type', 'application/json');
const makerTokenIfExists = _.get(TOKENS_BY_CHAIN, [chainConfig.chainId, requestedAssetType]);
if (makerTokenIfExists === undefined) {
throw new Error(`Unsupported asset type: ${requestedAssetType}`);
}
const takerTokenSymbol =
requestedAssetType === RequestedAssetType.WETH ? RequestedAssetType.ZRX : RequestedAssetType.WETH;
const takerTokenIfExists = _.get(TOKENS_BY_CHAIN, [chainConfig.chainId, takerTokenSymbol]);
if (takerTokenIfExists === undefined) {
throw new Error(`Unsupported asset type: ${takerTokenSymbol}`);
}
const makerAssetAmount = Web3Wrapper.toBaseUnitAmount(ASSET_AMOUNT, makerTokenIfExists.decimals);
const takerAssetAmount = Web3Wrapper.toBaseUnitAmount(ASSET_AMOUNT, takerTokenIfExists.decimals);
const makerAssetData = assetDataUtils.encodeERC20AssetData(makerTokenIfExists.address);
const takerAssetData = assetDataUtils.encodeERC20AssetData(takerTokenIfExists.address);
const contractAddresses = getContractAddressesForChainOrThrow(chainConfig.chainId);
const order: Order = {
makerAddress: configs.DISPENSER_ADDRESS,
takerAddress: req.params.recipient as string,
makerFee: ZERO,
takerFee: ZERO,
makerAssetAmount,
takerAssetAmount,
makerAssetData,
takerAssetData,
salt: generatePseudoRandomSalt(),
makerFeeAssetData: makerAssetData,
takerFeeAssetData: takerAssetData,
feeRecipientAddress: NULL_ADDRESS,
senderAddress: NULL_ADDRESS,
expirationTimeSeconds: new BigNumber(Date.now() + FIVE_DAYS_IN_MS)
// tslint:disable-next-line:custom-no-magic-numbers
.div(1000)
.integerValue(BigNumber.ROUND_FLOOR),
exchangeAddress: contractAddresses.exchange,
chainId: chainConfig.chainId,
};
const orderHash = orderHashUtils.getOrderHashHex(order);
const signature = await signatureUtils.ecSignHashAsync(
chainConfig.web3Wrapper.getProvider(),
orderHash,
configs.DISPENSER_ADDRESS,
);
const signedOrder: SignedOrder = {
...order,
signature,
};
const payload = JSON.stringify(signedOrder);
logUtils.log(`Dispensed signed order: ${payload}`);
res.status(constants.SUCCESS_STATUS).send(payload);
}
}

View File

@ -1,28 +0,0 @@
import { addressUtils } from '@0x/utils';
import { NextFunction, Request, Response } from 'express';
import * as _ from 'lodash';
import { constants } from './constants';
import { rpcUrls } from './rpc_urls';
const DEFAULT_CHAIN_ID = 42; // kovan
export const parameterTransformer = {
transform(req: Request, res: Response, next: NextFunction): void {
const recipientAddress = req.params.recipient;
if (recipientAddress === undefined || !addressUtils.isAddress(recipientAddress)) {
res.status(constants.BAD_REQUEST_STATUS).send('INVALID_RECIPIENT_ADDRESS');
return;
}
const lowerCaseRecipientAddress = recipientAddress.toLowerCase();
req.params.recipient = lowerCaseRecipientAddress;
const chainId = _.get(req.query, 'chainId', DEFAULT_CHAIN_ID);
const rpcUrlIfExists = _.get(rpcUrls, chainId);
if (rpcUrlIfExists === undefined) {
res.status(constants.BAD_REQUEST_STATUS).send('UNSUPPORTED_CHAIN_ID');
return;
}
req.params.chainId = chainId;
next();
},
};

View File

@ -1,12 +0,0 @@
import { configs } from './configs';
const productionRpcUrls = {
'3': `https://ropsten.infura.io/${configs.INFURA_API_KEY}`,
'42': `https://kovan.infura.io/${configs.INFURA_API_KEY}`,
};
const developmentRpcUrls = {
'50': 'http://127.0.0.1:8545',
};
export const rpcUrls = configs.ENVIRONMENT === 'development' ? developmentRpcUrls : productionRpcUrls;

View File

@ -1,52 +0,0 @@
import * as bodyParser from 'body-parser';
import * as express from 'express';
import { constants } from './constants';
import { errorReporter } from './error_reporter';
import { Handler } from './handler';
import { parameterTransformer } from './parameter_transformer';
// Setup the errorReporter to catch uncaught exceptions and unhandled rejections
errorReporter.setup();
const app = express();
app.use(bodyParser.json()); // for parsing application/json
// tslint:disable-next-line:no-unused-variable
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
const handler = new Handler();
// tslint:disable-next-line:no-unused-variable
app.get('/ping', (req: express.Request, res: express.Response) => {
res.status(constants.SUCCESS_STATUS).send('pong');
});
app.get('/info', handler.getQueueInfo.bind(handler));
app.get(
'/ether/:recipient',
parameterTransformer.transform.bind(parameterTransformer),
handler.dispenseEther.bind(handler),
);
app.get(
'/zrx/:recipient',
parameterTransformer.transform.bind(parameterTransformer),
handler.dispenseZRX.bind(handler),
);
app.get(
'/order/weth/:recipient',
parameterTransformer.transform.bind(parameterTransformer),
handler.dispenseWETHOrderAsync.bind(handler),
);
app.get(
'/order/zrx/:recipient',
parameterTransformer.transform.bind(parameterTransformer),
handler.dispenseZRXOrderAsync.bind(handler),
);
// Log to rollbar any errors unhandled by handlers
app.use(errorReporter.errorHandler());
const DEFAULT_PORT = 3000;
const port = process.env.PORT || DEFAULT_PORT;
app.listen(port);

View File

@ -1,44 +0,0 @@
interface TokensByChain {
[chainId: number]: { [tokenSymbol: string]: { address: string; decimals: number } };
}
export const tokens = {
ZRX: {
decimals: 18,
},
WETH: {
decimals: 18,
},
};
export const TOKENS_BY_CHAIN: TokensByChain = {
3: {
ZRX: {
...tokens.ZRX,
address: '0xff67881f8d12f372d91baae9752eb3631ff0ed00',
},
WETH: {
...tokens.WETH,
address: '0xc778417e063141139fce010982780140aa0cd5ab',
},
},
42: {
ZRX: {
...tokens.ZRX,
address: '0x2002d3812f58e35f0ea1ffbf80a75a38c32175fa',
},
WETH: {
...tokens.WETH,
address: '0xd0a1e359811322d97991e03f863a0c30c2cf029c',
},
},
50: {
ZRX: {
...tokens.ZRX,
address: '0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c',
},
WETH: {
...tokens.WETH,
address: '0x0b1ba0af832d7c05fd64161e0db78e85978e8082',
},
},
};

View File

@ -1,9 +0,0 @@
{
"extends": "../../tsconfig",
"compilerOptions": {
"outDir": "lib",
"rootDir": "src/ts",
"strictPropertyInitialization": false
},
"include": ["./src/ts/**/*"]
}

View File

@ -1,3 +0,0 @@
{
"extends": ["@0x/tslint-config"]
}

View File

@ -60,7 +60,6 @@
{ "path": "./packages/sol-resolver" }, { "path": "./packages/sol-resolver" },
{ "path": "./packages/sra-spec" }, { "path": "./packages/sra-spec" },
{ "path": "./packages/subproviders" }, { "path": "./packages/subproviders" },
{ "path": "./packages/testnet-faucets" },
{ "path": "./packages/tslint-config" }, { "path": "./packages/tslint-config" },
{ "path": "./packages/types" }, { "path": "./packages/types" },
{ "path": "./packages/typescript-typings" }, { "path": "./packages/typescript-typings" },