Every wallet address that uses Blockchain Drive has a storage quota recorded directly on theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/ankit-bista/Final-Project/llms.txt
Use this file to discover all available pages before exploring further.
BlockchainDriveUnified smart contract. The contract is the single authoritative source for how many bytes a wallet is allowed to use, how many it has consumed, and whether an upload should be permitted. The Express backend reads and writes this state through the STORAGE_ALLOC_CONTRACT ABI, coordinating with a separate drive-level quota stored in the database so that both layers can independently block an upload when a limit is reached.
The UserQuota struct
Each wallet’s quota state is stored in aUserQuota struct in the quotas mapping on the contract:
contracts/BlockchainDriveUnified.sol
| Field | Type | Description |
|---|---|---|
tier | string | Label for the quota plan. Set to "CUSTOM" by allocateUserQuota. |
quotaLimitBytes | uint256 | Maximum bytes the wallet may store. |
usedBytes | uint256 | Bytes currently consumed across all uploaded files. |
usagePercent | uint8 | (usedBytes * 100) / quotaLimitBytes, recalculated on every mutation. |
filesUploaded | uint256 | Running count of files uploaded by this wallet. |
maxFiles | uint256 | Maximum number of files allowed. Set to type(uint256).max on first allocation if not already set. |
isActive | bool | Must be true for updateQuotaAfterUpload to succeed. |
lastUpdated | uint256 | Block timestamp of the last quota mutation. |
A wallet with no allocated quota has
isActive = false. Uploading with ENFORCE_QUOTA_ON_UPLOAD=true will fail for these wallets because updateQuotaAfterUpload reverts when the quota is not active.Quota lifecycle
Quota moves through three transitions during normal use:Admin allocates quota
An admin calls
POST /api/admin/quota/allocate with a poolName, userAddress, and bytesAmount. The backend signs and submits an allocateUserQuota transaction to the contract using ADMIN_PRIVATE_KEY. The contract sets tier = "CUSTOM", quotaLimitBytes = bytesAmount, and isActive = true, then emits UserQuotaAllocated(poolName, userAddress, bytesAmount).Upload calls updateQuotaAfterUpload
After a file is successfully pinned to IPFS, the backend calls
updateQuotaAfterUpload(walletAddress, fileSizeBytes) on the contract. The contract reverts if isActive is false or if usedBytes + fileSizeBytes would exceed quotaLimitBytes. On success it increments usedBytes and filesUploaded, recalculates usagePercent, updates lastUpdated, and emits QuotaUpdated(userAddress, usedBytes, quotaLimitBytes).contracts/BlockchainDriveUnified.sol
Delete calls refundQuota
When a file is deleted, the backend calls
refundQuota(walletAddress, fileSizeBytes). Unlike updateQuotaAfterUpload, this function does not revert on underflow — if the refund would make usedBytes negative, it clamps to zero. filesUploaded is decremented by one (clamped at zero) and usagePercent is recalculated. The function emits QuotaRefunded(userAddress, refundedBytes).contracts/BlockchainDriveUnified.sol
Enforcement mode
TheENFORCE_QUOTA_ON_UPLOAD environment variable controls what happens when the on-chain quota check fails during an upload:
.env
ENFORCE_QUOTA_ON_UPLOAD=true
The backend submits
updateQuotaAfterUpload and treats a revert or error as a hard block. The upload is rejected and the file is not pinned to IPFS. Use this in production when quota enforcement must be guaranteed.ENFORCE_QUOTA_ON_UPLOAD=false
The backend attempts
updateQuotaAfterUpload but continues if the call fails or the quota is not active. A warning is logged. Use this in development or when the contract is not yet deployed.Querying quota via the API
GET /blockchain/quota returns the on-chain quota state for the authenticated user’s wallet address. Authentication is required — the endpoint reads the wallet address from the session.
Response in real contract mode (USE_REAL_CONTRACTS=true):
USE_REAL_CONTRACTS=false):
tier field is only present in real mode. The quotaLimitBytes, usedBytes, and remainingBytes fields are returned as strings because Ethereum uint256 values exceed JavaScript’s safe integer range — parse them with BigInt when doing arithmetic on the client side.
The
remainingBytes field is computed on-chain by getQuotaStats as quotaLimitBytes - usedBytes, clamped to zero. It is not derived client-side.The getQuotaStats function
TheGET /blockchain/quota endpoint calls getQuotaStats on the contract, which returns the full quota state in a single view call:
contracts/BlockchainDriveUnified.sol
view function — it reads state only and costs no gas. The backend accesses return values by named field when ethers v6 populates them, with positional index fallback:
routes/blockchainRoutes.js
Drive-level quota
In addition to the on-chain quota, Blockchain Drive enforces a per-drive byte limit stored in the MySQL database. Both limits are evaluated independently during an upload — if either is exceeded, the upload is blocked.On-chain quota
Stored in the
UserQuota struct on the contract. Scoped to a wallet address across all drives. Enforced by updateQuotaAfterUpload.Drive-level quota
Stored in the database alongside the drive record. Scoped to a single drive. Enforced by the Express upload handler before the IPFS pin and contract call.
Quota transfer between users
ThequotaService.js service exposes transferQuota(fromUserId, toUserId, amountBytes), which redistributes allocated quota between two users without involving the smart contract. It reads both users’ current quota snapshots from the database, verifies the sender has sufficient remaining quota, then calls assignRoleAndQuota to deduct from the sender and add to the receiver:
services/quotaService.js
transferQuota operates on the database quota snapshot, not the on-chain UserQuota struct. If you also want the transferred amount reflected on-chain, issue a separate allocateUserQuota transaction for each affected address.Related pages
Smart contract reference
Full function signatures, events, and deployment steps for
BlockchainDriveUnified.Architecture overview
How IPFS, Ethereum, and the Express backend interact end-to-end.