Summary
Lack of checks in TempleGold::constructor allows to distribute all TLGD to only one address because it allows to use the same address in initArgs.staking , initArgs.escrow and initArgs.gnosis from initArgs constructor parameter.
Vulnerability Details
This lack of check allows setting the same address to this 3 initArgs fields: _initArgs.staking , initArgs.escrow and _initArgs.gnosis, leading to 100% of minted TLGD be distributed to only one address.
The flaw is present in TempleGold::constructor because it blindly trust initArgs's parameter values:
constructor(
InitArgs memory _initArgs
) OFT(_initArgs.name, _initArgs.symbol, _initArgs.layerZeroEndpoint, _initArgs.executor) Ownable(_initArgs.executor){
staking = ITempleGoldStaking(_initArgs.staking);
escrow = IDaiGoldAuction(_initArgs.escrow);
teamGnosis = _initArgs.gnosis;
_mintChainId = _initArgs.mintChainId;
So if the same address is used all minted TLGD will be distributed only to the defined address
The following PoC shows how using only one address will distribute the 100% minted TGLD, and how the lacks of checks in TempleGold contract allows it:
function test_all_tgld_to_one_address_initArgs() public {
arbitrumOneForkId = fork("arbitrum_one");
address sameAddress = makeAddr("sameaddress");
ITempleGold.InitArgs memory initArgs;
initArgs.executor = executor;
initArgs.staking = sameAddress;
initArgs.escrow = sameAddress;
initArgs.gnosis = sameAddress;
initArgs.layerZeroEndpoint = layerZeroEndpointArbitrumOne;
initArgs.mintChainId = arbitrumOneChainId;
initArgs.name = TEMPLE_GOLD_NAME;
initArgs.symbol = TEMPLE_GOLD_SYMBOL;
templeGold = new TempleGold(initArgs);
templeToken = new FakeERC20("Temple Token", "TEMPLE", executor, 1000 ether);
staking = new TempleGoldStaking(rescuer, executor, address(templeToken), address(templeGold));
daiGoldAuction = new DaiGoldAuction(
address(templeGold),
daiToken,
treasury,
rescuer,
executor
);
vm.startPrank(executor);
templeGold.setEscrow(address(daiGoldAuction));
_configureTempleGold();
console.log("[i] test_nontransferrable_tgld");
vm.startPrank(executor);
vm.warp(block.timestamp + 20 days);
templeGold.mint();
address teamGnosis = templeGold.teamGnosis();
vm.startPrank(teamGnosis);
uint256 balance = templeGold.balanceOf(teamGnosis);
console.log("TeamGnosis Balance => ",balance);
}
To run import console.sol library in TempleGold.t.sol
import "forge-std/console.sol";
And copy the following as a new test case in test/forge/templegold/TempleGold.t.sol
run with verbose mode
forge test -vv --mt test_all_tgld_to_one_address_initArgs
Observe all the minted tokens are distributed to only one address
Impact
High cause if an EOA is used, will allow to get all minted TGLD.
However attack complexity is High.
Tools Used
Manual Review
Recommendations
Implement a check to at least ensure _initArgs.staking and initArgs.escrow are different:
constructor(
InitArgs memory _initArgs
) OFT(_initArgs.name, _initArgs.symbol, _initArgs.layerZeroEndpoint, _initArgs.executor) Ownable(_initArgs.executor){
if (_initArgs.staking == _initArgs.escrow) { revert CommonEventsAndErrors.InvalidAddress(); }
staking = ITempleGoldStaking(_initArgs.staking);
escrow = IDaiGoldAuction(_initArgs.escrow);
teamGnosis = _initArgs.gnosis;
_mintChainId = _initArgs.mintChainId;
}