Skip to content
This repository has been archived by the owner on Sep 27, 2022. It is now read-only.

[Under Audit] Add Bucket-Lender #357

Merged
merged 22 commits into from
Aug 8, 2018
Merged
107 changes: 107 additions & 0 deletions contracts/margin/external/BucketLender/BucketLenderFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*

Copyright 2018 dYdX Trading Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

pragma solidity 0.4.24;
pragma experimental "v0.5.0";

import { Ownable } from "zeppelin-solidity/contracts/ownership/Ownable.sol";
import { BucketLender } from "./BucketLender.sol";


/**
* @title BucketLenderFactory
* @author dYdX
*
* Contract that allows anyone to deploy a BucketLender contract by sending a transaction.
*/
contract BucketLenderFactory {

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

event BucketLenderCreated(
address indexed creator,
address at,
bytes32 positionId
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should index the positionId this will likely be a thing people search by

);

// ============ State Variables ============

// Address of the Margin contract for the dYdX Margin Trading Protocol
address public DYDX_MARGIN;

// ============ Constructor ============

constructor(
address margin
)
public
{
DYDX_MARGIN = margin;
}

// ============ Public Functions ============

/**
* Deploy a new BucketLender contract to the blockchain
*
* @param positionId Unique ID of the position
* @param heldToken Address of the token held in the position as collateral
* @param owedToken Address of the token being lent by the BucketLender
* @param parameters Values corresponding to:
*
* [0] = number of seconds per bucket
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: to be consistent with Margin align these with 2 spaces after the *

* [1] = interest rate
* [2] = interest period
* [3] = maximum loan duration
* [4] = margin-call timelimit
* [5] = numerator of minimum heldToken-per-owedToken
* [6] = denominator of minimum heldToken-per-owedToken
*
* @param marginCallers Accounts that are permitted to margin-call positions (or cancel the margin call)
* @return The address of the new BucketLender contract
*/
function createBucketLender(
bytes32 positionId,
address heldToken,
address owedToken,
uint32[7] parameters,
address[] marginCallers
)
external
returns (address)
{
address newBucketLender = new BucketLender(
DYDX_MARGIN,
positionId,
heldToken,
owedToken,
parameters,
marginCallers
);

Ownable(newBucketLender).transferOwnership(msg.sender);

emit BucketLenderCreated(
msg.sender,
newBucketLender,
positionId
);

return newBucketLender;
}
}
116 changes: 116 additions & 0 deletions test/margin/external/bucketlender/TestBucketLenderFactory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
const chai = require('chai');
const expect = chai.expect;
chai.use(require('chai-bignumber')());
const BigNumber = require('bignumber.js');

const Margin = artifacts.require("Margin");
const HeldToken = artifacts.require("TokenA");
const OwedToken = artifacts.require("TokenB");
const BucketLender = artifacts.require("BucketLender");
const BucketLenderFactory = artifacts.require("BucketLenderFactory");

const { transact } = require('../../../helpers/ContractHelper');
const { ADDRESSES, BYTES32 } = require('../../../helpers/Constants');

contract('BucketLenderFactory', () => {

// ============ Before/After ============

beforeEach('set up contracts', async () => {
});

// ============ Constructor ============

describe('Constructor', () => {
it('sets constants correctly', async () => {
const marginAddress = ADDRESSES.TEST[0];
const factory = await BucketLenderFactory.new(marginAddress);
const dydxMargin = await factory.DYDX_MARGIN.call();
expect(dydxMargin).to.be.eq(marginAddress);
});
});

// ============ Functions ============

describe('createBucketLender', () => {
it('succeeds', async () => {
const positionId = BYTES32.TEST[0];
const bucketTime = new BigNumber(123);
const interestRate = new BigNumber(456);
const interestPeriod = new BigNumber(789);
const maxDuration = new BigNumber(101112);
const callTimelimit = new BigNumber(131415);
const numerator = new BigNumber(161718);
const denominator = new BigNumber(192021);
const marginCaller = ADDRESSES.TEST[0];
const notMarginCaller = ADDRESSES.TEST[1];

const factory = await BucketLenderFactory.new(Margin.address);

const newBucketLender = await transact(
factory.createBucketLender,
positionId,
HeldToken.address,
OwedToken.address,
[
bucketTime,
interestRate,
interestPeriod,
maxDuration,
callTimelimit,
numerator,
denominator
],
[
marginCaller
]
);

const bucketLender = await BucketLender.at(newBucketLender.result);

const [
bl_margin,
bl_positionId,
bl_heldToken,
bl_owedToken,
bl_bucketTime,
bl_interestRate,
bl_interestPeriod,
bl_maxDuration,
bl_callTimelimit,
bl_numerator,
bl_denominator,
bl_marginCallerOkay,
bl_notMarginCallerNotOkay,
] = await Promise.all([
bucketLender.DYDX_MARGIN.call(),
bucketLender.POSITION_ID.call(),
bucketLender.HELD_TOKEN.call(),
bucketLender.OWED_TOKEN.call(),
bucketLender.BUCKET_TIME.call(),
bucketLender.INTEREST_RATE.call(),
bucketLender.INTEREST_PERIOD.call(),
bucketLender.MAX_DURATION.call(),
bucketLender.CALL_TIMELIMIT.call(),
bucketLender.MIN_HELD_TOKEN_NUMERATOR.call(),
bucketLender.MIN_HELD_TOKEN_DENOMINATOR.call(),
bucketLender.TRUSTED_MARGIN_CALLERS.call(marginCaller),
bucketLender.TRUSTED_MARGIN_CALLERS.call(notMarginCaller),
]);

expect(bl_margin).to.be.eq(Margin.address);
expect(bl_positionId).to.be.eq(positionId);
expect(bl_heldToken).to.be.eq(HeldToken.address);
expect(bl_owedToken).to.be.eq(OwedToken.address);
expect(bl_bucketTime).to.be.bignumber.eq(bucketTime);
expect(bl_interestRate).to.be.bignumber.eq(interestRate);
expect(bl_interestPeriod).to.be.bignumber.eq(interestPeriod);
expect(bl_maxDuration).to.be.bignumber.eq(maxDuration);
expect(bl_callTimelimit).to.be.bignumber.eq(callTimelimit);
expect(bl_numerator).to.be.bignumber.eq(numerator);
expect(bl_denominator).to.be.bignumber.eq(denominator);
expect(bl_marginCallerOkay).to.be.eq(true);
expect(bl_notMarginCallerNotOkay).to.be.eq(false);
});
});
});