Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge 'revision' branch with 'develop' branch #232

Merged
merged 61 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
cf629b1
feat: added kakarot rpc
0xneves Mar 28, 2024
8655daa
fix: before moved out of context and changed into beforeEach in cance…
0xneves Apr 1, 2024
e22c041
feat: encodeConfig with native ether payment
0xneves Apr 1, 2024
6ccbaef
fix: grammar
0xneves Apr 1, 2024
2a73b3f
fix: grammar on _payNativeEther
0xneves Apr 3, 2024
095f5b6
fix: better narrative for contract description
0xneves Apr 3, 2024
e583f76
fix: Swap struct description
0xneves Apr 3, 2024
9f35fee
fix: swapId should start at 1
0xneves Apr 6, 2024
432d18c
fix: enhancing documentation descriptions
0xneves Apr 11, 2024
c5ededf
fix: enhance documentation of makeAsset
0xneves Apr 11, 2024
60d5a35
feat: adding ERC1155 mock and test
0xneves Apr 11, 2024
2e71905
refactor: adding URI to the mock erc721
0xneves Apr 11, 2024
c707b99
refactor: including new encoder in the pipeline
0xneves Apr 11, 2024
f79e1c6
feat: ERC1155 can be traded
0xneves Apr 17, 2024
95f72f1
feat: encode and decode Asset
0xneves Apr 17, 2024
0f2a9f4
fix: leading zero addresses incorrectly padded
0xneves Apr 17, 2024
fd56aa3
fix: types was not adjusting to uint120 at first
0xneves Apr 17, 2024
dd6f663
refactor: removed ITransfer as it's being used func selector
0xneves Apr 17, 2024
7d5b4cc
delete: ITransfer sol file
0xneves Apr 17, 2024
705084a
fix: incorrect padding when address has leading zeroes
0xneves Apr 17, 2024
ef23c09
feat: tests with ERC1155
0xneves Apr 17, 2024
3ee4660
docs: explaining support for ERC1155
0xneves Apr 17, 2024
5ded446
Merge pull request #203 from 0xneves/revision
0xneves Apr 17, 2024
e123c97
closes #205
blackbeard002 May 1, 2024
8adf3a0
Merge pull request #213 from blackbeard002/ethTransferBuild
0xneves May 8, 2024
b46209e
closes #214
blackbeard002 May 9, 2024
e1832ed
Merge pull request #215 from blackbeard002/value
0xneves May 9, 2024
18d04d1
closes #206
blackbeard002 May 10, 2024
5e91f54
closes #207
blackbeard002 May 10, 2024
5ba0149
closes #208
blackbeard002 May 10, 2024
1da56f7
feat: Kakarot rpc and ipfs on mockerc721
RafaDSan May 10, 2024
9ffdc10
closes #210
blackbeard002 May 13, 2024
bb4b941
closes #211
blackbeard002 May 13, 2024
4a2849f
closes #212
blackbeard002 May 17, 2024
9f7128e
Merge pull request #222 from RafaDSan/revision
0xneves May 17, 2024
247965f
Requested changes made #217
blackbeard002 May 18, 2024
9da0bd4
fix: removing length tests from echidna as it no longer exists
0xneves May 19, 2024
12a2b89
Merge pull request #218 from blackbeard002/buildERC1155
0xneves May 19, 2024
99311e5
Merge pull request #219 from blackbeard002/ERC1155
0xneves May 19, 2024
645a818
Merge pull request #224 from blackbeard002/mint1155
0xneves May 19, 2024
7c7ca2c
Merge pull request #223 from blackbeard002/deploy1155
0xneves May 19, 2024
2bed24f
fix: adding ERC1155 on sample
0xneves May 19, 2024
7fb7743
Merge branch 'revision' into createSwapERC1155
0xneves May 19, 2024
c9f3df0
removed white space #217
blackbeard002 May 19, 2024
b4b46cc
fix: encodeAsset was not being exported
0xneves May 19, 2024
b20f395
Merge pull request #225 from blackbeard002/createSwapERC1155
0xneves May 19, 2024
718b4ca
Merge pull request #217 from blackbeard002/ethTransfer
0xneves May 19, 2024
e84906a
closes #227
blackbeard002 Jun 5, 2024
bd5a902
Fixed a blunder #227
blackbeard002 Jun 6, 2024
bc8d90f
remove: unused error
0xneves Jun 6, 2024
175709b
fix: wrong identation and error name
0xneves Jun 6, 2024
c39dfaf
fix: wrong test name
0xneves Jun 6, 2024
694c4a1
docs: explaining the recipient and value var usage
0xneves Jun 6, 2024
490f89d
fix: erc20/erc721 was using wrong amountOrId parameter
0xneves Jun 6, 2024
41ef97d
fix: no need to check for recipient since value can only be present w…
0xneves Jun 6, 2024
0917897
fix: wrong return variable name
0xneves Jun 6, 2024
55963c8
docs: added param types and stricly rules of variables
0xneves Jun 6, 2024
ce31d03
fix: wrong error name
0xneves Jun 6, 2024
d452e4f
fix: cancel swap test wasn't being validated
0xneves Jun 6, 2024
a773428
fix: expired swaps couldnt withdraw their ether
0xneves Jun 6, 2024
b8a6990
Merge pull request #230 from blackbeard002/revert
0xneves Jun 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ SWAPLACE_ADDRESS=0x000000000000000000000000000000000000000000
# Mocks last deployed contracts addresses.
ERC20_ADDRESS=0x000000000000000000000000000000000000000000
ERC721_ADDRESS=0x000000000000000000000000000000000000000000
ERC1155_ADDRESS=0x000000000000000000000000000000000000000000
# Amount of ERC20 tokens to be minted for signer address.
AMOUNT=1000
# Token ID of ERC721 to be minted for signer address.
TOKEN_ID=1
# The swap to be accepted by the acceptee.
SWAP_ID=3
SWAP_ID=1

# These are public known private keys and are here as an example.
# You should change the private keys to your own private keys.
Expand All @@ -28,6 +29,7 @@ SEPOLIA_RPC_URL=https://ethereum-sepolia.publicnode.com
MUMBAI_RPC_URL=https://polygon-mumbai-bor.publicnode.com
FUJI_RPC_URL=https://avalanche-fuji-c-chain.publicnode.com
BNB_TESTNET_RPC_URL=https://bsc-testnet.publicnode.com
KAKAROT_SEPOLIA_RPC_URL=https://sepolia-rpc.kakarot.org

# MAINNETS
ETH_RPC_URL="https://eth.llamarpc.com"
Expand Down
93 changes: 74 additions & 19 deletions contracts/SwapFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,22 @@ import {ISwapFactory} from "./interfaces/ISwapFactory.sol";
* - The `allowed` address is the address that can accept the Swap. If the allowed
* address is the zero address, then anyone can accept the Swap.
* - The `expiry` date is the timestamp that the Swap will be available to accept.
* - The `recipient` is the address that will receive the ETH as type uint8. If the
* recipient is equals to 0, the acceptee will receive the ETH. If the recipient is
* between 1<>255 then the recipient will be the owner of the Swap.
* - The `value` is the amount of ETH that the recipient will receive with a maximum
* of 6 decimals (0.000001 ETH). The contract will fill the value up to 18 decimals.
* - The `biding` are the assets that the owner is offering.
* - The `asking` are the assets that the owner wants in exchange.
*
* The Swap struct uses an {Asset} struct to represent the asset. This struct is
* composed of:
*
* - The `address` of the asset. This address can be from an ERC20 or ERC721 contract.
* - The `address` of the token asset.
* - The `amount` or `id` of the asset. This amount can be the amount of ERC20 tokens
* or the ID of an ERC721 token.
*
* To use other standards, like ERC1155, you can wrap the ownership of the asset
* in an a trusted contract and Swap as an ERC721. This way, you can tokenize any
* on-chain execution and trade on Swaplace.
* or the NFT ID of an ERC721.
* - The `amount` and `id` can be encoded together in a single uint256, allowing the
* ERC1155 tokens to be swapped.
*/
abstract contract SwapFactory is ISwapFactory, ISwap, IErrors {
/**
Expand All @@ -43,39 +46,91 @@ abstract contract SwapFactory is ISwapFactory, ISwap, IErrors {
return Asset(addr, amountOrId);
}

/**
* @dev See {ISwapFactory-make1155Asset}.
*/
function make1155Asset(
address addr,
uint120 tokenId,
uint120 tokenAmount
) public pure virtual returns (Asset memory) {
return Asset(addr, encodeAsset(tokenId, tokenAmount));
}

/**
* @dev See {ISwapFactory-makeSwap}.
*/
function makeSwap(
address owner,
address allowed,
uint256 expiry,
uint32 expiry,
uint8 recipient,
uint56 value,
Asset[] memory biding,
Asset[] memory asking
) public view virtual returns (Swap memory) {
if (expiry < block.timestamp) revert InvalidExpiry(expiry);

if (biding.length == 0 || asking.length == 0) revert InvalidAssetsLength();
if (expiry < block.timestamp) revert InvalidExpiry();
uint256 config = encodeConfig(allowed, expiry, recipient, value);
return Swap(owner, config, biding, asking);
}

uint256 config = packData(allowed, expiry);
/**
* @dev See {ISwapFactory-encodeAsset}.
*/
function encodeAsset(
uint120 tokenId,
uint120 tokenAmount
) public pure returns (uint256 amountAndId) {
return
(uint256(type(uint16).max) << 240) |
(uint256(tokenId) << 120) |
uint256(tokenAmount);
}

return Swap(owner, config, biding, asking);
/**
* @dev See {ISwapFactory-decodeAsset}.
*/
function decodeAsset(
uint256 amountOrId
)
public
pure
returns (uint16 tokenType, uint256 tokenId, uint256 tokenAmount)
{
return (
uint16(amountOrId >> 240),
uint256(uint120(amountOrId >> 120)),
uint256(uint120(amountOrId))
);
}

/**
* @dev See {ISwapFactory-packData}.
* @dev See {ISwapFactory-encodeConfig}.
*/
function packData(
function encodeConfig(
address allowed,
uint256 expiry
uint32 expiry,
uint8 recipient,
uint56 value
) public pure returns (uint256) {
return (uint256(uint160(allowed)) << 96) | uint256(expiry);
return
(uint256(uint160(allowed)) << 96) |
(uint256(expiry) << 64) |
(uint256(recipient) << 56) |
uint256(value);
}

/**
* @dev See {ISwapFactory-parseData}.
* @dev See {ISwapFactory-decodeConfig}.
*/
function parseData(uint256 config) public pure returns (address, uint256) {
return (address(uint160(config >> 96)), uint256(config & ((1 << 96) - 1)));
function decodeConfig(
uint256 config
) public pure returns (address, uint32, uint8, uint56) {
return (
address(uint160(config >> 96)),
uint32(config >> 64),
uint8(config >> 56),
uint56(config)
);
}
}
150 changes: 97 additions & 53 deletions contracts/Swaplace.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ pragma solidity ^0.8.17;

import {IERC165} from "./interfaces/IERC165.sol";
import {ISwaplace} from "./interfaces/ISwaplace.sol";
import {ITransfer} from "./interfaces/ITransfer.sol";
import {SwapFactory} from "./SwapFactory.sol";

/**
* @author @0xneves | @blockful_io
* @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.
* @dev Swaplace is a decentralized and feeless DEX/OTC. Ownerless, it cannot be stopped.
* It's core is to facilitate swaps between virtual assets using the ERC standard.
* Users can propose or accept swaps by allowing Swaplace to move their assets using the
* `approve` or `permit` function.
* `approve`, `permit` or similar functions.
*/
contract Swaplace is SwapFactory, ISwaplace, IERC165 {
/// @dev Swap Identifier counter.
Expand All @@ -20,21 +19,42 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 {
/// @dev Mapping of Swap ID to Swap struct. See {ISwap-Swap}.
mapping(uint256 => Swap) private _swaps;

/**
* @dev See {ISwaplace-getSwap}.
*/
function getSwap(uint256 swapId) public view returns (Swap memory) {
return _swaps[swapId];
}

/**
* @dev Getter function for _totalSwaps.
*/
function totalSwaps() public view returns (uint256) {
return _totalSwaps;
}

/**
* @dev See {ISwaplace-createSwap}.
*/
function createSwap(Swap calldata swap) public returns (uint256) {
if (swap.owner != msg.sender) revert InvalidAddress(msg.sender);
function createSwap(Swap calldata swap) public payable returns (uint256) {
if (swap.owner != msg.sender) revert InvalidAddress();

assembly {
sstore(_totalSwaps.slot, add(sload(_totalSwaps.slot), 1))
}

uint256 swapId = _totalSwaps;

_swaps[swapId] = swap;

(address allowed, ) = parseData(swap.config);
(address allowed, , uint8 recipient, uint256 value) = decodeConfig(
swap.config
);

if (value > 0) {
if (recipient == 0) {
if (value * 1e12 != msg.value) revert InvalidValue();
} else if (msg.value > 0) revert InvalidValue();
}

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

Expand All @@ -44,43 +64,31 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 {
/**
* @dev See {ISwaplace-acceptSwap}.
*/
function acceptSwap(uint256 swapId, address receiver) public returns (bool) {
function acceptSwap(
uint256 swapId,
address receiver
) public payable returns (bool) {
Swap memory swap = _swaps[swapId];

(address allowed, uint256 expiry) = parseData(swap.config);

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

if (expiry < block.timestamp) revert InvalidExpiry(expiry);
(
address allowed,
uint32 expiry,
uint8 recipient,
uint256 value
) = decodeConfig(swap.config);

if (allowed != address(0) && allowed != msg.sender) revert InvalidAddress();
if (expiry < block.timestamp) revert InvalidExpiry();
_swaps[swapId].config = 0;

Asset[] memory assets = swap.asking;
_transferFrom(msg.sender, swap.owner, swap.asking);
_transferFrom(swap.owner, receiver, swap.biding);

for (uint256 i = 0; i < assets.length; ) {
ITransfer(assets[i].addr).transferFrom(
msg.sender,
swap.owner,
assets[i].amountOrId
);
assembly {
i := add(i, 1)
}
}

assets = swap.biding;

for (uint256 i = 0; i < assets.length; ) {
ITransfer(assets[i].addr).transferFrom(
swap.owner,
receiver,
assets[i].amountOrId
);
assembly {
i := add(i, 1)
}
}
if (value > 0)
if (recipient == 0) _payNativeEth(receiver, value * 1e12);
else if (recipient > 0 && value * 1e12 == msg.value)
_payNativeEth(swap.owner, value * 1e12);
else revert InvalidValue();

emit SwapAccepted(swapId, swap.owner, msg.sender);

Expand All @@ -91,22 +99,65 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 {
* @dev See {ISwaplace-cancelSwap}.
*/
function cancelSwap(uint256 swapId) public {
if (_swaps[swapId].owner != msg.sender) revert InvalidAddress(msg.sender);

(, uint256 expiry) = parseData(_swaps[swapId].config);
Swap memory swap = _swaps[swapId];
if (swap.owner != msg.sender) revert InvalidAddress();

if (expiry < block.timestamp) revert InvalidExpiry(expiry);
(, uint32 expiry, uint8 recipient, uint256 value) = decodeConfig(
swap.config
);

if (expiry < block.timestamp && (value == 0 || recipient > 0)) {
revert InvalidExpiry();
}
_swaps[swapId].config = 0;

if (value > 0 && recipient == 0) {
_payNativeEth(msg.sender, value * 1e12);
}

emit SwapCanceled(swapId, msg.sender);
}

/**
* @dev See {ISwaplace-getSwap}.
* @dev Send an amount of native Ether to the receiver address.
*/
function getSwap(uint256 swapId) public view returns (Swap memory) {
return _swaps[swapId];
function _payNativeEth(address receiver, uint256 value) internal {
(bool success, ) = receiver.call{value: value}("");
if (!success) revert InvalidValue();
}

/**
* @dev Transfer multiple 'assets' from 'from' to 'to'.
*
* `0x23b872dd` - Selector of the `transferFrom` function (ERC20, ERC721).
* `0xf242432a` - Selector of the `safeTransferFrom` function (ERC1155).
*/
function _transferFrom(
address from,
address to,
Asset[] memory assets
) internal {
for (uint256 i; i < assets.length; ) {
(uint16 assetType, uint256 tokenId, uint256 tokenAmount) = decodeAsset(
assets[i].amountOrId
);

if (assetType == type(uint16).max) {
(bool success, ) = address(assets[i].addr).call(
abi.encodeWithSelector(0xf242432a, from, to, tokenId, tokenAmount, "")
);
if (!success) revert InvalidCall();
} else {
(bool success, ) = address(assets[i].addr).call(
abi.encodeWithSelector(0x23b872dd, from, to, assets[i].amountOrId)
);
if (!success) revert InvalidCall();
}

assembly {
i := add(i, 1)
}
}
}

/**
Expand All @@ -119,11 +170,4 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 {
interfaceID == type(IERC165).interfaceId ||
interfaceID == type(ISwaplace).interfaceId;
}

/**
* @dev Getter function for _totalSwaps.
*/
function totalSwaps() public view returns (uint256) {
return _totalSwaps;
}
}
Loading
Loading