Skip to content

Commit

Permalink
Merge pull request #165 from blackbeard002/bit
Browse files Browse the repository at this point in the history
refactor: using bitwise operations to save gas #104
  • Loading branch information
0xneves authored Jan 16, 2024
2 parents af3370f + 957a203 commit dc22b4a
Show file tree
Hide file tree
Showing 8 changed files with 253 additions and 114 deletions.
23 changes: 22 additions & 1 deletion contracts/SwapFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,27 @@ abstract contract SwapFactory is ISwapFactory, ISwap, IErrors {

if (biding.length == 0 || asking.length == 0) revert InvalidAssetsLength();

return Swap(owner, allowed, expiry, biding, asking);
uint256 config = packData(allowed, expiry);

return Swap(owner,config, biding, asking);
}

/**
* @dev See {ISwapFactory-packData}.
*/
function packData(
address allowed,
uint256 expiry
) public pure returns(uint256) {
return (uint256(uint160(allowed)) << 96) | uint256(expiry);
}

/**
* @dev See {ISwapFactory-parseData}.
*/
function parseData(
uint256 config
) public pure returns(address,uint256) {
return (address(uint160(config >> 96)),uint256(config & ((1 << 96) - 1)));
}
}
22 changes: 14 additions & 8 deletions contracts/Swaplace.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {SwapFactory} from "./SwapFactory.sol";

/**
* @author @0xneves | @blockful_io
* @dev Swaplace is a Decentralized Feeless DEX. It has no owners, it cannot be stoped.
* @dev Swaplace is a Decentralized Feeless DEX. It has no owners, it cannot be stopped.
* Its cern is to facilitate swaps between virtual assets following the ERC standard.
* Users can propose or accept swaps by allowing Swaplace to move their assets using the
* `approve` or `permit` function.
Expand All @@ -26,7 +26,9 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 {
function createSwap(Swap calldata swap) public returns (uint256) {
if (swap.owner != msg.sender) revert InvalidAddress(msg.sender);

if (swap.expiry < block.timestamp) revert InvalidExpiry(swap.expiry);
(address allowed, uint256 expiry) = parseData(swap.config);

if (expiry < block.timestamp) revert InvalidExpiry(expiry);

if (swap.biding.length == 0 || swap.asking.length == 0)
revert InvalidAssetsLength();
Expand All @@ -41,7 +43,7 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 {

_swaps[swapId] = swap;

emit SwapCreated(swapId, msg.sender, swap.allowed, swap.expiry);
emit SwapCreated(swapId, msg.sender, allowed, expiry);

return swapId;
}
Expand All @@ -52,12 +54,14 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 {
function acceptSwap(uint256 swapId, address receiver) public returns (bool) {
Swap memory swap = _swaps[swapId];

if (swap.allowed != address(0) && swap.allowed != msg.sender)
(address allowed, uint256 expiry) = parseData(swap.config);

if (allowed != address(0) && allowed != msg.sender)
revert InvalidAddress(msg.sender);

if (swap.expiry < block.timestamp) revert InvalidExpiry(swap.expiry);
if (expiry < block.timestamp) revert InvalidExpiry(expiry);

_swaps[swapId].expiry = 0;
_swaps[swapId].config = packData(allowed, 0);

Asset[] memory assets = swap.asking;

Expand Down Expand Up @@ -98,9 +102,11 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 {

if (swap.owner != msg.sender) revert InvalidAddress(msg.sender);

if (swap.expiry < block.timestamp) revert InvalidExpiry(swap.expiry);
(address allowed, uint256 expiry) = parseData(swap.config);

if (expiry < block.timestamp) revert InvalidExpiry(expiry);

_swaps[swapId].expiry = 0;
_swaps[swapId].config = packData(allowed, 0);

emit SwapCanceled(swapId, msg.sender);
}
Expand Down
4 changes: 3 additions & 1 deletion contracts/echidna/TestSwapFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ contract TestFactory is SwapFactory {
make_asset_array(addr, amountOrId)
);

assert(swap.expiry > block.timestamp);
( , uint256 expiry) = parseData(swap.config);

assert(expiry > block.timestamp);
assert(swap.biding.length > 0);
assert(swap.asking.length > 0);
return swap;
Expand Down
63 changes: 31 additions & 32 deletions contracts/interfaces/ISwap.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,36 @@ pragma solidity ^0.8.17;
* @dev Interface for the Swap Struct, used in the {Swaplace} implementation.
*/
interface ISwap {
/**
* @dev Assets can be ERC20 or ERC721.
*
* It is composed of:
* - `addr` of the asset.
* - `amountOrId` of the asset based on the standard.
*
* NOTE: `amountOrId` is the `amount` of ERC20 or the `tokenId` of ERC721.
*/
struct Asset {
address addr;
uint256 amountOrId;
}
/**
* @dev Assets can be ERC20 or ERC721.
*
* It is composed of:
* - `addr` of the asset.
* - `amountOrId` of the asset based on the standard.
*
* NOTE: `amountOrId` is the `amount` of ERC20 or the `tokenId` of ERC721.
*/
struct Asset {
address addr;
uint256 amountOrId;
}

/**
* @dev The Swap struct is the heart of Swaplace.
*
* It is composed of:
* - `owner` of the Swap.
* - `allowed` address to accept the Swap.
* - `expiry` date of the Swap.
* - `biding` assets that are being bided by the owner.
* - `asking` assets that are being asked by the owner.
*
* NOTE: When `allowed` address is the zero address, anyone can accept the Swap.
*/
struct Swap {
address owner;
address allowed;
uint256 expiry;
Asset[] biding;
Asset[] asking;
}
/**
* @dev The Swap struct is the heart of Swaplace.
*
* It is composed of:
* - `owner` of the Swap.
* - `config` represents two packed values: 'allowed' for the allowed address
* to accept the swap and 'expiry' for the expiration date of the swap.
* - `biding` assets that are being bided by the owner.
* - `asking` assets that are being asked by the owner.
*
* NOTE: When `allowed` address is the zero address, anyone can accept the Swap.
*/
struct Swap {
address owner;
uint256 config;
Asset[] biding;
Asset[] asking;
}
}
17 changes: 17 additions & 0 deletions contracts/interfaces/ISwapFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,21 @@ interface ISwapFactory {
ISwap.Asset[] memory assets,
ISwap.Asset[] memory asking
) external view returns (ISwap.Swap memory);

/**
* @dev Packs `allowed` and the `expiry`.
* This function returns the bitwise packing of `allowed` and `expiry` as a uint256.
*/
function packData(
address allowed,
uint256 expiry
) external pure returns(uint256);

/**
* @dev Parsing the `config`.
* This function returns the extracted values of `allowed` and `expiry`.
*/
function parseData(
uint256 config
) external pure returns (address,uint256);
}
67 changes: 41 additions & 26 deletions test/TestSwapFactory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,22 +44,25 @@ describe("Swaplace Factory", async function () {
});

it("Should be able to {makeSwap} with ERC20 and ERC721", async function () {
const expiry = (await blocktimestamp()) * 2;
const currentTimestamp = (await blocktimestamp()) * 2;

const ERC20Asset: Asset = await makeAsset(MockERC20.address, 1000);
const ERC721Asset: Asset = await makeAsset(MockERC721.address, 1);

const config = await Swaplace.packData(zeroAddress, currentTimestamp);

const swap = await makeSwap(
owner.address,
zeroAddress,
expiry,
config,
[ERC20Asset],
[ERC721Asset],
);

const [allowed, expiry] = await Swaplace.parseData(swap.config);

expect(swap.owner).to.be.equals(owner.address);
expect(swap.expiry).to.be.equals(expiry);
expect(swap.allowed).to.be.equals(zeroAddress);
expect(expiry).to.be.equals(currentTimestamp);
expect(allowed).to.be.equals(zeroAddress);
expect(swap.biding[0]).to.be.equals(ERC20Asset);
expect(swap.asking[0]).to.be.equals(ERC721Asset);
});
Expand All @@ -70,10 +73,11 @@ describe("Swaplace Factory", async function () {
const ERC20Asset: Asset = await makeAsset(MockERC20.address, 1000);
const ERC721Asset: Asset = await makeAsset(MockERC721.address, 1);

const config = await Swaplace.packData(zeroAddress, expiry);

const swap = await makeSwap(
owner.address,
zeroAddress,
expiry,
config,
[ERC20Asset],
[ERC721Asset],
);
Expand All @@ -86,9 +90,14 @@ describe("Swaplace Factory", async function () {
[ERC721Asset],
);

const [swapAllowed, swapExpiry] = await Swaplace.parseData(swap.config);
const [onChainAllowed, onChainExpiry] = await Swaplace.parseData(
onchainSwap.config,
);

expect(swap.owner).to.be.equals(onchainSwap.owner);
expect(swap.expiry).to.be.equals(onchainSwap.expiry);
expect(swap.allowed).to.be.equals(onchainSwap.allowed);
expect(swapExpiry).to.be.equals(onChainExpiry);
expect(swapAllowed).to.be.equals(onChainAllowed);

expect(swap.biding[0].addr).to.be.equals(onchainSwap.biding[0].addr);
expect(swap.biding[0].amountOrId).to.be.equals(
Expand All @@ -102,49 +111,55 @@ describe("Swaplace Factory", async function () {
});

it("Should be able to {makeSwap} with multiple assets", async function () {
const expiry = (await blocktimestamp()) * 2;
const currentTimestamp = (await blocktimestamp()) * 2;

const ERC20Asset = await makeAsset(MockERC20.address, 1000);
const ERC721Asset = await makeAsset(MockERC721.address, 1);

const config = await Swaplace.packData(zeroAddress, currentTimestamp);

const swap = await makeSwap(
owner.address,
zeroAddress,
expiry,
config,
[ERC20Asset, ERC721Asset],
[ERC20Asset, ERC721Asset],
);

const [, expiry] = await Swaplace.parseData(swap.config);

expect(swap.owner).to.be.equals(owner.address);
expect(swap.expiry).to.be.equals(expiry);
expect(expiry).to.be.equals(expiry);
expect(swap.biding[0]).to.be.equals(ERC20Asset);
expect(swap.biding[1]).to.be.equals(ERC721Asset);
expect(swap.asking[0]).to.be.equals(ERC20Asset);
expect(swap.asking[1]).to.be.equals(ERC721Asset);
});

it("Should be able to {composeSwap} using both ERC20, ERC721", async function () {
const expiry = (await blocktimestamp()) * 2;
const currentTimestamp = (await blocktimestamp()) * 2;

const bidingAddr = [MockERC20.address, MockERC721.address];
const bidingAmountOrId = [1000, 1];

const askingAddr = [MockERC721.address];
const askingAmountOrId = [2];

const config = await Swaplace.packData(zeroAddress, currentTimestamp);

const swap = await composeSwap(
owner.address,
zeroAddress,
expiry,
config,
bidingAddr,
bidingAmountOrId,
askingAddr,
askingAmountOrId,
);

const [allowed, expiry] = await Swaplace.parseData(swap.config);

expect(swap.owner).to.be.equals(owner.address);
expect(swap.allowed).to.be.equals(zeroAddress);
expect(swap.expiry).to.be.equals(expiry);
expect(allowed).to.be.equals(zeroAddress);
expect(expiry).to.be.equals(expiry);
});

it("Should revert using {composeSwap} without minimum expiry", async function () {
Expand All @@ -157,10 +172,10 @@ describe("Swaplace Factory", async function () {
const askingAmountOrId = [2];

try {
const config = await Swaplace.packData(zeroAddress, expiry);
await composeSwap(
owner.address,
zeroAddress,
expiry,
config,
bidingAddr,
bidingAmountOrId,
askingAddr,
Expand All @@ -181,10 +196,10 @@ describe("Swaplace Factory", async function () {
const askingAmountOrId = [2];

try {
const config = await Swaplace.packData(zeroAddress, expiry);
await composeSwap(
zeroAddress,
zeroAddress,
expiry,
config,
bidingAddr,
bidingAmountOrId,
askingAddr,
Expand All @@ -205,10 +220,10 @@ describe("Swaplace Factory", async function () {
const askingAmountOrId: any[] = [];

try {
const config = await Swaplace.packData(zeroAddress, expiry);
await composeSwap(
owner.address,
zeroAddress,
expiry,
config,
bidingAddr,
bidingAmountOrId,
askingAddr,
Expand All @@ -229,10 +244,10 @@ describe("Swaplace Factory", async function () {
const askingAmountOrId = [1, 999, 777];

try {
const config = await Swaplace.packData(zeroAddress, expiry);
await composeSwap(
owner.address,
zeroAddress,
expiry,
config,
bidingAddr,
bidingAmountOrId,
askingAddr,
Expand Down
Loading

0 comments on commit dc22b4a

Please sign in to comment.