GigaSwap Position Trading V3

Smart Contract Audit Report

Audit Summary

GigaSwap Position Trading V3 Audit Report GigaSwap is building a new platform where users can create their own exchanges and token lockers involving ERC-20, ERC-721, and ETH Assets. Users can also propose 'Deals' in which specified amounts of Assets are exchanged between users.

For this audit, we reviewed GigaSwap's project at commit ced0a44cae4d637d0403a22d7e92d62a58af3a0f on the team's GitHub repository.

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

Audit Findings

All findings have been resolved, though some centralized aspects are present.
Date: December 16th, 2022.
Updated: January 4th, 2023 to reflect changes from commit a305dc52790e84c28c553b45b31d863365ca09c6 to commit 843ed26653940fa78376702ad480f1e488d6aa14 on the team's private repository.
Updated: January 6th, 2023 to reflect changes from commit 843ed26653940fa78376702ad480f1e488d6aa14 to commit 7d42d1d78c1e80d29b81cc6f5777745db7695b6a on the team's private repository.
Updated: January 9th, 2023 to reflect changes from commit 7d42d1d78c1e80d29b81cc6f5777745db7695b6a to commit d750bdc562ba94bc06331b4e483bb935b9c5fe11 on the team's private repository.
Updated: January 11th, 2023 to update from private commit d750bdc562ba94bc06331b4e483bb935b9c5fe11 to the team's public repository at commit 943de9f51526bfa2d8f4c95ee5bb3e140accaab0.
Updated: January 26th, 2023 to reflect changes from commit 943de9f51526bfa2d8f4c95ee5bb3e140accaab0 on the team's public repository to commit 0668c3eb642eec4e903743574f0e007bdda0ee50 on the team's private repository.
Updated: February 2nd, 2023 to reflect changes from commit 0668c3eb642eec4e903743574f0e007bdda0ee50 on the team's private repository to commit 7d609daf059dae4cbdbdc8b9673fc14400f4f676 on the team's private repository.
Updated: February 7th, 2023 to update from commit 7d609daf059dae4cbdbdc8b9673fc14400f4f676 on the team's private repository to commit ced0a44cae4d637d0403a22d7e92d62a58af3a0f on the team's public repository.

Finding #1 - PositionsController - High (Resolved)

Description: The afterAssetTransfer() function, which executes Algorithm functionality after funds are transferred to an Asset, is not access restricted to Asset contracts. As a result, a user can execute Trading Pair swaps without having to supply funds.
Risk/Impact: A user can extract all funds from a Trading Pair by calling the afterAssetTransfer() function, acting as if they have transferred a large amount of Assets to the Trading Pair in order to receive a large amount of the other Asset.
Recommendation: The afterAssetTransfer() function should be access restricted.
Resolution: The team has implemented the above recommendation.

Finding #2 - Erc20Sale - High (Resolved)

Description: When purchasing, the user does not specify a desired price, only the number of tokens they wish to buy. This can result in a frontrunning scenario where the price of a token Sale is updated before a purchase is executed.
Risk/Impact: A Sale owner can frontrun a purchase with a price increase, resulting in a loss of the buyer's funds.
Recommendation: The Buy() function should include a desired price parameter. The Sale price at the time of execution should then be required to be equal to the buyer's desired price.
Resolution: The team has implemented the above recommendation.

Finding #3 - DealPointsController - High (Resolved)

Description: An executed DealPoint is reverted to unexecuted after a withdrawal occurs instead of before it occurs. If the transferred funds allow code execution upon receival, a user can reenter the contract and swap the Deal before the DealPoint is reverted to unexecuted.
Risk/Impact: If all DealPoints of a Deal have been executed, a user can withdraw an ERC721, ETH, or ERC-777 DealPoint and call the swap() function before the DealPoint is reverted to being unexecuted. The withdrawer will be refunded their supplied funds and will also gain ownership of the other Deal owner's supplied funds. The other owner will then be unable to withdraw the promised funds from the DealPoint that was withdrawn from.
Recommendation: A DealPoint should be set to unexecuted before the DealPoint's _withdraw() function is called.
Resolution: The team has implemented the above recommendation.

Finding #4 - DealPointsController - High (Resolved)

Description: The Checks-Effects-Interactions pattern is not followed in the withdraw() function, where a DealPoint's balance is set to 0 after funds are transferred. A user can execute a withdrawal and reenter the contract to call withdraw() again before the DealPoint's balance is set to 0 to receive more funds than they are owed.
Risk/Impact: A user can repeatedly reenter this contract if the funds allow code execution on receival to drain the contract's balance of the specific token. This will prevent other users from being able to withdraw funds of the same type.
Recommendation: A DealPoint's balance should be set to 0 before the DealPoint's _withdraw() function is called.
Resolution: The team has implemented the above recommendation.

Finding #5 - EthAssetsController - High (Resolved)

Description: The _transferToAsset() function does not ensure that ETH has been supplied by the caller. This allows users to increase the tracked balance of an ETH asset without having to supply any ETH.
Risk/Impact: A user can create a new ETH Asset with an associated balance without supplying any ETH. They can then withdraw the balance from the contract, stealing other users' stored funds. Users can also interact with Trading Pairs which contain an ETH Asset without supplying funds. This will allow them to receive the Trading Pair's other asset for free.
Recommendation: The _transferToAsset() function should require msg.value to be equal to the function's passed "count" parameter.
Resolution: The team has implemented the above recommendation.

Finding #6 - PositionsFactory - High (Resolved)

Description: The createLockPosition() function is not declared as payable, disallowing ETH to be supplied for Position creation.
Risk/Impact: Users will not be able to supply ETH to create Lock Positions. This will still create the ETH Asset with an artificial supply due to the issue described in the finding above. They can then drain any ETH in the contract.
Recommendation: The team should declare the createLockPosition() function as payable.
Resolution: The team has implemented the above recommendation.

Finding #7 - AssetsControllerBase & PositionsController - High (Resolved)

Description: In the AssetsControllerBase contract's transferToAsset() function, ETH is incorrectly transferred to the PositionsController contract. Upon receiving these funds, the PositionsController’s afterAssetTransfer() function transfers ETH to the Position's associated Algorithm contract. Since these funds are intended to be stored in the AssetsControllerBase contract, the Asset's count will not remain accurate.
Risk/Impact: ETH Position owners will lose funds that are sent to their Position.
Recommendation: The AssetsControllerBase contract's transferToAsset() function should not transfer funds to the PositionsController contract, and the PositionsController contract should not send ETH to the Position's associated Algorithm.
Resolution: The team has implemented the above recommendations.

Finding #8 - TradingPairAlgorithm - High (Resolved)

Description: The addLiquidity() function is not declared as payable, preventing users from supplying ETH in a function call. Despite not supplying funds, users will still be minted liquidity tokens due to the EthAssetsController's _transferToAsset() finding mentioned above.
Risk/Impact: Users will be able to mint liquidity tokens to a Position with an ETH Asset without supplying any ETH. They can then redeem the minted tokens for a share of the Position's funds.
Recommendation: The addLiquidity() function should be declared as payable. The msg.value should be checked to ensure that it is equal to the "count" or "countB" value to ensure that the proper amount of liquidity tokens are minted to the user.
Resolution: The addLiquidity() function has been declared as payable.

Finding #9 - Erc721ItemDealPointsController & Erc721CountDealPointsController - High (Resolved)

Description: The _execute() functions of the Erc721ItemDealPointsController & Erc721CountDealPointsController contracts do not ensure that the passed msg.value is equal to the ETH fee.
Risk/Impact: When withdrawing from an executed DealPoint of one of these types, ETH is transferred to the fee address regardless of if it was paid upon execution. This will transfer ETH that was not originally paid for as part of the Deal. This could potentially prevent the withdrawal if there is no ETH remaining in the contract, or prevent future withdrawals by transferring ETH that was reserved for other Deals.
Recommendation: The _execute() functions should require that the msg.value is equal to the required fee amount.
Update: The _withdraw() functions now ensure that the msg.value is at least the fee amount; however, users cannot supply ETH to the DealsController contract's withdraw() function in order to provide funds for the fee. The DealsController.withdraw() function should be made payable to allow users to pay the required fee.
Resolution: The team has implemented the above recommendations.

Finding #10 - TradingPairAlgorithm - High (Resolved)

Description: When adding liquidity to a Trading Pair, the PositionsController's transferToAssetFrom() function is called to transfer the supplied funds to each Asset in the Pair. However, if a user attempts to add liquidity to a Trading Pair containing an ETH Asset, the user's supplied ETH is not forwarded to the ETH Asset contract.
Risk/Impact: Liquidity adds to Trading Pairs containing an ETH Asset will fail.
Recommendation: The team should include an ETH value when calling the transferToAssetFrom() function if adding liquidity to an ETH Asset.
Resolution: The team has implemented the above recommendation.

Finding #11 - DealsController - High (Resolved)

Description: If a Deal with multiple Ether DealPoints with the same from address is created, execution will fail as the entire msg.value is sent to the EtherDealPointsController contract for the first executed DealPoint of that type.
Risk/Impact: Deals with multiple ETH DealPoints to be sent from a single address will not be able to be executed.
Recommendation: The team should either disallow multiple ETH DealPoints to be created for a single Deal, or only send the DealPoint's required amount of ETH upon its execution.
Resolution: The proper amount of ETH is now used for each DealPoint execution.

Finding #12 - PositionsController - High (Resolved)

Description: In the transferToAnotherAssetInternal() function, the receiving Asset's tracked "count" is not incremented properly when using a fee-on-transfer token. The count is incremented by the amount withdrawn from the "from" Asset, which will be a larger amount that the amount received by the "to" Asset.
from.withdraw(to.addr, count);
to.addCount(count);
Risk/Impact: The "to" Asset's tracked balance will be larger than it should be when transferring a fee-on-transfer token. When tokens are withdrawn from this Asset, the excess amount will be taken from other Asset's funds, or the withdrawal will fail.
Recommendation: The "to" Asset's count should be incremented by the amount it receives instead of the amount withdrawn from the "from" Asset.
Resolution: The team has implemented the above recommendation.

Finding #13 - TradingPairAlgorithm - Medium (Resolved)

Description: The addLiquidity() function does not ensure that the correct ratio of tokens are added to a Position if the Position contains fee-on-transfer Assets.
Risk/Impact: If one Asset is a fee-on-transfer token or the Assets have different fees, the Asset amounts transferred to the Position will not match the existing ratio of tokens.
Recommendation: The contract should require that the deposited Asset amounts match the existing ratio between the two Assets. This could require the user to input the amount of each Asset to be deposited.
Resolution: The addLiquidity() function now ensures that the calculated amounts of each token was received by the Position. As a result, adding liquidity is no longer permitted for fee-on-transfer Assets unless the proper exemptions have been made.

Finding #14 - EtherDealPointsController - Medium (Resolved)

Description: In the _withdraw() function, ETH is sent to users and the fee address using transfer() instead of call(), limiting the gas allowance of the receiver. If the recipient of a transfer is a contract that executes logic upon the receival of funds, there may be insufficient gas forwarded to complete the transaction.
Risk/Impact: Certain contracts may be unable to withdraw their owed ETH. As a result, the funds will be trapped in the contract.
Recommendation: The team should use the call() function instead of transfer() to send ETH.
Resolution: The team has implemented the above recommendation.

Finding #15 - EthAssetsController, Erc721CountDealPointsController, Erc721ItemDealPointsController, & gWETH - Medium (Resolved)

Description: In the mentioned contracts, ETH is sent to users and the fee address using transfer() instead of call(), limiting the gas allowance of the receiver. If the recipient of a transfer is a contract that executes logic upon the receival of funds, there may be insufficient gas forwarded to complete the transaction.
Risk/Impact: Transactions may fail if the recipient of a transfer contains logic that requires more than 2,100 gas. This could prevent withdrawals and unwrapping of gWETH for certain contracts.
Recommendation: The team should use the call() function instead of transfer() to send ETH.
Update: This has been resolved in the Erc721ItemDealPointsController and Erc721CountDealPointsController's _withdraw() functions, still persists in the EthAssetsController._withdraw(), gWETH.unwrap(), gWETH._afterTokenTransfer(), and Erc20SaleWeth.buy() functions.
Resolution: The team has resolved all instances of this issue.

Finding #16 - Erc20SaleWeth - Medium (Resolved)

Description: When a user purchases a Sale using ETH, the difference between the user's supplied ETH amount and the calculated required ETH amount is not returned to the user.
Risk/Impact: If a user sends excess ETH for a Sale, they will lose it to the Erc20SaleWeth contract where it will be trapped.
Recommendation: Excess ETH should be returned to the user after a Sale purchase is executed.
Resolution: The team has implemented the above recommendation.

Finding #17 - DealsController - Medium (Resolved)

Description: When a user withdraws, any excess ETH paid for fees is not returned to the user. The function also does not ensure that the user has supplied enough ETH for a Deal's withdrawal fees.
Risk/Impact: If a user sends excess ETH for a withdrawal, they will lose it to the contract. Other users can then use the excess ETH to pay their own fees.
Recommendation: Excess ETH should be returned to the user at the end of a withdrawal.
Resolution: The team implemented the above recommendation.

Finding #18 - DealsController - Medium (Resolved)

Description: Any excess ETH supplied for a Deal execution is not returned to the user.
Risk/Impact: Any excess ETH sent for Deal execution will remain in the contract. This ETH can then be used by other users for their own Deals.
Recommendation: Any excess ETH should be returned to the user at the end of Deal execution.
Resolution: The team implemented the above recommendation.

Finding #19 - DealsFactory - Low (Resolved)

Description: There is no limit on the number of DealPoints that can be created for a single Deal.
Risk/Impact: Creation or execution of a Deal could fail due to the execution cost of the transaction exceeding the block's gas limit.
Recommendation: The project team should limit the total number of DealPoints that can be added to a single Deal.
Resolution: A limit of 20 DealPoints per Deal has been implemented.

Finding #20 - Erc20SaleWeth - Low (Resolved)

Description: A Sale can be executed through the Erc20SaleWeth contract even if it does not take payment in gWETH. This could result in users mistakenly supplying ETH for Sales that do not support the payment.
Risk/Impact: Users may lose funds to the contract if they provide ETH for a Sale purchase that does not accept gWETH. These funds will become trapped in the Erc20Sale/Erc20SaleWeth contracts.
Recommendation: Sale purchases through this contract should disallow ETH to be supplied if the Sale does not use gWETH as its desired token.
Resolution: The team has implemented the above recommendation.

Finding #21 - DealsFactory - Informational (Resolved)

Description: The createDeal() function does not restrict a DealPoint's specified 'to' and 'from' addresses. A DealPoint's funds can only be withdrawn if the withdrawer is both an owner of the Deal and the owner of the DealPoint. If a DealPoint is created with a 'to' or a 'from' address which is not also one of the Deal's owners, the DealPoint's funds will become trapped in the contract if it is executed.
Risk/Impact: If a DealPoint is created with an address that is not one of the Deal's owners, funds will be lost if the DealPoint is executed.
Recommendation: The following requirements should be added to the createDeal() function:
  • The 'to' and 'from' addresses cannot both be set to the 0 address.
  • The 'to' and 'from' addresses must be the Deal's owners, or the 0 address if the Deal's "owner2" is set to the 0 address.
Resolution: The team has implemented the above recommendation.

Finding #22 - Erc721ItemAssetsController - Informational (Resolved)

Description: Both the Erc20AssetsController and Erc721ItemAssetsController contracts have an Asset Type ID of 2. This can result in incorrect Assets when fetching Asset Data.
Recommendation: The Erc721ItemAssetsController's type ID should be set as 3 instead of 2.
Resolution: The team has implemented the above recommendation.

Finding #23 - PositionAlgorithm - Informational (Resolved)

Description: The transferAssetOwnerShipTo() function does not properly transfer ownership. The PositionsController.owners mapping is used to verify ownership of a Position, but it is not updated in this function.
Risk/Impact: Users will not be able to transfer Position ownership despite intended functionality.
Recommendation: This function should be removed or should be updated to make changes to the mentioned owners mapping.
Resolution: This function has been removed.

Finding #24 - EtherDealPointsController - Informational (Resolved)

Description: The _execute() function contains the following code which transfers ETH to itself:
payable(address(this)).transfer(count);
Recommendation: This line should be removed.
Resolution: This line has been removed.

Finding #25 - EthAssetsController - Informational (Resolved)

Description: The _transferToAsset() function contains the following code which transfers ETH to itself, wasting gas and increasing transaction costs:
payable(this).transfer(count);
Recommendation: This line has been removed.

Finding #26 - PositionAlgorithm - Informational (Resolved)

Description: The onlyPositionsControllerFactory() modifier is never used.
Recommendation: This modifier can be removed for gas savings upon deployment and on function calls.
Resolution: This function has been removed.

Finding #27 - DealsController - Informational (Resolved)

Description: The stopEdit() function is declared, but serves no purpose and is never used.
Recommendation: This function can be removed for gas savings upon deployment.

Contracts Overview

  • As the contracts are implemented with Solidity 0.8.x, they are protected from overflows.
FeeSettings Contract:
  • This contract is used throughout the platform to fetch fee percentages and amounts.
  • The owner can update the fee percent to a maximum of 1% at any time.
  • The owner can update the ETH fee amount used for ERC721 sales to up to 0.035 ETH at any time.
  • The owner can update the fee address at any time
Erc20Sale Contract:
  • This contract allows users to create Sales for specified ERC20 tokens.
  • Any user can create a new Sale at any time, consisting of a token asset for sale, supply, desired token asset, and price.
  • The specified supply is transferred from the user to this contract, where other users can then purchase the asset for sale with the desired asset at the Position's specified price.
  • Any user can purchase a Sale's tokens for sale at any time.
  • When purchasing, the buyer specifies the Sale ID, the number of tokens to purchase, and the address to receive the purchased tokens.
  • The desired token payment, determined by the Sale's price, is transferred from the buyer to this contract.
  • A fee is taken from the purchased tokens and transferred to a fee recipient address. The remainder is transferred to the buyer.
  • A Sale owner can withdraw any of the Sale's tokens for sale or any desired tokens earned from sales at any time.
  • The same fee percentage is taken from the withdrawal amount and transferred to the fee recipient address. The remainder is transferred to the Sale owner.
  • The fee percentage and fee recipient is determined by the associated FeeSettings address.
  • A Sale owner can update the price of their token for sale at any time.
  • Any user can deposit additional funds to increase the supply of a Sale's asset for sale at any time. This will only benefit the owner of the Sale as only they can withdraw funds from the Sale.
gWETH Contract:
  • Any user can supply ETH to the contract in order to receive the equivalent quantity of gWETH.
  • A user can "unwrap" their tokens at any time, which burns them and transfers the user the amount in ETH.
  • Whitelisted users can transfer any user's tokens to any address at any time.
  • The owner can add or remove any address from the Whitelist at any time.
  • The owner can transfer ownership to any address at any time.
  • This contract complies with the ERC-20 standard.
Erc20SaleWeth Contract:
  • This contract allows users to use ETH to purchase tokens from an Erc20Sale Sale using gWETH as its desired token.
  • When purchasing, a user provides the same inputs as they would for a normal Sale purchase, but also supply an ETH amount.
  • This ETH is exchanged for gWETH and used for the Sale purchase.
  • Users should ensure that the specified Sale is using gWETH as the payment token to avoid potentially losing their supplied funds.
PositionsFactory Contract:
  • This contract allows users to create various Positions with specified ERC20, ERC721, or ETH Assets.
  • A user can create either a "Trading Pair Position" with two specified Assets or a "Lock Position" with a single Asset at any time.
  • A upon creation, a new Position ID is created and assigned to the user through the PositionsController contract.
  • When creating a Trading Pair Position, the specified "Owner" and "Output" Assets are added to the Position and are each assigned Asset IDs in the PositionsController contract.
  • The user's specified Assets are each transferred to the associated Erc20AssetsController, Erc721ItemAssetsController, or EthAssetsController contract according to the type of Asset it is.
  • A user can also create a Lock Algorithm Position with one specified Asset, where the Position is created with only the Owner Asset.
  • 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 of Assets and Positions and allows users to interact with their own and other users' Positions.
  • When a new Position using the PositionsFactory contract, this contract is used to create the specified Assets and assign the specified Algorithm to the Position.
  • Funds deposited during Position creation are transferred to either the Erc20AssetsController, Erc721ItemAssetsController, or EthAssetsController contracts depending on the Asset's type.
  • These AssetsController contracts are used to store and distribute Assets, as well as to facilitate user interaction with Positions.
  • A user can interact with a Position by calling the transferToAsset() function, specifying the Position and Asset ID.
  • A user can withdraw funds from their Position if it is unlocked.
  • A Position is considered locked or unlocked based on its status within its associated Algorithm contract.
PositionLockerAlgorithm Contract:
  • Users can create a Position using this algorithm to lock a specified ERC20 token, ERC721 token, or ETH.
  • The Position owner can extend the lock time of the Asset by any amount of time.
  • The Position owner can also choose to permanently lock the Asset, preventing withdrawals forever.
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 that uses this algorithm.
  • 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.
  • 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.
  • Users can add liquidity to a Trading Pair at any time. When adding liquidity, the user specifies the token "A" amount to supply to the Position's corresponding AssetController contract; the corresponding token B amount is then calculated and transferred based on the current ratio of the Pair.
  • The user is subsequently minted Liquidity tokens corresponding to the Position based on their portion of the Position's total liquidity.
  • Liquidity cannot be added to a Position using fee-on-transfer Assets unless the proper exemptions have been made.
  • A Position using this Algorithm is considered locked if the Position has a Liquidity Token assigned to it, but users holding Liquidity tokens can still withdraw funds by withdrawing directly from this contract.
  • 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.
  • If a Position is using this algorithm, a user can call the PositionsController's transferToAsset() function to trade between the Position's Assets at a rate determined by the balance of each Asset.
  • When trading, the user will also provide slippage conditions. When checking slippage, the user's desired price is compared against the actual price of the purchased Asset after the trade occurs.
  • A fee is taken from both the deposited Asset and the user's resulting output amount based on the Position's associated TradingPairFeeDistributer contract.
  • These Fees are transferred to the Position's cloned Assets owned by its TradingPairFeeDistributer.
  • 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%.
Erc20ForFactory Contract:
  • This contract is intended to be used as the Liquidity token for a Position using the TradingPairAlgorithm contract.
  • The Factory Role has the ability to mint tokens to any address at any time.
  • The Factory Role can also burn tokens from any address at any time.
  • The TradingPairAlgorithm is set as the Factory whenever a new instance of the contract is created from the TradingPairAlgorithm contract.
  • This contract complies with the ERC-20 standard.
TradingPairFeeDistributer Contract:
  • This contract facilitates the distribution of fees received from a TradingPair Position.
  • Holders of Fee Tokens created for a Position by the TradingPairAlgorithm 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.
  • The length of each round is set by the Position owner at the time of the creation of the TradingPair; a user must trigger the next round to begin in order for the previous one to end.
  • If sufficient time has passed, the next round is triggered on any deposit or withdrawal, or 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.
DealsFactory Contract:
  • In addition to Position trading, the platform allows users to create "Deals", enabling users to propose an exchange of various funds.
  • A Deal is a proposal for the exchange of various specified Assets. Each Asset to be involved in the exchange is represented by a "DealPoint".
  • This contract allows users to create Deals consisting of anywhere between two and twenty DealPoints of any type.
  • DealPoints can consist of an Ether amount, ERC20 token amount, an NFT, or multiple NFTs from the same contract.
  • A Deal is owned by its creator and a specified second owner. If no second owner is specified, the Deal is set as open to anyone.
  • An Ether DealPoint consists of an ETH amount, a 'from' address, and a 'to' address.
  • An ERC20 DealPoint consists of a token address, amount, 'from' address, and 'to' address.
  • An ERC721 DealPoint consists of a token address, token ID, 'from' address, and 'to' address.
  • An ERC721Count DealPoint consists of a token address, amount, 'from' address, and 'to' address.
  • If creating an ERC721Count DealPoint, a user cannot specify which token IDs to sell; the specified number of NFTs are transferred by iterating from the beginning of their indexed NFTs.
  • A DealPoint's 'from' address is the user who is intended to supply the DealPoint's specified funds, and the 'to' address is intended to be one of the Deal owners.
  • Each DealPoint's owner is set to its specified 'from' address upon Deal creation.
  • Users should exercise caution and ensure that the from and 'to' addresses of each DealPoint are one of the Deal owners. If a different address is specified, funds will become locked in the contract if the 'from' address provides them.
  • A Deal is then created through the DealsController contract and new Deal Points are created using the EtherDealPointsController, Erc20DealPointsController, ERC721ItemDealPointsController, and ERC721CountDealPointsController contracts.
  • The newly created Deal is automatically set to "executing state", meaning it can no longer be edited.
DealsController Contract:
  • This contract helps to facilitate the creation of Deals from the DealFactory contract and allows users to interact with existing Deals.
  • Any user can "execute" a Deal at any time. If a created Deal is open and has not been executed yet, the executor is set as the Deal's other owner.
  • When executed, each of the Deal's DealPoints are iterated through. If the caller is the DealPoint's 'from' address or the 'from' address is not set, the DealPoint itself is executed and funds are transferred from the caller to the DealPointsController contract that corresponds to the Asset type.
  • If the Deal is set as open, any DealPoints with a 'to' address that is not set is set to the caller.
  • Once all DealPoints have been executed, meaning funds have been supplied for each DealPoint, any user can "swap", which will transfer ownership of each DealPoint's 'from' address to its 'to' address.
  • Ownership transfer of a DealPoint allows the users to withdraw the funds intended for them as part of the Deal.
  • When withdrawing funds from a swapped Deal, a fee is taken and transferred to the fee address. The fee percentage and fee address are determined by the associated DealsController contract.
  • A user who has supplied funds as part of a DealPoint execution can withdraw their funds at any time as long as the Deal has not been swapped. This will remove the DealPoint as being executed and prevent a swap from occurring.
  • Fees are not taken on a withdrawal if the Deal was not swapped.
EtherDealPointsController, Erc20DealPointsController, Erc721ItemDealPointsController, & Erc721CountDealPointsController Contracts:
  • These contracts are used to store created DealPoint data and hold funds which are transferred upon DealPoint executions.
  • When a new Deal is created through the DealFactory contract, Each DealPoint's associated 'from' address, 'to' address, Deal ID, token address, and token count or ID is stored into one of these contracts, depending on its Asset type.
  • When a DealPoint is executed through the DealsController contract, its specified Assets are transferred from its 'from' address into one of these associated contracts.
  • Similarly, when a DealPoint is withdrawn using the DealsController contract, tokens are transferred from one of these contracts to the current owner of the DealPoint.
  • The owner of a DealPoint begins as its 'from' address; once the Deal has been fully executed and swapped, ownership is transferred to its 'to' address.
  • The Erc721CountDealPointsController contract is used for a DealPoint containing multiple NFTs, while the Erc721ItemDealPointsController contract is used for a single NFT.

Audit Results

Vulnerability Category Notes Result
Arbitrary Jump/Storage Write N/A PASS
Centralization of Control
  • The owner, Position owners, and the Factory and Controller Roles have the permissions described above.
  • A Erc20Sale owner can update the Sale price to any amount at any time.
  • Whitelisted users can transfer gWETH from any address 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)

    FeeSettings

    Private GitHub (Not yet deployed on mainnet)

    Inheritance Chart.  Function Graph.

    Erc20Sale

    Private GitHub (Not yet deployed on mainnet)

    Inheritance Chart.  Function Graph.

    gWETH

    Private GitHub (Not yet deployed on mainnet)

    Inheritance Chart.  Function Graph.

    Erc20SaleWeth

    Private GitHub (Not yet deployed on mainnet)

    Inheritance Chart.  Function Graph.

    PositionsFactory

    Private GitHub (Not yet deployed on mainnet)

    Inheritance Chart.  Function Graph.

    EthAssetsController

    Private GitHub (Not yet deployed on mainnet)

    Inheritance Chart.  Function Graph.

    Erc20AssetsController

    Private GitHub (Not yet deployed on mainnet)

    Inheritance Chart.  Function Graph.

    Erc721ItemAssetsController

    Private GitHub (Not yet deployed on mainnet)

    Inheritance Chart.  Function Graph.

    PositionsController

    Private GitHub (Not yet deployed on mainnet)

    Inheritance Chart.  Function Graph.

    PositionLockerAlgorithm

    Private GitHub (Not yet deployed on mainnet)

    Inheritance Chart.  Function Graph.

    TradingPairAlgorithm

    Private GitHub (Not yet deployed on mainnet)

    Inheritance Chart.  Function Graph.

    Erc20ForFactory

    Private GitHub (Not yet deployed on mainnet)

    Inheritance Chart.  Function Graph.

    TradingPairFeeDistributer

    Private GitHub (Not yet deployed on mainnet)

    Inheritance Chart.  Function Graph.

    DealsController

    Private GitHub (Not yet deployed on mainnet)

    Inheritance Chart.  Function Graph.

    EtherDealPointsController

    Private GitHub (Not yet deployed on mainnet)

    Inheritance Chart.  Function Graph.

    Erc20DealPointsController

    Private GitHub (Not yet deployed on mainnet)

    Inheritance Chart.  Function Graph.

    Erc721ItemDealPointsController

    Private GitHub (Not yet deployed on mainnet)

    Inheritance Chart.  Function Graph.

    Erc721CountDealPointsController

    Private GitHub (Not yet deployed on mainnet)

    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 1800+ solidity smart contract audits covering all major project types and protocols, securing a total of over $10 billion U.S. dollars in on-chain value!
    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.