Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions contracts/interfaces/IWIP.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

/// @title IWIP Interface
interface IWIP {
function withdraw(uint256 amount) external;
}
42 changes: 38 additions & 4 deletions contracts/modules/dispute/policies/UMA/ArbitrationPolicyUMA.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";

import { IDisputeModule } from "../../../../interfaces/modules/dispute/IDisputeModule.sol";
import { IRoyaltyModule } from "../../../../interfaces/modules/royalty/IRoyaltyModule.sol";
import { IArbitrationPolicyUMA } from "../../../../interfaces/modules/dispute/policies/UMA/IArbitrationPolicyUMA.sol";
import { IOOV3 } from "../../../../interfaces/modules/dispute/policies/UMA/IOOV3.sol";
import { IWIP } from "../../../../interfaces/IWIP.sol";
import { ProtocolPausableUpgradeable } from "../../../../pause/ProtocolPausableUpgradeable.sol";
import { BytesConversion } from "../../../../lib/BytesConversion.sol";
import { Errors } from "../../../../lib/Errors.sol";
Expand Down Expand Up @@ -43,6 +45,7 @@ contract ArbitrationPolicyUMA is
/// @param assertionIdToDisputeId The mapping of assertion id to dispute id
/// @param counterEvidenceHashes The mapping of assertion id to counter evidence hash
/// @param ipOwnerTimePercents The mapping of dispute id to ip owner time percent of the dispute
/// @param disputers The mapping of dispute id to disputer
/// @custom:storage-location erc7201:story-protocol.ArbitrationPolicyUMA
struct ArbitrationPolicyUMAStorage {
uint64 minLiveness;
Expand All @@ -54,12 +57,15 @@ contract ArbitrationPolicyUMA is
mapping(bytes32 assertionId => uint256 disputeId) assertionIdToDisputeId;
mapping(bytes32 assertionId => bytes32 counterEvidenceHash) counterEvidenceHashes;
mapping(uint256 disputeId => uint32 ipOwnerTimePercent) ipOwnerTimePercents;
mapping(uint256 disputeId => address disputer) disputers;
}

// keccak256(abi.encode(uint256(keccak256("story-protocol.ArbitrationPolicyUMA")) - 1)) & ~bytes32(uint256(0xff));
bytes32 private constant ArbitrationPolicyUMAStorageLocation =
0xbd39630b628d883a3167c4982acf741cbddb24bae6947600210f8eb1db515300;

address public constant WIP = 0x1514000000000000000000000000000000000000;

/// @dev Restricts the calls to the dispute module
modifier onlyDisputeModule() {
if (msg.sender != address(DISPUTE_MODULE)) revert Errors.ArbitrationPolicyUMA__NotDisputeModule();
Expand Down Expand Up @@ -165,7 +171,7 @@ contract ArbitrationPolicyUMA is

bytes32 assertionId = oov3.assertTruth(
_constructClaim(targetIpId, targetTag, disputeEvidenceHash, disputeId),
disputeInitiator, // asserter
address(this), // asserter
address(this), // callbackRecipient
address(0), // escalationManager
liveness,
Expand Down Expand Up @@ -236,27 +242,53 @@ contract ArbitrationPolicyUMA is
);

$.counterEvidenceHashes[assertionId] = counterEvidenceHash;
$.disputers[disputeId] = msg.sender;

IERC20 currencyToken = IERC20(assertion.currency);
IOOV3 oov3 = $.oov3;
currencyToken.safeTransferFrom(msg.sender, address(this), assertion.bond);
currencyToken.safeIncreaseAllowance(address(oov3), assertion.bond);

oov3.disputeAssertion(assertionId, msg.sender);
oov3.disputeAssertion(assertionId, address(this));

emit AssertionDisputed(disputeId, assertionId, counterEvidenceHash);
}

/// @notice OOV3 callback function forwhen an assertion is resolved
/// @notice OOV3 callback function for when an assertion is resolved
/// @param assertionId The resolved assertion identifier
/// @param assertedTruthfully Indicates if the assertion was resolved as truthful or not
function assertionResolvedCallback(bytes32 assertionId, bool assertedTruthfully) external nonReentrant {
ArbitrationPolicyUMAStorage storage $ = _getArbitrationPolicyUMAStorage();
if (msg.sender != address($.oov3)) revert Errors.ArbitrationPolicyUMA__NotOOV3();
IOOV3 oov3 = $.oov3;
if (msg.sender != address(oov3)) revert Errors.ArbitrationPolicyUMA__NotOOV3();

uint256 disputeId = $.assertionIdToDisputeId[assertionId];
(, address disputeInitiator, , , , , , ) = DISPUTE_MODULE.disputes(disputeId);
IOOV3.Assertion memory assertion = oov3.getAssertion(assertionId);

// determine the amount to transfer and the recipient of the transfer
uint256 amountToTransfer;
address bondRecipient;
if (assertion.disputer == address(0)) {
// if the assertion was not disputed, the bond is transferred to the dispute initiator
amountToTransfer = assertion.bond;
bondRecipient = disputeInitiator;
} else {
uint256 oracleFee = (oov3.burnedBondPercentage() * assertion.bond) / 1e18;
amountToTransfer = assertion.bond * 2 - oracleFee;
bondRecipient = assertedTruthfully ? disputeInitiator : $.disputers[disputeId];
}

// set the dispute judgement in the dispute module
DISPUTE_MODULE.setDisputeJudgement(disputeId, assertedTruthfully, "");

// transfer the amount to the recipient
if (address(assertion.currency) == WIP && bondRecipient.code.length == 0) {
IWIP(WIP).withdraw(amountToTransfer);
Address.sendValue(payable(bondRecipient), amountToTransfer);
} else {
IERC20(assertion.currency).safeTransfer(bondRecipient, amountToTransfer);
}
}

/// @notice OOV3 callback function for when an assertion is disputed
Expand Down Expand Up @@ -311,6 +343,8 @@ contract ArbitrationPolicyUMA is
return _getArbitrationPolicyUMAStorage().assertionIdToDisputeId[assertionId];
}

receive() external payable {}

/// @notice Constructs the claim for a given dispute
/// @param targetIpId The ipId that is the target of the dispute
/// @param disputeEvidenceHash The hash pointing to the dispute evidence
Expand Down
4 changes: 2 additions & 2 deletions script/foundry/utils/DeployHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -582,14 +582,14 @@ contract DeployHelper is Script, BroadcastManager, JsonDeploymentHandler, Storag

_predeploy("ArbitrationPolicyUMA");
impl = address(new ArbitrationPolicyUMA(address(disputeModule), address(royaltyModule)));
arbitrationPolicyUMA = ArbitrationPolicyUMA(
arbitrationPolicyUMA = ArbitrationPolicyUMA(payable(
TestProxyHelper.deployUUPSProxy(
create3Deployer,
_getSalt(type(ArbitrationPolicyUMA).name),
impl,
abi.encodeCall(ArbitrationPolicyUMA.initialize, address(protocolAccessManager))
)
);
));
require(
_getDeployedAddress(type(ArbitrationPolicyUMA).name) == address(arbitrationPolicyUMA),
"Deploy: Arbitration Policy Address Mismatch"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ contract ArbitrationPolicyUMATest is BaseTest {
wip = 0x1514000000000000000000000000000000000000; // WIP address
disputeModule = DisputeModule(0x9b7A9c70AFF961C799110954fc06F3093aeb94C5);
royaltyModule = RoyaltyModule(0xD2f60c40fEbccf6311f8B47c4f2Ec6b040400086);
arbitrationPolicyUMA = ArbitrationPolicyUMA(0xfFD98c3877B8789124f02C7E8239A4b0Ef11E936);
arbitrationPolicyUMA = ArbitrationPolicyUMA(payable(0xfFD98c3877B8789124f02C7E8239A4b0Ef11E936));
protocolAdmin = 0x623Cb5A594dAD5cc1Ea1bDb0b084bf8F1fE4B2e4;
protocolPauseAdmin = 0xdd661f55128A80437A0c0BDA6E13F214A3B2EB24;
address upgrader = 0x4C30baDa479D0e13300b31b1696A5E570848bbEe;
Expand Down Expand Up @@ -447,13 +447,13 @@ contract ArbitrationPolicyUMATest is BaseTest {

(, , , , , , bytes32 currentTagBefore, ) = disputeModule.disputes(disputeId);

uint256 disputerBalBefore = currency.balanceOf(disputeInitiator);
uint256 disputerBalBefore = address(disputeInitiator).balance;

// settle the assertion
bytes32 assertionId = arbitrationPolicyUMA.disputeIdToAssertionId(disputeId);
IOOV3(newOOV3).settleAssertion(assertionId);

uint256 disputerBalAfter = currency.balanceOf(disputeInitiator);
uint256 disputerBalAfter = address(disputeInitiator).balance;

(, , , , , , bytes32 currentTagAfter, ) = disputeModule.disputes(disputeId);

Expand Down Expand Up @@ -490,15 +490,15 @@ contract ArbitrationPolicyUMATest is BaseTest {

(, , , , , , bytes32 currentTagBefore, ) = disputeModule.disputes(disputeId);

uint256 disputerBalBefore = currency.balanceOf(disputeInitiator);
uint256 callerBalBefore = currency.balanceOf(caller);
uint256 disputerBalBefore = address(disputeInitiator).balance;
uint256 callerBalBefore = address(caller).balance;

// settle the assertion
bytes32 assertionId = arbitrationPolicyUMA.disputeIdToAssertionId(disputeId);
IOOV3(newOOV3).settleAssertion(assertionId);

uint256 disputerBalAfter = currency.balanceOf(disputeInitiator);
uint256 callerBalAfter = currency.balanceOf(caller);
uint256 disputerBalAfter = address(disputeInitiator).balance;
uint256 callerBalAfter = address(caller).balance;
(, , , , , , bytes32 currentTagAfter, ) = disputeModule.disputes(disputeId);

assertEq(currentTagBefore, bytes32("IN_DISPUTE"));
Expand Down Expand Up @@ -796,15 +796,15 @@ contract ArbitrationPolicyUMATest is BaseTest {

(, , , , , , bytes32 currentTagBefore, ) = disputeModule.disputes(disputeId);

uint256 disputeInitiatorBalBefore = currency.balanceOf(disputeInitiator);
uint256 defenderIpIdOwnerBalBefore = currency.balanceOf(randomIpId);
uint256 disputeInitiatorBalBefore = address(disputeInitiator).balance;
uint256 defenderIpIdOwnerBalBefore = address(randomIpId).balance;

oov3.settleAssertion(assertionId);

(, , , , , , bytes32 currentTagAfter, ) = disputeModule.disputes(disputeId);

uint256 disputeInitiatorBalAfter = currency.balanceOf(disputeInitiator);
uint256 defenderIpIdOwnerBalAfter = currency.balanceOf(randomIpId);
uint256 disputeInitiatorBalAfter = address(disputeInitiator).balance;
uint256 defenderIpIdOwnerBalAfter = address(randomIpId).balance;

uint256 oracleFee = (oov3.burnedBondPercentage() * assertion.bond) / 1e18;
uint256 bondRecipientAmount = assertion.bond * 2 - oracleFee;
Expand All @@ -813,6 +813,8 @@ contract ArbitrationPolicyUMATest is BaseTest {
assertEq(currentTagAfter, bytes32("IMPROPER_REGISTRATION"));
assertEq(disputeInitiatorBalAfter - disputeInitiatorBalBefore, bondRecipientAmount);
assertEq(defenderIpIdOwnerBalAfter - defenderIpIdOwnerBalBefore, 0);
assertEq(address(arbitrationPolicyUMA).balance, 0);
assertEq(IERC20(wip).balanceOf(address(arbitrationPolicyUMA)), 0);
}

function test_ArbitrationPolicyUMA_disputeAssertion_WithBondAndIpNotTagged() public {
Expand Down Expand Up @@ -861,15 +863,15 @@ contract ArbitrationPolicyUMATest is BaseTest {

(, , , , , , bytes32 currentTagBefore, ) = disputeModule.disputes(disputeId);

uint256 disputeInitiatorBalBefore = currency.balanceOf(disputeInitiator);
uint256 defenderIpIdOwnerBalBefore = currency.balanceOf(randomIpId);
uint256 disputeInitiatorBalBefore = address(disputeInitiator).balance;
uint256 defenderIpIdOwnerBalBefore = address(randomIpId).balance;

oov3.settleAssertion(assertionId);

(, , , , , , bytes32 currentTagAfter, ) = disputeModule.disputes(disputeId);

uint256 disputeInitiatorBalAfter = currency.balanceOf(disputeInitiator);
uint256 defenderIpIdOwnerBalAfter = currency.balanceOf(randomIpId);
uint256 disputeInitiatorBalAfter = address(disputeInitiator).balance;
uint256 defenderIpIdOwnerBalAfter = address(randomIpId).balance;

uint256 oracleFee = (oov3.burnedBondPercentage() * assertion.bond) / 1e18;
uint256 bondRecipientAmount = assertion.bond * 2 - oracleFee;
Expand Down
Loading