From 9b6617a7028354b71dbee612d24ff08ec89d898a 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 f9f41f6..4b55d40 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 f5473cc..2022899 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 414b4c2..3626848 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 092aada..59ffae6 100644 --- a/test/p2pix.test.ts +++ b/test/p2pix.test.ts @@ -622,6 +622,7 @@ describe("P2PIX", () => { price, [], [], + false, ); const fail2 = p2pix.lock( zero, @@ -629,6 +630,7 @@ describe("P2PIX", () => { price, [], [], + false, ); await expect(fail).to.be.revertedWithCustomError( @@ -658,6 +660,7 @@ describe("P2PIX", () => { price.mul(ethers.BigNumber.from(2)), [], [], + false, ); await expect(fail).to.be.revertedWithCustomError( @@ -683,6 +686,7 @@ describe("P2PIX", () => { ethers.BigNumber.from(1000), [ethers.utils.keccak256(ethers.utils.toUtf8Bytes("wrong"))], [], + false, ); await expect(fail).to.be.revertedWithCustomError( @@ -710,6 +714,7 @@ describe("P2PIX", () => { price.mul(2), [], [], + false, ); await expect(fail).to.be.revertedWithCustomError( @@ -717,6 +722,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.address, price); @@ -735,6 +776,7 @@ describe("P2PIX", () => { price, proof, [], + false, ); const storage: Lock = await p2pix.callStatic.mapLocks( 1, @@ -779,6 +821,7 @@ describe("P2PIX", () => { price, [], [], + false, ); const storage: Lock = await p2pix.callStatic.mapLocks( 1, @@ -840,6 +883,7 @@ describe("P2PIX", () => { price, [], [], + false, ); await p2pix .connect(acc01) @@ -856,6 +900,7 @@ describe("P2PIX", () => { price.add(ethers.constants.One), [], [], + false, ); const storage: Lock = await p2pix.callStatic.mapLocks( 2, @@ -906,6 +951,7 @@ describe("P2PIX", () => { newPrice, proof, [], + false, ); const storage1: Lock = await p2pix.callStatic.mapLocks( 1, @@ -922,6 +968,7 @@ describe("P2PIX", () => { ethers.BigNumber.from(100), [], [], + false, ); const storage2: Lock = await p2pix.callStatic.mapLocks( 2, @@ -938,6 +985,7 @@ describe("P2PIX", () => { ethers.BigNumber.from(100), [], [], + false, ); const storage3: Lock = await p2pix.callStatic.mapLocks( 3, @@ -1214,6 +1262,7 @@ describe("P2PIX", () => { ethers.BigNumber.from(100), [], [], + false, ); const lockID = ethers.constants.One; await mine(13); @@ -1255,6 +1304,7 @@ describe("P2PIX", () => { ethers.BigNumber.from(100), [], [], + false, ); const lockID = ethers.constants.One; await p2pix.release( @@ -1299,6 +1349,7 @@ describe("P2PIX", () => { ethers.BigNumber.from(100), [], [], + false, ); await p2pix @@ -1316,6 +1367,7 @@ describe("P2PIX", () => { ethers.BigNumber.from(100), [], [], + false, ); const fail = p2pix .connect(acc01) @@ -1357,6 +1409,7 @@ describe("P2PIX", () => { ethers.BigNumber.from(100), [], [], + false, ); const fail = p2pix .connect(acc01) @@ -1413,6 +1466,7 @@ describe("P2PIX", () => { ethers.BigNumber.from(100), [], [], + false, ); const acc01Key = await p2pix.callStatic._castAddrToKey( acc01.address, @@ -1577,6 +1631,7 @@ describe("P2PIX", () => { ethers.BigNumber.from(100), [], [], + false, ); await p2pix .connect(acc03) @@ -1586,6 +1641,7 @@ describe("P2PIX", () => { ethers.BigNumber.from(50), [], [], + false, ); await p2pix .connect(acc03) @@ -1595,6 +1651,7 @@ describe("P2PIX", () => { ethers.BigNumber.from(25), [], [], + false, ); const lockStatus1 = @@ -1764,6 +1821,7 @@ describe("P2PIX", () => { ethers.constants.One, [], [], + false, ); const lockID = ethers.constants.One; const fail = p2pix.unlockExpired([lockID]); @@ -1802,6 +1860,7 @@ describe("P2PIX", () => { ethers.constants.One, [], [], + false, ); const lockID = ethers.constants.One; // await mine(10); @@ -1835,6 +1894,7 @@ describe("P2PIX", () => { ethers.constants.One, [], [], + false, ); const lockID = ethers.constants.One; await mine(11); @@ -1898,6 +1958,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. @@ -1937,6 +1998,7 @@ describe("P2PIX", () => { ethers.BigNumber.from(100), [], [lockID], + false ); const remaining = await p2pix.callStatic.getBalance( owner.address, @@ -1970,6 +2032,7 @@ describe("P2PIX", () => { price, proof, [], + false, ); const lockID = ethers.constants.One; // mine blocks to expire lock