Skip to content

Commit

Permalink
Merge pull request #23 from niftyhorde/mpetrun5/allow-ether-as-swap-item
Browse files Browse the repository at this point in the history
Allow ether as swap item
  • Loading branch information
morrigan committed Aug 10, 2021
2 parents 2c984fc + 9ca4351 commit 4fa1b49
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 14 deletions.
62 changes: 52 additions & 10 deletions contracts/SwapKiwi.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,22 @@ import "@openzeppelin/contracts/access/Ownable.sol";
contract SwapKiwi is Ownable, IERC721Receiver {

uint256 private _swapsCounter;
uint256 private _etherLocked;

uint256 public fee;

mapping (address => uint256) private _balances;
mapping (uint256 => Swap) private _swaps;

struct Swap {
address initiator;
address payable initiator;
address[] initiatorNftAddresses;
uint256[] initiatorNftIds;
address secondUser;
uint256 initiatorEtherValue;
address payable secondUser;
address[] secondUserNftAddresses;
uint256[] secondUserNftIds;
uint256 secondUserEtherValue;
}

event SwapExecuted(address indexed from, address indexed to, uint256 indexed swapId);
Expand All @@ -28,14 +33,16 @@ contract SwapKiwi is Ownable, IERC721Receiver {
address indexed to,
uint256 indexed swapId,
address[] nftAddresses,
uint256[] nftIds
uint256[] nftIds,
uint256 etherValue
);
event SwapInitiated(
address indexed from,
address indexed to,
uint256 indexed swapId,
address[] nftAddresses,
uint256[] nftIds
uint256[] nftIds,
uint256 etherValue
);

modifier onlyInitiator(uint256 swapId) {
Expand All @@ -50,7 +57,7 @@ contract SwapKiwi is Ownable, IERC721Receiver {
}

modifier chargeAppFee() {
require(msg.value == fee, "SwapKiwi: Sent ETH amount needs to equal application fee");
require(msg.value >= fee, "SwapKiwi: Sent ETH amount needs to be more or equal application fee");
_;
}

Expand Down Expand Up @@ -84,12 +91,17 @@ contract SwapKiwi is Ownable, IERC721Receiver {
);

Swap storage swap = _swaps[_swapsCounter];
swap.initiator = msg.sender;
swap.initiator = payable(msg.sender);
swap.initiatorNftAddresses = nftAddresses;
swap.initiatorNftIds = nftIds;
swap.secondUser = secondUser;
if (msg.value > fee) {
swap.initiatorEtherValue = msg.value - fee;
_etherLocked += swap.initiatorEtherValue;
}
swap.secondUser = payable(secondUser);

emit SwapProposed(msg.sender, secondUser, _swapsCounter, nftAddresses, nftIds);

emit SwapProposed(msg.sender, secondUser, _swapsCounter, nftAddresses, nftIds, swap.initiatorEtherValue);
}

/**
Expand Down Expand Up @@ -118,8 +130,19 @@ contract SwapKiwi is Ownable, IERC721Receiver {

_swaps[swapId].secondUserNftAddresses = nftAddresses;
_swaps[swapId].secondUserNftIds = nftIds;
if (msg.value > fee) {
_swaps[swapId].secondUserEtherValue = msg.value - fee;
_etherLocked += _swaps[swapId].secondUserEtherValue;
}

emit SwapInitiated(msg.sender, _swaps[swapId].initiator, swapId, nftAddresses, nftIds);
emit SwapInitiated(
msg.sender,
_swaps[swapId].initiator,
swapId,
nftAddresses,
nftIds,
_swaps[swapId].secondUserEtherValue
);
}

/**
Expand Down Expand Up @@ -151,6 +174,15 @@ contract SwapKiwi is Ownable, IERC721Receiver {
_swaps[swapId].initiatorNftIds
);

if (_swaps[swapId].initiatorEtherValue != 0) {
_etherLocked -= _swaps[swapId].initiatorEtherValue;
_swaps[swapId].secondUser.transfer(_swaps[swapId].initiatorEtherValue);
}
if (_swaps[swapId].secondUserEtherValue != 0) {
_etherLocked -= _swaps[swapId].secondUserEtherValue;
_swaps[swapId].initiator.transfer(_swaps[swapId].secondUserEtherValue);
}

emit SwapExecuted(_swaps[swapId].initiator, _swaps[swapId].secondUser, swapId);

delete _swaps[swapId];
Expand Down Expand Up @@ -185,6 +217,15 @@ contract SwapKiwi is Ownable, IERC721Receiver {
);
}

if (_swaps[swapId].initiatorEtherValue != 0) {
_etherLocked -= _swaps[swapId].initiatorEtherValue;
_swaps[swapId].secondUser.transfer(_swaps[swapId].initiatorEtherValue);
}
if (_swaps[swapId].secondUserEtherValue != 0) {
_etherLocked -= _swaps[swapId].secondUserEtherValue;
_swaps[swapId].initiator.transfer(_swaps[swapId].secondUserEtherValue);
}


emit SwapCanceled(msg.sender, swapId);

Expand Down Expand Up @@ -215,9 +256,10 @@ contract SwapKiwi is Ownable, IERC721Receiver {
function withdrawEther(address payable recipient, uint256 amount) external onlyOwner {
require(recipient != address(0), "SwapKiwi: transfer to the zero address");
require(
address(this).balance >= amount,
address(this).balance - _etherLocked >= amount,
"SwapKiwi: insufficient ETH in contract"
);

recipient.transfer(amount);
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@niftyhorde/swap-kiwi-contracts",
"version": "0.3.1",
"version": "0.4.0-beta",
"private": false,
"repository": {
"url": "[email protected]:niftyhorde/swap.kiwi.git",
Expand Down
77 changes: 74 additions & 3 deletions test/swapKiwi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { TestNFT } from "../typechain/TestNFT";
import { Contract, Signer } from "ethers";
import { TransactionReceipt } from "@ethersproject/providers";
import chaiAsPromised from 'chai-as-promised';
import { parseEther } from "ethers/lib/utils";

use(chaiAsPromised);

Expand Down Expand Up @@ -46,10 +47,10 @@ describe("Escrow", async function () {
filter = swapKiwi.filters.SwapCanceled(null, null);
break;
case "SwapProposed":
filter = swapKiwi.filters.SwapProposed(null, null, null, null, null);
filter = swapKiwi.filters.SwapProposed(null, null, null, null, null, null);
break;
case "SwapInitiated":
filter = swapKiwi.filters.SwapInitiated(null, null, null, null, null);
filter = swapKiwi.filters.SwapInitiated(null, null, null, null, null, null);
default: null
}
return filter;
Expand Down Expand Up @@ -237,6 +238,42 @@ describe("Escrow", async function () {
expect(await appUserNFT.ownerOf(136)).to.be.deep.equal(otherAppUserAddress);
});

it('Should succesfully cancel swap with created with ether value', async function () {
const firstUserBalance = await appUser.signer.getBalance();
const secondUserBalance = await otherAppUser.signer.getBalance();

await appUserNFT.mint(appUserAddress, 430);
await appUserNFT.approve(swapKiwi.address, 430);
const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [430], {
value: VALID_APP_FEE.add(parseEther("50"))
});
const txReceipt = await tx.wait(1);
const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
const swapIdFromLogs = Number(logs.args.swapId.toString());

await otherAppUserNFT.mint(otherAppUserAddress, 431);
await otherAppUserNFT.approve(swapKiwi.address, 431);
const initiateSwapTx = await otherAppUser.initiateSwap(
swapIdFromLogs,
[otherAppUserNFT.address],
[431],
{
value: VALID_APP_FEE.add(parseEther("50"))
}
);
const initiateSwapTxReceipt = await initiateSwapTx.wait(1);
await getEventWithArgsFromLogs(initiateSwapTxReceipt, "SwapInitiated");

const cancelTx = await otherAppUser.cancelSwap(swapIdFromLogs);
const cancelTxReceipt = await cancelTx.wait(1);
await getEventWithArgsFromLogs(cancelTxReceipt, "SwapCanceled");

expect(await appUserNFT.ownerOf(430)).to.be.deep.equal(appUserAddress);
expect(await appUserNFT.ownerOf(431)).to.be.deep.equal(otherAppUserAddress);
expect(firstUserBalance.sub(await appUser.signer.getBalance()).lt(parseEther("1"))).to.be.equal(true);
expect(secondUserBalance.sub(await otherAppUser.signer.getBalance()).lt(parseEther("1"))).to.be.equal(true);
});

it('Should fail to initiate swap if swap canceled', async function () {
await appUserNFT.mint(appUserAddress, 170);
await appUserNFT.approve(swapKiwi.address, 170);
Expand Down Expand Up @@ -384,14 +421,48 @@ describe("Escrow", async function () {
expect(await appUserNFT.ownerOf(88)).to.be.deep.equal(appUserAddress);
});

it('Should successfully execute swap with ether', async function () {
const firstUserBalance = await appUser.signer.getBalance();
const secondUserBalance = await otherAppUser.signer.getBalance();

await appUserNFT.mint(appUserAddress, 375);
await appUserNFT.approve(swapKiwi.address, 375);
const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [375], {
value: VALID_APP_FEE.add(parseEther("50"))
});
const txReceipt = await tx.wait(1);
const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
const swapIdFromLogs = Number(logs.args.swapId.toString());

await otherAppUserNFT.mint(otherAppUserAddress, 376);
await otherAppUserNFT.approve(swapKiwi.address, 376);
const initiateSwapTx = await otherAppUser.initiateSwap(
swapIdFromLogs,
[otherAppUserNFT.address],
[376],
{
value: VALID_APP_FEE.add(parseEther("25"))
}
);
await initiateSwapTx.wait(1);

const acceptSwapTx = await appUser.acceptSwap(swapIdFromLogs);
await acceptSwapTx.wait(1);

expect(await appUserNFT.ownerOf(375)).to.be.deep.equal(otherAppUserAddress);
expect(await otherAppUserNFT.ownerOf(376)).to.be.deep.equal(appUserAddress);
expect(firstUserBalance.sub((await appUser.signer.getBalance()).add(parseEther("25"))).lt(parseEther("1"))).to.be.equal(true);
expect(secondUserBalance.sub((await otherAppUser.signer.getBalance()).sub(parseEther("25"))).lt(parseEther("1"))).to.be.equal(true);
});

it("Should successful withdraw collected fees from SwapKiwi if called by owner", async function () {
await swapKiwi.withdrawEther(appUserNFT.address, ethers.utils.parseEther("0.1"));

expect((await ethers.provider.getBalance(appUserNFT.address)).toString())
.to.be.deep.equal(ethers.utils.parseEther("0.1").toString());
});

it("Should fail to withdraw collected fees from SwapKiwi if called not owner", async function () {
it("Should fail to withdraw collected fees from SwapKiwi if not owner", async function () {
await expect(appUser.withdrawEther(appUser.address, ethers.utils.parseEther("1.0")))
.to.be.rejectedWith(
"VM Exception while processing transaction: revert Ownable: caller is not the owner");
Expand Down

0 comments on commit 4fa1b49

Please sign in to comment.