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
3 changes: 2 additions & 1 deletion .solcover.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module.exports = {
'Migrations.sol',
'test',
'external',
'core/lib/ERC20Wrapper.sol' // TODO: Find a cleaner way to test state mutating functions, we will skip
'core/lib/ERC20Wrapper.sol', // TODO: Find a cleaner way to test state mutating functions, we will skip
'core/exchange-wrappers/ZeroExExchangeWrapper.sol' // TODO: test this
],
};
4 changes: 2 additions & 2 deletions .soliumignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
node_modules
contracts/lib
contracts/test
contracts/Migrations.sol
contracts/Migrations.sol
contracts/external
62 changes: 21 additions & 41 deletions contracts/core/TransferProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ import { ERC20Wrapper } from "./lib/ERC20Wrapper.sol";
* @title TransferProxy
* @author Set Protocol
*
* The proxy contract is responsible for transferring funds from the user to the vault during Set issuance.
* The contract is separated to allow for upgrades, particularly if new token standards emerge or upgrades are required.
* The proxy contract is responsible for updating token balances to assist with issuance
* and filling issuance orders.
*/

contract TransferProxy is
Expand All @@ -36,69 +36,49 @@ contract TransferProxy is
// Use SafeMath library for all uint256 arithmetic
using SafeMath for uint256;

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

// Address of the Vault contract
address public vaultAddress;

/* ============ No Constructor ============ */

/* ============ Setter Functions ============ */

/**
* Set vaultAddress. Can only be set by owner of TransferProxy.
*
* @param _vaultAddress The address of the Vault
*/

function setVaultAddress(
address _vaultAddress
)
external
onlyOwner
{
// Commit passed address to vaultAddress state variable
vaultAddress = _vaultAddress;
}

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

/**
* Transfers tokens from an address (that has set allowance on the proxy) to the vault.
* Transfers tokens from an address (that has set allowance on the proxy).
* Can only be called by authorized core contracts.
*
* @param _from The address to transfer tokens from
* @param _tokenAddress The address of the ERC20 token
* @param _quantity The number of tokens to transfer
* @param _from The address to transfer from
* @param _to The address to transfer to
*/
function transferToVault(
address _from,
function transfer(
address _tokenAddress,
uint _quantity
uint _quantity,
address _from,
address _to
)
external
onlyAuthorized
{
// Retrieve current balance of token for the vault
uint existingVaultBalance = ERC20Wrapper.balanceOf(
// Retrieve current balance of token for the receiver
uint existingBalance = ERC20Wrapper.balanceOf(
_tokenAddress,
vaultAddress
_to
);

// Call specified ERC20 contract to transfer tokens from user to Vault (via proxy).

// Call specified ERC20 contract to transfer tokens (via proxy).
ERC20Wrapper.transferFrom(
_tokenAddress,
_from,
vaultAddress,
_to,
_quantity
);

// Verify transfer quantity is reflected in balance
uint newVaultBalance = ERC20Wrapper.balanceOf(
// Get new balance of transferred token for receiver
uint newBalance = ERC20Wrapper.balanceOf(
_tokenAddress,
vaultAddress
_to
);
require(newVaultBalance == existingVaultBalance.add(_quantity));

// Verify transfer quantity is reflected in balance
require(newBalance == existingBalance.add(_quantity));
}
}
1 change: 1 addition & 0 deletions contracts/core/exchange-wrappers/ZeroExExchangeWrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ contract ZeroExExchangeWrapper
/* ============ Getters ============ */

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

function fillZeroExOrder(
bytes _zeroExOrderData
)
Expand Down
19 changes: 13 additions & 6 deletions contracts/core/exchange-wrappers/lib/ZeroExOrderDataHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ library ZeroExOrderDataHandler {
using SafeMath for uint256;
using LibBytes for bytes;

// ============ Constants ============

bytes4 constant ERC20_SELECTOR = bytes4(keccak256("ERC20Token(address)"));

string constant INVALID_TOKEN_ADDRESS = 'Address is not for ERC20 asset.';

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

struct ZeroExHeader {
Expand Down Expand Up @@ -160,11 +166,11 @@ library ZeroExOrderDataHandler {
// ** - Maker Asset Data Length
// *** - Taker Asset Data Length
assembly {
mstore(order, mload(orderDataAddr)) // maker
mstore(order, mload(orderDataAddr)) // maker
mstore(add(order, 32), mload(add(orderDataAddr, 32))) // taker
mstore(add(order, 64), mload(add(orderDataAddr, 64))) // feeRecipient
mstore(add(order, 96), mload(add(orderDataAddr, 96))) // senderAddress
mstore(add(order, 128), mload(add(orderDataAddr, 128))) // makerAssetAmount
mstore(add(order, 128), mload(add(orderDataAddr, 128))) // makerAssetAmount
mstore(add(order, 160), mload(add(orderDataAddr, 160))) // takerAssetAmount
mstore(add(order, 192), mload(add(orderDataAddr, 192))) // makerFee
mstore(add(order, 224), mload(add(orderDataAddr, 224))) // takerFee
Expand Down Expand Up @@ -202,11 +208,12 @@ library ZeroExOrderDataHandler {
pure
returns(address)
{
bytes4 ERC20_SELECTOR = bytes4(keccak256("ERC20Token(address)"));
// Ensure that the asset is ERC20
bytes4 orderProxyId = _assetData.readBytes4(0);

require(ERC20_SELECTOR == orderProxyId);
bytes4 assetType = _assetData.readBytes4(0);
require(
ERC20_SELECTOR == assetType,
INVALID_TOKEN_ADDRESS
);

address tokenAddress = address(_assetData.readBytes32(4));

Expand Down
7 changes: 4 additions & 3 deletions contracts/core/extensions/CoreAccounting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,11 @@ contract CoreAccounting is
isPositiveQuantity(_quantity)
{
// Call TransferProxy contract to transfer user tokens to Vault
ITransferProxy(state.transferProxyAddress).transferToVault(
msg.sender,
ITransferProxy(state.transferProxyAddress).transfer(
_tokenAddress,
_quantity
_quantity,
msg.sender,
state.vaultAddress
);

// Call Vault contract to attribute deposited tokens to user
Expand Down
7 changes: 4 additions & 3 deletions contracts/core/extensions/CoreIssuance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -259,10 +259,11 @@ contract CoreIssuance is
uint amountToDeposit = requiredComponentQuantity.sub(vaultBalance);

// Transfer the remainder component quantity required to vault
ITransferProxy(state.transferProxyAddress).transferToVault(
_owner,
ITransferProxy(state.transferProxyAddress).transfer(
component,
requiredComponentQuantity.sub(vaultBalance)
requiredComponentQuantity.sub(vaultBalance),
_owner,
state.vaultAddress
);

// Log transfer of component from issuer waller
Expand Down
12 changes: 7 additions & 5 deletions contracts/core/interfaces/ITransferProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,19 @@ pragma solidity 0.4.24;
interface ITransferProxy {

/**
* Transfers tokens from an address (that has set allowance on the proxy) to the vault.
* Transfers tokens from an address (that has set allowance on the proxy).
* Can only be called by authorized core contracts.
*
* @param _from The address to transfer tokens from
* @param _tokenAddress The address of the ERC20 token
* @param _quantity The number of tokens to transfer
* @param _from The address to transfer from
* @param _to The address to transfer to
*/
function transferToVault(
address _from,
function transfer(
address _tokenAddress,
uint _quantity
uint _quantity,
address _from,
address _to
)
external;
}
2 changes: 1 addition & 1 deletion test/core/extensions/coreAccounting.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ contract("CoreAccounting", (accounts) => {
beforeEach(async () => {
core = await coreWrapper.deployCoreAsync();
vault = await coreWrapper.deployVaultAsync();
transferProxy = await coreWrapper.deployTransferProxyAsync(vault.address);
transferProxy = await coreWrapper.deployTransferProxyAsync();
setTokenFactory = await coreWrapper.deploySetTokenFactoryAsync();
await coreWrapper.setDefaultStateAndAuthorizationsAsync(core, vault, transferProxy, setTokenFactory);
});
Expand Down
4 changes: 2 additions & 2 deletions test/core/extensions/coreInternal.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ contract("CoreInternal", (accounts) => {

beforeEach(async () => {
vault = await coreWrapper.deployVaultAsync();
transferProxy = await coreWrapper.deployTransferProxyAsync(vault.address);
transferProxy = await coreWrapper.deployTransferProxyAsync();

subjectCaller = ownerAccount;
});
Expand Down Expand Up @@ -209,7 +209,7 @@ contract("CoreInternal", (accounts) => {

beforeEach(async () => {
vault = await coreWrapper.deployVaultAsync();
transferProxy = await coreWrapper.deployTransferProxyAsync(vault.address);
transferProxy = await coreWrapper.deployTransferProxyAsync();
setTokenFactory = await coreWrapper.deploySetTokenFactoryAsync();
await coreWrapper.setDefaultStateAndAuthorizationsAsync(core, vault, transferProxy, setTokenFactory);

Expand Down
2 changes: 1 addition & 1 deletion test/core/extensions/coreIssuance.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ contract("CoreIssuance", (accounts) => {
beforeEach(async () => {
core = await coreWrapper.deployCoreAsync();
vault = await coreWrapper.deployVaultAsync();
transferProxy = await coreWrapper.deployTransferProxyAsync(vault.address);
transferProxy = await coreWrapper.deployTransferProxyAsync();
setTokenFactory = await coreWrapper.deploySetTokenFactoryAsync();
await coreWrapper.setDefaultStateAndAuthorizationsAsync(core, vault, transferProxy, setTokenFactory);
});
Expand Down
2 changes: 1 addition & 1 deletion test/core/extensions/coreIssuanceOrder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ contract("CoreIssuanceOrder", (accounts) => {
beforeEach(async () => {
core = await coreWrapper.deployCoreAsync();
vault = await coreWrapper.deployVaultAsync();
transferProxy = await coreWrapper.deployTransferProxyAsync(vault.address);
transferProxy = await coreWrapper.deployTransferProxyAsync();
setTokenFactory = await coreWrapper.deploySetTokenFactoryAsync();
await coreWrapper.setDefaultStateAndAuthorizationsAsync(core, vault, transferProxy, setTokenFactory);
});
Expand Down
20 changes: 16 additions & 4 deletions test/core/external/lib/mockZeroExOrderDataHandlerLibrary.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ BigNumberSetup.configure();
ChaiSetup.configure();
const { expect, assert } = chai;

import {
expectRevertError,
} from "../../../utils/tokenAssertions";

import {
DEFAULT_GAS,
} from "../../../utils/constants";
Expand Down Expand Up @@ -95,7 +99,6 @@ contract("MockZeroExOrderDataHandlerLibrary", (accounts) => {
let makerAssetDataLength: BigNumber;
let takerAssetDataLength: BigNumber;


let subjectOrderData: Bytes32;

beforeEach(async () => {
Expand Down Expand Up @@ -213,20 +216,29 @@ contract("MockZeroExOrderDataHandlerLibrary", (accounts) => {
});

describe("#parseERC20TokenAddress", async () => {
let subjectOrderData: Bytes32;
let subjectAssetData: Bytes32;

beforeEach(async () => {
subjectOrderData = makerAssetData;
subjectAssetData = makerAssetData;
});

async function subject(): Promise<any> {
return zeroExExchangeWrapper.parseERC20TokenAddress.callAsync(subjectOrderData);
return zeroExExchangeWrapper.parseERC20TokenAddress.callAsync(subjectAssetData);
}

it("should correctly parse the maker token address", async () => {
const makerTokenAddressResult = await subject();
expect(makerTokenAddressResult).to.equal(makerTokenAddress);
});

describe("when the asset type for the token is not ERC20", async () => {
beforeEach(async () => {
subjectAssetData = '0xInvalidAssetSelector';
});

it("should revert", async () => {
await expectRevertError(subject());
});
});
});
});
43 changes: 5 additions & 38 deletions test/core/transferProxy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,41 +58,7 @@ contract("TransferProxy", (accounts) => {
ABIDecoder.removeABI(TransferProxy.abi);
});

describe("#setVaultAddress", async () => {
// Setup
beforeEach(async () => {
transferProxy = await coreWrapper.deployTransferProxyAsync(vaultAccount);
});

// Subject
let caller: Address = ownerAccount;

async function subject(): Promise<string> {
return transferProxy.setVaultAddress.sendTransactionAsync(
vaultAccount,
{ from: caller },
);
}

it("sets vault address correctly", async () => {
await subject();

const storedVaultAddress = await transferProxy.vaultAddress.callAsync();
expect(storedVaultAddress).to.eql(vaultAccount);
});

describe("when the caller is not the owner of the contract", async () => {
before(async () => {
caller = otherAccount;
});

it("should revert", async () => {
await expectRevertError(subject());
});
});
});

describe("#transferToVault", async () => {
describe("#transfer", async () => {
// Setup
let approver: Address = ownerAccount;
let authorizedContract: Address = authorizedAccount;
Expand All @@ -101,7 +67,7 @@ contract("TransferProxy", (accounts) => {
let tokenAddress: Address;

beforeEach(async () => {
transferProxy = await coreWrapper.deployTransferProxyAsync(vaultAccount);
transferProxy = await coreWrapper.deployTransferProxyAsync();
await coreWrapper.addAuthorizationAsync(transferProxy, authorizedContract);
mockToken = await erc20Wrapper.deployTokenAsync(ownerAccount);
await erc20Wrapper.approveTransferAsync(mockToken, transferProxy.address, approver);
Expand All @@ -118,10 +84,11 @@ contract("TransferProxy", (accounts) => {
// Initialize tokenToTransfer to deployed token's address unless tokenAddress is overwritten in test cases
const tokenToTransfer = tokenAddress || mockToken.address;

return transferProxy.transferToVault.sendTransactionAsync(
subjectCaller,
return transferProxy.transfer.sendTransactionAsync(
tokenToTransfer,
amountToTransfer,
subjectCaller,
vaultAccount,
{ from: authorizedContract },
);
}
Expand Down
Loading