Skip to content

Commit 3318fcb

Browse files
authored
Merge pull request #361 from rsksmart/GBI-2875/2-make-transferred-amount-check-strict
Gbi 2875/2 make transferred amount check strict
2 parents 978e61e + 2475759 commit 3318fcb

File tree

3 files changed

+56
-34
lines changed

3 files changed

+56
-34
lines changed

contracts/libraries/Quotes.sol

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ library Quotes {
4747
bytes lpBtcAddress;
4848
}
4949

50+
uint256 public constant SAT_TO_WEI_CONVERSION = 10**10;
51+
5052
error AmountTooLow(uint256 value, uint256 target);
5153

5254
function checkAgreedAmount(
@@ -56,12 +58,15 @@ library Quotes {
5658
uint agreedAmount = 0;
5759
agreedAmount = quote.value + quote.callFee + quote.productFeeAmount + quote.gasFee;
5860

61+
// Adjust for rounding when converting from wei to sats and back
62+
// This protects users from precision issues when client apps don't round properly
63+
if (agreedAmount > SAT_TO_WEI_CONVERSION && (agreedAmount % SAT_TO_WEI_CONVERSION) != 0) {
64+
agreedAmount -= (agreedAmount % SAT_TO_WEI_CONVERSION);
65+
}
5966

60-
uint delta = agreedAmount / 10000;
61-
// transferred amount should not be lower than (agreed amount - delta),
62-
// where delta is intended to tackle rounding problems
63-
if (agreedAmount - delta > transferredAmount) {
64-
revert AmountTooLow(transferredAmount, agreedAmount - delta);
67+
// transferred amount should not be lower than agreed amount
68+
if (agreedAmount > transferredAmount) {
69+
revert AmountTooLow(transferredAmount, agreedAmount);
6570
}
6671
}
6772

test/pegin/register-pegin.test.ts

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
getRewardForQuote,
1818
getTestPeginQuote,
1919
totalValue,
20+
expectedRoundedAmount,
2021
} from "../utils/quotes";
2122
import { expect } from "chai";
2223
import { ethers } from "hardhat";
@@ -234,9 +235,10 @@ describe("PegInContract registerPegIn function should", () => {
234235
);
235236
});
236237

237-
it("refund user when call was not done and user under paid the quote", async () => {
238-
const { contract, fullLp, signers, bridgeMock, collateralManagement } =
239-
await loadFixture(deployPegInContractFixture);
238+
it("revert when user under paid the quote", async () => {
239+
const { contract, fullLp, signers, bridgeMock } = await loadFixture(
240+
deployPegInContractFixture
241+
);
240242
const lbcAddress = await contract.getAddress();
241243
const user = signers[0];
242244

@@ -259,6 +261,12 @@ describe("PegInContract registerPegIn function should", () => {
259261
nConfirmationSeconds: 600,
260262
});
261263

264+
const QuotesLib = new ethers.Contract(
265+
ethers.ZeroAddress,
266+
Quotes__factory.abi,
267+
ethers.provider
268+
);
269+
262270
const height = PEGIN_CONSTANTS.HEIGHT_MOCK;
263271
const peginAmount = totalValue(quote) - ethers.parseEther("0.0001");
264272
await bridgeMock.setPegin(quoteHash, { value: peginAmount });
@@ -268,32 +276,22 @@ describe("PegInContract registerPegIn function should", () => {
268276
nConfirmationHeader
269277
);
270278

271-
const tx = contract
272-
.connect(fullLp)
273-
.registerPegIn(
274-
quote,
275-
signature,
276-
PEGIN_CONSTANTS.RAW_TRANSACTION_MOCK,
277-
PEGIN_CONSTANTS.PMT_MOCK,
278-
height
279-
);
280-
await expect(tx)
281-
.to.emit(contract, "PegInRegistered")
282-
.withArgs(quoteHash, peginAmount);
283-
await expect(tx).not.to.emit(collateralManagement, "Penalized");
284-
await expect(tx)
285-
.to.emit(contract, "Refund")
286-
.withArgs(user.address, quoteHash, peginAmount, true);
287-
await expect(tx).not.to.emit(contract, "CallForUser");
288-
await expect(tx).not.to.emit(contract, "BridgeCapExceeded");
289-
await expect(tx).not.to.emit(contract, "DaoContribution");
279+
await expect(
280+
contract
281+
.connect(fullLp)
282+
.registerPegIn(
283+
quote,
284+
signature,
285+
PEGIN_CONSTANTS.RAW_TRANSACTION_MOCK,
286+
PEGIN_CONSTANTS.PMT_MOCK,
287+
height
288+
)
289+
)
290+
.to.be.revertedWithCustomError(QuotesLib, "AmountTooLow")
291+
.withArgs(peginAmount, expectedRoundedAmount(quote));
290292
await expect(contract.getCurrentContribution()).to.eventually.eq(0n);
291293
await expect(contract.getQuoteStatus(quoteHash)).to.eventually.eq(
292-
PegInStates.PROCESSED_QUOTE
293-
);
294-
await expect(tx).to.changeEtherBalances(
295-
[user, fullLp, contract],
296-
[peginAmount, 0n, 0n]
294+
PegInStates.UNPROCESSED_QUOTE
297295
);
298296
await expect(contract.getBalance(fullLp)).to.eventually.eq(0n);
299297
});
@@ -821,7 +819,6 @@ describe("PegInContract registerPegIn function should", () => {
821819
height + Number(quote.depositConfirmations) - 1,
822820
nConfirmationHeader
823821
);
824-
const minValue = totalValue(quote) - totalValue(quote) / 10_000n;
825822
await expect(
826823
contract
827824
.connect(fullLp)
@@ -834,7 +831,7 @@ describe("PegInContract registerPegIn function should", () => {
834831
)
835832
)
836833
.to.be.revertedWithCustomError(QuotesLib, "AmountTooLow")
837-
.withArgs(peginAmount, minValue);
834+
.withArgs(peginAmount, expectedRoundedAmount(quote));
838835
await expect(contract.getCurrentContribution()).to.eventually.eq(0n);
839836
await expect(contract.getQuoteStatus(quoteHash)).to.eventually.eq(
840837
PegInStates.UNPROCESSED_QUOTE

test/utils/quotes.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,26 @@ export function totalValue(
108108
);
109109
}
110110

111+
/**
112+
* Calculate the expected amount after rounding for SAT to WEI conversion
113+
* This matches the logic in Quotes.checkAgreedAmount
114+
*/
115+
export function expectedRoundedAmount(
116+
quote: QuotesV2.PeginQuoteStruct | QuotesV2.PegOutQuoteStruct
117+
): bigint {
118+
const SAT_TO_WEI_CONVERSION = 10n ** 10n;
119+
const agreedAmount = totalValue(quote);
120+
121+
if (
122+
agreedAmount > SAT_TO_WEI_CONVERSION &&
123+
agreedAmount % SAT_TO_WEI_CONVERSION !== 0n
124+
) {
125+
return agreedAmount - (agreedAmount % SAT_TO_WEI_CONVERSION);
126+
}
127+
128+
return agreedAmount;
129+
}
130+
111131
/**
112132
* Get mock bitcoin block headers for the given quote
113133
* @param args.quote The quote to get the block headers for

0 commit comments

Comments
 (0)