Skip to content
This repository has been archived by the owner on Jan 13, 2023. It is now read-only.

Commit

Permalink
add concluder contract
Browse files Browse the repository at this point in the history
  • Loading branch information
Brendan Chou committed Aug 22, 2019
1 parent 1a2364d commit 6eb2976
Show file tree
Hide file tree
Showing 3 changed files with 592 additions and 84 deletions.
1 change: 1 addition & 0 deletions .soliumignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules
contracts/external/traders/Expiry.sol
contracts/testing
contracts/Migrations.sol
198 changes: 114 additions & 84 deletions contracts/external/traders/Expiry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,18 @@ import { OnlySolo } from "../helpers/OnlySolo.sol";


/**
* @title Expiry
* @title ExpiryV2
* @author dYdX
*
* Sets the negative balance for an account to expire at a certain time. This allows any other
* account to repay that negative balance after expiry using any positive balance in the same
* account. The arbitrage incentive is the same as liquidation in the base protocol.
* Expiry contract that also allows approved senders to set expiry to be 28 days in the future.
*/
contract Expiry is
contract ExpiryV2 is
Ownable,
OnlySolo,
ICallee,
IAutoTrader
{
using Math for uint256;
using SafeMath for uint32;
using SafeMath for uint256;
using Types for Types.Par;
Expand All @@ -56,6 +55,26 @@ contract Expiry is

bytes32 constant FILE = "Expiry";

// ============ Enums ============

enum CallFunctionType {
SetExpiry,
SetApproval
}

// ============ Structs ============

struct SetExpiryArg {
Account.Info account;
uint256 marketId;
uint32 timeDelta;
}

struct SetApprovalArg {
address sender;
uint32 minTimeDelta;
}

// ============ Events ============

event ExpirySet(
Expand All @@ -69,11 +88,20 @@ contract Expiry is
uint256 expiryRampTime
);

event LogSenderApproved(
address approver,
address sender,
uint32 minTimeDelta
);

// ============ Storage ============

// owner => number => market => time
mapping (address => mapping (uint256 => mapping (uint256 => uint32))) g_expiries;

// owner => sender => minimum time delta
mapping (address => mapping (address => uint32)) public g_approvedSender;

// time over which the liquidation ratio goes from zero to maximum
uint256 public g_expiryRampTime;

Expand Down Expand Up @@ -101,6 +129,17 @@ contract Expiry is
g_expiryRampTime = newExpiryRampTime;
}

// ============ Approval Functions ============

function approveSender(
address sender,
uint32 minTimeDelta
)
external
{
setApproval(msg.sender, sender, minTimeDelta);
}

// ============ Getters ============

function getExpiry(
Expand Down Expand Up @@ -154,17 +193,12 @@ contract Expiry is
public
onlySolo(msg.sender)
{
(
uint256 marketId,
uint32 expiryTime
) = parseCallArgs(data);

// don't set expiry time for accounts with positive balance
if (expiryTime != 0 && !SOLO_MARGIN.getAccountPar(account, marketId).isNegative()) {
return;
CallFunctionType callType = abi.decode(data, (CallFunctionType));
if (callType == CallFunctionType.SetExpiry) {
callFunctionSetExpiry(account.owner, data);
} else {
callFunctionSetApproval(account.owner, data);
}

setExpiry(account, marketId, expiryTime);
}

function getTradeCost(
Expand All @@ -191,10 +225,7 @@ contract Expiry is
});
}

(
uint256 owedMarketId,
uint32 maxExpiry
) = parseTradeArgs(data);
(uint256 owedMarketId, uint256 maxExpiry) = abi.decode(data, (uint256, uint256));

uint32 expiry = getExpiry(makerAccount, owedMarketId);

Expand Down Expand Up @@ -234,6 +265,59 @@ contract Expiry is

// ============ Private Functions ============

function callFunctionSetExpiry(
address sender,
bytes memory data
)
private
{
(
CallFunctionType callType,
SetExpiryArg[] memory expiries
) = abi.decode(data, (CallFunctionType, SetExpiryArg[]));

assert(callType == CallFunctionType.SetExpiry);

for (uint256 i = 0; i < expiries.length; i++) {
SetExpiryArg memory exp = expiries[i];
uint32 timeDelta = exp.timeDelta;
if (exp.account.owner != sender) {
uint32 minApprovedTimeDelta = g_approvedSender[exp.account.owner][sender];
if (minApprovedTimeDelta == 0) {
// don't do anything if sender is not approved
continue;
} else {
// bound the time by the minimum approved timeDelta
timeDelta = Math.max(minApprovedTimeDelta, exp.timeDelta).to32();
}
}

// if timeDelta is zero, interpret it as unset expiry
if (
timeDelta > 0 &&
SOLO_MARGIN.getAccountPar(exp.account, exp.marketId).isNegative()
) {
setExpiry(exp.account, exp.marketId, Time.currentTime().add(timeDelta).to32());
} else {
setExpiry(exp.account, exp.marketId, 0);
}
}
}

function callFunctionSetApproval(
address sender,
bytes memory data
)
private
{
(
CallFunctionType callType,
SetApprovalArg memory approvalArg
) = abi.decode(data, (CallFunctionType, SetApprovalArg));
assert(callType == CallFunctionType.SetApproval);
setApproval(sender, approvalArg.sender, approvalArg.minTimeDelta);
}

function getTradeCostInternal(
uint256 inputMarketId,
uint256 outputMarketId,
Expand Down Expand Up @@ -336,7 +420,6 @@ contract Expiry is
private
{
g_expiries[account.owner][account.number][marketId] = time;

emit ExpirySet(
account.owner,
account.number,
Expand All @@ -345,6 +428,17 @@ contract Expiry is
);
}

function setApproval(
address approver,
address sender,
uint32 minTimeDelta
)
private
{
g_approvedSender[approver][sender] = minTimeDelta;
emit LogSenderApproved(approver, sender, minTimeDelta);
}

function heldWeiToOwedWei(
Types.Wei memory heldWei,
uint256 heldMarketId,
Expand Down Expand Up @@ -410,68 +504,4 @@ contract Expiry is
value: heldAmount
});
}

function parseCallArgs(
bytes memory data
)
private
pure
returns (
uint256,
uint32
)
{
Require.that(
data.length == 64,
FILE,
"Call data invalid length",
data.length
);

uint256 marketId;
uint256 rawExpiry;

/* solium-disable-next-line security/no-inline-assembly */
assembly {
marketId := mload(add(data, 32))
rawExpiry := mload(add(data, 64))
}

return (
marketId,
Math.to32(rawExpiry)
);
}

function parseTradeArgs(
bytes memory data
)
private
pure
returns (
uint256,
uint32
)
{
Require.that(
data.length == 64,
FILE,
"Trade data invalid length",
data.length
);

uint256 owedMarketId;
uint256 rawExpiry;

/* solium-disable-next-line security/no-inline-assembly */
assembly {
owedMarketId := mload(add(data, 32))
rawExpiry := mload(add(data, 64))
}

return (
owedMarketId,
Math.to32(rawExpiry)
);
}
}
Loading

0 comments on commit 6eb2976

Please sign in to comment.