GigaSwap Position Trading V2

Smart Contract Audit Report

Audit Summary

GigaSwap Position Trading V2 Audit Report GigaSwap is building a new platform where users can initiate ERC-20, ERC-721, and ETH Position trades.

For this audit, we reviewed GigaSwap's position_trading folder using code provided by the project team.

We previously reviewed the GigaSwap here, and GigaSwap's Position Trading V1 project here.

Audit Findings

All findings have been resolved, though centralized aspects are present.
Date: September 8th, 2022.
Updated: September 13th, 2022 to reflect updates made by the project team.
Updated: September 14th, 2022 to reflect updates made by the project team.
Updated: September 15th, 2022 to reflect updates made by the project team.
Updated: September 17th, 2022 to reflect updates made by the project team.

Finding #1 - PositionAlgorithm - High (Resolved)

Description: Any user can call the afterAssetTransfer() function to send tokens or ETH from any owner's Asset contract to any address at no cost.
function afterAssetTransfer(
	address asset,
	address from,
	address to,
	uint256 amount,
	uint256[] memory data
) external override {
	_afterAssetTransfer(asset, from, to, amount, data);
}
Risk/Impact: The tokens or ETH in the owners asset contracts can be withdrawn by any user at no cost.
Recommendation: The team should modify this function to only allow an exchange of tokens or ETH to be made, or to fit their intended functionality.
Resolution: The afterAssetTransfer() and beforeAssetTransfer() functions can now only be called by an Asset contract.

Finding #2 - TradingPair - High (Resolved)

Description: Users are not minted the correct number of liquidity tokens when providing liquidity to a Trading Pair Position.
Risk/Impact: Users will be minted less than their fair share of liquidity tokens and will subsequently not be able to withdraw the amount of liquidity that they are due.
Recommendation: The team should update the calculations in the addLiquidityByOwnerAsset() to mint the proper amount of liquidity tokens after a liquidity add.
Resolution: The correct number of liquidity tokens are now minted to a liquidity provider.

Finding #3 - TradingPair - High (Resolved)

Description: Trade price calculations do not account for the price impact of the trade itself.
Risk/Impact: Trades with a large price impact can result in users paying a different price than they should. This can allow users to drain funds from the Trading Pair Assets.
Recommendation: The team should update the price calculations to account for the trade's price impact.
Resolution: Price calculations now correctly account for the price impact of the trade.

Finding #4 - PositionsController - High (Resolved)

Description: The positionUnLocked() modifier incorrectly ensures that the Position is locked instead of unlocked if the Position has an associated Algorithm.
Risk/Impact: A user can update their Position's Asset while the Position is locked due to an incorrect modifier, allowing them to update their Trading Pair Asset to disallow users from redeeming their liquidity tokens.
modifier positionUnLocked(uint256 positionId) {
	...
	if (data.contractAddr != address(0)) {
		require(
			IPositionAlgorithm(data.contractAddr).isPositionLocked(
				positionId
			),
			'position algogithm is not allowed to edit position'
		);
	}
	_;
}
Recommendation: The team should update the positionUnLocked() to properly ensure that the Position is unlocked.
Resolution: A Position's Asset can now only be updated while unlocked:
modifier positionUnLocked(uint256 positionId) {
	...
	if (data.contractAddr != address(0)) {
		require(
			!IPositionAlgorithm(data.contractAddr).isPositionLocked(
				positionId
			),
			'position algogithm is not allowed to edit position'
		);
	}
	_;
}

Finding #5 - FeeDistributer - High (Resolved)

Description: A user's total deposited token amount is not properly set upon depositing.
feeTokenLocks[msg.sender] = amount;
Risk/Impact: If a user deposits twice, their original deposit amount will be erased.
Recommendation: The users deposited amount should be incremented instead of overwritten.
Resolution: The project team has implemented the above recommendation.

Finding #6 - TradingPair - High (Resolved)

Description: Trading Pair Positions can be created with any tokens; however, users will be minted an incorrect number of liquidity tokens when adding liquidity to a Trading Pair Position using a fee-on-transfer token.
Risk/Impact: If a fee-on-transfer token is used as an Asset, a liquidity add will provide less tokens to the Position than initially transferred. The user will then be minted more than their share of liquidity tokens.
Recommendation: The team should add support for fee-on-transfer tokens by checking the Asset balances before and after the liquidity add in order to calculate the amount received. Liquidity tokens should then be minted based on these amounts.
Resolution: The team has added support for fee-on-transfer tokens.

Finding #7 - TradingPair - High (Resolved)

Description: The result of a Trading Pair swap does not account for fee-on-transfer tokens and will provide tokens based on the user's input amount instead of the amount received by the Asset.
Risk/Impact: If a fee-on-transfer token is used as an Asset, the Position will lose funds over time due to transfer fees.
Recommendation: The team should add support for fee-on-transfer tokens by calculating output amounts based on the number of tokens received by the Asset contract.
Resolution: The team has added support for fee-on-transfer token trades.

Finding #8 - Erc20Asset & Erc721ItemAsset - Medium (Resolved)

Description: The transferToAsset() functions allow any user to input a "spender" address that will transfer tokens to the contract.
Risk/Impact: Any user can initiate a transfer on behalf of another user if any excess allowance has been granted to the contract by the "spender".
Recommendation: The "spender" address should be removed as a parameter and msg.sender should instead be used throughout the transferToAsset() functions.
Resolution: The project team has implemented the above recommendation.

Contracts Overview

  • The contracts should be deployed using Solidity v0.8.x for safety against any possible overflows/underflows.
EthAsset, ERC20Asset, & Erc721ItemAsset Contracts:
  • These contracts are used by their owners to manage their available Assets. This can be an ERC20 token, ERC721 token, or ETH.
  • Each contract represents a single Asset.
  • Any address can deposit into an Asset contract; however, only the owner of the contract can withdraw.
  • Users can use the Asset's transferToAsset() function to deposit funds in order to interact with the Position's Algorithm.
  • Users should ensure that the Asset belongs to a Position with an associated Algorithm before depositing.
  • An Erc20Asset or EthAsset contract can be used to manage any amount of an associated ERC20 or ETH, respectively.
  • An Erc721ItemAsset contract is only used to manage a single token ID from its associated ERC721 contract.
  • Any user can deploy a clone of any Asset contract with a new specified owner using the clone() function.
  • The owner can withdraw to any address at any time.
EthAssetFactory, ERC20AssetFactory, & Erc721ItemAssetFactory Contracts:
  • These contracts facilitate the creation of new Asset contracts and the cloning of existing Asset contracts.
  • Once a Position is created, users can set and update its Owner Asset and Output Assets through this contract.
  • A Position's Asset, Output Asset, Algorithm, can be set as long as the Position is locked within its associated Algorithm contract.
  • Setting a Position's Asset will create a new Asset contract and update the user's Position in the PositionsController contract.
  • If the Position has an associated Algorithm, ownership of the new Asset is transferred to it.
PositionFactory Contract:
  • This contract allows users to create various Positions with specified Assets.
  • A user can create a Position with two specified Assets at any time.
  • A new Position ID is created and assigned to the user through the PositionsController contract.
  • The Position's "Owner Asset" and "Output Asset" contracts are then created and assigned to the Position using the appropriate Asset Factory contracts based on the user's inputs.
  • Ownership of each of these Assets are set to the PositionsController contract, and ownership of the Position is then transferred to the user.
  • A user can also create a Lock Algorithm Position with one specified Asset, where the Position is created with only the Owner Asset set.
  • This follows the same process, but ownership of the Owner Asset is instead transferred to this contract's associated PositionLockerAlgorithm address.
  • Lastly, a user can create a Sale Algorithm Position with two Assets and a specified sale price.
  • Ownership of both Owner and Output assets are transferred to this contract's associated Sale contract.
  • The owner can update the PositionsController address at any time.
  • The owner can update the EthAssetFactory address at any time, but cannot update the Erc20AssetFactory or Erc721ItemAssetFactory addresses.
PositionsController Contract:
  • This contract facilitates the creation and editing of Positions.
  • Instead of creating a Position using the PositionFactory, users can execute each step of Position and Asset creation separately through this contract if they choose.
  • A Position's owner can transfer its ownership at any time.
  • A Position is considered locked or unlocked based on its status within its associated Algorithm contract. If its Algorithm has not been set, it is considered unlocked.
  • When an Authorized contract is used to update a Position's algorithm, ownership of both of the Position's Assets are transferred to the specified contract as well. If no Algorithm exists for the Position, ownership is transferred from this contract; otherwise the Position's Algorithm contract is called to transfer its ownership of the Assets.
Sale Contract:
  • This Algorithm enables users to purchase a Position's Owner Asset by providing its Output Asset.
  • The price of the Owner Asset is specified by the Position owner and can be updated at any time.
  • When the transferToAsset() function is called to the Output Asset of a Position implementing this Algorithm, the Owner Asset amount is calculated based on the Output Asset amount supplied and price. This amount is then transferred to the buyer.
  • Users will specify their desired price when purchasing; if the Asset's set price is not equal to the specified price, the transaction will fail.
  • The Owner Asset must have enough funds in order to facilitate the trade.
  • A Position's owner can update its Algorithm to this contract if the Position is unlocked.
  • A user can lock their Position for any amount of time if it is unlocked.
  • A user can withdraw any amount of their Position's Owner Asset or Output Asset through this contract while the Position's Algorithm is set to this contract.
TradingPair Contract:
  • This algorithm allows users to perform swaps between a Position's two Assets at a rate determined by the ratio of funds between the Assets.
  • This Algorithm also allows users to provide liquidity to an existing Position using this algorithm.
  • A Position's owner can update its Algorithm to this contract if the Position is unlocked.
  • When this contract is set as a Position's Algorithm, two Erc20ForFactory contracts are created to be used as the Position's Liquidity Token and Fee Token.
  • As the Erc20ForFactory contract was not included in the scope of this audit, we are unable to provide an assessment with regards to security or functionality.
  • A Position using this Algorithm is considered locked if the Position has a Liquidity Token assigned to it.
  • Fee Tokens and Liquidity Tokens are minted to the Position owner at an amount equal to the product of the Owner Asset balance and the Output Asset balance.
  • Clones of both Asset contracts and a FeeDistributer contract are also created. This FeeDistributer contract is set as the owner of these Assets.
  • A user can then use this contract to create clones of the Position's Assets and supply them with funds so that liquidity can be added to the Position.
  • When Liquidity is added, funds are transferred from the cloned Assets to the Position's Assets and the user is minted the Position's Liquidity Tokens.
  • The provider's Output Asset balance must be greater than or equal to the product of their Owner Asset balance and the ratio of the Position's Owner Asset balance to its Output Asset Balance.
  • Users can redeem their Liquidity Tokens at any time in order to withdraw a portion of the Position's Asset's funds; this will burn the Liquidity Tokens in the process.
  • Users should exercise caution when adding liquidity as the value of their liquidity tokens may decrease.
  • While the Position is using this Algorithm, a user can use either Asset's transferToAsset() function to trade for the Positions other Asset at a rate determined by the balance of each Asset.
  • When trading, the user will also provide slippage conditions.
  • A fee is taken from both the deposited Asset and the user's resulting output amount based on the Position's associated FeeDistributer contract.
  • These Fees are transferred to the Position's cloned Assets owned by its FeeDistributer.
  • The trader is then transferred their remaining output amount after the fee.
  • A trade cannot occur if it results in a price impact greater than 50%.
FeeDistributer Contract:
  • This contract facilitates the distribution of fees received from a TradingPair Position.
  • Holders of Fee Tokens created for a Position by the TradingPair contract have the ability to deposit their Fee Tokens into this contract in order to receive a share of accumulated rewards.
  • Users can claim their share of rewards from a deposit once the contract's next "round" has begun.
  • Each round lasts one day; a user must trigger the next round to begin in order for the previous one to end.
  • The next round is triggered on any deposit or withdrawal, by calling the tryNextFeeRound() function.
  • When claiming for a round, the reward amount of each Asset distributed to a user is determined by their percentage of total deposited fee tokens multiplied by the total amount of collected fees for the Asset.
  • Users can withdraw their deposited fee tokens at any time.

Audit Results

Vulnerability Category Notes Result
Arbitrary Jump/Storage Write N/A PASS
Centralization of Control
  • The owner and Position owners have the permissions described above.
  • A Trading Pair Position fees can be set to any amounts at the time the Algorithm is set.
  • A Sale Position owner can update the price of an Asset to any amount at any time.
  • The Position Controller's authorized addresses can update a Position's Algorithm and Asset ownership at any time.
  • WARNING
    Compiler Issues N/A PASS
    Delegate Call to Untrusted Contract N/A PASS
    Dependence on Predictable Variables N/A PASS
    Ether/Token Theft N/A PASS
    Flash Loans N/A PASS
    Front Running N/A PASS
    Improper Events N/A PASS
    Improper Authorization Scheme N/A PASS
    Integer Over/Underflow N/A PASS
    Logical Issues N/A PASS
    Oracle Issues N/A PASS
    Outdated Compiler Version N/A PASS
    Race Conditions N/A PASS
    Reentrancy N/A PASS
    Signature Issues N/A PASS
    Unbounded Loops N/A PASS
    Unused Code N/A PASS
    Overall Contract Safety   PASS

    Contract Source Summary and Visualizations

    Name

    Address/Source Code

    Visualized
    (Hover-Zoom Recommended)

    ERC20Asset

    ​N/A​

    Inheritance Chart.  Function Graph.

    Erc721ItemAsset

    ​N/A​

    Inheritance Chart.  Function Graph.

    EthAsset

    ​N/A​

    Inheritance Chart.  Function Graph.

    Erc20AssetFactory

    ​N/A​

    Inheritance Chart.  Function Graph.

    Erc721ItemAssetFactory

    ​N/A​

    Inheritance Chart.  Function Graph.

    PositionFactory

    ​N/A​

    Inheritance Chart.  Function Graph.

    PositionsController

    ​N/A​

    Inheritance Chart.  Function Graph.

    PositionLockerAlgorithm

    ​N/A​

    Inheritance Chart.  Function Graph.

    Sale

    ​N/A​

    Inheritance Chart.  Function Graph.

    TradingPair

    ​N/A​

    Inheritance Chart.  Function Graph.

    FeeDistributer

    ​N/A​

    Inheritance Chart.  Function Graph.

    About SourceHat

    SourceHat has quickly grown to have one of the most experienced and well-equipped smart contract auditing teams in the industry. Our team has conducted 1300+ solidity smart contract audits covering all major project types and protocols, securing a total of over $50 billion U.S. dollars in on-chain value across 1500 projects!.
    Our firm is well-reputed in the community and is trusted as a top smart contract auditing company for the review of solidity code, no matter how complex. Our team of experienced solidity smart contract auditors performs audits for tokens, NFTs, crowdsales, marketplaces, gambling games, financial protocols, and more!

    Contact us today to get a free quote for a smart contract audit of your project!

    What is a SourceHat Audit?

    Typically, a smart contract audit is a comprehensive review process designed to discover logical errors, security vulnerabilities, and optimization opportunities within code. A SourceHat Audit takes this a step further by verifying economic logic to ensure the stability of smart contracts and highlighting privileged functionality to create a report that is easy to understand for developers and community members alike.

    How Do I Interpret the Findings?

    Each of our Findings will be labeled with a Severity level. We always recommend the team resolve High, Medium, and Low severity findings prior to deploying the code to the mainnet. Here is a breakdown on what each Severity level means for the project:

    • High severity indicates that the issue puts a large number of users' funds at risk and has a high probability of exploitation, or the smart contract contains serious logical issues which can prevent the code from operating as intended.
    • Medium severity issues are those which place at least some users' funds at risk and has a medium to high probability of exploitation.
    • Low severity issues have a relatively minor risk association; these issues have a low probability of occurring or may have a minimal impact.
    • Informational issues pose no immediate risk, but inform the project team of opportunities for gas optimizations and following smart contract security best practices.