Skip to content

Commit

Permalink
BatchTradeExtensionAPI [SIM-251] (#113)
Browse files Browse the repository at this point in the history
* Initial BatchTradeExtensionAPI

* Add batchFetchTradeQuoteAsync to TradeAPI

* Batch fetch quotes, decode 0x custom errors, restructure

* Add tests

* Fix tsc error

* Remove unused code / console.log

* Update version to 0.6.0-batch.0

* Add DelegatedManager#addExtensions & BatchTradeExtension#initializeExtension

* Add DelegatedManagerAPI to Set.ts

* Fix DelegatedManagerAPI interface (make multi-instance)

* Improve UtilAPI docs about trade/swap quotes

* Fix incorrect BatchTradeExtension_factory import

* Fix from/to tokenAmount calculation in TradeQuoterAPI

* Upgrade version to batch.4

* Update set-v2-strategies to 0.0.11 & update TradeInfo type

* Update version to batch.5

* Fix tsc for unit tests

* Fix tsc errors in BatchTradeExtensionAPI

* Update batchTrade receipt fixture

* Revert "Fix from/to tokenAmount calculation in TradeQuoterAPI"

This reverts commit c67ffa0.

* Update version to batch.6

* Account for Dust Positions in Batch Trade [SIM-286] (#118)

* update code comments in set token api + protocol viewer to reflect correct param usage

* add null address constant

* update validate quotes method to be more generalized

* check batch trade does not produce dust positions on utils api layer

* update trade quoter to use BigNumber instead of decimal

* update validation helper to allow only checking from token quantities option

* refactor conversion of tokens from and to pre token positions into helper method

* remove remaining comments for checking dust positions

* correctly use current token position if max implied + zero ex match

* only validate batch trade does not produce dust positions for tokens spread across multiple trades

* add more clarity to comments

* add comment re: max selling across multiple trades

* Upgrade version to batch.7

Co-authored-by: Dylan Tran <[email protected]>
  • Loading branch information
cgewecke and controtie authored Jun 1, 2022
1 parent f63fe20 commit ca30cc8
Show file tree
Hide file tree
Showing 23 changed files with 1,708 additions and 272 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "set.js",
"version": "0.5.6",
"version": "0.6.0",
"description": "A javascript library for interacting with the Set Protocol v2",
"keywords": [
"set.js",
Expand Down Expand Up @@ -55,11 +55,12 @@
"typescript": "^4.4.2"
},
"dependencies": {
"@0x/utils": "^6.5.3",
"@0xproject/types": "^1.1.4",
"@0xproject/typescript-typings": "^3.0.2",
"@0xproject/utils": "^2.0.2",
"@setprotocol/set-protocol-v2": "^0.1.15",
"@setprotocol/set-v2-strategies": "^0.0.7",
"@setprotocol/set-v2-strategies": "^0.0.11",
"@types/chai-as-promised": "^7.1.3",
"@types/jest": "^26.0.5",
"@types/web3": "^1.2.2",
Expand Down
15 changes: 13 additions & 2 deletions src/Set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import {
IssuanceExtensionAPI,
TradeExtensionAPI,
StreamingFeeExtensionAPI,
BatchTradeExtensionAPI,
DelegatedManagerAPI,
} from './api/index';

const ethersProviders = require('ethers').providers;
Expand Down Expand Up @@ -145,6 +147,12 @@ class Set {
*/
public perpV2BasisTradingViewer: PerpV2LeverageViewerAPI;

/**
* An instance of DelegatedManager class. Contains methods for owner-administering Delegated
* Manager contracts
*/
public delegatedManager: DelegatedManagerAPI;

/**
* An instance of DelegatedManagerFactory class. Contains methods for deploying and initializing
* DelegatedManagerSystem deployed SetTokens and Manager contracts
Expand Down Expand Up @@ -189,7 +197,7 @@ class Set {
assertions
);
this.system = new SystemAPI(ethersProvider, config.controllerAddress);
this.trade = new TradeAPI(ethersProvider, config.tradeModuleAddress, config.zeroExApiKey, config.zeroExApiUrls);
this.trade = new TradeAPI(ethersProvider, config.tradeModuleAddress);
this.navIssuance = new NavIssuanceAPI(ethersProvider, config.navIssuanceModuleAddress);
this.priceOracle = new PriceOracleAPI(ethersProvider, config.masterOracleAddress);
this.debtIssuance = new DebtIssuanceAPI(ethersProvider, config.debtIssuanceModuleAddress);
Expand All @@ -201,7 +209,9 @@ class Set {
this.perpV2BasisTradingViewer = new PerpV2LeverageViewerAPI(ethersProvider,
config.perpV2BasisTradingModuleViewerAddress);
this.blockchain = new BlockchainAPI(ethersProvider, assertions);
this.utils = new UtilsAPI(ethersProvider, config.zeroExApiKey, config.zeroExApiUrls);
this.utils = new UtilsAPI(ethersProvider, config.tradeModuleAddress, config.zeroExApiKey, config.zeroExApiUrls);

this.delegatedManager = new DelegatedManagerAPI(ethersProvider);

this.delegatedManagerFactory = new DelegatedManagerFactoryAPI(
ethersProvider,
Expand All @@ -212,6 +222,7 @@ class Set {
streamingFeeExtension: new StreamingFeeExtensionAPI(ethersProvider, config.streamingFeeExtensionAddress),
issuanceExtension: new IssuanceExtensionAPI(ethersProvider, config.issuanceExtensionAddress),
tradeExtension: new TradeExtensionAPI(ethersProvider, config.tradeExtensionAddress),
batchTradeExtension: new BatchTradeExtensionAPI(ethersProvider, config.batchTradeExtensionAddress),
};
}
}
Expand Down
74 changes: 74 additions & 0 deletions src/api/DelegatedManagerAPI.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
Copyright 2022 Set Labs Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict';

import { ContractTransaction } from 'ethers';
import { Provider } from '@ethersproject/providers';
import { Address } from '@setprotocol/set-protocol-v2/utils/types';
import { TransactionOverrides } from '@setprotocol/set-protocol-v2/dist/typechain';

import DelegatedManagerWrapper from '../wrappers/set-v2-strategies/DelegatedManagerWrapper';
import Assertions from '../assertions';

/**
* @title DelegatedManagerAPI
* @author Set Protocol
*
* The DelegatedManagerAPI exposes methods to call functions only available directly on the
* DelegatedManager contract. For the most part these are owner admin operations to reconfigure
* permissions and add modules / extensions.
*
* (This API will be extended as required by set-ui (tokensets). For other use-cases interacting
* with the contract via the Etherscan write API is the simplest option)
*/
export default class DelegatedManagerAPI {
private DelegatedManagerWrapper: DelegatedManagerWrapper;
private assert: Assertions;

public constructor(
provider: Provider,
assertions?: Assertions) {
this.DelegatedManagerWrapper = new DelegatedManagerWrapper(provider);
this.assert = assertions || new Assertions();
}

/**
* ONLY OWNER: Add new extension(s) that the DelegatedManager can call. Puts extensions into PENDING
* state, each must be initialized in order to be used.
*
* @param _delegatedManagerAddress DelegatedManager to addExtension for
* @param _extensions New extension(s) to add
* @param callerAddress Address of caller (optional)
* @param txOpts Overrides for transaction (optional)
*/
public async addExtensionsAsync(
delegatedManagerAddress: Address,
extensions: Address[],
callerAddress: Address = undefined,
txOpts: TransactionOverrides = {}
): Promise<ContractTransaction> {
this.assert.schema.isValidAddress('delegatedManagerAddress', delegatedManagerAddress);
this.assert.schema.isValidAddressList('extensions', extensions);

return await this.DelegatedManagerWrapper.addExtensions(
delegatedManagerAddress,
extensions,
callerAddress,
txOpts
);
}
}
2 changes: 1 addition & 1 deletion src/api/SetTokenAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export default class SetTokenAPI {
* the initialization statuses of each of the modules for the SetToken
*
* @param setTokenAddress Address of SetToken to fetch details for
* @param moduleAddresses Addresses of ERC20 contracts to check balance for
* @param moduleAddresses Addresses of modules to check initialization statuses for
* @param callerAddress Address to use as the caller (optional)
*/
public async fetchSetDetailsAsync(
Expand Down
84 changes: 0 additions & 84 deletions src/api/TradeAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,8 @@ import { TransactionOverrides } from '@setprotocol/set-protocol-v2/dist/typechai
import { BigNumber } from 'ethers/lib/ethers';

import TradeModuleWrapper from '../wrappers/set-protocol-v2/TradeModuleWrapper';
import SetTokenAPI from './SetTokenAPI';
import Assertions from '../assertions';

import {
TradeQuoter
} from './utils';

import {
TradeQuote,
ZeroExApiUrls
} from '../types';

/**
* @title TradeAPI
* @author Set Protocol
Expand All @@ -46,19 +36,13 @@ import {
export default class TradeAPI {
private tradeModuleWrapper: TradeModuleWrapper;
private assert: Assertions;
private provider: Provider;
private tradeQuoter: TradeQuoter;

public constructor(
provider: Provider,
tradeModuleAddress: Address,
zeroExApiKey?: string,
zeroExApiUrls?: ZeroExApiUrls
) {
this.provider = provider;
this.tradeModuleWrapper = new TradeModuleWrapper(provider, tradeModuleAddress);
this.assert = new Assertions();
this.tradeQuoter = new TradeQuoter(zeroExApiKey, zeroExApiUrls);
}

/**
Expand Down Expand Up @@ -128,72 +112,4 @@ export default class TradeAPI {
txOpts
);
}

/**
* Call 0x API to generate a trade quote for two SetToken components.
*
* @param fromToken Address of token being sold
* @param toToken Address of token being bought
* @param fromTokenDecimals Token decimals of token being sold (ex: 18)
* @param toTokenDecimals Token decimals of token being bought (ex: 18)
* @param rawAmount String quantity of token to sell (ex: "0.5")
* @param fromAddress SetToken address which holds the buy / sell components
* @param setToken SetTokenAPI instance
* @param gasPrice (Optional) gasPrice to calculate gas costs with (Default: fetched from EthGasStation)
* @param slippagePercentage (Optional) maximum slippage, determines min receive quantity. (Default: 2%)
* @param isFirmQuote (Optional) Whether quote request is indicative or firm
* @param feePercentage (Optional) Default: 0
* @param feeRecipient (Optional) Default: 0xD3D555Bb655AcBA9452bfC6D7cEa8cC7b3628C55
* @param excludedSources (Optional) Exchanges to exclude (Default: ['Kyber', 'Eth2Dai', 'Mesh'])
* @param simulatedChainId (Optional) ChainId of target network (useful when using a forked development client)
*
* @return {Promise<TradeQuote>}
*/
public async fetchTradeQuoteAsync(
fromToken: Address,
toToken: Address,
fromTokenDecimals: number,
toTokenDecimals: number,
rawAmount: string,
fromAddress: Address,
setToken: SetTokenAPI,
gasPrice?: number,
slippagePercentage?: number,
isFirmQuote?: boolean,
feePercentage?: number,
feeRecipient?: Address,
excludedSources?: string[],
simulatedChainId?: number,
): Promise<TradeQuote> {
this.assert.schema.isValidAddress('fromToken', fromToken);
this.assert.schema.isValidAddress('toToken', toToken);
this.assert.schema.isValidAddress('fromAddress', fromAddress);
this.assert.schema.isValidJsNumber('fromTokenDecimals', fromTokenDecimals);
this.assert.schema.isValidJsNumber('toTokenDecimals', toTokenDecimals);
this.assert.schema.isValidString('rawAmount', rawAmount);

// The forked Hardhat network has a chainId of 31337 so we can't rely on autofetching this value
const chainId = (simulatedChainId !== undefined)
? simulatedChainId
: (await this.provider.getNetwork()).chainId;

return this.tradeQuoter.generateQuoteForTrade({
fromToken,
toToken,
fromTokenDecimals,
toTokenDecimals,
rawAmount,
fromAddress,
chainId,
tradeModule: this.tradeModuleWrapper,
provider: this.provider,
setToken,
gasPrice,
slippagePercentage,
isFirmQuote,
feePercentage,
feeRecipient,
excludedSources,
});
}
}
Loading

0 comments on commit ca30cc8

Please sign in to comment.