From b2198306e6fc52c06d2dcc997605eb3fa1dba6b1 Mon Sep 17 00:00:00 2001 From: hueso Date: Sun, 3 Mar 2024 21:58:41 -0300 Subject: [PATCH] bond to override lock limit --- contracts/core/Constants.sol | 1 + contracts/core/DataTypes.sol | 1 + contracts/p2pix.sol | 23 +++++++++---- docs/core/Constants.md | 6 ++++ docs/core/DataTypes.md | 1 + docs/p2pix.md | 5 +-- test/p2pix.test.ts | 63 ++++++++++++++++++++++++++++++++++++ 7 files changed, 91 insertions(+), 9 deletions(-) diff --git a/contracts/core/Constants.sol b/contracts/core/Constants.sol index 21e2449..239d4b6 100644 --- a/contracts/core/Constants.sol +++ b/contracts/core/Constants.sol @@ -44,4 +44,5 @@ abstract contract Constants { uint256 constant MAXBALANCE_UPPERBOUND = 1e8 ether; uint256 constant REPUTATION_LOWERBOUND = 1e2 ether; uint256 constant LOCKAMOUNT_UPPERBOUND = 1e6 ether; + uint256 constant BOND_DIVISOR = 4; // 6,25% } diff --git a/contracts/core/DataTypes.sol b/contracts/core/DataTypes.sol index f7b901b..df3a098 100644 --- a/contracts/core/DataTypes.sol +++ b/contracts/core/DataTypes.sol @@ -13,6 +13,7 @@ library DataTypes { ERC20 token; address buyerAddress; address seller; + bool bond; } // prettier-ignore diff --git a/contracts/p2pix.sol b/contracts/p2pix.sol index 641da79..3a11675 100644 --- a/contracts/p2pix.sol +++ b/contracts/p2pix.sol @@ -135,7 +135,8 @@ contract P2PIX is BaseUtils { ERC20 token, uint80 amount, bytes32[] calldata merkleProof, - uint256[] calldata expiredLocks + uint256[] calldata expiredLocks, + bool bond ) public nonReentrant returns (uint256 lockID) { unlockExpired(expiredLocks); @@ -154,10 +155,17 @@ contract P2PIX is BaseUtils { bytes32 _pixTarget = getPixTarget(seller, token); - // transaction forwarding must leave `merkleProof` empty; - // otherwise, the trustedForwarder must be previously added - // to a seller whitelist. - if (merkleProof.length != 0) { + if (bond){ + SafeTransferLib.safeTransferFrom( + token, + _msgSender(), + address(this), + amount >> BOND_DIVISOR + ); + } else if (merkleProof.length != 0) { + // transaction forwarding must leave `merkleProof` empty; + // otherwise, the trustedForwarder must be previously added + // to a seller whitelist. _merkleVerify( merkleProof, sellerAllowList(seller), _msgSender()); } else if ( amount > REPUTATION_LOWERBOUND && msg.sender == _msgSender() ) { @@ -178,7 +186,8 @@ contract P2PIX is BaseUtils { amount, token, _msgSender(), - seller + seller, + bond ); _addLock(bal, l); @@ -237,7 +246,7 @@ contract P2PIX is BaseUtils { SafeTransferLib.safeTransfer( t, l.buyerAddress, - lockAmount + l.bond ? lockAmount + lockAmount >> BOND_DIVISOR : lockAmount ); emit LockReleased(l.buyerAddress, lockID, lockAmount); diff --git a/docs/core/Constants.md b/docs/core/Constants.md index 3caea5b..baf27a9 100644 --- a/docs/core/Constants.md +++ b/docs/core/Constants.md @@ -94,3 +94,9 @@ uint256 REPUTATION_LOWERBOUND uint256 LOCKAMOUNT_UPPERBOUND ``` +### BOND_DIVISOR + +```solidity +uint256 BOND_DIVISOR +``` + diff --git a/docs/core/DataTypes.md b/docs/core/DataTypes.md index 304d309..615e1bf 100644 --- a/docs/core/DataTypes.md +++ b/docs/core/DataTypes.md @@ -13,6 +13,7 @@ struct Lock { contract ERC20 token; address buyerAddress; address seller; + bool bond; } ``` diff --git a/docs/p2pix.md b/docs/p2pix.md index 18f494d..98a0a30 100644 --- a/docs/p2pix.md +++ b/docs/p2pix.md @@ -67,7 +67,7 @@ _Function sighash: 0x6d82d9e0_ ### lock ```solidity -function lock(address seller, contract ERC20 token, uint80 amount, bytes32[] merkleProof, uint256[] expiredLocks) public returns (uint256 lockID) +function lock(address seller, contract ERC20 token, uint80 amount, bytes32[] merkleProof, uint256[] expiredLocks, bool bond) public returns (uint256 lockID) ``` Public method designed to lock an remaining amount of @@ -78,7 +78,7 @@ to a seller whitelist. This method can be performed either by: - An user allowed via the seller's allowlist; - An user with enough userRecord to lock the wished amount; -There can only exist a lock per each `_amount` partitioned +There can only exist a lock per each `amount` partitioned from the total `remaining` value. Locks can only be performed in valid orders. @@ -93,6 +93,7 @@ _Function sighash: 0xdc43221c_ | amount | uint80 | The deposit's remaining amount wished to be locked. | | merkleProof | bytes32[] | Provided as a pass if the `msg.sender` is in the seller's allowlist; Left empty otherwise; | | expiredLocks | uint256[] | An array of identifiers to be provided so to unexpire locks using this transaction gas push. | +| bond | bool | | #### Return Values diff --git a/test/p2pix.test.ts b/test/p2pix.test.ts index 912d994..636bc4f 100644 --- a/test/p2pix.test.ts +++ b/test/p2pix.test.ts @@ -615,6 +615,7 @@ describe("P2PIX", () => { price, [], [], + false, ); const fail2 = p2pix.lock( zero, @@ -622,6 +623,7 @@ describe("P2PIX", () => { price, [], [], + false, ); expect(fail).to.be.revertedWithCustomError( @@ -651,6 +653,7 @@ describe("P2PIX", () => { price * 2n, [], [], + false, ); await expect(fail).to.be.revertedWithCustomError( @@ -676,6 +679,7 @@ describe("P2PIX", () => { 1000n, [ethers.keccak256(ethers.toUtf8Bytes("wrong"))], [], + false, ); await expect(fail).to.be.revertedWithCustomError( @@ -703,6 +707,7 @@ describe("P2PIX", () => { price * 2n, [], [], + false, ); await expect(fail).to.be.revertedWithCustomError( @@ -710,6 +715,42 @@ describe("P2PIX", () => { P2PixErrors.AmountNotAllowed, ); }); + it("should override spend limit if buyer pays the bond", async () => { + await erc20.approve( + p2pix.address, + price.mul(3), + ); + await p2pix.deposit( + "1", + merkleRoot, + erc20.address, + price.mul(3), + true, + ); + await erc20 + .transfer( + acc02.address, + price, + ); + await erc20 + .connect(acc02) + .approve( + p2pix.address, + price.mul(30000), + ); + const bond = p2pix + .connect(acc02) + .lock( + owner.address, + erc20.address, + price.mul(2), + [], + [], + true, + ); + + await expect(bond).to.be.ok; + }); it("should create a lock, update storage and emit events via the allowlist path", async () => { const target = "333"; await erc20.approve(p2pix.target, price); @@ -728,6 +769,7 @@ describe("P2PIX", () => { price, proof, [], + false, ); const storage: Lock = await p2pix.mapLocks.staticCall( 1, @@ -772,6 +814,7 @@ describe("P2PIX", () => { price, [], [], + false, ); const storage: Lock = await p2pix.mapLocks.staticCall( 1, @@ -831,6 +874,7 @@ describe("P2PIX", () => { price, [], [], + false, ); await p2pix .connect(acc01) @@ -847,6 +891,7 @@ describe("P2PIX", () => { price + 1n, [], [], + false, ); const storage: Lock = await p2pix.mapLocks.staticCall( 2, @@ -897,6 +942,7 @@ describe("P2PIX", () => { newPrice, proof, [], + false, ); const storage1: Lock = await p2pix.mapLocks.staticCall( 1, @@ -913,6 +959,7 @@ describe("P2PIX", () => { BigInt(100), [], [], + false, ); const storage2: Lock = await p2pix.mapLocks.staticCall( 2, @@ -929,6 +976,7 @@ describe("P2PIX", () => { BigInt(100), [], [], + false, ); const storage3: Lock = await p2pix.mapLocks.staticCall( 3, @@ -1205,6 +1253,7 @@ describe("P2PIX", () => { BigInt(100), [], [], + false, ); const lockID = BigInt(1); await mine(13); @@ -1246,6 +1295,7 @@ describe("P2PIX", () => { BigInt(100), [], [], + false, ); const lockID = BigInt(1); await p2pix.release( @@ -1290,6 +1340,7 @@ describe("P2PIX", () => { BigInt(100), [], [], + false, ); await p2pix @@ -1307,6 +1358,7 @@ describe("P2PIX", () => { BigInt(100), [], [], + false, ); const fail = p2pix .connect(acc01) @@ -1348,6 +1400,7 @@ describe("P2PIX", () => { BigInt(100), [], [], + false, ); const fail = p2pix .connect(acc01) @@ -1404,6 +1457,7 @@ describe("P2PIX", () => { BigInt(100), [], [], + false, ); const acc01Key = await p2pix._castAddrToKey.staticCall( acc01.address, @@ -1568,6 +1622,7 @@ describe("P2PIX", () => { BigInt(100), [], [], + false, ); await p2pix .connect(acc03) @@ -1577,6 +1632,7 @@ describe("P2PIX", () => { BigInt(50), [], [], + false, ); await p2pix .connect(acc03) @@ -1586,6 +1642,7 @@ describe("P2PIX", () => { BigInt(25), [], [], + false, ); const lockStatus1 = @@ -1755,6 +1812,7 @@ describe("P2PIX", () => { BigInt(1), [], [], + false, ); const lockID = BigInt(1); const fail = p2pix.unlockExpired([lockID]); @@ -1793,6 +1851,7 @@ describe("P2PIX", () => { BigInt(1), [], [], + false, ); const lockID = BigInt(1); // await mine(10); @@ -1826,6 +1885,7 @@ describe("P2PIX", () => { BigInt(1), [], [], + false, ); const lockID = BigInt(1); await mine(11); @@ -1889,6 +1949,7 @@ describe("P2PIX", () => { price, proof, [], + false, ); // as return values of non view functions can't be accessed // outside the evm, we fetch the lockID from the emitted event. @@ -1928,6 +1989,7 @@ describe("P2PIX", () => { BigInt(100), [], [lockID], + false ); const remaining = await p2pix.getBalance.staticCall( owner.address, @@ -1961,6 +2023,7 @@ describe("P2PIX", () => { price, proof, [], + false, ); const lockID = BigInt(1); // mine blocks to expire lock