Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Teton Finance] v4 support #384

Merged
merged 6 commits into from Feb 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
47 changes: 14 additions & 33 deletions README.md
Expand Up @@ -2,32 +2,27 @@

# 0x Starter Project

## ⚠️ Deprecation Warning ️️⚠️

This project does **not support 0x Protocol V4** and is no longer being actively maintained.

[![CircleCI](https://circleci.com/gh/0xProject/0x-starter-project.svg?style=svg)](https://circleci.com/gh/0xProject/0x-starter-project)

![cli](https://user-images.githubusercontent.com/27389/42074402-6dcc5ccc-7baf-11e8-84f1-9a27f1a96b08.png)
![cli](./content/screenshot.png)

This project will take you through a number of scenarios using the 0x v3 protocol.
The previous v1 starter project has been moved to the ['v1' branch](https://github.com/0xProject/0x-starter-project/tree/v1).
This project will take you through a number of scenarios using the 0x v4 protocol.

## Scenarios

This repository contains a bunch of scenarios that you can run from the command-line:

- Fill order (ERC20)
- Fill order Fees
- Fill order (ERC721)
- Fill order (Multiple assets)
- Cancel orders up to
- Match orders
- Execute transaction
- Forwarder buy orders (ERC20)
- Forwarder buy orders (ERC721)
- Standard Relayer API fill order example
- Dutch Auction (decreasing price auction)
- [Fill a 0x API quote](./src/scenarios/fill_0x_api_swap.ts)
- [Fill ERC20 limit order](./src/scenarios/fill_erc20_limit_order.ts)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add the "Fill a 0x API swap/quote response" to this list?

- [Cancel pair limit orders](./src/scenarios/cancel_pair_limit_orders.ts)
- [Fill ERC20 RFQ order](./src/scenarios/fill_erc20_rfq_order.ts)
- [Fill ERC20 RFQ order with a maker order signer](./src/scenarios/fill_erc20_rfq_order_with_maker_order_signer.ts)
- [Subscribe to RFQ order fill events](./src/scenarios/fill_erc20_limit_order.ts)
- [Execute Metatransaction](src/scenarios/execute_metatransaction_fill_rfq_order.ts)
- [Fill ERC20 OTC order](./src/scenarios/fill_erc20_otc_order.ts)
- [Fill taker-signed ERC20 OTC order](./src/scenarios/fill_taker_signed_erc20_otc_order.ts)
- [(Advanced) Fill an aggregated quote via TransformERC20](./src/scenarios/transform_erc20.ts)
- [Create a staking pool](./src/scenarios/create_staking_pool.ts)

## Getting Started

Expand All @@ -50,7 +45,7 @@ yarn build
Run a scenario in another terminal:

```
yarn scenario:fill_order_erc20
yarn scenario:fill_erc20_limit_order
```

To run all scenarios:
Expand All @@ -77,20 +72,6 @@ For Kovan:
export const NETWORK_CONFIGS = KOVAN_CONFIGS;
```

### Fill Order SRA Example

To run the Fill Order SRA Example you must first start up a server in another terminal:

```
yarn fake_sra_server
```

Then in another terminal run:

```
yarn scenario:fill_order_sra
```

### Windows Development Setup

If you're setting up Node.js for the first time on Windows, you may find the following [StackOverflow guide](https://stackoverflow.com/questions/15126050/running-python-on-windows-for-node-js-dependencies/39648550#39648550) useful. There are a few build tools required for Node.js on Windows which are not installed by default (such as Python). Please follow that guide before running through the tutorials.
Binary file added content/screenshot.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 20 additions & 22 deletions package.json
@@ -1,45 +1,43 @@
{
"name": "0x-starter-project",
"version": "2.0.0",
"version": "4.0.0",
"description": "0x Starter Project",
"scripts": {
"lint": "tslint --format stylish --project . --exclude **/src/generated_contract_wrappers/**/*",
"watch": "cross-env tsc -w",
"build": "cross-env tsc",
"clean": "shx rm -rf lib",
"fake_sra_server": "cross-env yarn build && node ./lib/sra_server.js",
"scenario:all": "cross-env yarn build && node ./lib/scenarios/all.js",
"scenario:dutch_auction": "cross-env yarn build && node ./lib/scenarios/dutch_auction.js",
"scenario:fill_order_erc20": "cross-env yarn build && node ./lib/scenarios/fill_order_erc20.js",
"scenario:fill_order_sra": "cross-env yarn build && node ./lib/scenarios/fill_order_sra.js",
"scenario:fill_order_fees": "cross-env yarn build && node ./lib/scenarios/fill_order_fees.js",
"scenario:fill_order_erc721": "cross-env yarn build && node ./lib/scenarios/fill_order_erc721.js",
"scenario:fill_order_multi_asset": "cross-env yarn build && node ./lib/scenarios/fill_order_multi_asset.js",
"scenario:match_orders": "cross-env yarn build && node ./lib/scenarios/match_orders.js",
"scenario:execute_transaction": "cross-env yarn build && node ./lib/scenarios/execute_transaction.js",
"scenario:execute_transaction_cancel_order": "cross-env yarn build && node ./lib/scenarios/execute_transaction_cancel_order.js",
"scenario:cancel_orders_up_to": "cross-env yarn build && node ./lib/scenarios/cancel_orders_up_to.js",
"scenario:forwarder_buy_erc20_tokens": "cross-env yarn build && node ./lib/scenarios/forwarder_buy_erc20_tokens.js",
"scenario:forwarder_buy_erc721_tokens": "cross-env yarn build && node ./lib/scenarios/forwarder_buy_erc721_tokens.js",
"scenario:exchange_subscribe": "cross-env yarn build && node ./lib/scenarios/exchange_subscribe.js"
"scenario:fill_0x_api_swap": "cross-env yarn build && node ./lib/scenarios/fill_0x_api_swap.js",
"scenario:fill_erc20_limit_order": "cross-env yarn build && node ./lib/scenarios/fill_erc20_limit_order.js",
"scenario:fill_erc20_rfq_order": "cross-env yarn build && node ./lib/scenarios/fill_erc20_rfq_order.js",
"scenario:fill_erc20_rfq_order_with_maker_order_signer": "cross-env yarn build && node ./lib/scenarios/fill_erc20_rfq_order_with_maker_order_signer.js",
"scenario:fill_erc20_otc_order": "cross-env yarn build && node ./lib/scenarios/fill_erc20_otc_order.js",
"scenario:fill_erc20_taker_signed_otc_order": "cross-env yarn build && node ./lib/scenarios/fill_erc20_taker_signed_otc_order.js",
"scenario:execute_metatransaction_fill_rfq_order": "cross-env yarn build && node ./lib/scenarios/execute_metatransaction_fill_rfq_order.js",
"scenario:cancel_pair_limit_orders": "cross-env yarn build && node ./lib/scenarios/cancel_pair_limit_orders.js",
"scenario:transform_erc20": "cross-env yarn build && node ./lib/scenarios/transform_erc20.js"
},
"config": {},
"author": "",
"license": "Apache-2.0",
"dependencies": {
"@0x/connect": "^6.0.0",
"@0x/contract-wrappers": "^13.0.0",
"@0x/contract-wrappers": "^13.18.2",
"@0x/contracts-erc721": "^3.0.0",
"@0x/migrations": "^5.0.0",
"@0x/order-utils": "^9.0.0",
"@0x/subproviders": "^6.0.0",
"@0x/utils": "^5.0.0",
"@0x/web3-wrapper": "^7.0.0",
"@0x/migrations": "^8.1.10",
"@0x/protocol-utils": "^1.9.4",
"@0x/subproviders": "^6.6.0",
"@0x/utils": "^6.4.4",
"@0x/web3-wrapper": "^7.6.1",
"axios": "^0.25.0",
"axios-mock-adapter": "^1.20.0",
"body-parser": "^1.19.0",
"cli-table": "^0.3.1",
"copyfiles": "^2.1.1",
"ethereumjs-util": "^6.1.0",
"ethereumjs-util": "^7.1.3",
"express": "^4.17.1",
"http-status-codes": "^2.2.0",
"lodash": "^4.17.15",
"ora": "^3.4.0",
"run-s": "^0.0.0",
Expand Down
2 changes: 2 additions & 0 deletions src/constants.ts
Expand Up @@ -11,9 +11,11 @@ export const UNLIMITED_ALLOWANCE_IN_BASE_UNITS = new BigNumber(2).pow(256).minus
// tslint:disable-next-line:custom-no-magic-numbers
export const DECIMALS = 18;
export const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
export const ETH_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
export const NULL_BYTES = '0x';
export const ZERO = new BigNumber(0);
export const GANACHE_NETWORK_ID = 50;
export const KOVAN_NETWORK_ID = 42;
export const ROPSTEN_NETWORK_ID = 3;
export const RINKEBY_NETWORK_ID = 4;
export const MOCK_0x_API_BASE_URL = 'https://mock.api.0x.org';
119 changes: 119 additions & 0 deletions src/mock_0x_api_response_utils.ts
@@ -0,0 +1,119 @@
import { ContractWrappers, ERC20TokenContract } from '@0x/contract-wrappers';
import { RfqOrder, SignatureType } from '@0x/protocol-utils';
import { Web3ProviderEngine } from '@0x/subproviders';
import { BigNumber, hexUtils } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { AxiosInstance } from 'axios';
import AxiosMockAdapter from 'axios-mock-adapter';
import * as HttpStatus from 'http-status-codes';

import { NETWORK_CONFIGS, TX_DEFAULTS } from './configs';
import { DECIMALS, MOCK_0x_API_BASE_URL, UNLIMITED_ALLOWANCE_IN_BASE_UNITS } from './constants';
import { getRandomFutureDateInSeconds } from './utils';

/**
* Set up mock responses to hypothetical 0x API calls.
*
*/
export async function setUpMock0xApiResponsesAsync(
axiosInstance: AxiosInstance,
maker: string,
taker: string,
providerEngine: Web3ProviderEngine,
): Promise<void> {
const axiosMock = new AxiosMockAdapter(axiosInstance);

await setUpWethZrxMockResponseAsync(axiosMock, providerEngine, maker, taker);
}

/**
* Set up a mock swap/quote response to buy ZRX with WETH
*
*/
async function setUpWethZrxMockResponseAsync(
axiosMock: AxiosMockAdapter,
providerEngine: Web3ProviderEngine,
maker: string,
taker: string,
): Promise<void> {
const contractWrappers = new ContractWrappers(providerEngine, { chainId: NETWORK_CONFIGS.chainId });
const web3Wrapper = new Web3Wrapper(providerEngine);
const exchangeProxyAddress = contractWrappers.contractAddresses.exchangeProxy;

const zrxTokenAddress = contractWrappers.contractAddresses.zrxToken;
const zrkToken = new ERC20TokenContract(zrxTokenAddress, providerEngine);
const etherTokenAddress = contractWrappers.contractAddresses.etherToken;
const makerAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(5), DECIMALS);
const takerAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(0.1), DECIMALS);

// make sure maker has an allowance
await zrkToken
.approve(exchangeProxyAddress, UNLIMITED_ALLOWANCE_IN_BASE_UNITS)
.sendTransactionAsync({ from: maker });

// Create the order
const randomExpiration = getRandomFutureDateInSeconds();
const pool = hexUtils.leftPad(1);

const rfqOrder: RfqOrder = new RfqOrder({
chainId: NETWORK_CONFIGS.chainId,
verifyingContract: exchangeProxyAddress,
maker,
taker,
makerToken: zrxTokenAddress,
takerToken: etherTokenAddress,
makerAmount,
takerAmount,
txOrigin: taker,
expiry: randomExpiration,
pool,
salt: new BigNumber(Date.now()),
});

// Generate the order hash and sign it
const signature = await rfqOrder.getSignatureWithProviderAsync(
web3Wrapper.getProvider(),
SignatureType.EthSign,
maker,
);

const callData = contractWrappers.exchangeProxy
.fillRfqOrder(rfqOrder, signature, takerAmount)
.getABIEncodedTransactionData();

const mockWethZrxResponse = {
chainId: NETWORK_CONFIGS.chainId,
// If buyAmount was specified in the request it provides the price of
// buyToken in sellToken and vice versa. This price does not include
// the slippage provided in the request above, and therefore represents
// the best possible price.
price: makerAmount.div(takerAmount).toString(),
// The price which must be met or else the entire transaction will
// revert. This price is influenced by the slippagePercentage parameter.
// On-chain sources may encounter price movements from quote to settlement.
// This quote will be totally RFQ, returning the order price.
guaranteedPrice: makerAmount.div(takerAmount).toString(),
to: exchangeProxyAddress,
data: callData,
value: '0',
gas: '200000',
estimatedGas: '200000',
from: taker,
gasPrice: TX_DEFAULTS.gasPrice.toString(),
protocolFee: '0',
minimumProtocolFee: '0',
buyTokenAddress: zrxTokenAddress,
sellTokenAddress: etherTokenAddress,
buyAmount: makerAmount.toString(),
sellAmount: takerAmount.toString(),
sources: [
{
name: '0x',
proportion: '1',
},
],
allowanceTarget: exchangeProxyAddress,
};

axiosMock.onGet(`${MOCK_0x_API_BASE_URL}/swap/v1/quote`).reply(HttpStatus.StatusCodes.OK, mockWethZrxResponse);
}
13 changes: 5 additions & 8 deletions src/print_utils.ts
Expand Up @@ -2,10 +2,8 @@ import {
ContractWrappers,
ERC20TokenContract,
ERC721TokenContract,
OrderInfo,
OrderStatus,
} from '@0x/contract-wrappers';
import { Order, SignedOrder } from '@0x/order-utils';
import { LimitOrder, OrderInfo, OrderStatus, OtcOrder, RfqOrder } from '@0x/protocol-utils';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { DecodedLogArgs, LogWithDecodedArgs, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
Expand Down Expand Up @@ -166,8 +164,7 @@ export class PrintUtils {
PrintUtils.printHeader('Balances');
PrintUtils.pushAndPrint(table, flattenedBalances);
}
public async fetchAndPrintContractAllowancesAsync(): Promise<void> {
const erc20ProxyAddress = this._contractWrappers.contractAddresses.erc20Proxy;
public async fetchAndPrintContractAllowancesAsync(spenderAddress: string): Promise<void> {
const flattenedAllowances = [];
const flattenedAccounts = Object.keys(this._accounts).map(
account => account.charAt(0).toUpperCase() + account.slice(1),
Expand All @@ -178,7 +175,7 @@ export class PrintUtils {
for (const account in this._accounts) {
const address = this._accounts[account];
const token = new ERC20TokenContract(tokenAddress, this._contractWrappers.getProvider());
const allowance = await token.allowance(address, erc20ProxyAddress).callAsync();
const allowance = await token.allowance(address, spenderAddress).callAsync();
allowances.push(allowance.toString());
}
flattenedAllowances.push(allowances);
Expand Down Expand Up @@ -242,11 +239,11 @@ export class PrintUtils {
// tslint:disable-next-line:prefer-function-over-method
public printOrderInfos(orderInfos: { [orderName: string]: OrderInfo }): void {
const data: string[][] = [];
_.forOwn(orderInfos, (value, key) => data.push([key, OrderStatus[value.orderStatus]]));
_.forOwn(orderInfos, (value, key) => data.push([key, OrderStatus[value.status]]));
PrintUtils.printData('Order Info', data);
}
// tslint:disable-next-line:prefer-function-over-method
public printOrder(order: Order | SignedOrder): void {
public printOrder(order: LimitOrder | RfqOrder | OtcOrder): void {
PrintUtils.printData('Order', Object.entries(order));
}
public async fetchAndPrintERC721OwnerAsync(erc721TokenAddress: string, tokenId: BigNumber): Promise<void> {
Expand Down
1 change: 1 addition & 0 deletions src/provider_engine.ts
Expand Up @@ -16,6 +16,7 @@ const determineProvider = (): Web3ProviderEngine => {
new GanacheSubprovider({
vmErrorsOnRPCResponse: false,
network_id: GANACHE_CONFIGS.networkId,
_chainId: GANACHE_CONFIGS.chainId,
mnemonic: MNEMONIC,
}),
);
Expand Down
38 changes: 18 additions & 20 deletions src/scenarios/all.ts
@@ -1,28 +1,26 @@
import { providerEngine } from '../provider_engine';

import { scenarioAsync as cancelOrdersUpTo } from './cancel_orders_up_to';
import { scenarioAsync as executeTransaction } from './execute_transaction';
import { scenarioAsync as executeTransactionCancelOrder } from './execute_transaction_cancel_order';
import { scenarioAsync as fillOrderERC20 } from './fill_order_erc20';
import { scenarioAsync as fillOrderERC721 } from './fill_order_erc721';
import { scenarioAsync as fillOrderFees } from './fill_order_fees';
import { scenarioAsync as fillOrderMultiAsset } from './fill_order_multi_asset';
import { scenarioAsync as forwarder_buy_erc20_tokens } from './forwarder_buy_erc20_tokens';
import { scenarioAsync as forwarder_buy_erc721_tokens } from './forwarder_buy_erc721_tokens';
import { scenarioAsync as matchOrders } from './match_orders';
import { scenarioAsync as cancelPairLimitOrders } from './cancel_pair_limit_orders';
import { scenarioAsync as executeMetatransactionFillRfqOrder } from './execute_metatransaction_fill_rfq_order';
import { scenarioAsync as fill0xApiSwap } from './fill_0x_api_swap';
import { scenarioAsync as fillERC20LimitOrder } from './fill_erc20_limit_order';
import { scenarioAsync as fillERC20OtcOrder } from './fill_erc20_otc_order';
import { scenarioAsync as fillERC20RfqOrder } from './fill_erc20_rfq_order';
import { scenarioAsync as fillERC20RfqOrderWithMakerOrderSigner } from './fill_erc20_rfq_order_with_maker_order_signer';
import { scenarioAsync as fillERC20TakerSignedOtcOrder } from './fill_erc20_taker_signed_otc_order';
import { scenarioAsync as transformERC20 } from './transform_erc20';

void (async () => {
try {
await fillOrderERC20();
await fillOrderFees();
await fillOrderERC721();
await matchOrders();
await executeTransaction();
await executeTransactionCancelOrder();
await cancelOrdersUpTo();
await forwarder_buy_erc20_tokens();
await forwarder_buy_erc721_tokens();
await fillOrderMultiAsset();
await fill0xApiSwap();
await fillERC20LimitOrder();
await cancelPairLimitOrders();
await fillERC20RfqOrder();
await fillERC20RfqOrderWithMakerOrderSigner();
await fillERC20OtcOrder();
await executeMetatransactionFillRfqOrder();
await fillERC20TakerSignedOtcOrder();
await transformERC20();
} catch (e) {
console.log(e);
providerEngine.stop();
Expand Down