Skip to content

Commit 9d224e9

Browse files
Merge pull request #334 from unknownunknown1/master
fix(LinguoToken.sol): add uniswap price
2 parents 91b8342 + eebcb4a commit 9d224e9

File tree

3 files changed

+167
-111
lines changed

3 files changed

+167
-111
lines changed

contracts/standard/arbitration/Linguo.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ contract Linguo is Arbitrable {
2525
using CappedMath for uint;
2626

2727
/* *** Contract variables *** */
28+
uint8 public constant VERSION_ID = 0; // Value that represents the version of the contract. The value is incremented each time the new version is deployed. Range for LinguoETH: 0-127, LinguoToken: 128-255.
2829
uint public constant MULTIPLIER_DIVISOR = 10000; // Divisor parameter for multipliers.
2930
uint constant NOT_PAYABLE_VALUE = (2**256-2)/2; // A value depositor won't be able to pay.
3031

contracts/standard/arbitration/LinguoToken.sol

Lines changed: 63 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* @authors: [@unknownunknown1]
3-
* @reviewers: [@remedcu]
3+
* @reviewers: [@remedcu*]
44
* @auditors: []
55
* @bounties: []
66
* @deployments: []
@@ -14,6 +14,12 @@ import "./Arbitrable.sol";
1414
import "../../libraries/CappedMath.sol";
1515
import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
1616

17+
interface IUniswapV2Pair {
18+
19+
function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast);
20+
21+
}
22+
1723
/** @title LinguoToken
1824
* Linguo is a decentralized platform where anyone can submit a document for translation and have it translated by freelancers.
1925
* It has no platform fees and disputes about translation quality are handled by Kleros jurors.
@@ -27,6 +33,7 @@ contract LinguoToken is Arbitrable {
2733
using CappedMath for uint;
2834

2935
/* *** Contract variables *** */
36+
uint8 public constant VERSION_ID = 128; // Value that represents the version of the contract. The value is incremented each time the new version is deployed. Range for LinguoToken: 128-255, LinguoETH: 0-127.
3037
uint public constant MULTIPLIER_DIVISOR = 10000; // Divisor parameter for multipliers.
3138
uint constant NOT_PAYABLE_VALUE = (2**256-2)/2; // A value depositor won't be able to pay.
3239

@@ -63,10 +70,12 @@ contract LinguoToken is Arbitrable {
6370
mapping(address => uint[3]) contributions; // Maps contributors to their contributions for each side.
6471
}
6572

73+
ERC20 public WETH; // Address of the wETH token contract. It's required for token -> ETH conversion.
74+
address public uniswapFactory; // Address of the UniswapPair factory. It's required for token -> ETH conversion.
75+
6676
address public governor; // The governor of the contract.
6777
uint public reviewTimeout; // Time in seconds, during which the submitted translation can be challenged.
68-
uint public translatorBaseDeposit; // The base deposit a translator must pay to self-assign a task, in wei.
69-
uint public challengerBaseDeposit; // The base deposit a challenger must pay to challenge a translation, in wei.
78+
uint public translationMultiplier; // Multiplier for calculating the value of the deposit translator must pay to self-assign a task.
7079

7180
// All multipliers below are in basis points.
7281
uint public sharedStakeMultiplier; // Multiplier for calculating the appeal fee that must be paid by submitter in the case where there is no winner or loser (e.g. when the arbitrator ruled "refuse to arbitrate").
@@ -129,27 +138,30 @@ contract LinguoToken is Arbitrable {
129138
/** @dev Constructor.
130139
* @param _arbitrator The arbitrator of the contract.
131140
* @param _arbitratorExtraData Extra data for the arbitrator.
141+
* @param _WETH Address of the WETH token contract.
142+
* @param _uniswapFactory Address of the UniswapPair factory contract.
132143
* @param _reviewTimeout Time in seconds during which a translation can be challenged.
133-
* @param _translatorBaseDeposit The base deposit that must be paid in order to self-assign the task, in wei.
134-
* @param _challengerBaseDeposit The base deposit that must be paid in order to challenge the translation, in wei.
144+
* @param _translationMultiplier Multiplier for calculating translator's deposit. In basis points.
135145
* @param _sharedStakeMultiplier Multiplier of the appeal cost that submitter must pay for a round when there is no winner/loser in the previous round. In basis points.
136146
* @param _winnerStakeMultiplier Multiplier of the appeal cost that the winner has to pay for a round. In basis points.
137147
* @param _loserStakeMultiplier Multiplier of the appeal cost that the loser has to pay for a round. In basis points.
138148
*/
139149
constructor(
140150
Arbitrator _arbitrator,
141151
bytes _arbitratorExtraData,
152+
ERC20 _WETH,
153+
address _uniswapFactory,
142154
uint _reviewTimeout,
143-
uint _translatorBaseDeposit,
144-
uint _challengerBaseDeposit,
155+
uint _translationMultiplier,
145156
uint _sharedStakeMultiplier,
146157
uint _winnerStakeMultiplier,
147158
uint _loserStakeMultiplier
148159
) public Arbitrable(_arbitrator, _arbitratorExtraData){
149160
governor = msg.sender;
161+
WETH = _WETH;
162+
uniswapFactory = _uniswapFactory;
150163
reviewTimeout = _reviewTimeout;
151-
translatorBaseDeposit = _translatorBaseDeposit;
152-
challengerBaseDeposit = _challengerBaseDeposit;
164+
translationMultiplier = _translationMultiplier;
153165
sharedStakeMultiplier = _sharedStakeMultiplier;
154166
winnerStakeMultiplier = _winnerStakeMultiplier;
155167
loserStakeMultiplier = _loserStakeMultiplier;
@@ -173,18 +185,11 @@ contract LinguoToken is Arbitrable {
173185
reviewTimeout = _reviewTimeout;
174186
}
175187

176-
/** @dev Changes the base deposit for translator.
177-
* @param _translatorBaseDeposit A new value of the base deposit required for self-assigning the task, in wei.
188+
/** @dev Changes the multiplier for translator's deposit.
189+
* @param _translationMultiplier A new value of the multiplier for calculating translator's deposit. In basis points.
178190
*/
179-
function changeTranslatorBaseDeposit(uint _translatorBaseDeposit) public onlyGovernor {
180-
translatorBaseDeposit = _translatorBaseDeposit;
181-
}
182-
183-
/** @dev Changes the base deposit for challenger.
184-
* @param _challengerBaseDeposit A new value of the base deposit required for challenging, in wei.
185-
*/
186-
function changeChallengerBaseDeposit(uint _challengerBaseDeposit) public onlyGovernor {
187-
challengerBaseDeposit = _challengerBaseDeposit;
191+
function changeTranslationMultiplier(uint _translationMultiplier) public onlyGovernor {
192+
translationMultiplier = _translationMultiplier;
188193
}
189194

190195
/** @dev Changes the percentage of arbitration fees that must be paid by parties as a fee stake if there was no winner and loser in the previous round.
@@ -253,8 +258,10 @@ contract LinguoToken is Arbitrable {
253258
require(now - task.lastInteraction <= task.submissionTimeout, "The deadline has already passed.");
254259

255260
uint price = task.minPrice + (task.maxPrice - task.minPrice) * (now - task.lastInteraction) / task.submissionTimeout;
261+
262+
uint priceETH = getTaskPriceInETH(_taskID);
256263
uint arbitrationCost = arbitrator.arbitrationCost(arbitratorExtraData);
257-
uint translatorDeposit = arbitrationCost.addCap(translatorBaseDeposit);
264+
uint translatorDeposit = arbitrationCost.addCap((translationMultiplier.mulCap(priceETH)) / MULTIPLIER_DIVISOR);
258265

259266
require(task.status == Status.Created, "Task has already been assigned or reimbursed.");
260267
require(msg.value >= translatorDeposit, "Not enough ETH to reach the required deposit value.");
@@ -264,7 +271,7 @@ contract LinguoToken is Arbitrable {
264271

265272
// Update requester's deposit since we reimbursed him the difference between maximal and actual price.
266273
task.requesterDeposit = price;
267-
task.sumDeposit += translatorDeposit;
274+
task.sumDeposit = translatorDeposit;
268275

269276
uint remainder = msg.value - translatorDeposit;
270277
msg.sender.send(remainder);
@@ -301,7 +308,7 @@ contract LinguoToken is Arbitrable {
301308
uint sumDeposit = task.sumDeposit;
302309
task.requesterDeposit = 0;
303310
task.sumDeposit = 0;
304-
// Requester gets his deposit back and also the deposit of the translator, if there was one. Note that sumDeposit can't contain challenger's deposit until the task is in DisputeCreated status.
311+
// Requester gets his deposit back and also the deposit of the translator, if there was one.
305312
task.requester.send(sumDeposit);
306313
require(task.token.transfer(task.requester, requesterDeposit), "The token transfer was unsuccessful.");
307314

@@ -316,7 +323,7 @@ contract LinguoToken is Arbitrable {
316323
require(task.status == Status.AwaitingReview, "The task is in the wrong status.");
317324
require(now - task.lastInteraction > reviewTimeout, "The review phase hasn't passed yet.");
318325
task.status = Status.Resolved;
319-
// Translator gets the price of the task and his deposit back. Note that sumDeposit can't contain challenger's deposit until the task is in DisputeCreated status.
326+
// Translator gets the price of the task and his deposit back.
320327
address translator = task.parties[uint(Party.Translator)];
321328
uint requesterDeposit = task.requesterDeposit;
322329
uint sumDeposit = task.sumDeposit;
@@ -335,22 +342,22 @@ contract LinguoToken is Arbitrable {
335342
function challengeTranslation(uint _taskID, string _evidence) external payable {
336343
Task storage task = tasks[_taskID];
337344

345+
// The challenger should only deposit the value of arbitration cost.
338346
uint arbitrationCost = arbitrator.arbitrationCost(arbitratorExtraData);
339-
uint challengeDeposit = arbitrationCost.addCap(challengerBaseDeposit);
340347

341348
require(task.status == Status.AwaitingReview, "The task is in the wrong status.");
342349
require(now - task.lastInteraction <= reviewTimeout, "The review phase has already passed.");
343-
require(msg.value >= challengeDeposit, "Not enough ETH to cover challenge deposit.");
350+
require(msg.value >= arbitrationCost, "Not enough ETH to cover challenge deposit.");
344351

345352
task.status = Status.DisputeCreated;
346353
task.parties[uint(Party.Challenger)] = msg.sender;
347354

348355
task.disputeID = arbitrator.createDispute.value(arbitrationCost)(2, arbitratorExtraData);
349356
disputeIDtoTaskID[task.disputeID] = _taskID;
350357
task.rounds.length++;
351-
task.sumDeposit = task.sumDeposit.addCap(challengeDeposit).subCap(arbitrationCost);
358+
// We don't change sumDeposit because adding challenger's deposit while subtracting arbitration fee will give 0 as a result.
352359

353-
uint remainder = msg.value - challengeDeposit;
360+
uint remainder = msg.value - arbitrationCost;
354361
msg.sender.send(remainder);
355362

356363
emit Dispute(arbitrator, task.disputeID, _taskID, _taskID);
@@ -521,7 +528,7 @@ contract LinguoToken is Arbitrable {
521528
task.sumDeposit = 0;
522529

523530
if(_ruling == uint(Party.None)){
524-
// The value of sumDeposit is split among parties in this case. If the sum is uneven the value of 1 wei can be burnt.
531+
// The value of sumDeposit is split among parties in this case. If it's uneven the value of 1 wei can be burnt.
525532
sumDeposit = sumDeposit / 2;
526533
task.parties[uint(Party.Translator)].send(sumDeposit);
527534
task.parties[uint(Party.Challenger)].send(sumDeposit);
@@ -592,8 +599,11 @@ contract LinguoToken is Arbitrable {
592599
if (now - task.lastInteraction > task.submissionTimeout || task.status != Status.Created) {
593600
deposit = NOT_PAYABLE_VALUE;
594601
} else {
602+
uint priceETH = getTaskPriceInETH(_taskID);
603+
595604
uint arbitrationCost = arbitrator.arbitrationCost(arbitratorExtraData);
596-
deposit = arbitrationCost.addCap(translatorBaseDeposit);
605+
deposit = arbitrationCost.addCap((translationMultiplier.mulCap(priceETH)) / MULTIPLIER_DIVISOR);
606+
597607
}
598608
}
599609

@@ -606,12 +616,11 @@ contract LinguoToken is Arbitrable {
606616
if (now - task.lastInteraction > reviewTimeout || task.status != Status.AwaitingReview) {
607617
deposit = NOT_PAYABLE_VALUE;
608618
} else {
609-
uint arbitrationCost = arbitrator.arbitrationCost(arbitratorExtraData);
610-
deposit = arbitrationCost.addCap(challengerBaseDeposit);
619+
deposit = arbitrator.arbitrationCost(arbitratorExtraData);
611620
}
612621
}
613622

614-
/** @dev Gets the current price of a specified task.
623+
/** @dev Gets the current price of a specified task. Returns 0 if the task can no longer be assigned.
615624
* @param _taskID The ID of the task.
616625
* @return price The price of the task.
617626
*/
@@ -624,6 +633,27 @@ contract LinguoToken is Arbitrable {
624633
}
625634
}
626635

636+
/** @dev Gets the current price of a specified task in ETH. Returns 0 if the task can no longer be assigned.
637+
* @param _taskID The ID of the task.
638+
* @return price The price of the task.
639+
*/
640+
function getTaskPriceInETH(uint _taskID) public view returns (uint priceETH) {
641+
Task storage task = tasks[_taskID];
642+
uint tokenPrice = task.minPrice + (task.maxPrice - task.minPrice) * (now - task.lastInteraction) / task.submissionTimeout;
643+
if (now - task.lastInteraction > task.submissionTimeout || task.status != Status.Created)
644+
priceETH = 0;
645+
else if (task.token == WETH)
646+
priceETH = tokenPrice;
647+
else {
648+
(ERC20 token0, ERC20 token1) = task.token < WETH ? (task.token, WETH) : (WETH, task.token);
649+
IUniswapV2Pair pair = IUniswapV2Pair(address(uint(keccak256(abi.encodePacked(hex"ff", uniswapFactory, keccak256(abi.encodePacked(token0, token1)), hex"96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f")))));
650+
(uint reserve0, uint reserve1,) = pair.getReserves();
651+
(uint reserveA, uint reserveB) = token0 == task.token ? (reserve0, reserve1) : (reserve1, reserve0);
652+
require(reserveA > 0 && reserveB > 0, "Could not calculate the price.");
653+
priceETH = tokenPrice.mulCap(reserveB) / reserveA;
654+
}
655+
}
656+
627657
/** @dev Gets the total number of created tasks.
628658
* @return The number of created tasks.
629659
*/

0 commit comments

Comments
 (0)