Skip to content

Commit

Permalink
remove nonce in the permit2 integrated contract
Browse files Browse the repository at this point in the history
  • Loading branch information
cwsnt committed Jan 28, 2024
1 parent f87209e commit ac98395
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 52 deletions.
27 changes: 0 additions & 27 deletions contracts/integration/MoC/MocIntegration.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ contract MocIntegration is OwnableUpgradeable, ERC1967UpgradeUpgradeable {

IPermit2 public immutable permit2;

mapping(address => Counters.Counter) private _permit2Nonces;

event GetDocFromDllrAndRedeemRBTC(address indexed from, uint256 fromDLLR, uint256 toRBTC);
event MocVendorAccountSet(address newMocVendorAccount);

Expand Down Expand Up @@ -136,8 +134,6 @@ contract MocIntegration is OwnableUpgradeable, ERC1967UpgradeUpgradeable {

permit2.permitTransferFrom(permit, transferDetails, msg.sender, signature);

_useNonce(msg.sender);

// redeem DoC from DLLR
require(
massetManager.redeemTo(address(doc), _dllrAmount, thisAddress) == _dllrAmount,
Expand Down Expand Up @@ -190,27 +186,4 @@ contract MocIntegration is OwnableUpgradeable, ERC1967UpgradeUpgradeable {

return transferDetails;
}

/**
* @dev "Consume a nonce": return the current value and increment.
*
* @param _address address of owner
*
* @return current nonce of the owner's address
*/
function _useNonce(address _address) internal virtual returns (uint256 current) {
Counters.Counter storage nonce = _permit2Nonces[_address];
current = nonce.current();
nonce.increment();
}

/**
* @dev getter for currernt nonce
*
* @param _address address of owner
* @return current nonce of the owner's address
*/
function getPermit2Nonce(address _address) public view returns (uint256) {
return _permit2Nonces[_address].current();
}
}
80 changes: 55 additions & 25 deletions tests/integrations/MoC/MocIntegration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ function toDeadline(expiration: number): number {
return Math.floor((Date.now() + expiration) / 1000)
}

function generateNonce() {
return new BN(Math.floor(Date.now() + Math.random() * 100));
}

function extractSignature(signature: string) {
const r = signature.slice(0, 66);
const s = '0x' + signature.slice(66, 130);
Expand All @@ -32,6 +36,27 @@ function extractSignature(signature: string) {
return {v, r, s};
}

function bitmapPositions(nonce) {
// Simulate logic to calculate wordPos and bitPos based on nonce
const wordPos = Math.floor(nonce / 256);
const bitPos = nonce % 256;
return { wordPos, bitPos };
}

async function isUsedNonce(permit2, from, nonce) {
const { wordPos, bitPos } = bitmapPositions(nonce);
const bit = BigInt(1) << BigInt(bitPos);

const nonceBitmapOnchain = BigInt((await permit2.nonceBitmap(from, wordPos)));
const flipped = nonceBitmapOnchain ^ bit;

if ((flipped & bit) === BigInt(0)) {
return true;
}

return false;
}

describe("MoC Integration", async () => {
// let mocFake: FakeContract<IMocMintRedeemDoc>;
let moc: MocMock;
Expand Down Expand Up @@ -192,7 +217,7 @@ describe("MoC Integration", async () => {

await dllr.connect(alice).approve(permit2.address, ethers.constants.MaxUint256);

const nonce = await mocIntegration.getPermit2Nonce(alice.address);
const nonce = generateNonce();
const deadline = toDeadline(1000 * 60 * 60 * 30 /** 30 minutes */);

const permitTransferFrom: PermitTransferFrom = {
Expand All @@ -201,7 +226,7 @@ describe("MoC Integration", async () => {
amount: dllrAmount,
},
spender: mocIntegration.address.toLowerCase(),
nonce: nonce,
nonce: nonce.toNumber(),
deadline: deadline
}
const network = await ethers.provider.getNetwork();
Expand All @@ -225,6 +250,7 @@ describe("MoC Integration", async () => {

// await mocIntegration.connect(alice).getDocFromDllrAndRedeemRbtcWithPermit2(dllrAmount, permit);

expect(await isUsedNonce(permit2, alice.address, nonce)).to.equal(false);
await expect(
mocIntegration.connect(alice).getDocFromDllrAndRedeemRbtcWithPermit2(permitTransferFrom, signature)
)
Expand All @@ -236,7 +262,8 @@ describe("MoC Integration", async () => {

expect((await dllr.balanceOf(alice.address)).toString()).eq("0");
expect(await ethers.provider.getBalance(moc.address)).eq(expectedRbtcValue);
expect(await mocIntegration.getPermit2Nonce(alice.address)).eq(nonce.add(1));

expect(await isUsedNonce(permit2, alice.address, nonce)).to.equal(true);
});

it("should be able to redeem from DLLR Money on Chain DoC and then redeem RBTC from DoC through Permit2, with two transactions", async () => {
Expand All @@ -257,7 +284,8 @@ describe("MoC Integration", async () => {

await dllr.connect(alice).approve(permit2.address, ethers.constants.MaxUint256);

const nonce = await mocIntegration.getPermit2Nonce(alice.address);
const nonce = generateNonce();
expect(await isUsedNonce(permit2, alice.address, nonce)).to.equal(false);
const deadline = toDeadline(1000 * 60 * 60 * 30 /** 30 minutes */);
let dllrAmountToRedeem = new BN(dllrAmount).div(new BN(2)).toString();

Expand All @@ -267,7 +295,7 @@ describe("MoC Integration", async () => {
amount: dllrAmountToRedeem,
},
spender: mocIntegration.address.toLowerCase(),
nonce: nonce,
nonce: nonce.toNumber(),
deadline: deadline
}
const network = await ethers.provider.getNetwork();
Expand Down Expand Up @@ -304,9 +332,9 @@ describe("MoC Integration", async () => {
)
.to.changeTokenBalance(dllr, alice.address, `-${dllrAmountToRedeem}`);

expect(await mocIntegration.getPermit2Nonce(alice.address)).eq(nonce.add(1));

const nonce2 = await mocIntegration.getPermit2Nonce(alice.address);
expect(await isUsedNonce(permit2, alice.address, nonce)).to.equal(true);
const nonce2 = generateNonce();
expect(await isUsedNonce(permit2, alice.address, nonce2)).to.equal(false);
const deadline2 = toDeadline(1000 * 60 * 60 * 30 /** 30 minutes */);

const permitTransferFrom2: PermitTransferFrom = {
Expand All @@ -315,7 +343,7 @@ describe("MoC Integration", async () => {
amount: dllrAmountToRedeem,
},
spender: mocIntegration.address.toLowerCase(),
nonce: nonce2,
nonce: nonce2.toNumber(),
deadline: deadline2
}

Expand All @@ -336,7 +364,7 @@ describe("MoC Integration", async () => {

expect((await dllr.balanceOf(alice.address)).toString()).eq("0");
expect(await ethers.provider.getBalance(moc.address)).eq(initialExpectedRbtcValue);
expect(await mocIntegration.getPermit2Nonce(alice.address)).eq(nonce2.add(1));
expect(await isUsedNonce(permit2, alice.address, nonce2)).to.equal(true);
});

it("should revert to redeem from DLLR Money on Chain DoC and then redeem RBTC from DoC if yet approved the permit2", async () => {
Expand All @@ -354,7 +382,8 @@ describe("MoC Integration", async () => {
.to.changeTokenBalance(dllr, alice.address, dllrAmount)
.to.changeTokenBalance(bAssetDoc, alice.address, `-${dllrAmount}`);

const nonce = await mocIntegration.getPermit2Nonce(alice.address);
const nonce = generateNonce();
expect(await isUsedNonce(permit2, alice.address, nonce)).to.equal(false);
const deadline = toDeadline(1000 * 60 * 60 * 30 /** 30 minutes */);

const permitTransferFrom: PermitTransferFrom = {
Expand All @@ -363,7 +392,7 @@ describe("MoC Integration", async () => {
amount: dllrAmount,
},
spender: mocIntegration.address.toLowerCase(),
nonce: nonce,
nonce: nonce.toNumber(),
deadline: deadline
}
const network = await ethers.provider.getNetwork();
Expand All @@ -390,8 +419,7 @@ describe("MoC Integration", async () => {
await expect(
mocIntegration.connect(alice).getDocFromDllrAndRedeemRbtcWithPermit2(permitTransferFrom, signature)
).to.be.revertedWith("TRANSFER_FROM_FAILED");

expect(await mocIntegration.getPermit2Nonce(alice.address)).eq(nonce);
expect(await isUsedNonce(permit2, alice.address, nonce)).to.equal(false);
});

it("should revert to redeem from DLLR Money on Chain DoC and then redeem RBTC from DoC if signature is assigned to wrong spender", async () => {
Expand All @@ -411,7 +439,8 @@ describe("MoC Integration", async () => {

await dllr.connect(alice).approve(permit2.address, ethers.constants.MaxUint256);

const nonce = await mocIntegration.getPermit2Nonce(alice.address);
const nonce = generateNonce();
expect(await isUsedNonce(permit2, alice.address, nonce)).to.equal(false);
const deadline = toDeadline(1000 * 60 * 60 * 30 /** 30 minutes */);

const permitTransferFrom: PermitTransferFrom = {
Expand All @@ -420,7 +449,7 @@ describe("MoC Integration", async () => {
amount: dllrAmount,
},
spender: alice.address, // wrong spender contract here
nonce: nonce,
nonce: nonce.toNumber(),
deadline: deadline
}
const network = await ethers.provider.getNetwork();
Expand All @@ -447,8 +476,7 @@ describe("MoC Integration", async () => {
await expect(
mocIntegration.connect(alice).getDocFromDllrAndRedeemRbtcWithPermit2(permitTransferFrom, signature)
).to.revertedWithCustomError(permit2, "InvalidSigner");

expect(await mocIntegration.getPermit2Nonce(alice.address)).eq(nonce);
expect(await isUsedNonce(permit2, alice.address, nonce)).to.equal(false);
});

it("should revert to redeem from DLLR Money on Chain DoC and then redeem RBTC from DoC through Permit2, with two transactions with invalid nonce", async () => {
Expand All @@ -469,7 +497,8 @@ describe("MoC Integration", async () => {

await dllr.connect(alice).approve(permit2.address, ethers.constants.MaxUint256);

const nonce = await mocIntegration.getPermit2Nonce(alice.address);
const nonce = generateNonce();
expect(await isUsedNonce(permit2, alice.address, nonce)).to.equal(false);
const deadline = toDeadline(1000 * 60 * 60 * 30 /** 30 minutes */);
let dllrAmountToRedeem = new BN(dllrAmount).div(new BN(2)).toString();

Expand All @@ -479,7 +508,7 @@ describe("MoC Integration", async () => {
amount: dllrAmountToRedeem,
},
spender: mocIntegration.address.toLowerCase(),
nonce: nonce,
nonce: nonce.toNumber(),
deadline: deadline
}
const network = await ethers.provider.getNetwork();
Expand Down Expand Up @@ -516,8 +545,7 @@ describe("MoC Integration", async () => {
)
.to.changeTokenBalance(dllr, alice.address, `-${dllrAmountToRedeem}`);

expect(await mocIntegration.getPermit2Nonce(alice.address)).eq(nonce.add(1));

expect(await isUsedNonce(permit2, alice.address, nonce)).to.equal(true);
const deadline2 = toDeadline(1000 * 60 * 60 * 30 /** 30 minutes */);

const permitTransferFrom2: PermitTransferFrom = {
Expand All @@ -526,7 +554,7 @@ describe("MoC Integration", async () => {
amount: dllrAmountToRedeem,
},
spender: mocIntegration.address.toLowerCase(),
nonce: nonce,
nonce: nonce.toNumber(),
deadline: deadline2
}

Expand Down Expand Up @@ -558,7 +586,8 @@ describe("MoC Integration", async () => {

await dllr.connect(alice).approve(permit2.address, ethers.constants.MaxUint256);

const nonce = await mocIntegration.getPermit2Nonce(alice.address);
const nonce = generateNonce();
expect(await isUsedNonce(permit2, alice.address, nonce)).to.equal(false);
const deadline = toDeadline(1000 * 60 * 60 * 30 /** 30 minutes */);

const permitTransferFrom: PermitTransferFrom = {
Expand All @@ -567,7 +596,7 @@ describe("MoC Integration", async () => {
amount: dllrAmount,
},
spender: mocIntegration.address.toLowerCase(),
nonce: nonce,
nonce: nonce.toNumber(),
deadline: deadline
}

Expand All @@ -589,6 +618,7 @@ describe("MoC Integration", async () => {

await expect(dllr.connect(attacker).transferWithPermit(alice.address, attacker.address, dllrAmount, deadline, v, r, s)).to.revertedWith("ERC20Permit: invalid signature");
await expect(permit2["permitTransferFrom(((address,uint256),uint256,uint256),(address,uint256),address,bytes)"](permitTransferFrom, transferDetails, alice.address, signature)).to.revertedWithCustomError(permit2, "InvalidSigner");
expect(await isUsedNonce(permit2, alice.address, nonce)).to.equal(false);
});
})
});
Expand Down

0 comments on commit ac98395

Please sign in to comment.