DeFiFoundry
20,000 USDC
View results
Submission Details
Severity: medium
Valid

` Auction tokens ` can be stuck in the ` FjordAuctionFactory ` contract

Description + Impact

The owner of FjordAuctionFactory contract will execute FjordAuctionFactory :: createAuction() to deploy a copy of FjordAuction and to transfer a specific amount of auction tokens to the newly deployed FjordAuction through this function .

In FjordAuction , the owner state variable will be equal with the value of msg.sender which is the FjordAuctionFactory contract .

In FjordAuction :: auctionEnd() , if totalBids state variable is equal with 0 , all the auction tokens will be transfered to the owner of the FjordAuction contract which is the FjordAuctionFactory .

The problem is that the auction tokens can't be retrieved no more from FjordAuctionFactory because the contract only has 2 functions and no function deals with the retrieve of tokens .

The issue can be prevented if the owner of the FjordAuction will be set in the constructor to the address that executes the createAuction() function

( ! ) This means that this issue is only possible if there were no user to make a bid, it is a very specific scenario that will most likely will not happen, I am aware of this !

PoC

How to execute the PoC ?

  • Create a Foundry project with everything needed

  • Add the PoC in test/PoC.t.sol

  • Execute the PoC using the command forge test --match-path test/PoC.t.sol --match-test test________ -vv

PoC
​
​
​
// SPDX-License-Identifier: MIT
​
pragma solidity ^0.8.0 ;
​
​
​
​
​
​
​
​
​
import { Test } from "lib/forge-std/src/Test.sol" ;
​
import { console } from "lib/forge-std/src/console.sol" ;
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
import { FjordAuction } from 'src/FjordAuction.sol' ;
​
​
import { AuctionFactory } from 'src/FjordAuctionFactory.sol' ;
​
​
import { FjordPoints } from 'src/FjordPoints.sol' ;
​
​
import { FjordStaking } from 'src/FjordStaking.sol' ;
​
​
import { FjordToken } from 'src/FjordToken.sol' ;
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
import { ERC20 } from "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
​
​
​
import { IFjordPoints } from "src/interfaces/IFjordPoints.sol";
​
​
​
import { IERC20 } from "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
​
​
​
import { ERC20Burnable } from "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Burnable.sol";
​
​
​
import { ISablierV2Lockup } from "lib/v2-core/src/interfaces/ISablierV2Lockup.sol";
​
​
​
import { ISablierV2LockupLinear } from "lib/v2-core/src/interfaces/ISablierV2LockupLinear.sol";
​
​
​
import { Broker , LockupLinear } from "lib/v2-core/src/types/DataTypes.sol";
​
​
​
import { ud60x18 } from "@prb/math/src/UD60x18.sol";
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
interface I______ERC20__decimals {
​
function decimals() external view returns (uint8);
​
}
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
contract Mock______ERC20 is ERC20 {
​
uint8 private _decimals;
​
constructor( string memory a , string memory b , uint8 c ) ERC20( a , b ) {
​
_decimals = c;
​
}
​
function mint(address to, uint256 amount) public {
​
_mint(to, amount);
​
}
​
function decimals() public view virtual override returns (uint8) {
​
return _decimals;
​
}
​
}
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
contract Contract is Test {
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
struct Users {
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
User PRIVILEGED ;
​
​
​
​
User ATTACKER ;
​
​
​
​
User ______USER__1______ ;
​
​
​
​
User ______USER__2______ ;
​
​
​
​
User ______USER__3______ ;
​
​
​
​
User ______USER__4______ ;
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
struct User {
​
​
​
​
​
​
​
​
​
​
​
​
​
uint256 PRIVATE__KEY ;
​
​
​
​
address ADDRESS ;
​
​
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
struct Scope {
​
​
​
​
​
​
​
​
​
​
​
​
​
FjordAuction ______FjordAuction______ ;
​
​
AuctionFactory ______AuctionFactory______ ;
​
​
FjordPoints ______FjordPoints______ ;
​
​
FjordStaking ______FjordStaking______ ;
​
​
FjordToken ______FjordToken______ ;
​
​
​
​
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
struct Mocks {
​
​
​
​
​
​
​
​
​
​
​
​
​
Mock______ERC20 ______Mock______ERC20______MOCK__ERC20__1 ;
​
​
​
​
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
struct Addresses {
​
​
​
​
​
​
​
​
​
​
​
Ethereum Ethereum ;
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
struct Ethereum {
​
​
​
​
​
​
​
​
​
​
​
​
​
​
ISablierV2LockupLinear ______Sablier______ ;
​
​
​
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
Users ________Users ;
​
​
​
​
Scope ________Scope ;
​
​
​
​
Mocks ________Mocks ;
​
​
​
​
Addresses ________Addresses ;
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
function test________() public {
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
fork( 0 ) ;
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
users() ;
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
// NOTE :: Initialize miscellaneous state variables used across the PoC
​
​
​
{
​
​
​
​
​
​
​
​
​
​
​
​
________Addresses.Ethereum.______Sablier______ = ISablierV2LockupLinear( 0xB10daee1FCF62243aE27776D7a92D39dC8740f95 ) ;
​
​
​
​
​
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
// NOTE :: Deploy the smart contracts and mocks
​
​
​
​
{
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
/// Deploy mock ` MOCK__ERC20__1 ` ///
​
{
​
​
​
​
​
​
​
​
vm.startPrank( ________Users.PRIVILEGED.ADDRESS );
​
​
​
​
​
​
​
​
________Mocks.______Mock______ERC20______MOCK__ERC20__1 = new Mock______ERC20(
​
​
​
​
​
​
" MOCK__ERC20__1 " ,
​
​
​
​
​
" MOCK__ERC20__1 " ,
​
​
​
​
​
​
18
​
​
​
​
);
​
​
​
​
​
​
​
​
vm.stopPrank();
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
/// Deploy ` FjordToken ` ///
​
​
{
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
vm.startPrank( ________Users.PRIVILEGED.ADDRESS );
​
​
​
​
​
​
​
​
________Scope.______FjordToken______ = new FjordToken( );
​
​
​
​
​
​
​
​
vm.stopPrank();
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
/// Deploy ` FjordPoints ` ///
​
{
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
vm.startPrank( ________Users.PRIVILEGED.ADDRESS );
​
​
​
​
​
​
​
​
________Scope.______FjordPoints______ = new FjordPoints( );
​
​
​
​
​
​
​
​
vm.stopPrank();
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
/// Deploy ` AuctionFactory ` ///
​
{
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
vm.startPrank( ________Users.PRIVILEGED.ADDRESS );
​
​
​
​
​
​
​
​
________Scope.______AuctionFactory______ = new AuctionFactory({
​
​
​
​
​
​
_fjordPoints : address( ________Scope.______FjordPoints______ )
​
​
​
​
​
​
​
});
​
​
​
​
​
​
​
​
vm.stopPrank();
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
/// Deploy ` FjordStaking ` ///
​
​
{
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
vm.startPrank( ________Users.PRIVILEGED.ADDRESS );
​
​
​
​
​
​
​
​
________Scope.______FjordStaking______ = new FjordStaking({
​
​
​
​
​
​
​
_fjordToken : address( ________Scope.______FjordToken______ ) ,
​
​
​
​
​
​
​
​
_rewardAdmin : address( ________Users.PRIVILEGED.ADDRESS ) ,
​
​
​
​
​
​
​
​
​
_sablier : address( ________Addresses.Ethereum.______Sablier______ ) ,
​
​
​
​
​
​
​
​
​
​
​
_authorizedSablierSender : address( ________Users.PRIVILEGED.ADDRESS ) ,
​
​
​
​
​
​
​
​
​
​
_fjordPoints : address( ________Scope.______FjordPoints______ )
​
​
​
​
​
​
});
​
​
​
​
​
​
​
​
vm.stopPrank();
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
// NOTE :: Simulate the deployment of ` FjordAuction ` because ` AuctionFactory :: createAuction() ` doesn't return the deployed address to make testing easier
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
/// Deploy ` FjordAuction ` ///
​
{
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
vm.startPrank( address( ________Scope.______AuctionFactory______ ) );
​
​
​
​
​
​
​
​
________Scope.______FjordAuction______ = new FjordAuction({
​
​
​
​
​
​
​
​
​
_fjordPoints : address( ________Scope.______FjordPoints______ ) ,
​
​
​
​
​
​
​
​
​
_auctionToken : address( ________Mocks.______Mock______ERC20______MOCK__ERC20__1 ) ,
​
​
​
​
​
​
​
​
_biddingTime : uint256( 12 hours ) ,
​
​
​
​
​
​
​
​
​
_totalTokens : uint256(
​
​
( 1000 ) * ( 10 ** I______ERC20__decimals( address( ________Mocks.______Mock______ERC20______MOCK__ERC20__1 ) ).decimals() )
​
​
)
​
​
​
​
​
​
​
});
​
​
​
​
​
​
​
​
vm.stopPrank();
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
// NOTE :: Give money
​
​
​
/// ERC20 :: ` MOCK__ERC20__1 ` :: ` << FjordAuction >> ` ///
​
​
{
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
( address[] memory ADDRESS , ) = get_list_of_addresses() ;
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
address ERC20 = address( ________Mocks.______Mock______ERC20______MOCK__ERC20__1 ) ;
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
uint256 AMOUNT__OF__ERC20 = ( 1000 ) * ( 10 ** I______ERC20__decimals( ERC20 ).decimals() ) ;
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
for ( uint256 III = 6 ; III < 7 ; III++ ) {
​
​
​
​
​
​
​
​
​
​
​
​
​
deal(
​
​
​
​
​
address( ERC20 ) ,
​
​
​
​
​
​
address( ADDRESS[ III ] ) ,
​
​
​
​
​
​
AMOUNT__OF__ERC20
​
​
​
​
​
​
);
​
​
​
​
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
{
​
​
​
​
​
​
​
​
​
require(
​
​
​
​
​
​
​
​
(
​
​
​
​
________Scope.______FjordAuction______.owner()
​
​
​
​
)
​
​
​
​
==
​
​
​
​
(
​
​
​
​
address( ________Scope.______AuctionFactory______ )
​
​
​
) ,
​
​
​
​
​
​
​
​
unicode' ⛔️ '
​
​
​
​
​
​
​
​
);
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
​
​
​
{
​
​
​
​
​
​
​
​
​
require(
​
​
​
​
​
​
​
​
(
​
​
​
​
IERC20( ________Mocks.______Mock______ERC20______MOCK__ERC20__1 ).balanceOf( address( ________Scope.______AuctionFactory______ ) )
​
​
​
​
)
​
​
​
​
==
​
​
​
​
(
​
​
​
​
( 0 ) * ( 10 ** I______ERC20__decimals( address( ________Mocks.______Mock______ERC20______MOCK__ERC20__1 ) ).decimals() )
​
​
​
) ,
​
​
​
​
​
​
​
​
unicode' ⛔️ '
​
​
​
​
​
​
​
​
);
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
skip( 12 hours + 5 minutes ) ;
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
/// ` FjordAuction ` :: auctionEnd ///
​
​
{
​
​
​
​
​
​
​
​
​
​
vm.startPrank( ________Users.ATTACKER.ADDRESS );
​
​
​
​
​
​
​
​
________Scope.______FjordAuction______.auctionEnd( );
​
​
​
​
​
​
​
​
vm.stopPrank();
​
​
​
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
{
​
​
​
​
​
​
​
​
​
require(
​
​
​
​
​
​
​
​
(
​
​
​
​
IERC20( ________Mocks.______Mock______ERC20______MOCK__ERC20__1 ).balanceOf( address( ________Scope.______AuctionFactory______ ) )
​
​
​
​
)
​
​
​
​
==
​
​
​
​
(
​
​
​
​
( 1000 ) * ( 10 ** I______ERC20__decimals( address( ________Mocks.______Mock______ERC20______MOCK__ERC20__1 ) ).decimals() )
​
​
​
) ,
​
​
​
​
​
​
​
​
unicode' ⛔️ '
​
​
​
​
​
​
​
​
);
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
function users() private {
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
( ________Users.PRIVILEGED.ADDRESS , ________Users.PRIVILEGED.PRIVATE__KEY ) = makeAddrAndKey( ' PRIVILEGED ' ) ;
​
​
​
​
( ________Users.ATTACKER.ADDRESS , ________Users.ATTACKER.PRIVATE__KEY ) = makeAddrAndKey( ' ATTACKER ' ) ;
​
​
​
​
( ________Users.______USER__1______.ADDRESS , ________Users.______USER__1______.PRIVATE__KEY ) = makeAddrAndKey( ' USER 1 ' ) ;
​
​
​
( ________Users.______USER__2______.ADDRESS , ________Users.______USER__2______.PRIVATE__KEY ) = makeAddrAndKey( ' USER 2 ' ) ;
​
​
( ________Users.______USER__3______.ADDRESS , ________Users.______USER__3______.PRIVATE__KEY ) = makeAddrAndKey( ' USER 3 ' ) ;
​
​
​
( ________Users.______USER__4______.ADDRESS , ________Users.______USER__4______.PRIVATE__KEY ) = makeAddrAndKey( ' USER 4 ' ) ;
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
​
​
​
​
function fork(
uint8 index
) private {
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
string[] memory CHAIN = new string[]( 1 ) ;
​
​
​
​
CHAIN[ 0 ] = "https://rpc.ankr.com/eth" ; // Ethereum
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
vm.createSelectFork( CHAIN[ index ] ) ;
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
​
​
​
​
​
function get_list_of_addresses() private
​
​
returns( address[] memory ADDRESS , string[] memory NAME__OF__ADDRESS )
​
​
​
​
​
​
​
{
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
ADDRESS = new address[]( 11 ) ;
​
​
​
​
ADDRESS[ 0 ] = address( ________Users.ATTACKER.ADDRESS ) ;
​
​
ADDRESS[ 1 ] = address( ________Users.______USER__1______.ADDRESS ) ;
​
​
ADDRESS[ 2 ] = address( ________Users.______USER__2______.ADDRESS ) ;
​
​
ADDRESS[ 3 ] = address( ________Users.______USER__3______.ADDRESS ) ;
​
​
ADDRESS[ 4 ] = address( ________Users.______USER__4______.ADDRESS ) ;
​
​
​
​
ADDRESS[ 5 ] = address( ________Users.PRIVILEGED.ADDRESS ) ;
​
​
​
​
ADDRESS[ 6 ] = address( ________Scope.______FjordAuction______ ) ;
​
​
ADDRESS[ 7 ] = address( ________Scope.______AuctionFactory______ ) ;
​
​
ADDRESS[ 8 ] = address( ________Scope.______FjordPoints______ ) ;
​
​
ADDRESS[ 9 ] = address( ________Scope.______FjordStaking______ ) ;
​
​
ADDRESS[ 10 ] = address( ________Scope.______FjordToken______ ) ;
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
NAME__OF__ADDRESS = new string[]( 11 ) ;
​
​
​
​
NAME__OF__ADDRESS[ 0 ] = " ATTACKER " ;
​
​
NAME__OF__ADDRESS[ 1 ] = " USER 1 " ;
​
​
NAME__OF__ADDRESS[ 2 ] = " USER 2 " ;
​
​
NAME__OF__ADDRESS[ 3 ] = " USER 3 " ;
​
​
NAME__OF__ADDRESS[ 4 ] = " USER 4 " ;
​
​
​
​
NAME__OF__ADDRESS[ 5 ] = " PRIVILEGED " ;
​
​
​
​
NAME__OF__ADDRESS[ 6 ] = " << FjordAuction >> " ;
​
​
NAME__OF__ADDRESS[ 7 ] = " << AuctionFactory >> " ;
​
​
NAME__OF__ADDRESS[ 8 ] = " << FjordPoints >> " ;
​
​
NAME__OF__ADDRESS[ 9 ] = " << FjordStaking >> " ;
​
​
NAME__OF__ADDRESS[ 10 ] = " << FjordToken >> " ;
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
}
​
​
​
​
​
The output from the terminal
​
​
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 615.90ms (615.29ms CPU time)
​
Ran 1 test suite in 617.29ms (615.90ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)
​
​
Updates

Lead Judging Commences

inallhonesty Lead Judge
about 2 years ago
inallhonesty Lead Judge about 2 years ago
Submission Judgement Published
Validated
Assigned finding tags:

If no bids are placed during the auction, the `auctionToken` will be permanently locked within the `AuctionFactory`

An auction with 0 bids will get the `totalTokens` stuck inside the contract. Impact: High - Tokens are forever lost Likelihood - Low - Super small chances of happening, but not impossible

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!