Skip to content

Commit 8a2e7a6

Browse files
committed
add custom error and fix tests
1 parent 6375887 commit 8a2e7a6

File tree

2 files changed

+33
-20
lines changed

2 files changed

+33
-20
lines changed

contracts/token/ERC7984/extensions/ERC7984ERC20Wrapper.sol

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ abstract contract ERC7984ERC20Wrapper is ERC7984, IERC1363Receiver {
3030
event UnwrapFinalized(address indexed receiver, euint64 encryptedAmount, uint64 cleartextAmount);
3131

3232
error InvalidUnwrapRequest(euint64 amount);
33+
error ERC7984TotalSupplyOverflow();
3334

3435
constructor(IERC20 underlying_) {
3536
_underlying = underlying_;
@@ -150,17 +151,24 @@ abstract contract ERC7984ERC20Wrapper is ERC7984, IERC1363Receiver {
150151
}
151152

152153
/**
153-
* @dev Returns the underlying balance divided by the {rate}, closely approximating the total supply of wrapped tokens.
154+
* @dev Returns the underlying balance divided by the {rate}, over-approximating the total supply of wrapped tokens.
154155
*
155156
* NOTE: The return value of this function can be inflated by directly sending underlying tokens to the wrapper contract.
156157
*/
157-
function totalSupply() public view virtual returns (uint64) {
158-
return SafeCast.toUint64(underlying().balanceOf(address(this)) / rate());
158+
function totalSupply() public view virtual returns (uint256) {
159+
return underlying().balanceOf(address(this)) / rate();
160+
}
161+
162+
/// @dev Returns the maximum total supply of wrapped tokens supported by the encrypted datatype.
163+
function maxTotalSupply() public view virtual returns (uint256) {
164+
return type(uint64).max;
159165
}
160166

161167
/// @dev This function must revert if the new {ERC7984-confidentialTotalSupply} state is invalid.
162168
function _checkTotalSupply() internal virtual {
163-
SafeCast.toUint64(underlying().balanceOf(address(this)) / rate());
169+
if (totalSupply() > maxTotalSupply()) {
170+
revert ERC7984TotalSupplyOverflow();
171+
}
164172
}
165173

166174
function _update(address from, address to, euint64 amount) internal virtual override returns (euint64) {

test/token/ERC7984/extensions/ERC7984Wrapper.test.ts

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,16 @@ describe('ERC7984Wrapper', function () {
8383
});
8484

8585
it('max amount works', async function () {
86-
const maxAmount = (2n ** 64n - 1n) / 10n ** 6n;
87-
await this.token.$_mint(this.holder.address, ethers.parseUnits(maxAmount.toString(), 18));
88-
const amountToWrap = ethers.parseUnits(maxAmount.toString(), 18);
86+
await this.token.$_mint(this.holder.address, ethers.MaxUint256 / 2n); // mint a lot of tokens
87+
88+
const rate = await this.wrapper.rate();
89+
const maxConfidentialSupply = await this.wrapper.maxTotalSupply();
90+
const maxUnderlyingSupply = maxConfidentialSupply * rate;
8991

9092
if (viaCallback) {
91-
await this.token.connect(this.holder).transferAndCall(this.wrapper, amountToWrap);
93+
await this.token.connect(this.holder).transferAndCall(this.wrapper, maxUnderlyingSupply);
9294
} else {
93-
await this.wrapper.connect(this.holder).wrap(this.holder.address, amountToWrap);
95+
await this.wrapper.connect(this.holder).wrap(this.holder.address, maxUnderlyingSupply);
9496
}
9597

9698
await expect(
@@ -100,22 +102,25 @@ describe('ERC7984Wrapper', function () {
100102
this.wrapper.target,
101103
this.holder,
102104
),
103-
).to.eventually.equal(ethers.parseUnits(maxAmount.toString(), 6));
105+
).to.eventually.equal(maxConfidentialSupply);
104106
});
105107

106108
it('amount exceeding max fails', async function () {
107-
const maxAmount = (2n ** 64n - 1n) / 10n ** 6n;
108-
await this.token.$_mint(this.holder.address, ethers.parseUnits(maxAmount.toString(), 18));
109-
const amountToWrap = ethers.parseUnits((maxAmount + 1n).toString(), 18);
109+
await this.token.$_mint(this.holder.address, ethers.MaxUint256 / 2n); // mint a lot of tokens
110110

111-
let tx;
112-
if (viaCallback) {
113-
tx = this.token.connect(this.holder).transferAndCall(this.wrapper, amountToWrap);
114-
} else {
115-
tx = this.wrapper.connect(this.holder).wrap(this.holder.address, amountToWrap);
116-
}
111+
const rate = await this.wrapper.rate();
112+
const maxConfidentialSupply = await this.wrapper.maxTotalSupply();
113+
const maxUnderlyingSupply = maxConfidentialSupply * rate;
117114

118-
await expect(tx).to.be.revertedWithCustomError(this.wrapper, 'SafeCastOverflowedUintDowncast');
115+
// first deposit close to the max
116+
await this.wrapper.connect(this.holder).wrap(this.holder.address, maxUnderlyingSupply);
117+
118+
// try to deposit more, causing the total supply to exceed the max supported amount
119+
await expect(
120+
viaCallback
121+
? this.token.connect(this.holder).transferAndCall(this.wrapper, rate)
122+
: this.wrapper.connect(this.holder).wrap(this.holder.address, rate),
123+
).to.be.revertedWithCustomError(this.wrapper, 'ERC7984TotalSupplyOverflow');
119124
});
120125

121126
if (viaCallback) {

0 commit comments

Comments
 (0)