Skip to content

Commit 18834af

Browse files
feat(xKlerosLiquid): upgrade for v2
1 parent 4e90b12 commit 18834af

11 files changed

+2177
-6
lines changed

contracts/src/gateway/ForeignGateway.sol

+8
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,14 @@ contract ForeignGateway is IForeignGateway {
155155
emit DisputeCreation(disputeID, IArbitrable(msg.sender));
156156
}
157157

158+
function createDisputeERC20(
159+
uint256 /*_choices*/,
160+
bytes calldata /*_extraData*/,
161+
uint256 /*_amount*/
162+
) external override returns (uint256 /*disputeID*/) {
163+
revert("Not supported yet");
164+
}
165+
158166
function arbitrationCost(bytes calldata _extraData) public view override returns (uint256 cost) {
159167
(uint96 courtID, uint256 minJurors) = extraDataToCourtIDMinJurors(_extraData);
160168
cost = feeForJuror[courtID] * minJurors;

contracts/src/gateway/interfaces/IForeignGateway.sol

+9-6
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,18 @@ interface IForeignGateway is IArbitrator, IReceiverGateway {
1717
/**
1818
* Relay the rule call from the home gateway to the arbitrable.
1919
*/
20-
function relayRule(
21-
address _messageSender,
22-
bytes32 _disputeHash,
23-
uint256 _ruling,
24-
address _forwarder
25-
) external;
20+
function relayRule(address _messageSender, bytes32 _disputeHash, uint256 _ruling, address _forwarder) external;
2621

2722
function withdrawFees(bytes32 _disputeHash) external;
2823

24+
// TODO: add withdrawal for ERC20?
25+
2926
// For cross-chain Evidence standard
3027
function disputeHashToForeignID(bytes32 _disputeHash) external view returns (uint256);
28+
29+
function createDisputeERC20(
30+
uint256 _choices,
31+
bytes calldata _extraData,
32+
uint256 _amount
33+
) external returns (uint256 disputeID);
3134
}
+238
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
/**
4+
* @authors: [@jaybuidl, @shotaronowhere, @shalzz, @unknownunknown1]
5+
* @reviewers: []
6+
* @auditors: []
7+
* @bounties: []
8+
* @deployments: []
9+
*/
10+
11+
pragma solidity ^0.8.0;
12+
13+
import "../arbitration/IArbitrable.sol";
14+
import "./interfaces/IForeignGateway.sol";
15+
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
16+
17+
/**
18+
* Foreign Gateway
19+
* Counterpart of `HomeGateway`
20+
*/
21+
contract xForeignGateway is IForeignGateway {
22+
// ************************************* //
23+
// * Enums / Structs * //
24+
// ************************************* //
25+
26+
struct DisputeData {
27+
uint248 id;
28+
bool ruled;
29+
address arbitrable;
30+
uint256 paid;
31+
address relayer;
32+
}
33+
34+
// ************************************* //
35+
// * Events * //
36+
// ************************************* //
37+
38+
event OutgoingDispute(
39+
bytes32 disputeHash,
40+
bytes32 blockhash,
41+
uint256 localDisputeID,
42+
uint256 _choices,
43+
bytes _extraData,
44+
address arbitrable
45+
);
46+
47+
event ArbitrationCostModified(uint96 indexed _courtID, uint256 _feeForJuror);
48+
49+
// ************************************* //
50+
// * Storage * //
51+
// ************************************* //
52+
53+
uint256 public constant MIN_JURORS = 3; // The global default minimum number of jurors in a dispute.
54+
uint256 public immutable override senderChainID;
55+
address public immutable override senderGateway;
56+
IERC20 public immutable weth; // WETH token on xDai.
57+
uint256 internal localDisputeID = 1; // The disputeID must start from 1 as the KlerosV1 proxy governor depends on this implementation. We now also depend on localDisputeID not ever being zero.
58+
mapping(uint96 => uint256) public feeForJuror; // feeForJuror[courtID], it mirrors the value on KlerosCore.
59+
address public governor;
60+
IFastBridgeReceiver public fastBridgeReceiver;
61+
IFastBridgeReceiver public depreciatedFastbridge;
62+
uint256 public depreciatedFastBridgeExpiration;
63+
mapping(bytes32 => DisputeData) public disputeHashtoDisputeData;
64+
65+
// ************************************* //
66+
// * Function Modifiers * //
67+
// ************************************* //
68+
69+
modifier onlyFromFastBridge() {
70+
require(
71+
address(fastBridgeReceiver) == msg.sender ||
72+
((block.timestamp < depreciatedFastBridgeExpiration) && address(depreciatedFastbridge) == msg.sender),
73+
"Access not allowed: Fast Bridge only."
74+
);
75+
_;
76+
}
77+
78+
modifier onlyByGovernor() {
79+
require(governor == msg.sender, "Access not allowed: Governor only.");
80+
_;
81+
}
82+
83+
constructor(
84+
address _governor,
85+
IFastBridgeReceiver _fastBridgeReceiver,
86+
address _senderGateway,
87+
uint256 _senderChainID,
88+
IERC20 _weth
89+
) {
90+
governor = _governor;
91+
fastBridgeReceiver = _fastBridgeReceiver;
92+
senderGateway = _senderGateway;
93+
senderChainID = _senderChainID;
94+
weth = _weth;
95+
}
96+
97+
// ************************************* //
98+
// * Governance * //
99+
// ************************************* //
100+
101+
/**
102+
* @dev Changes the fastBridge, useful to increase the claim deposit.
103+
* @param _fastBridgeReceiver The address of the new fastBridge.
104+
* @param _gracePeriod The duration to accept messages from the deprecated bridge (if at all).
105+
*/
106+
function changeFastbridge(IFastBridgeReceiver _fastBridgeReceiver, uint256 _gracePeriod) external onlyByGovernor {
107+
// grace period to relay remaining messages in the relay / bridging process
108+
depreciatedFastBridgeExpiration = block.timestamp + _fastBridgeReceiver.epochPeriod() + _gracePeriod; // 2 weeks
109+
depreciatedFastbridge = fastBridgeReceiver;
110+
fastBridgeReceiver = _fastBridgeReceiver;
111+
}
112+
113+
/**
114+
* @dev Changes the `feeForJuror` property value of a specified court.
115+
* @param _courtID The ID of the court.
116+
* @param _feeForJuror The new value for the `feeForJuror` property value.
117+
*/
118+
function changeCourtJurorFee(uint96 _courtID, uint256 _feeForJuror) external onlyByGovernor {
119+
feeForJuror[_courtID] = _feeForJuror;
120+
emit ArbitrationCostModified(_courtID, _feeForJuror);
121+
}
122+
123+
// ************************************* //
124+
// * State Modifiers * //
125+
// ************************************* //
126+
127+
function createDispute(
128+
uint256 _choices,
129+
bytes calldata _extraData
130+
) external payable override returns (uint256 disputeID) {
131+
// TODO
132+
}
133+
134+
function createDisputeERC20(
135+
uint256 _choices,
136+
bytes calldata _extraData,
137+
uint256 _amount
138+
) external override returns (uint256 disputeID) {
139+
// This check is duplicated in xKlerosLiquid and transferred is done there as well.
140+
require(_amount >= arbitrationCost(_extraData), "Not paid enough for arbitration");
141+
142+
disputeID = localDisputeID++;
143+
uint256 chainID;
144+
assembly {
145+
chainID := chainid()
146+
}
147+
bytes32 disputeHash = keccak256(
148+
abi.encodePacked(
149+
chainID,
150+
blockhash(block.number - 1),
151+
"createDispute",
152+
disputeID,
153+
_choices,
154+
_extraData,
155+
msg.sender
156+
)
157+
);
158+
159+
disputeHashtoDisputeData[disputeHash] = DisputeData({
160+
id: uint248(disputeID),
161+
arbitrable: msg.sender,
162+
paid: _amount,
163+
relayer: address(0),
164+
ruled: false
165+
});
166+
167+
emit OutgoingDispute(disputeHash, blockhash(block.number - 1), disputeID, _choices, _extraData, msg.sender);
168+
emit DisputeCreation(disputeID, IArbitrable(msg.sender));
169+
}
170+
171+
function arbitrationCost(bytes calldata _extraData) public view override returns (uint256 cost) {
172+
(uint96 courtID, uint256 minJurors) = extraDataToCourtIDMinJurors(_extraData);
173+
cost = feeForJuror[courtID] * minJurors;
174+
}
175+
176+
/**
177+
* Relay the rule call from the home gateway to the arbitrable.
178+
*/
179+
function relayRule(
180+
address _messageSender,
181+
bytes32 _disputeHash,
182+
uint256 _ruling,
183+
address _relayer
184+
) external override onlyFromFastBridge {
185+
require(_messageSender == senderGateway, "Only the homegateway is allowed.");
186+
DisputeData storage dispute = disputeHashtoDisputeData[_disputeHash];
187+
188+
require(dispute.id != 0, "Dispute does not exist");
189+
require(!dispute.ruled, "Cannot rule twice");
190+
191+
dispute.ruled = true;
192+
dispute.relayer = _relayer;
193+
194+
IArbitrable arbitrable = IArbitrable(dispute.arbitrable);
195+
arbitrable.rule(dispute.id, _ruling);
196+
}
197+
198+
// TODO: separate regular withdrawal from ERC20
199+
function withdrawFees(bytes32 _disputeHash) external override {
200+
DisputeData storage dispute = disputeHashtoDisputeData[_disputeHash];
201+
require(dispute.id != 0, "Dispute does not exist");
202+
require(dispute.ruled, "Not ruled yet");
203+
204+
uint256 amount = dispute.paid;
205+
dispute.paid = 0;
206+
weth.transfer(dispute.relayer, amount);
207+
}
208+
209+
// ************************************* //
210+
// * Public Views * //
211+
// ************************************* //
212+
213+
function disputeHashToForeignID(bytes32 _disputeHash) external view override returns (uint256) {
214+
return disputeHashtoDisputeData[_disputeHash].id;
215+
}
216+
217+
// ************************ //
218+
// * Internal * //
219+
// ************************ //
220+
221+
function extraDataToCourtIDMinJurors(
222+
bytes memory _extraData
223+
) internal view returns (uint96 courtID, uint256 minJurors) {
224+
// Note that here we ignore DisputeKitID
225+
if (_extraData.length >= 64) {
226+
assembly {
227+
// solium-disable-line security/no-inline-assembly
228+
courtID := mload(add(_extraData, 0x20))
229+
minJurors := mload(add(_extraData, 0x40))
230+
}
231+
if (feeForJuror[courtID] == 0) courtID = 0;
232+
if (minJurors == 0) minJurors = MIN_JURORS;
233+
} else {
234+
courtID = 0;
235+
minJurors = MIN_JURORS;
236+
}
237+
}
238+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
pragma solidity ^0.8;
2+
3+
/// @dev The token controller contract must implement these functions
4+
abstract contract TokenController {
5+
/// @notice Called when `_owner` sends ether to the MiniMe Token contract
6+
/// @param _owner The address that sent the ether to create tokens
7+
/// @return True if the ether is accepted, false if it throws
8+
function proxyPayment(address _owner) public payable virtual returns (bool);
9+
10+
/// @notice Notifies the controller about a token transfer allowing the
11+
/// controller to react if desired
12+
/// @param _from The origin of the transfer
13+
/// @param _to The destination of the transfer
14+
/// @param _amount The amount of the transfer
15+
/// @return False if the controller does not authorize the transfer
16+
function onTransfer(address _from, address _to, uint _amount) public virtual returns (bool);
17+
18+
/// @notice Notifies the controller about an approval allowing the
19+
/// controller to react if desired
20+
/// @param _owner The address that calls `approve()`
21+
/// @param _spender The spender in the `approve()` call
22+
/// @param _amount The amount in the `approve()` call
23+
/// @return False if the controller does not authorize the approval
24+
function onApprove(address _owner, address _spender, uint _amount) public virtual returns (bool);
25+
}

0 commit comments

Comments
 (0)