Skip to content
This repository was archived by the owner on Jan 18, 2023. It is now read-only.
Merged
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
135 changes: 126 additions & 9 deletions contracts/core/Core.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ pragma solidity 0.4.24;


import { ISetFactory } from "./interfaces/ISetFactory.sol";
import { ISetToken } from "./interfaces/ISetToken.sol";
import { ITransferProxy } from "./interfaces/ITransferProxy.sol";
import { IVault } from "./interfaces/IVault.sol";
import { Ownable } from "zeppelin-solidity/contracts/ownership/Ownable.sol";
import { SafeMath } from "zeppelin-solidity/contracts/math/SafeMath.sol";


/**
Expand All @@ -34,13 +36,18 @@ import { Ownable } from "zeppelin-solidity/contracts/ownership/Ownable.sol";
contract Core is
Ownable
{
// Use SafeMath library for all uint256 arithmetic
using SafeMath for uint256;

/* ============ Constants ============ */

string constant ADDRESSES_MISSING = "Addresses must not be empty.";
string constant BATCH_INPUT_MISMATCH = "Addresses and quantities must be the same length.";
string constant INVALID_FACTORY = "Factory is disabled or does not exist.";
string constant INVALID_QUANTITY = "Quantity must be multiple of the natural unit of the set.";
string constant INVALID_SET = "Set token is disabled or does not exist.";
string constant QUANTITES_MISSING = "Quantities must not be empty.";
string constant ZERO_QUANTITY = "Quantity must be greater than zero.";
string constant INVALID_FACTORY = "Factory must be tracked by Core.";

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

Expand All @@ -51,12 +58,13 @@ contract Core is
address public vaultAddress;

// Mapping of tracked SetToken factories
mapping(address => bool) public isValidFactory;
mapping(address => bool) public validFactories;

// Mapping of tracked SetTokens
mapping(address => bool) public isValidSet;
mapping(address => bool) public validSets;

/* ============ Events ============ */

event LogCreate(
address indexed _setTokenAddress,
address _factoryAddress,
Expand All @@ -69,6 +77,15 @@ contract Core is

/* ============ Modifiers ============ */

// Validate quantity is multiple of natural unit
modifier isNaturalUnitMultiple(uint _quantity, address _setToken) {
require(
_quantity % ISetToken(_setToken).naturalUnit() == 0,
INVALID_QUANTITY
);
_;
}

modifier isNonZero(uint _quantity) {
require(
_quantity > 0,
Expand All @@ -77,14 +94,23 @@ contract Core is
_;
}

modifier isValidFactoryCheck(address _factoryAddress) {
modifier isValidFactory(address _factoryAddress) {
require(
isValidFactory[_factoryAddress],
validFactories[_factoryAddress],
INVALID_FACTORY
);
_;
}

// Verify set was created by core and is enabled
modifier isValidSet(address _setAddress) {
require(
validSets[_setAddress],
INVALID_SET
);
_;
}

// Confirm that all inputs are valid for batch transactions
modifier isValidBatchTransaction(address[] _tokenAddresses, uint[] _quantities) {
// Confirm an empty _addresses array is not passed
Expand Down Expand Up @@ -152,7 +178,7 @@ contract Core is
external
onlyOwner
{
isValidFactory[_factoryAddress] = true;
validFactories[_factoryAddress] = true;
}

/**
Expand All @@ -166,11 +192,81 @@ contract Core is
external
onlyOwner
{
isValidFactory[_factoryAddress] = false;
validFactories[_factoryAddress] = false;
}

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

/**
* Issue
*
* @param _setAddress Address of set to issue
* @param _quantity Quantity of set to issue
*/
function issue(
address _setAddress,
uint _quantity
)
public
isValidSet(_setAddress)
isNaturalUnitMultiple(_quantity, _setAddress)
{
// Fetch set token components
address[] memory components = ISetToken(_setAddress).getComponents();
// Fetch set token component units
uint[] memory units = ISetToken(_setAddress).getUnits();

// Inspect vault for required component quantity
for (uint16 i = 0; i < components.length; i++) {
address component = components[i];
uint unit = units[i];

// Calculate required component quantity
uint requiredComponentQuantity = calculateTransferValue(
unit,
ISetToken(_setAddress).naturalUnit(),
_quantity
);

// Fetch component quantity in vault
uint vaultBalance = IVault(vaultAddress).getOwnerBalance(msg.sender, component);
if (vaultBalance >= requiredComponentQuantity) {
// Decrement vault balance by the required component quantity
IVault(vaultAddress).decrementTokenOwner(
msg.sender,
component,
requiredComponentQuantity
);
} else {
// User has less than required amount, decrement the vault by full balance
if (vaultBalance > 0) {
IVault(vaultAddress).decrementTokenOwner(
msg.sender,
component,
vaultBalance
);
}

// Transfer the remainder component quantity required to vault
ITransferProxy(transferProxyAddress).transferToVault(
msg.sender,
component,
requiredComponentQuantity.sub(vaultBalance)
);
}

// Increment the vault balance of the set token for the component
IVault(vaultAddress).incrementTokenOwner(
Copy link
Contributor

Choose a reason for hiding this comment

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

are we storing set token balance in vault? @bweick told me we were just storing balances of sets on the SetToken itself?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this is to tell us the balance of the components in the vault that belong to the set
the set token itself keeps the balance of each user

_setAddress,
component,
requiredComponentQuantity
);
}

// Issue set token
ISetToken(_setAddress).mint(msg.sender, _quantity);
}

/**
* Deposit multiple tokens to the vault. Quantities should be in the
* order of the addresses of the tokens being deposited.
Expand Down Expand Up @@ -292,7 +388,7 @@ contract Core is
string _symbol
)
public
isValidFactoryCheck(_factoryAddress)
isValidFactory(_factoryAddress)
returns (address)
{
// Create the Set
Expand All @@ -305,7 +401,7 @@ contract Core is
);

// Add Set to the list of tracked Sets
isValidSet[newSetTokenAddress] = true;
validSets[newSetTokenAddress] = true;

emit LogCreate(
newSetTokenAddress,
Expand All @@ -319,4 +415,25 @@ contract Core is

return newSetTokenAddress;
}

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

/**
* Function to calculate the transfer value of a component given quantity of Set
*
* @param _componentUnits The units of the component token
* @param _naturalUnit The natural unit of the Set token
* @param _quantity The number of tokens being redeem
*/
function calculateTransferValue(
uint _componentUnits,
uint _naturalUnit,
uint _quantity
)
pure
internal
returns(uint)
{
return _quantity.div(_naturalUnit).mul(_componentUnits);
}
}
1 change: 1 addition & 0 deletions contracts/core/Vault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ contract Vault is
}

/* ============ Getter Functions ============ */

Copy link
Contributor

Choose a reason for hiding this comment

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

remove

Copy link
Contributor

Choose a reason for hiding this comment

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

nvm this space is on purpose

/*
* Get balance of particular contract for owner.
*
Expand Down
45 changes: 45 additions & 0 deletions contracts/core/interfaces/ISetToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
Copyright 2018 Set Labs 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;

/**
* @title ISetToken
* @author Set Protocol
*
* The ISetToken interface provides a light-weight, structured way to interact with the
* SetToken contract from another contract.
*/

interface ISetToken {
function naturalUnit()
public
returns (uint);

function getComponents()
public
returns(address[]);

function getUnits()
public
returns(uint[]);

function mint(
address _issuer,
uint _quantity
)
external;
}
1 change: 0 additions & 1 deletion contracts/core/interfaces/ITransferProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/

pragma solidity 0.4.24;
pragma experimental "ABIEncoderV2";

/**
* @title ITransferProxy
Expand Down
14 changes: 13 additions & 1 deletion contracts/core/interfaces/IVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/

pragma solidity 0.4.24;
pragma experimental "ABIEncoderV2";

/**
* @title IVault
Expand Down Expand Up @@ -71,4 +70,17 @@ interface IVault {
uint _quantity
)
external;

/*
* Get balance of particular contract for owner.
*
* @param _owner The address of the token owner
* @param _tokenAddress The address of the ERC20 token
*/
function getOwnerBalance(
address _owner,
address _tokenAddress
)
external
returns (uint256);
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"lint-ts": "tslint --fix test/*.ts",
"lint-sol": "solium -d contracts/",
"lint": "yarn run lint-sol && yarn run lint-ts",
"chain": "ganache-cli --networkId 70 --accounts 20 -l 7000000 -e 1000000",
"chain": "ganache-cli --networkId 70 --accounts 20 -l 10000000 -e 1000000",
"coverage-setup": "cp -r transpiled/test/* test/. && cp -r transpiled/types/* types/.",
"coverage-cleanup": "find test -name \\*.js* -type f -delete && find types -name \\*.js* -type f -delete",
"coverage": "yarn prepare-test && yarn coverage-setup && ./node_modules/.bin/solidity-coverage && yarn coverage-cleanup"
Expand Down
2 changes: 1 addition & 1 deletion test/authorizable.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import {
expectRevertError,
} from "./utils/tokenAssertions";
import {
STANDARD_INITIAL_TOKENS,
DEPLOYED_TOKEN_QUANTITY,
UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
} from "./constants/constants";

Expand Down
4 changes: 2 additions & 2 deletions test/constants/constants.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { BigNumber } from "bignumber.js";
import { ether, gWei } from "../utils/units";

export const DEFAULT_GAS = 7000000;
export const DEFAULT_GAS = 10000000;
export const INVALID_OPCODE = "invalid opcode";
export const NULL_ADDRESS = "0x0000000000000000000000000000000000000000";
export const REVERT_ERROR = "revert";
export const STANDARD_INITIAL_TOKENS: BigNumber = ether(100000000000);
export const DEPLOYED_TOKEN_QUANTITY: BigNumber = ether(100000000000);
export const STANDARD_QUANTITY_ISSUED: BigNumber = ether(10);
export const STANDARD_NATURAL_UNIT = ether(1);
export const UNLIMITED_ALLOWANCE_IN_BASE_UNITS = new BigNumber(2).pow(256).minus(1);
Expand Down
Loading