Skip to content

GeneralIndexModule internal review and external audit revisions #75

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

Merged
merged 41 commits into from
Apr 22, 2021

Conversation

cgewecke
Copy link
Contributor

@cgewecke cgewecke commented Apr 19, 2021

This is a set of small revisions suggested by the internal review of GeneralIndexModule. Each idea is expressed as a commit, so it can be considered on it's own and cherry-picked if useful.

The existing contract is highly optimized for gas - it minimizes state reads in loops as well as method encapsulation that would impose costs for extra function calls.

This version of the contract does the opposite - it introduces additional getters and generally ignores state read costs. For example, _normalizedTargetUnits is removed. It's also more aggressive about param validation. checks are added for

  • the public getters: these might be used in automated scripts and ambiguous outputs are generated for invalid inputs
  • the exchange name is validated in setExchange (it's also validated during trades). Also storing this as a bytes32 hash, mirroring the IntegrationRegistry.

The Berlin hardfork has made initial state reads for a location more expensive, but subsequent "warm" reads from the same place are much cheaper, changing the gas calculus a bit. Despite the inefficiencies introduced in this draft, the diff between versions suggests that gas costs increased only marginally overall. They would increase more in a module with a larger number of components.


Update I

Additional commits have been added after the review was finalized on Monday. Notable things are:

  • storage of exchangeNames as bytes32 hashes has been reverted following initial review here - these are strings again
  • initialize and startRebalance now revert if there are external positions for a component
  • _createTradeInfo and _createTradeRemainingInfo have been substantially restructured
    • common logic has been pulled out and encapsulated in additional helpers
    • the field naming for the TradeInfo struct has changed to more closely match TradeModule's syntax:
      • from "fixed/floatingQuantity" to sendQuantity and minReceiveQuantity
      • from isSendFixed to isSell

Update II: (Internal review remainders, review feedback, audit corrections)

TODO:

  • Revert isSell, minReceivedQuantity to isFixedSend, floatingQuantityLimit
  • Move _validate array methods to AddressArrayLibs (and add additional)
  • Optimize external position detection
  • Optimize modifier inlining
  • Declare TradeInfo as return parameter
  • Add Position.unitsMatchWithinRange method for targets logic (invert)
  • Restrict _getAggregateComponentsAndUnits state mutability to pure
  • Iosoro 5.3.2: add removeModule tests for preserved state, eval trader deletion
  • Iosoro 5.4.1: event (w/ position multiplier) for raiseAssetTarget
  • Iosoro 5.4.3: immutable weth
  • Iosoco 5.4.3: typos
  • Ask Felix for clarification about fees ambiguity in _calculateTradeSizeAndDirection
    • Response:
    • It’s a code organization comment, where either 1) all fee-related calculations and logic should be grouped together or 2) a big comment is used to flag that logic and how it interacts w/ the rest of the module. In addition, I wanted to ascertain that there are no problematic edge cases and that the math is correct

  • Update gas analysis
  • Re-review docs and normalize namings
  • Address fee concern on _calculateTradeSizeAndDirection
    • move fn level comment about issue inline
    • check test cases
  • fix validatePairs bytes array method
  • rename PositionMultiplier update --> AssetTargetsRaised
  • remove de-authorized traders from history
  • add changelog entry in ... (one of the libs)

Report shows original GeneralIndexModule vs. this PR (run on [email protected])

(Updated for most recent commits - April 20)

Deployments

Gas Diff Diff % Block % usd avg
GeneralIndexModule 4,358,869 passed -45,230 -1% 35 % 1389.93

Methods

Gas Diff Diff % Calls usd avg
GeneralIndexModule
       initialize 189,082 passed -35,895 -16% 12 60.29
       raiseAssetTargets 82,425 failed +1,159 +1% 6 26.28
       setAnyoneTrade 62,487 - - 4 19.93
       setCoolOffPeriods 121,277 failed +7,130 +6% 4 38.67
       setExchanges 120,933 failed +31,341 +35% 6 38.56
       setRaiseTargetPercentage 62,616 - - 12 19.97
       setTradeMaximums 93,017 failed +8,397 +10% 7 29.66
       setTraderStatus 109,688 - - 13 34.98
       startRebalance 193,078 failed +23,031 +14% 30 61.57
       trade 364,066 passed -1,932 -1% 59 116.09
       tradeRemainingWETH 286,393 passed -226 0% 9 91.32

Build Configuration

Option Settings
solc: version 0.6.10
solc: optimized true
solc: runs 200
gas: block limit 12,450,000
gas: price 123 gwei/gas
gas: currency/eth rate 2592.47 usd/eth

@bweick
Copy link
Contributor

bweick commented Apr 19, 2021

What are the benefits of storing the exchange name as a hash? To me this hurts readability.

@bweick
Copy link
Contributor

bweick commented Apr 19, 2021

So with Berlin would we expect the gas increase to be slightly less? (I'm assuming this wasn't run with Berlin changes)

@cgewecke
Copy link
Contributor Author

What are the benefits of storing the exchange name as a hash? To me this hurts readability.

Agree, it's not as nice. There's a note in the review about the cost of storing them as a string.

Note that exchangeNames as strings will be very expensive to store and update

@cgewecke
Copy link
Contributor Author

cgewecke commented Apr 19, 2021

@bweick Yes, the gas report was run on a Berlin EVM. In Istanbul I'd expect these changes to increase costs a lot, but with Berlin they're more expensive but...by a smaller margin.

@bweick
Copy link
Contributor

bweick commented Apr 19, 2021

What are the benefits of storing the exchange name as a hash? To me this hurts readability.

Agree, it's not as nice. There's a note in the review about the cost of storing them as a string.

Note that exchangeNames as strings will be very expensive to store and update

Gotcha, that makes sense, though am I reading the table right that the cost of setting exchanges went up?

@cgewecke
Copy link
Contributor Author

cgewecke commented Apr 19, 2021

@bweick Yes, it went up quite a bit, but it's also validating the exchange name now. From the internal review for setExchange....

Validate that the exchangeName is on the integrationRegistry

This is a redundant check b/c the trade methods also check. But maybe makes sense from a UX perspective? In practice how frequently are the settings changed?

@bweick
Copy link
Contributor

bweick commented Apr 19, 2021

@bweick Yes, it went up quite a bit, but it's also validating the exchange name now. From the internal review....

Validate that the exchangeName is on the integrationRegistry

This is a redundant check b/c the trade methods also check. But maybe makes sense from a UX perspective? In practice how frequently are the settings changed?

I like the check here, if we could remove the check on the trade that'd be great since that is redundant but I think our inherited helper functions there enforce a check?

Maybe we can discuss a bit as a group but the readability is really nice. Do we know what the marginal cost of storing a string vs bytes32 is? I know it's going to be different based on string length but would be great to isolate the "cost of readability".

@cgewecke
Copy link
Contributor Author

@bweick

Changing back to string increases the costs for setExchanges 14% (from this PR as a baseline, with the tests using 4 components). The deployment cost increased 3%.

With a single exchange name longer than 32bytes setExchanges increased 24%. (Ex: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)

There was no impact on the trade methods.

In the end this is just a few dollars for sets with a small number of components...if exchanges aren't configured very often idk that these gas differences matter that much.

@cgewecke
Copy link
Contributor Author

if we could remove the check on the trade that'd be great

Do you know if this is possible? I was thinking that validating the adapter is necessary in trade because it could have been removed at some point between setting the exchanges and calling trade.

@bweick
Copy link
Contributor

bweick commented Apr 19, 2021

if we could remove the check on the trade that'd be great

Do you know if this is possible? I was thinking that validating the adapter is necessary in trade because it could have been removed at some point between setting the exchanges and calling trade.

Well the only way it could be removed is through setExchanges, in which case a valid exchange would need to be passed in its place. Unless there's a path to removal I'm not thinking of? Also usually we update maybe an exchange or two each time. Obviously if we're adding new components that also requires setting an exchange.

@cgewecke
Copy link
Contributor Author

Can the adapter be removed via the IntegrationRegistry contract? I wrote a test for this possibility in #trade but maybe it's not right...

describe("when exchange adapter has been removed from integration registry", async () => {
beforeEach(async () => {
await indexModule.setExchanges(subjectSetToken.address, [subjectComponent], [balancerAdapterName]);
await setup.integrationRegistry.removeIntegration(indexModule.address, balancerAdapterName);
});
afterEach(async () => {
await setup.integrationRegistry.addIntegration(
indexModule.address,
balancerAdapterName,
balancerExchangeAdapter.address
);
});
it("the trade reverts", async () => {
await expect(subject()).to.be.revertedWith("Must be valid adapter");
});
});

@bweick
Copy link
Contributor

bweick commented Apr 19, 2021

Can the adapter be removed via the IntegrationRegistry contract? I wrote a test for this possibility in #trade but maybe it's not right...

describe("when exchange adapter has been removed from integration registry", async () => {
beforeEach(async () => {
await indexModule.setExchanges(subjectSetToken.address, [subjectComponent], [balancerAdapterName]);
await setup.integrationRegistry.removeIntegration(indexModule.address, balancerAdapterName);
});
afterEach(async () => {
await setup.integrationRegistry.addIntegration(
indexModule.address,
balancerAdapterName,
balancerExchangeAdapter.address
);
});
it("the trade reverts", async () => {
await expect(subject()).to.be.revertedWith("Must be valid adapter");
});
});

Ah yes it could that's a good catch. Then yes I'd say we should check it, even though it would revert if the adapter was invalid it'd be good to return a message.

@cgewecke cgewecke changed the title GeneralIndexModule internal review revisions GeneralIndexModule internal review and external audit revisions Apr 22, 2021
address[] calldata _components,
uint256[] calldata _tradeMaximums
address[] memory _components,
uint256[] memory _tradeMaximums
Copy link
Contributor Author

@cgewecke cgewecke Apr 22, 2021

Choose a reason for hiding this comment

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

This change from calldata to memory (in four methods) is due to migrating the array pair validators to AddressArrayUtils. There are compiler errors for both permutations of calldata/memory

  • preserving calldata in this file and making the method signatures at lib and mocks calldata results in solc erroring with ... (this is a solc bug, according to the error type)
Using solcjs
Compiling 118 files with 0.6.10
InternalCompilerError: Invalid conversion to calldata type.

Error HH600: Compilation failed
  • Keeping calldata signatures here and memory signatures there has solc error with:
Using solcjs
Compiling 118 files with 0.6.10
UnimplementedFeatureError: Copying nested dynamic calldata arrays to memory is not implemented in the old code generator.

Error HH600: Compilation failed

function approximatelyEquals(uint256 a, uint256 b, uint256 range) internal pure returns (bool) {
return a <= b.add(range) && a >= b.sub(range);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note: this returns true if vals are equal up to and including +/- range (preserving the logic at _targetUnmet)

@cgewecke cgewecke force-pushed the chris/index-internal-review branch from 6a7a7c0 to 1ff05b5 Compare April 22, 2021 15:00
@cgewecke cgewecke mentioned this pull request Apr 22, 2021
17 tasks
if (_status) {
permissionInfo[_setToken].tradersHistory.push(_trader);
} else if(permissionInfo[_setToken].tradersHistory.contains(_trader)) {
permissionInfo[_setToken].tradersHistory = permissionInfo[_setToken].tradersHistory.remove(_trader);
Copy link
Contributor

Choose a reason for hiding this comment

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

We have a method in AddressArrayUtils that's way cheaper called removeStorage, better than having to read it into memory, manipulate, and store.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@@ -2187,6 +2187,24 @@ describe("GeneralIndexModule", () => {
);
});

describe("when de-authorizing a trader", async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably worth checking that the trader history array is updated properly as well

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The tradersHistory is not available on the automatic getter generated by solidity. This is the permissionInfo abi.

{
  "inputs": [
    {
      "internalType": "contract ISetToken",
      "name": "",
      "type": "address"
    }
  ],
  "name": "permissionInfo",
  "outputs": [
    {
      "internalType": "bool",
      "name": "anyoneTrade",
      "type": "bool"
    }
  ],
  "stateMutability": "view",
  "type": "function",
  "gas": "0x81b320"
},

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Only thing we can read out of that without writing a getter is anyoneTrade. Should we write one?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@cgewecke cgewecke merged commit 944c015 into master Apr 22, 2021
@cgewecke cgewecke deleted the chris/index-internal-review branch April 22, 2021 22:53
richardliang pushed a commit that referenced this pull request Jul 24, 2021
* Make all revert reason strings < 32bytes

* Remove _normalizedTargetMethod getter

* Encapsulate _noTokensToSell#canSell logic as internal method

* Encapsulate _allTargetsMet#targetUnmet logic in internal method

* Validate exchange adapter when setting & store name as bytes32 hash

* Consolidate position state and timestamp updates

* Validate params for external view methods

* Make all external setter method names use prefix `set`

* Add RebalanceStarted event

* Remove _caller param from onlyAllowedTrader modifier

* Add getDefaultPositionRealUnit getter

* Encapsulate data aggregation logic in #startRebalance in own method

* Prohibit external positions for components in #initialize and #startRebalance

* Refactor #createTradeInfo methods 

* Make weth state variable immutable

* Delegate modifier logic to internal helpers in ModuleBase and GeneralIndexModule

* Delete trader permissions on module removal / add removeModule tests

* Add #validatePairsWithArray methods to AddressArrayUtils library and use in setter methods

* Add approximatelyEquals helper in PreciseUnitMath and use in #_targetUnmet

* Add AssetTargetsRaised event in RaiseAssetTargets with positionMultiplier arg

* Remove trader from permissionInfo.tradersHistory when they are de-authorized

* Add getAllowedTraders method and make tradersHistory contain unique elements
richardliang pushed a commit that referenced this pull request Jul 24, 2021
Inject gas setting into ABIs and move tasks from hardhat.config to tasks/ (#50)

* Inject gas limit into abi on compilation
* Move solc & typechain tasks from hardhat.config to tasks/

Add AaveGovernanceV2Adapter (#14)

* add basic structure for SnapshotDelegationModule

* add delegate function

* add SnapshotGovernanceAdapter

* code review changes

* add aave v2 governance contracts to aave fixture

* add AaveGovernanceV2Adapter

* add tests for getting proper calldata

* add integration test for aave governance v2 adapter

* style fixes

* code review changes

Add CompoundBravoGovernanceAdapter (#17)

* add GovernorBravo to compoundFixture

* add CompoundBravoGovernanceAdapter

* add CompoundBravoGovernanceAdapter tests and fix new gov deploys/fixture

* add integration tests for CompoundBravoGovernanceAdapter

* code review changes

Fix AaveTokenV2Mintable type imports in utils/fixtures

Add batch fetch balances and allowances to protocol viewer

Remove aliases from import paths in utils/fixtures

Bump package version to 0.0.29

Update build and coverage badges for repo rename

Move libraryUtils to test folder

Fix image tags in build badges for repo renaming

Require fully qualified contract name params for library link util

Move libraryUtils back to utils/common

Bump new version

Fix bug in SingleIndexModule's tradeRemainingWETH test (#65)

Add CompoundWrapAdapter (#55)

Add CompoundWrapAdapter

Co-authored-by: mario <[email protected]>

port ctoken oracle (#57)

Port CToken Oracle

Fix failing CompoundWrap tests (#66)

Move getRandomAddress (w/out hardhat) to utils/common (#63)

Add GeneralIndexModule contract (#59)

Add GeneralIndexModule contract, UniswapV2ExchangeAdapterV2 updates, and BalancerV1ExchangeAdapter

Co-authored-by: bweick <[email protected]>

Remove UniswapYieldStrategy and tests. (#68)

Remove UniswapYieldStrategy and tests

Add yearn vault wrapper module and oracle (#61)

Add Yearn vaults wrap adapter and oracle for valuing yTokens.

Co-authored-by: bweick <[email protected]>

Organize integrations folder into integration type. (#70)

Updated javadocs for GeneralIndexModule. (#71)

Updated javadocs for GeneralIndexModule.

Bumped package. (#73)

Remove trailing whitespace in GeneralIndexModule.sol

Specify gas and blockGasLimit for localhost network

Add new Uniswap transfer fee adapter

Bump to 0.0.33

Add BalancerV1ExchangeAdapter tests (#72)

* Add balancerV1ExchangeAdapter tests

* Refactor uniswapV2ExchangeAdapter and add getDataParam test

* Re-add deleted functions in deployAdapter.ts

GeneralIndexModule internal review and external audit revisions (#75)

* Make all revert reason strings < 32bytes

* Remove _normalizedTargetMethod getter

* Encapsulate _noTokensToSell#canSell logic as internal method

* Encapsulate _allTargetsMet#targetUnmet logic in internal method

* Validate exchange adapter when setting & store name as bytes32 hash

* Consolidate position state and timestamp updates

* Validate params for external view methods

* Make all external setter method names use prefix `set`

* Add RebalanceStarted event

* Remove _caller param from onlyAllowedTrader modifier

* Add getDefaultPositionRealUnit getter

* Encapsulate data aggregation logic in #startRebalance in own method

* Prohibit external positions for components in #initialize and #startRebalance

* Refactor #createTradeInfo methods

* Make weth state variable immutable

* Delegate modifier logic to internal helpers in ModuleBase and GeneralIndexModule

* Delete trader permissions on module removal / add removeModule tests

* Add #validatePairsWithArray methods to AddressArrayUtils library and use in setter methods

* Add approximatelyEquals helper in PreciseUnitMath and use in #_targetUnmet

* Add AssetTargetsRaised event in RaiseAssetTargets with positionMultiplier arg

* Remove trader from permissionInfo.tradersHistory when they are de-authorized

* Add getAllowedTraders method and make tradersHistory contain unique elements

Added an exchangeData param on the GIM  (#76)

Added an exchangeData param on the GIM. Allows passing in arbitrary parameter or execution info to an exchange. Made all GIM adapters specific to GIM and not compatible with TradeModule.

restructure test/protocol/integration into folders (#79)

Final review fixes for GeneralIndexModule (#83)

* Add math boundary cases for protocol fee and raise target percentage

* Add external position modules check to #_validateTradeParameters

* Group internal methods by function and importance

* Remove TargetUnitsUpdated event, update RebalanceStarted event

* Add explanatory note about fixed / floating limit

* 0.0.35

Upgrade hardhat to 2.2.1 (Berlin) / add test:fast yarn commands (#78)

* Upgrade hardhat to 2.2.1 (Berlin) / add test:fast yarn commands

* Increase Yearn external abi gas estimates by 1 order of magnitude

KNC Migration Adapter (#86)

* Add Kyber migration adapter contract

* Add tests

* Add @openzeppelin/[email protected] as dependency

* Add external contract KyberNetworkTokenV2

* Updated KyberNetworkTokenV2 abi

* Add missing test and remove proxy from contract

* Remove external dependency openzeppelin/contracts-upgradeable

* Fix variable names and bump package

Add AXS migration wrap adapter (#87)

* Add Axie Infinity migration wrap adapter contract

* Add external TokenSwap contract

* Add tests for axie infinity migration wrap adapter

* Modify adapter to serve as middleware contract for migration

* Fix bugs and add tests

* Fixed external TokenSwap contract bytecode

* Explain migration process in javadocs

* Bump package to 0.0.37

Adding StandardTokenMock#mint

Adding CompClaimAdapter

feat: initial stab at Uniswap V3 adaptor

feat: verify uniswap path for single hops WIP

feat: verify path for multiple hops

fix: add proper tests, require statements for sellTokenForToken

refactor: move decoding tokens from encoded path into reusable function

feat: implement sellTokenForEthToUniswapV3 and sellEthForTokenToUniswapV3

fix: address review feedback

fix: sellEthForTokenToUniswapV3 impl and address review comments

refactor: clean up Uniswap V3 path decoding logic

fix: remove incorrect require check for sellEthForTokenToUniswapV3

Update package for public npm (make ethers a non-dev dep) (#92)

ZeroExAdapter null recipient fix (#93)

0.0.38 (#94)

Add Uniswap V3 Fixture (#95)

* add basic Uniswap V3 fixture

* add createPool to fixture

* add quoter

* add getTokenOrder helper

* add addLiquidityWide helper function

* add external contracts

* sort imports

* fix imports

* improve comments

* use exact values in tests

* split uniswap contracts into v2/v3 folders

* review changes

* add weth and wbtc price in initialize function

Uniswap V3 Exchange Adapter (#96)

* add basic Uniswap V3 fixture

* add createPool to fixture

* add quoter

* add getTokenOrder helper

* add addLiquidityWide helper function

* add external contracts

* sort imports

* fix imports

* improve comments

* use exact values in tests

* split uniswap contracts into v2/v3 folders

* add UniswapV3ExchangeAdapter

* remove stray console log

* fix comments

* cleanup

* add revert on path mismatch

* add uniswap v3 tests to trade module integration tests

* improve revert messages

Add forked provider infrastructure and simple TradeModule / ExchangeAdapter tests (#64)

Fix broken imports (#98)

* v0.0.39

* fix imports

Add uniswap v3 index exchange adapter (#89)

* Add external Uniswap V3 contracts

* Add UniswapV3 Index Exchange Adapter contract

* Modify compiler-version in .solhint.json

* Add tests

* Fix coverage

* Improve javadocs and add getEncodedTradePath() test

* Use soldityPack in getEncodedTradePath test

* Change compiler version to 0.6.10

* Add suggested changes
* Remove getTradeEncodedPath function
* Remove @uniswap/v3-periphery as dependency
* Add @uniswap/v3-core as dependency

* Use exactInputSingle and exactOutputSingle functions instead of exactInput and exactOutput

* Move BytesLib to external/contracts/uniswap/v3

* Add Note to set exchange data in GIM

* Remove @uniswap/v3-core dependency

*Add suggested changes

* Add getEncodedFeeData function and add suggested changes

* Refactor tests to use UniswapV3 Fixture

* Fix uniswap/v3/SwapRotuer ABI

* Add integration tests with GIM

* Trade DAI for WETH on UniswapV3 during rebalance
* Trade WETH for WBTC on UniswapV3 during rebalance

* Fix comment

fix broken test (#102)

Add AMMSplitter (#99)

* add _getUniSplit function

* add swapping

* only rever when total output < min output

* use better math for split

* add tests

* rename contracts and functions to be inline with spec

* add multi hop support

* add tradeExactOutput

* clean up imports

* shorten name

* add javadocs

* add getQuote function

* clean up ratio calculation

* remove hardhat console

* add TradeSplitterExchangeAdapter

* fix typo causing tests to break

* add TradeSplitterIndexExchangeAdapter

* improve coverage

* increase coverage

* refactor

* remove redundant if in getQuote

* improve reverts

* improve coverage

* move TradeSplitter into products

* add TradeSplitterExchangeAdapter integration test

* split getQuote into two functions for exact input/output

* add TradeSplitter GIM integration tests

* fix tests

* make TradeSplitter adhere to the UniswapV2Router interface

* use uniswap v2 adapters for trade splitter

* cleanup

* adhere to uniswap interface for getting quotes

* change name of contracts

* improve AMMSplitter tests

* refactoring

* fix bug with intermediary tokens with less than 18 decimals

* refactor

* add extra tests

* update revert messages

* improve comments

* add events

* use input and output tokens instead of path for events

v0.0.41 (#103)

change constructor parameters (#104)

fix Felix comments (#105)

Add Kyber V3 Index Exchange adapter (#101)

* Add KyberV3 Index Exchange adapter contract

* fix typo

* Add Kyber V3 DMM fixture

* Remove getPoolWithBestLiquidity() from adapter contract

* Add tests for adapter contract

* Add integration tests with GIM

* Add tests for Kyber V3 DMM fixture

* Fix bug

* Fix bug in uniswap V3 fixture tests

* Validate pool address in exchange adapter

* Bump package to 0.0.43

* Move down pool address validation

audit fixes (#108)

Patch for real -> virtual -> real unit conversion. (#100)

Patch for real -> virtual -> real unit conversion.

Bump package. (#109)

Update UniswapV2IndexExchangeAdapter to support two-hop trades (#110)

* update adapter and tests

* add integration test

* improve comments

* review changes

bump package.json version number (#111)

ClaimModule Audit fixes (#97)

* low hanging audit changes

* fix tests

* audit changes

* fix tests

* improve tests

* clean up contracts

* use removeStorage

* review changes

SingularityNet migrator that burns unmigrated tokens, thus zeroing ou… (#113)

SingularityNet migrator that burns unmigrated tokens, thus zeroing out position.

Bump package. (#114)

Address comments and add integration tests for Sushi wrap adapter
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants