feat: adds an optional `payoutAddress` to allow payouts to be paid to separate address (#144)
* initial commit for splitting payouts Collateral goes to slot's host address, while reward payouts go to the slot's host payoutAddress * Add fillSlot overload to make payoutAddress "optional" * add tests for payoutAddress * add doc to patchFillSlotOverloads * formatting * remove optional payoutAddress parameter * Move payoutAddress to freeSlot - remove payoutAddress parameter from `fillSlot` - remove `payoutAddress` from slot struct and storage - add payoutAddress parameter to `freeSlot`, preventing the need for storage * formatting * update certora spec to match updated function signature * Add withdrawAddress to withdrawFunds - prevent erc20 msg.sender blacklisting * Update tests for paying out to withdrawAddress * formatting * Add collateralRecipient * refactor: change withdrawFunds and freeSlot overloads - `withdrawFunds` now has an option withdrawRecipient parameter - `freeSlot` now has two optional parameters: rewardRecipient, and collateralRecipient. Both or none must be specified. * update certora spec for new sigs
This commit is contained in:
parent
29f39d52c7
commit
73a2ca0bd3
|
@ -111,12 +111,12 @@ function canStartRequest(method f) returns bool {
|
|||
}
|
||||
|
||||
function canFinishRequest(method f) returns bool {
|
||||
return f.selector == sig:freeSlot(Marketplace.SlotId).selector;
|
||||
return f.selector == sig:freeSlot(Marketplace.SlotId, address, address).selector;
|
||||
}
|
||||
|
||||
function canFailRequest(method f) returns bool {
|
||||
return f.selector == sig:markProofAsMissing(Marketplace.SlotId, Periods.Period).selector ||
|
||||
f.selector == sig:freeSlot(Marketplace.SlotId).selector;
|
||||
f.selector == sig:freeSlot(Marketplace.SlotId, address, address).selector;
|
||||
}
|
||||
|
||||
/*--------------------------------------------
|
||||
|
|
|
@ -46,7 +46,7 @@ contract Marketplace is Proofs, StateRetrieval, Endian {
|
|||
/// @dev When Slot is filled, the collateral is collected in amount of request.ask.collateral
|
||||
/// @dev When Host is slashed for missing a proof the slashed amount is reflected in this variable
|
||||
uint256 currentCollateral;
|
||||
address host;
|
||||
address host; // address used for collateral interactions and identifying hosts
|
||||
}
|
||||
|
||||
struct ActiveSlot {
|
||||
|
@ -114,6 +114,14 @@ contract Marketplace is Proofs, StateRetrieval, Endian {
|
|||
emit StorageRequested(id, request.ask, _requestContexts[id].expiresAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Fills a slot. Reverts if an invalid proof of the slot data is
|
||||
provided.
|
||||
* @param requestId RequestId identifying the request containing the slot to
|
||||
fill.
|
||||
* @param slotIndex Index of the slot in the request.
|
||||
* @param proof Groth16 proof procing possession of the slot data.
|
||||
*/
|
||||
function fillSlot(
|
||||
RequestId requestId,
|
||||
uint256 slotIndex,
|
||||
|
@ -158,19 +166,48 @@ contract Marketplace is Proofs, StateRetrieval, Endian {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Frees a slot, paying out rewards and returning collateral for
|
||||
finished or cancelled requests to the host that has filled the slot.
|
||||
* @param slotId id of the slot to free
|
||||
* @dev The host that filled the slot must have initiated the transaction
|
||||
(msg.sender). This overload allows `rewardRecipient` and
|
||||
`collateralRecipient` to be optional.
|
||||
*/
|
||||
function freeSlot(SlotId slotId) public slotIsNotFree(slotId) {
|
||||
return freeSlot(slotId, msg.sender, msg.sender);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Frees a slot, paying out rewards and returning collateral for
|
||||
finished or cancelled requests.
|
||||
* @param slotId id of the slot to free
|
||||
* @param rewardRecipient address to send rewards to
|
||||
* @param collateralRecipient address to refund collateral to
|
||||
*/
|
||||
function freeSlot(
|
||||
SlotId slotId,
|
||||
address rewardRecipient,
|
||||
address collateralRecipient
|
||||
) public slotIsNotFree(slotId) {
|
||||
Slot storage slot = _slots[slotId];
|
||||
require(slot.host == msg.sender, "Slot filled by other host");
|
||||
SlotState state = slotState(slotId);
|
||||
require(state != SlotState.Paid, "Already paid");
|
||||
|
||||
if (state == SlotState.Finished) {
|
||||
_payoutSlot(slot.requestId, slotId);
|
||||
_payoutSlot(slot.requestId, slotId, rewardRecipient, collateralRecipient);
|
||||
} else if (state == SlotState.Cancelled) {
|
||||
_payoutCancelledSlot(slot.requestId, slotId);
|
||||
_payoutCancelledSlot(
|
||||
slot.requestId,
|
||||
slotId,
|
||||
rewardRecipient,
|
||||
collateralRecipient
|
||||
);
|
||||
} else if (state == SlotState.Failed) {
|
||||
_removeFromMySlots(msg.sender, slotId);
|
||||
} else if (state == SlotState.Filled) {
|
||||
// free slot without returning collateral, effectively a 100% slash
|
||||
_forciblyFreeSlot(slotId);
|
||||
}
|
||||
}
|
||||
|
@ -231,6 +268,13 @@ contract Marketplace is Proofs, StateRetrieval, Endian {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Abandons the slot without returning collateral, effectively slashing the
|
||||
entire collateral.
|
||||
* @param slotId SlotId of the slot to free.
|
||||
* @dev _slots[slotId] is deleted, resetting _slots[slotId].currentCollateral
|
||||
to 0.
|
||||
*/
|
||||
function _forciblyFreeSlot(SlotId slotId) internal {
|
||||
Slot storage slot = _slots[slotId];
|
||||
RequestId requestId = slot.requestId;
|
||||
|
@ -260,7 +304,9 @@ contract Marketplace is Proofs, StateRetrieval, Endian {
|
|||
|
||||
function _payoutSlot(
|
||||
RequestId requestId,
|
||||
SlotId slotId
|
||||
SlotId slotId,
|
||||
address rewardRecipient,
|
||||
address collateralRecipient
|
||||
) private requestIsKnown(requestId) {
|
||||
RequestContext storage context = _requestContexts[requestId];
|
||||
Request storage request = _requests[requestId];
|
||||
|
@ -270,31 +316,62 @@ contract Marketplace is Proofs, StateRetrieval, Endian {
|
|||
|
||||
_removeFromMySlots(slot.host, slotId);
|
||||
|
||||
uint256 amount = _requests[requestId].pricePerSlot() +
|
||||
slot.currentCollateral;
|
||||
_marketplaceTotals.sent += amount;
|
||||
uint256 payoutAmount = _requests[requestId].pricePerSlot();
|
||||
uint256 collateralAmount = slot.currentCollateral;
|
||||
_marketplaceTotals.sent += (payoutAmount + collateralAmount);
|
||||
slot.state = SlotState.Paid;
|
||||
assert(_token.transfer(slot.host, amount));
|
||||
assert(_token.transfer(rewardRecipient, payoutAmount));
|
||||
assert(_token.transfer(collateralRecipient, collateralAmount));
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Pays out a host for duration of time that the slot was filled, and
|
||||
returns the collateral.
|
||||
* @dev The payouts are sent to the rewardRecipient, and collateral is returned
|
||||
to the host address.
|
||||
* @param requestId RequestId of the request that contains the slot to be paid
|
||||
out.
|
||||
* @param slotId SlotId of the slot to be paid out.
|
||||
*/
|
||||
function _payoutCancelledSlot(
|
||||
RequestId requestId,
|
||||
SlotId slotId
|
||||
SlotId slotId,
|
||||
address rewardRecipient,
|
||||
address collateralRecipient
|
||||
) private requestIsKnown(requestId) {
|
||||
Slot storage slot = _slots[slotId];
|
||||
_removeFromMySlots(slot.host, slotId);
|
||||
|
||||
uint256 amount = _expiryPayoutAmount(requestId, slot.filledAt) +
|
||||
slot.currentCollateral;
|
||||
_marketplaceTotals.sent += amount;
|
||||
uint256 payoutAmount = _expiryPayoutAmount(requestId, slot.filledAt);
|
||||
uint256 collateralAmount = slot.currentCollateral;
|
||||
_marketplaceTotals.sent += (payoutAmount + collateralAmount);
|
||||
slot.state = SlotState.Paid;
|
||||
assert(_token.transfer(slot.host, amount));
|
||||
assert(_token.transfer(rewardRecipient, payoutAmount));
|
||||
assert(_token.transfer(collateralRecipient, collateralAmount));
|
||||
}
|
||||
|
||||
/// @notice Withdraws storage request funds back to the client that deposited them.
|
||||
/// @dev Request must be expired, must be in RequestState.New, and the transaction must originate from the depositer address.
|
||||
/// @param requestId the id of the request
|
||||
/**
|
||||
* @notice Withdraws remaining storage request funds back to the client that
|
||||
deposited them.
|
||||
* @dev Request must be expired, must be in RequestStat e.New, and the
|
||||
transaction must originate from the depositer address.
|
||||
* @param requestId the id of the request
|
||||
*/
|
||||
function withdrawFunds(RequestId requestId) public {
|
||||
withdrawFunds(requestId, msg.sender);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Withdraws storage request funds to the provided address.
|
||||
* @dev Request must be expired, must be in RequestState.New, and the
|
||||
transaction must originate from the depositer address.
|
||||
* @param requestId the id of the request
|
||||
* @param withdrawRecipient address to return the remaining funds to
|
||||
*/
|
||||
function withdrawFunds(
|
||||
RequestId requestId,
|
||||
address withdrawRecipient
|
||||
) public {
|
||||
Request storage request = _requests[requestId];
|
||||
require(
|
||||
block.timestamp > requestExpiry(requestId),
|
||||
|
@ -313,7 +390,7 @@ contract Marketplace is Proofs, StateRetrieval, Endian {
|
|||
|
||||
uint256 amount = context.expiryFundsWithdraw;
|
||||
_marketplaceTotals.sent += amount;
|
||||
assert(_token.transfer(msg.sender, amount));
|
||||
assert(_token.transfer(withdrawRecipient, amount));
|
||||
}
|
||||
|
||||
function getActiveSlot(
|
||||
|
@ -356,7 +433,14 @@ contract Marketplace is Proofs, StateRetrieval, Endian {
|
|||
return _requestContexts[requestId].expiresAt;
|
||||
}
|
||||
|
||||
/// @notice Calculates the amount that should be payed out to a host if a request expires based on when the host fills the slot
|
||||
/**
|
||||
* @notice Calculates the amount that should be paid out to a host if a request
|
||||
* expires based on when the host fills the slot
|
||||
* @param requestId RequestId of the request used to calculate the payout
|
||||
* amount.
|
||||
* @param startingTimestamp timestamp indicating when a host filled a slot and
|
||||
* started providing proofs.
|
||||
*/
|
||||
function _expiryPayoutAmount(
|
||||
RequestId requestId,
|
||||
uint256 startingTimestamp
|
||||
|
|
|
@ -21,6 +21,7 @@ const {
|
|||
waitUntilFinished,
|
||||
waitUntilFailed,
|
||||
waitUntilSlotFailed,
|
||||
patchOverloads,
|
||||
} = require("./marketplace")
|
||||
const { price, pricePerSlot } = require("./price")
|
||||
const {
|
||||
|
@ -87,7 +88,14 @@ describe("Marketplace", function () {
|
|||
let marketplace
|
||||
let token
|
||||
let verifier
|
||||
let client, host, host1, host2, host3
|
||||
let client,
|
||||
clientWithdrawRecipient,
|
||||
host,
|
||||
host1,
|
||||
host2,
|
||||
host3,
|
||||
hostRewardRecipient,
|
||||
hostCollateralRecipient
|
||||
let request
|
||||
let slot
|
||||
|
||||
|
@ -96,12 +104,28 @@ describe("Marketplace", function () {
|
|||
beforeEach(async function () {
|
||||
await snapshot()
|
||||
await ensureMinimumBlockHeight(256)
|
||||
;[client, host1, host2, host3] = await ethers.getSigners()
|
||||
;[
|
||||
client,
|
||||
clientWithdrawRecipient,
|
||||
host1,
|
||||
host2,
|
||||
host3,
|
||||
hostRewardRecipient,
|
||||
hostCollateralRecipient,
|
||||
] = await ethers.getSigners()
|
||||
host = host1
|
||||
|
||||
const TestToken = await ethers.getContractFactory("TestToken")
|
||||
token = await TestToken.deploy()
|
||||
for (let account of [client, host1, host2, host3]) {
|
||||
for (let account of [
|
||||
client,
|
||||
clientWithdrawRecipient,
|
||||
host1,
|
||||
host2,
|
||||
host3,
|
||||
hostRewardRecipient,
|
||||
hostCollateralRecipient,
|
||||
]) {
|
||||
await token.mint(account.address, ACCOUNT_STARTING_BALANCE)
|
||||
}
|
||||
|
||||
|
@ -114,6 +138,7 @@ describe("Marketplace", function () {
|
|||
token.address,
|
||||
verifier.address
|
||||
)
|
||||
patchOverloads(marketplace)
|
||||
|
||||
request = await exampleRequest()
|
||||
request.client = client.address
|
||||
|
@ -131,6 +156,7 @@ describe("Marketplace", function () {
|
|||
function switchAccount(account) {
|
||||
token = token.connect(account)
|
||||
marketplace = marketplace.connect(account)
|
||||
patchOverloads(marketplace)
|
||||
}
|
||||
|
||||
describe("requesting storage", function () {
|
||||
|
@ -481,6 +507,37 @@ describe("Marketplace", function () {
|
|||
)
|
||||
})
|
||||
|
||||
it("pays to host reward address when contract has finished and returns collateral to host collateral address", async function () {
|
||||
await waitUntilStarted(marketplace, request, proof, token)
|
||||
await waitUntilFinished(marketplace, requestId(request))
|
||||
const startBalanceHost = await token.balanceOf(host.address)
|
||||
const startBalanceReward = await token.balanceOf(
|
||||
hostRewardRecipient.address
|
||||
)
|
||||
const startBalanceCollateral = await token.balanceOf(
|
||||
hostCollateralRecipient.address
|
||||
)
|
||||
await marketplace.freeSlot(
|
||||
slotId(slot),
|
||||
hostRewardRecipient.address,
|
||||
hostCollateralRecipient.address
|
||||
)
|
||||
const endBalanceHost = await token.balanceOf(host.address)
|
||||
const endBalanceReward = await token.balanceOf(
|
||||
hostRewardRecipient.address
|
||||
)
|
||||
const endBalanceCollateral = await token.balanceOf(
|
||||
hostCollateralRecipient.address
|
||||
)
|
||||
expect(endBalanceHost).to.equal(startBalanceHost)
|
||||
expect(endBalanceCollateral - startBalanceCollateral).to.equal(
|
||||
request.ask.collateral
|
||||
)
|
||||
expect(endBalanceReward - startBalanceReward).to.equal(
|
||||
pricePerSlot(request)
|
||||
)
|
||||
})
|
||||
|
||||
it("pays the host when contract was cancelled", async function () {
|
||||
// Lets advance the time more into the expiry window
|
||||
const filledAt = (await currentTime()) + Math.floor(request.expiry / 3)
|
||||
|
@ -500,12 +557,69 @@ describe("Marketplace", function () {
|
|||
)
|
||||
})
|
||||
|
||||
it("pays to host reward address when contract was cancelled, and returns collateral to host address", async function () {
|
||||
// Lets advance the time more into the expiry window
|
||||
const filledAt = (await currentTime()) + Math.floor(request.expiry / 3)
|
||||
const expiresAt = (
|
||||
await marketplace.requestExpiry(requestId(request))
|
||||
).toNumber()
|
||||
|
||||
await advanceTimeToForNextBlock(filledAt)
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await waitUntilCancelled(request)
|
||||
const startBalanceHost = await token.balanceOf(host.address)
|
||||
const startBalanceReward = await token.balanceOf(
|
||||
hostRewardRecipient.address
|
||||
)
|
||||
const startBalanceCollateral = await token.balanceOf(
|
||||
hostCollateralRecipient.address
|
||||
)
|
||||
await marketplace.freeSlot(
|
||||
slotId(slot),
|
||||
hostRewardRecipient.address,
|
||||
hostCollateralRecipient.address
|
||||
)
|
||||
|
||||
const expectedPartialPayout = (expiresAt - filledAt) * request.ask.reward
|
||||
|
||||
const endBalanceReward = await token.balanceOf(
|
||||
hostRewardRecipient.address
|
||||
)
|
||||
expect(endBalanceReward - startBalanceReward).to.be.equal(
|
||||
expectedPartialPayout
|
||||
)
|
||||
|
||||
const endBalanceHost = await token.balanceOf(host.address)
|
||||
expect(endBalanceHost).to.be.equal(startBalanceHost)
|
||||
|
||||
const endBalanceCollateral = await token.balanceOf(
|
||||
hostCollateralRecipient.address
|
||||
)
|
||||
expect(endBalanceCollateral - startBalanceCollateral).to.be.equal(
|
||||
request.ask.collateral
|
||||
)
|
||||
})
|
||||
|
||||
it("does not pay when the contract hasn't ended", async function () {
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
const startBalance = await token.balanceOf(host.address)
|
||||
const startBalanceHost = await token.balanceOf(host.address)
|
||||
const startBalanceReward = await token.balanceOf(
|
||||
hostRewardRecipient.address
|
||||
)
|
||||
const startBalanceCollateral = await token.balanceOf(
|
||||
hostCollateralRecipient.address
|
||||
)
|
||||
await marketplace.freeSlot(slotId(slot))
|
||||
const endBalance = await token.balanceOf(host.address)
|
||||
expect(endBalance).to.equal(startBalance)
|
||||
const endBalanceHost = await token.balanceOf(host.address)
|
||||
const endBalanceReward = await token.balanceOf(
|
||||
hostRewardRecipient.address
|
||||
)
|
||||
const endBalanceCollateral = await token.balanceOf(
|
||||
hostCollateralRecipient.address
|
||||
)
|
||||
expect(endBalanceHost).to.equal(startBalanceHost)
|
||||
expect(endBalanceReward).to.equal(startBalanceReward)
|
||||
expect(endBalanceCollateral).to.equal(startBalanceCollateral)
|
||||
})
|
||||
|
||||
it("can only be done once", async function () {
|
||||
|
@ -586,16 +700,16 @@ describe("Marketplace", function () {
|
|||
|
||||
it("rejects withdraw when request not yet timed out", async function () {
|
||||
switchAccount(client)
|
||||
await expect(marketplace.withdrawFunds(slot.request)).to.be.revertedWith(
|
||||
"Request not yet timed out"
|
||||
)
|
||||
await expect(
|
||||
marketplace.withdrawFunds(slot.request, clientWithdrawRecipient.address)
|
||||
).to.be.revertedWith("Request not yet timed out")
|
||||
})
|
||||
|
||||
it("rejects withdraw when wrong account used", async function () {
|
||||
await waitUntilCancelled(request)
|
||||
await expect(marketplace.withdrawFunds(slot.request)).to.be.revertedWith(
|
||||
"Invalid client address"
|
||||
)
|
||||
await expect(
|
||||
marketplace.withdrawFunds(slot.request, clientWithdrawRecipient.address)
|
||||
).to.be.revertedWith("Invalid client address")
|
||||
})
|
||||
|
||||
it("rejects withdraw when in wrong state", async function () {
|
||||
|
@ -610,29 +724,41 @@ describe("Marketplace", function () {
|
|||
}
|
||||
await waitUntilCancelled(request)
|
||||
switchAccount(client)
|
||||
await expect(marketplace.withdrawFunds(slot.request)).to.be.revertedWith(
|
||||
"Invalid state"
|
||||
)
|
||||
await expect(
|
||||
marketplace.withdrawFunds(slot.request, clientWithdrawRecipient.address)
|
||||
).to.be.revertedWith("Invalid state")
|
||||
})
|
||||
|
||||
it("emits event once request is cancelled", async function () {
|
||||
await waitUntilCancelled(request)
|
||||
switchAccount(client)
|
||||
await expect(marketplace.withdrawFunds(slot.request))
|
||||
await expect(
|
||||
marketplace.withdrawFunds(slot.request, clientWithdrawRecipient.address)
|
||||
)
|
||||
.to.emit(marketplace, "RequestCancelled")
|
||||
.withArgs(requestId(request))
|
||||
})
|
||||
|
||||
it("withdraws to the client", async function () {
|
||||
it("withdraws to the client payout address", async function () {
|
||||
await waitUntilCancelled(request)
|
||||
switchAccount(client)
|
||||
const startBalance = await token.balanceOf(client.address)
|
||||
await marketplace.withdrawFunds(slot.request)
|
||||
const endBalance = await token.balanceOf(client.address)
|
||||
expect(endBalance - startBalance).to.equal(price(request))
|
||||
const startBalanceClient = await token.balanceOf(client.address)
|
||||
const startBalancePayout = await token.balanceOf(
|
||||
clientWithdrawRecipient.address
|
||||
)
|
||||
await marketplace.withdrawFunds(
|
||||
slot.request,
|
||||
clientWithdrawRecipient.address
|
||||
)
|
||||
const endBalanceClient = await token.balanceOf(client.address)
|
||||
const endBalancePayout = await token.balanceOf(
|
||||
clientWithdrawRecipient.address
|
||||
)
|
||||
expect(endBalanceClient).to.equal(startBalanceClient)
|
||||
expect(endBalancePayout - startBalancePayout).to.equal(price(request))
|
||||
})
|
||||
|
||||
it("withdraws to the client for cancelled requests lowered by hosts payout", async function () {
|
||||
it("withdraws to the client payout address for cancelled requests lowered by hosts payout", async function () {
|
||||
// Lets advance the time more into the expiry window
|
||||
const filledAt = (await currentTime()) + Math.floor(request.expiry / 3)
|
||||
const expiresAt = (
|
||||
|
@ -642,14 +768,17 @@ describe("Marketplace", function () {
|
|||
await advanceTimeToForNextBlock(filledAt)
|
||||
await marketplace.fillSlot(slot.request, slot.index, proof)
|
||||
await waitUntilCancelled(request)
|
||||
const expectedPartialHostPayout =
|
||||
const expectedPartialhostRewardRecipient =
|
||||
(expiresAt - filledAt) * request.ask.reward
|
||||
|
||||
switchAccount(client)
|
||||
await marketplace.withdrawFunds(slot.request)
|
||||
const endBalance = await token.balanceOf(client.address)
|
||||
expect(ACCOUNT_STARTING_BALANCE - endBalance).to.equal(
|
||||
expectedPartialHostPayout
|
||||
await marketplace.withdrawFunds(
|
||||
slot.request,
|
||||
clientWithdrawRecipient.address
|
||||
)
|
||||
const endBalance = await token.balanceOf(clientWithdrawRecipient.address)
|
||||
expect(endBalance - ACCOUNT_STARTING_BALANCE).to.equal(
|
||||
price(request) - expectedPartialhostRewardRecipient
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -678,7 +807,10 @@ describe("Marketplace", function () {
|
|||
it("remains 'Cancelled' when client withdraws funds", async function () {
|
||||
await waitUntilCancelled(request)
|
||||
switchAccount(client)
|
||||
await marketplace.withdrawFunds(slot.request)
|
||||
await marketplace.withdrawFunds(
|
||||
slot.request,
|
||||
clientWithdrawRecipient.address
|
||||
)
|
||||
expect(await marketplace.requestState(slot.request)).to.equal(Cancelled)
|
||||
})
|
||||
|
||||
|
@ -1031,7 +1163,10 @@ describe("Marketplace", function () {
|
|||
it("removes request from list when funds are withdrawn", async function () {
|
||||
await marketplace.requestStorage(request)
|
||||
await waitUntilCancelled(request)
|
||||
await marketplace.withdrawFunds(requestId(request))
|
||||
await marketplace.withdrawFunds(
|
||||
requestId(request),
|
||||
clientWithdrawRecipient.address
|
||||
)
|
||||
expect(await marketplace.myRequests()).to.deep.equal([])
|
||||
})
|
||||
|
||||
|
|
|
@ -49,10 +49,41 @@ async function waitUntilSlotFailed(contract, request, slot) {
|
|||
}
|
||||
}
|
||||
|
||||
function patchOverloads(contract) {
|
||||
contract.freeSlot = async (slotId, rewardRecipient, collateralRecipient) => {
|
||||
const logicalXor = (a, b) => (a || b) && !(a && b)
|
||||
if (logicalXor(rewardRecipient, collateralRecipient)) {
|
||||
// XOR, if exactly one is truthy
|
||||
throw new Error(
|
||||
"Invalid freeSlot overload, you must specify both `rewardRecipient` and `collateralRecipient` or neither."
|
||||
)
|
||||
}
|
||||
|
||||
if (!rewardRecipient && !collateralRecipient) {
|
||||
// calls `freeSlot` overload without `rewardRecipient` and `collateralRecipient`
|
||||
const fn = contract["freeSlot(bytes32)"]
|
||||
return await fn(slotId)
|
||||
}
|
||||
|
||||
const fn = contract["freeSlot(bytes32,address,address)"]
|
||||
return await fn(slotId, rewardRecipient, collateralRecipient)
|
||||
}
|
||||
contract.withdrawFunds = async (requestId, withdrawRecipient) => {
|
||||
if (!withdrawRecipient) {
|
||||
// calls `withdrawFunds` overload without `withdrawRecipient`
|
||||
const fn = contract["withdrawFunds(bytes32)"]
|
||||
return await fn(requestId)
|
||||
}
|
||||
const fn = contract["withdrawFunds(bytes32,address)"]
|
||||
return await fn(requestId, withdrawRecipient)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
waitUntilCancelled,
|
||||
waitUntilStarted,
|
||||
waitUntilFinished,
|
||||
waitUntilFailed,
|
||||
waitUntilSlotFailed,
|
||||
patchOverloads,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue