xanoutas/solidity-security-findings huggingface dataset:
Shared Voting Power via `delegate` and Checkpoint due to incorrect `removeDelegation`
Audit: 2022-07-golom-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L208-L216


# Vulnerability details

NOTE: This finding requires fixing another bug in the codebase:
- Fix underflow on first checkpoint, see: "`delegate` and `removeDelegation` are broken due to `_writeCheckpoint` underflow"

`removeDelegation` will receive the tokenId of the token to remove the delegation, and it will then remove itself from it's own list of tokens delegated.

https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L208-L216

```solidity
    /// @notice Remove delegation
    /// @param tokenId TokenId of which delegation needs to be removed
    function removeDelegation(uint256 tokenId) external {
        require(ownerOf(tokenId) == msg.sender, 'VEDelegation: Not allowed');
        uint256 nCheckpoints = numCheckpoints[tokenId];
        Checkpoint storage checkpoint = checkpoints[tokenId][nCheckpoints - 1];
        removeElement(checkpoint.delegatedTokenIds, tokenId);
        _writeCheckpoint(tokenId, nCheckpoints, checkpoint.delegatedTokenIds);
    }
```

This is fine to remove voting from one-self (if that makes sense)

However this process will not remove the token from any other checkpoint, meaning that any user can create a token of id `tokenId` and then delegate it to any number of additional tokens `tokenA`, `tokenB`, etc.. and all of those will receive delegation, as there seems to be no way to remove the delegation given.

This results in the ability to re-use a token votes by delegating to a myriad of other tokens

## POC

```
## Create a new Lock
>> x.create_lock(1e18, chain.time() / 1000, {"from": a[0]})
Transaction sent: 0x8c6b7a8eb6ed92ab4dbfa27c391981da8b8e8c00c583f0a87bc50e55fa625727
  Gas price: 0.0 gwei   Gas limit: 12000000   Nonce: 15
  VoteEscrow.create_lock confirmed   Block: 15246523   Gas used: 319653 (2.66%)

<Transaction '0x8c6b7a8eb6ed92ab4dbfa27c391981da8b8e8c00c583f0a87bc50e55fa625727'>

## Id is 3
>>> history[-1].return_value
3

## Create a new lock (id will be 4), which we'll delegate to demonstrate the bug
>>> x.create_lock(1,  chain.time() / 1000, {"from": a[1]})
Transaction sent: 0x9bb3b3f1ae6ae69d2631378cff1ebc2f9022644138a665fc0416d8d101c80e0a
  Gas price: 0.0 gwei   Gas limit: 12000000   Nonce: 6
  VoteEscrow.create_lock confirmed   Block: 15246524   Gas used: 296193 (2.47%)

<Transaction '0x9bb3b3f1ae6ae69d2631378cff1ebc2f9022644138a665fc0416d8d101c80e0a'>

## Delegate to 4
>>> x.delegate(3, 4, {"from": a[0]})
Transaction sent: 0x63be9d38cc19443024c6b138bdb85489f7214d957cd46886f98ac9f51c82861c
  Gas price: 0.0 gwei   Gas limit: 12000000   Nonce: 16
  VoteEscrow.delegate confirmed   Block: 15246525   Gas used: 134655 (1.12%)

<Transaction '0x63be9d38cc19443024c6b138bdb85489f7214d957cd46886f98ac9f51c82861c'>

## Prove we have delegated as expected
>>> x._getCurr
Infinite Voting Power via `delegate` system
Audit: 2022-07-golom-findings
Severity: HIGH

# Lines of code

https://github.com/GalloDaSballo/brownie-starter/blob/28f708ebd8cfb4fa1a412df9fa376d33813f281f/contracts/VoteEscrowDelegation.sol#L67-L76


# Vulnerability details

NOTE: This finding requires fixing another bug in the codebase:
- Fix underflow on first checkpoint, see: "`delegate` and `removeDelegation` are broken due to `_writeCheckpoint` underflow"

Once the code is fixed as below, (handing of first checkpoint), another vulnerability will emerge

https://github.com/GalloDaSballo/brownie-starter/blob/28f708ebd8cfb4fa1a412df9fa376d33813f281f/contracts/VoteEscrowDelegation.sol#L67-L76

```solidity
    function delegate(uint256 tokenId, uint256 toTokenId) external {
        require(ownerOf(tokenId) == msg.sender, 'VEDelegation: Not allowed');
        require(this.balanceOfNFT(tokenId) >= MIN_VOTING_POWER_REQUIRED, 'VEDelegation: Need more voting power');

        delegates[tokenId] = toTokenId;
        uint256 nCheckpoints = numCheckpoints[toTokenId];

        if (nCheckpoints > 0) {
            Checkpoint storage checkpoint = checkpoints[toTokenId][nCheckpoints - 1];
            checkpoint.delegatedTokenIds.push(tokenId);
```


Because `delegate` does: `checkpoint.delegatedTokenIds.push(tokenId);` then any new delegated token will be added to the list, allowing users to self-delegate, delegate to other people and ultimately to inflate their voting power infinitely.

**Note about POC:**

The POC requires fixing a bug in `_writeCheckpoint`, see my report: "`delegate` and `removeDelegation` are broken due to `_writeCheckpoint` underflow"

To bring further clarity to the POC I've changed `_getCurrentDelegated` to be a public function so we can see the internal data structure for delegation

I've also changed `_deposit_for` to have the transfer commented to avoid needless setup

## POC
```python
Launching 'ganache-cli --accounts 10 --hardfork istanbul --fork https://mainnet.infura.io/v3/uFEqPEkn09U --gasLimit 12000000 --mnemonic brownie --port 8545 --chainId 1'...
Brownie environment is ready.

## Deploy Lib
>>> t = TokenUriHelper.deploy({"from": a[0]})
Transaction sent: 0x8743a7e9b2fff2e69d14f76fdce12588fb8c5c648f1ee5e6f38227dd4f873320
  Gas price: 0.0 gwei   Gas limit: 12000000   Nonce: 4
  TokenUriHelper.constructor confirmed   Block: 15246511   Gas used: 2092588 (17.44%)
  TokenUriHelper deployed at: 0xe0aA552A10d7EC8760Fc6c246D391E698a82dDf9

## And Deploy Contract
>>> x = VoteEscrow.deploy(ETH_ADDRESS, {"from": a[0]})
Transaction sent: 0x094a75c8868c5b94cfa511898953b0802251518b4bd9fbe73855d362c82ce476
  Gas price: 0.0 gwei   Gas limit: 12000000   Nonce: 5
  VoteEscrow.constructor confirmed   Block: 15246512   Gas used: 3865003 (32.21%)
  VoteEscrow deployed at: 0x6b4BDe1086912A6Cb24ce3dB43b3466e6c72AFd3

## Create Lock Id 1 || Edited to require no tokens || Just comment the transfer
>>> x.create_lock(123, chain.time() / 1000, {"from": a[0]})
Transaction sent: 0xb58b326e54e9d08079abf3c527882a975d2abd0e755efb1c63f9b191196d40ee
  G
`delegate` and `removeDelegation` are broken due to `_writeCheckpoint` underflow
Audit: 2022-07-golom-findings
Severity: HIGH

# Lines of code

https://github.com/GalloDaSballo/brownie-starter/blob/28f708ebd8cfb4fa1a412df9fa376d33813f281f/contracts/VoteEscrowDelegation.sol#L87-L105


# Vulnerability details

The function `_writeCheckpoint` performs: `checkpoints[toTokenId][nCheckpoints - 1];` which, on first checkpoint will always revert as the value `nCheckpoints` will be zero

https://github.com/GalloDaSballo/brownie-starter/blob/28f708ebd8cfb4fa1a412df9fa376d33813f281f/contracts/VoteEscrowDelegation.sol#L87-L105

```solidity
    /**
     * @notice Writes the checkpoint to store current NFTs in the specific block
     */
    function _writeCheckpoint(
        uint256 toTokenId,
        uint256 nCheckpoints,
        uint256[] memory _delegatedTokenIds
    ) internal {
        require(_delegatedTokenIds.length < 500, 'VVDelegation: Cannot stake more');

        Checkpoint memory oldCheckpoint = checkpoints[toTokenId][nCheckpoints - 1];

        if (nCheckpoints > 0 && oldCheckpoint.fromBlock == block.number) {
            oldCheckpoint.delegatedTokenIds = _delegatedTokenIds;
        } else {
            checkpoints[toTokenId][nCheckpoints] = Checkpoint(block.number, _delegatedTokenIds);
            numCheckpoints[toTokenId] = nCheckpoints + 1;
        }
    }
```

This is because `nCheckpoints` on first checkpoint will always be zero and on solidity >0.8 that will cause a revert

## Remediation Steps

Add a extra case to handle 0 `nCheckpoints`.

I've rewritten and ran the following code which seems to work fine

Rewrite to
```solidity
    /**
     * @notice Writes the checkpoint to store current NFTs in the specific block
     */
    function _writeCheckpoint(
        uint256 toTokenId,
        uint256 nCheckpoints,
        uint256[] memory _delegatedTokenIds
    ) internal {
        require(_delegatedTokenIds.length < 500, 'VVDelegation: Cannot stake more');

        if (nCheckpoints > 0) {

            Checkpoint memory oldCheckpoint = checkpoints[toTokenId][nCheckpoints - 1];
            
            if (oldCheckpoint.fromBlock == block.number){
                oldCheckpoint.delegatedTokenIds = _delegatedTokenIds;
                return; // End Early
            }
        }

        checkpoints[toTokenId][nCheckpoints] = Checkpoint(block.number, _delegatedTokenIds);
        numCheckpoints[toTokenId] = nCheckpoints + 1;
    }
```
Incorrect `protocolFee` calculation can cause users to receive much less ethereum then they were paid for
Audit: 2022-07-golom-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/core/GolomTrader.sol#L381
https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/core/GolomTrader.sol#L389-L394
https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/core/GolomTrader.sol#L396-L399


# Vulnerability details

## Impact
When calculating `protocolFee` in `_settleBalances()` in GolomTrader, the protocolFee is multiplied by `amount` twice which can drastically increase the fees that a user receives.

This issue counts as loss of funds and breaks a major selling point of the protocol (low fees) which I believe is high severity.

## Proof of Concept
1. A sender wants to sell 10 ERC1155 tokens to an off chain signer for 50 ETH each
2. The sender executes `fillBid` and `_settleBalances` is called
3. `protocolFee` is calculated as `50 * 50 / 10000 * 10 = 2.5 ETH`
4. Assuming all `paymentAmt` are zero, the sender receives `(50 - 2.5)*10 = 475 ETH`
5. The sender loses `5%` instead of `0.5%` with `22.5` ETH being left in the contract

## Tools Used
VS Code
## Recommended Mitigation Steps
Change the payments in `_settleBalances` from
```solidity
            payEther(
                (o.totalAmt - protocolfee - o.exchange.paymentAmt - o.prePayment.paymentAmt) * amount - p.paymentAmt,
                msg.sender
            );
```
to
```solidity
            payEther(
                (o.totalAmt - o.exchange.paymentAmt - o.prePayment.paymentAmt) * amount - p.paymentAmt - protocolfee,
                msg.sender
            );
```
removeDelegation always has an undeflow issues on first checkpoint
Audit: 2022-07-golom-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L210-L211


# Vulnerability details

When calling `removeDelegation` directly, the function will always revert with an underflow.

```solidity
/// @notice Remove delegation
/// @param tokenId TokenId of which delegation needs to be removed
function removeDelegation(uint256 tokenId) public {
    require(ownerOf(tokenId) == msg.sender, 'VEDelegation: Not allowed');
    uint256 nCheckpoints = numCheckpoints[tokenId];
    Checkpoint storage checkpoint = checkpoints[tokenId][nCheckpoints - 1];
    removeElement(checkpoint.delegatedTokenIds, tokenId);
    _writeCheckpoint(tokenId, nCheckpoints, checkpoint.delegatedTokenIds);
}
```

The underflow will happen here `checkpoints[tokenId][nCheckpoints - 1];`

As on first checkpoint, the `nCheckpoints` variable will be zero, meaning 0 - 1 will cause an underflow and a revert due to the variable being a uint256

## Remediation Step

A solution to this would be to rewrite:
`Checkpoint storage checkpoint = checkpoints[tokenId][nCheckpoints > 0 ? nCheckpoints - 1 : 0]`

```solidity
/// @notice Remove delegation
/// @param tokenId TokenId of which delegation needs to be removed
function removeDelegation(uint256 tokenId) public {
    require(ownerOf(tokenId) == msg.sender, 'VEDelegation: Not allowed');
    uint256 nCheckpoints = numCheckpoints[tokenId];
    if (nCheckpoints > 0) {
        Checkpoint storage checkpoint = checkpoints[tokenId][nCheckpoints - 1];
        removeElement(checkpoint.delegatedTokenIds, tokenId);
        _writeCheckpoint(tokenId, nCheckpoints, checkpoint.delegatedTokenIds);
    } else {
        Checkpoint storage checkpoint = checkpoints[tokenId][0];
        _writeCheckpoint(tokenId, nCheckpoints, checkpoint.delegatedTokenIds);
    }
}
```
TransferFrom doesn't work because of the external call
Audit: 2022-07-golom-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L242-L243


# Vulnerability details

Because `VoteEscrowDelegation.transferFrom` is performing an external call to `removeDelegation`, the `msg.sender` will always be `address(this)`, causing the function to always revert.

https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L242-L243

```solidity
        this.removeDelegation(_tokenId);
```

A simple fix would be to make `removeDelegation` public and then changing the code to JUMP to it by removing `this`

## POC

Any call will always revert

```bash
<Transaction '0xa7b81478677907a369cea2b73fd410ebf7842d46631689cc66e2ba6a1857736b'>
>>> history[-1].call_trace(True)
Call trace for '0xa7b81478677907a369cea2b73fd410ebf7842d46631689cc66e2ba6a1857736b':
Initial call cost  [21940 gas]
VoteEscrow.transferFrom  0:386  [527 / 5285 gas]
└── VoteEscrowCore.transferFrom  146:386  [26 / 4758 gas]
    └── VoteEscrow._transferFrom  154:386  [2659 / 4732 gas]
        │   
        └── VoteEscrow.removeDelegation  [CALL]  229:374  [2073 gas]
                ├── address: 0x6b4BDe1086912A6Cb24ce3dB43b3466e6c72AFd3
                ├── value: 0
                ├── input arguments:
                │   └── tokenId: 1
                └── revert reason: VEDelegation: Not allowed
```
Malicious user can manipulate delegated votes and vote for a malicious proposal
Audit: 2022-07-golom-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/vote-escrow/VoteEscrowDelegation.sol#L72-L75


# Vulnerability details

## Impact
In `delegate()` of VoteEscrowDelegate, no check is made that the token has been delegated to another token or delegated multiple times. This allows a user with a token to delegate to itself an arbitrary number of times so that the user in effect controls over 50% of votes. The user can then create a new malicious proposal on GovernerBravo to upgrade to a malicious contract and empty all funds from the protocol etc...

## Proof of Concept
1. A user owns a token with tokenId `1` with voting power of 0.2% of the entire voting power
2. They then delegate that token to themselves 500 times so that they have voting power equal to the total voting power
3. The user can then propose and execute any malicious proposal on GovernerBravo
## Tools Used
VS Code
## Recommended Mitigation Steps
Add a check which makes sure that a token has not already been delegated e.g.
In `delegate()`
```solidity
require(delegates[tokenId] == 0, "Already delegated");
```
Make sure that when you remove delegations that you reset this value
`VoteEscrowDelegation.delegate` does not clear current delegation
Audit: 2022-07-golom-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowDelegation.sol#L71


# Vulnerability details

## Impact

While delegating voting power to a new delegatee, if any current delegatee exists, it should be cleared to ensure no NFT can be used to vote twice. The current implementation does not do this and allows attackers to delegate multiple times and multiply their voting power.

## Proof of Concept

Current implementation of `VoteEscrowDelegation.delegate` does not check if any delegatee exists and clear it before assigning a new delegatee.

```
    function delegate(uint256 tokenId, uint256 toTokenId) external {
        require(ownerOf(tokenId) == msg.sender, 'VEDelegation: Not allowed');
        require(this.balanceOfNFT(tokenId) >= MIN_VOTING_POWER_REQUIRED, 'VEDelegation: Need more voting power');

        delegates[tokenId] = toTokenId;
        uint256 nCheckpoints = numCheckpoints[toTokenId];

        if (nCheckpoints > 0) {
            Checkpoint storage checkpoint = checkpoints[toTokenId][nCheckpoints - 1];
            checkpoint.delegatedTokenIds.push(tokenId);
            _writeCheckpoint(toTokenId, nCheckpoints, checkpoint.delegatedTokenIds);
        } else {
            uint256[] memory array = new uint256[](1);
            array[0] = tokenId;
            _writeCheckpoint(toTokenId, nCheckpoints, array);
        }

        emit DelegateChanged(tokenId, toTokenId, msg.sender);
    }
```

## Tools Used

Manual audit

## Recommended Mitigation Steps

Check and clear any existing delegations

```
    function delegate(uint256 tokenId, uint256 toTokenId) external {
        require(ownerOf(tokenId) == msg.sender, 'VEDelegation: Not allowed');
        require(this.balanceOfNFT(tokenId) >= MIN_VOTING_POWER_REQUIRED, 'VEDelegation: Need more voting power');

        if (delegates[tokenId] != 0) {
            removeDelegation(tokenId);
        }
        delegates[tokenId] = toTokenId;
        uint256 nCheckpoints = numCheckpoints[toTokenId];
        …
    }
```
Upgraded G -> M from 553 [1666369528441]
Audit: 2022-07-golom-findings
Severity: MEDIUM

Judge has assessed an item in Issue #553 as Medium risk. The relevant finding follows:

01 - payEther use transfer instead of call
Replace line 154 for

payable(payAddress).call{value: payAmt}("")
[Reason](https://stackoverflow.com/questions/64230863/avoid-using-soliditys-transfer-send)
Upgraded Q -> M from 703 [1666368006323]
Audit: 2022-07-golom-findings
Severity: MEDIUM

Judge has assessed an item in Issue #703 as Medium risk. The relevant finding follows:

L01: Usage of transfer to send eth
It is recommended to use call instead of transfer due to fixed gas stipend. [In the GolomTrader](https://github.com/code-423n4/2022-07-golom/blob/7bbb55fca61e6bae29e57133c1e45806cbb17aa4/contracts/core/GolomTrader.sol#L154), transfer is used to pay ether.
Upgraded Q -> M from 42 [1666367610163]
Audit: 2022-07-golom-findings
Severity: MEDIUM

Judge has assessed an item in Issue #42 as Medium risk. The relevant finding follows:

Permit signature replay across forks
Details: GolomTrader.sol defines chainId at contract deployment without reconstructing it for every signature. However, as stated in the security considerations section of [EIP2612](https://eips.ethereum.org/EIPS/eip-2612), "If the DOMAIN_SEPARATOR contains the chainId and is defined at contract deployment instead of reconstructed for every signature, there is a risk of possible replay attacks between chains in the event of a future chain split.” ****

Impact: Low (depends on the hard forking of a well-established blockchain)

Mitigation: Consider caching the chainId as is done in [OpenZeppelin contracts](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/draft-EIP712.sol).
Upgraded Q -> M from 330 [1666367006171]
Audit: 2022-07-golom-findings
Severity: MEDIUM

Judge has assessed an item in Issue #330 as Medium risk. The relevant finding follows:

GolomTrader is not using ERC721 safeTransferFrom when transferring tokens
All the functions that fill orders are not using safeTransferFrom when the owner of the NFT transfer the token to the user or contract that has made the purchase.

If the taker is a contract and has not implemented the needed functions, that token will be stuck on the contract itself forever.

Consider using safeTransferFrom as well for the ERC721.
Upgraded Q -> M from 400 [1666366553858]
Audit: 2022-07-golom-findings
Severity: MEDIUM

Judge has assessed an item in Issue #400 as Medium risk. The relevant finding follows:

Use safeTransferFrom instead of transferFrom
transferFrom can be replaced with safeTransferFrom as it makes sure that the receiver can handle ERC721 tokens and prevents it from being stuck in the contracts

Refer: https://docs.openzeppelin.com/contracts/4.x/api/token/erc721#IERC721-safeTransferFrom-address-address-uint256-

Proof of concept
https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/core/GolomTrader.sol#L236

    ERC721(o.collection).transferFrom(o.signer, receiver, o.tokenId);
Upgraded Q -> M from 400 [1666366548697]
Audit: 2022-07-golom-findings
Severity: MEDIUM

Judge has assessed an item in Issue #400 as Medium risk. The relevant finding follows:

Use address.call() instead of payable.transfer()
Avoid use of transfer() to send ether since transfer sends a fixed amount of gas and future gas cost change may cause revert,
The receiving contract will also revert if it executes statements that costs more than 2300 gas
or if the receiving contract is a proxy contract

Refer: https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/

Proof of concept
https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/core/GolomTrader.sol#L154

    payable(payAddress).transfer(payAmt);
Mitigation
Replace transfer with call to transfer ether
Upgraded Q -> M from 889 [1666366016998]
Audit: 2022-07-golom-findings
Severity: MEDIUM

Judge has assessed an item in Issue #889 as Medium risk. The relevant finding follows:

[L-05] safeTransferFrom of VoteEscrowCore does not fully comply ERC721 standard
Per https://eips.ethereum.org/EIPS/eip-721, safeTransferFrom needs to throw if the receiver is a contract that does not have the onERC721Received function that returns bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")). Based on the following code, the safeTransferFrom function of VoteEscrowCore does check if the receiving contract has onERC721Received but does not check if that function returns bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")). To comply the standard, this check needs to be added.

https://github.com/code-423n4/2022-07-golom/blob/main/contracts/vote-escrow/VoteEscrowCore.sol#L594-L616
```
function safeTransferFrom(
	address _from,
	address _to,
	uint256 _tokenId,
	bytes memory _data
) public {
	_transferFrom(_from, _to, _tokenId, msg.sender);

	if (_isContract(_to)) {
		// Throws if transfer destination is a contract which does not implement 'onERC721Received'
		try IERC721Receiver(_to).onERC721Received(msg.sender, _from, _tokenId, _data) returns (bytes4) {} catch (
			bytes memory reason
		) {
			if (reason.length == 0) {
				revert('ERC721: transfer to non ERC721Receiver implementer');
			} else {
				assembly {
					revert(add(32, reason), mload(reason))
				}
			}
		}
	}
}

```
Upgraded Q -> M from 94 [1666365598508]
Audit: 2022-07-golom-findings
Severity: MEDIUM

Judge has assessed an item in Issue #94 as Medium risk. The relevant finding follows:

[L-04] payEther() should use .call instead of .transfer
https://github.com/code-423n4/2022-07-golom/blob/e5efa8f9d6dda92a90b8b2c4902320acf0c26816/contracts/core/GolomTrader.sol#L154

Use of .transfer [is no longer encouraged](https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/), as it may fail if the receiver has any logic in their receive() function, due to the 2300 gas consumption limit.