Skip to content
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
15 changes: 12 additions & 3 deletions contracts/apx-protocol/ApxAssetsRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@ contract ApxAssetsRegistry is IApxAssetsRegistry {
// EVENTS
//------------------------
event RegisterAsset(address caller, address original, address mirrored, bool state, uint256 timestamp);
event UpdatePrice(address priceManager, address asset, uint256 price, uint256 expiry, uint256 timestamp);
event UpdatePrice(
address priceManager,
address asset,
uint256 price,
uint8 priceDecimal,
uint256 expiry,
uint256 timestamp
);
event UpdateState(address assetManager, address asset, bool active, uint256 timestamp);
event TransferMasterOwnerRole(address oldMasterOwner, address newMasterOwner, uint256 timestamp);
event TransferAssetManagerRole(address oldAssetManager, address newAssetManager, uint256 timestamp);
Expand Down Expand Up @@ -110,7 +117,7 @@ contract ApxAssetsRegistry is IApxAssetsRegistry {
true,
state,
block.timestamp,
0, 0, 0, 0, address(0)
0, 0, 0, 0, 0, address(0)
);
assetsList.push(mirrored);
emit RegisterAsset(msg.sender, original, mirrored, state, block.timestamp);
Expand All @@ -128,6 +135,7 @@ contract ApxAssetsRegistry is IApxAssetsRegistry {
function updatePrice(
address asset,
uint256 price,
uint8 priceDecimals,
uint256 expiry,
uint256 capturedSupply
) external override onlyPriceManagerOrMasterOwner assetExists(asset) {
Expand All @@ -136,11 +144,12 @@ contract ApxAssetsRegistry is IApxAssetsRegistry {
require(expiry > 0, "ApxAssetsRegistry: expiry has to be > 0;");
require(capturedSupply == IToken(asset).totalSupply(), "ApxAssetsRegistry: inconsistent asset supply.");
assets[asset].price = price;
assets[asset].priceDecimals = priceDecimals;
assets[asset].priceUpdatedAt = block.timestamp;
assets[asset].priceValidUntil = block.timestamp + expiry;
assets[asset].capturedSupply = capturedSupply;
assets[asset].priceProvider = msg.sender;
emit UpdatePrice(msg.sender, asset, price, expiry, block.timestamp);
emit UpdatePrice(msg.sender, asset, price, priceDecimals, expiry, block.timestamp);
}

function migrate(address newAssetsRegistry, address originalAsset) external override onlyMasterOwner {
Expand Down
1 change: 1 addition & 0 deletions contracts/apx-protocol/IApxAssetsRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ interface IApxAssetsRegistry is IVersioned {
function updatePrice(
address asset,
uint256 price,
uint8 priceDecimals,
uint256 expiry,
uint256 capturedSupply
) external;
Expand Down
5 changes: 0 additions & 5 deletions contracts/asset-simple/AssetSimple.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@ import "../shared/ICampaignCommon.sol";

contract AssetSimple is IAssetSimple, ERC20 {

//------------------------
// CONSTANTS
//------------------------
uint256 constant public override priceDecimalsPrecision = 10 ** 4;

//-----------------------
// STATE
//-----------------------
Expand Down
66 changes: 48 additions & 18 deletions contracts/asset-transferable/AssetTransferable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ import "../shared/Structs.sol";
contract AssetTransferable is IAssetTransferable, ERC20 {
using SafeERC20 for IERC20;

//------------------------
// CONSTANTS
//------------------------
uint256 constant public override priceDecimalsPrecision = 10 ** 4;

//----------------------
// STATE
//------------------------
Expand Down Expand Up @@ -72,7 +67,7 @@ contract AssetTransferable is IAssetTransferable, ERC20 {
params.info,
params.name,
params.symbol,
0, 0, 0,
0, 0, 0, 0,
false,
0, 0, 0
);
Expand Down Expand Up @@ -149,6 +144,7 @@ contract AssetTransferable is IAssetTransferable, ERC20 {
uint256 tokenValue = campaignState.fundsRaised;
uint256 tokenAmount = campaignState.tokensSold;
uint256 tokenPrice = campaignState.pricePerToken;
uint8 tokenPriceDecimals = campaignState.tokenPriceDecimals;
require(
tokenAmount > 0 && balanceOf(campaign) >= tokenAmount,
"AssetTransferable: Campaign has signalled the sale finalization but campaign tokens are not present"
Expand All @@ -164,7 +160,14 @@ contract AssetTransferable is IAssetTransferable, ERC20 {
);
sellHistory.push(tokenSaleInfo);
successfulTokenSalesMap[campaign] = tokenSaleInfo;
if (tokenPrice > state.highestTokenSellPrice) { state.highestTokenSellPrice = tokenPrice; }
if (
(state.highestTokenSellPrice == 0 && state.highestTokenSellPriceDecimals == 0) ||
_tokenValue(tokenAmount, tokenPrice, tokenPriceDecimals) >
_tokenValue(tokenAmount, state.highestTokenSellPrice, state.highestTokenSellPriceDecimals)
) {
state.highestTokenSellPrice = tokenPrice;
state.highestTokenSellPriceDecimals = tokenPriceDecimals;
}
emit FinalizeSale(
msg.sender,
tokenAmount,
Expand All @@ -179,17 +182,47 @@ contract AssetTransferable is IAssetTransferable, ERC20 {
require(assetRecord.exists, "AssetTransferable: Not registered in Apx Registry");
require(assetRecord.state, "AssetTransferable: Asset blocked in Apx Registry");
require(assetRecord.mirroredToken == address(this), "AssetTransferable: Invalid mirrored asset record");
require(block.timestamp <= assetRecord.priceValidUntil, "AssetTransferable: Price expired");
uint256 liquidationPrice =
(state.highestTokenSellPrice > assetRecord.price) ? state.highestTokenSellPrice : assetRecord.price;
require(block.timestamp <= assetRecord.priceValidUntil, "AssetTransferable: Price expired");
uint256 liquidationAmount;
uint256 liquidationPrice;
uint8 liquidationPriceDecimals;
uint256 liquidationAmountPerLargestSalePrice = _tokenValue(
totalSupply(),
state.highestTokenSellPrice,
state.highestTokenSellPriceDecimals
);
uint256 liquidationAmountPerMarketPrice = _tokenValue(
totalSupply(),
assetRecord.price,
assetRecord.priceDecimals
);
if (liquidationAmountPerLargestSalePrice > liquidationAmountPerMarketPrice) {
liquidationAmount = liquidationAmountPerLargestSalePrice;
liquidationPrice = state.highestTokenSellPrice;
liquidationPriceDecimals = state.highestTokenSellPriceDecimals;
} else {
liquidationAmount = liquidationAmountPerMarketPrice;
liquidationPrice = assetRecord.price;
liquidationPriceDecimals = assetRecord.priceDecimals;
}

uint256 liquidatorApprovedTokenAmount = this.allowance(msg.sender, address(this));
uint256 liquidatorApprovedTokenValue = _tokenValue(liquidatorApprovedTokenAmount, liquidationPrice);
uint256 liquidatorApprovedTokenValue = _tokenValue(
liquidatorApprovedTokenAmount,
liquidationPrice,
liquidationPriceDecimals
);

if (liquidatorApprovedTokenValue > 0) {
liquidationClaimsMap[msg.sender] += liquidatorApprovedTokenValue;
state.liquidationFundsClaimed += liquidatorApprovedTokenValue;
this.transferFrom(msg.sender, address(this), liquidatorApprovedTokenAmount);
}
uint256 liquidationFundsTotal = _tokenValue(totalSupply(), liquidationPrice);
uint256 liquidationFundsTotal = _tokenValue(
totalSupply(),
liquidationPrice,
liquidationPriceDecimals
);
uint256 liquidationFundsToPull = liquidationFundsTotal - liquidatorApprovedTokenValue;
if (liquidationFundsToPull > 0) {
_stablecoin().safeTransferFrom(msg.sender, address(this), liquidationFundsToPull);
Expand Down Expand Up @@ -308,19 +341,16 @@ contract AssetTransferable is IAssetTransferable, ERC20 {
return true;
}

function _tokenValue(uint256 amount, uint256 price) private view returns (uint256) {
function _tokenValue(uint256 amount, uint256 price, uint8 priceDecimals) private view returns (uint256) {
return amount
* price
* _stablecoin_decimals_precision()
/ (_asset_decimals_precision() * priceDecimalsPrecision);
/ (10 ** priceDecimals)
/ (10 ** decimals());
}

function _stablecoin_decimals_precision() private view returns (uint256) {
return 10 ** IToken(_stablecoin_address()).decimals();
}

function _asset_decimals_precision() private view returns (uint256) {
return 10 ** decimals();
}

}
59 changes: 46 additions & 13 deletions contracts/asset/Asset.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ import "../shared/Structs.sol";
contract Asset is IAsset, ERC20 {
using SafeERC20 for IERC20;

//------------------------
// CONSTANTS
//------------------------
uint256 constant public override priceDecimalsPrecision = 10 ** 4;

//-----------------------
// STATE
//-----------------------
Expand Down Expand Up @@ -69,7 +64,7 @@ contract Asset is IAsset, ERC20 {
params.info,
params.name,
params.symbol,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
false,
0, 0, 0
);
Expand Down Expand Up @@ -183,6 +178,7 @@ contract Asset is IAsset, ERC20 {
uint256 tokenValue = campaignState.fundsRaised;
uint256 tokenAmount = campaignState.tokensSold;
uint256 tokenPrice = campaignState.pricePerToken;
uint8 tokenPriceDecimals = campaignState.tokenPriceDecimals;
require(
tokenAmount > 0 && balanceOf(campaign) >= tokenAmount,
"Asset: Campaign has signalled the sale finalization but campaign tokens are not present"
Expand All @@ -198,7 +194,14 @@ contract Asset is IAsset, ERC20 {
);
sellHistory.push(tokenSaleInfo);
successfulTokenSalesMap[campaign] = tokenSaleInfo;
if (tokenPrice > state.highestTokenSellPrice) { state.highestTokenSellPrice = tokenPrice; }
if (
(state.highestTokenSellPrice == 0 && state.highestTokenSellPriceDecimals == 0) ||
_tokenValue(tokenAmount, tokenPrice, tokenPriceDecimals) >
_tokenValue(tokenAmount, state.highestTokenSellPrice, state.highestTokenSellPriceDecimals)
) {
state.highestTokenSellPrice = tokenPrice;
state.highestTokenSellPriceDecimals = tokenPriceDecimals;
}
emit FinalizeSale(
msg.sender,
tokenAmount,
Expand All @@ -209,29 +212,58 @@ contract Asset is IAsset, ERC20 {

function liquidate() external override ownerOnly {
require(!state.liquidated, "Asset: Action forbidden, asset liquidated.");
uint256 liquidationAmount;
uint256 liquidationPrice;
uint8 liquidationPriceDecimals;
uint256 liquidationAmountPerLargestSalePrice = _tokenValue(
totalSupply(),
state.highestTokenSellPrice,
state.highestTokenSellPriceDecimals
);
if (state.totalTokensLocked > 0) {
IApxAssetsRegistry apxRegistry = IApxAssetsRegistry(state.apxRegistry);
Structs.AssetRecord memory assetRecord = apxRegistry.getMirroredFromOriginal(address(this));
require(assetRecord.state, "Asset: Asset blocked in Apx Registry");
require(assetRecord.originalToken == address(this), "Asset: Invalid mirrored asset record");
require(block.timestamp <= assetRecord.priceValidUntil, "Asset: Price expired");
require(state.totalTokensLocked == assetRecord.capturedSupply, "Asset: MirroredToken supply inconsistent");
liquidationPrice =
(state.highestTokenSellPrice > assetRecord.price) ? state.highestTokenSellPrice : assetRecord.price;
uint256 liquidationAmountPerMarketPrice = _tokenValue(
totalSupply(),
assetRecord.price,
assetRecord.priceDecimals
);
if (liquidationAmountPerLargestSalePrice > liquidationAmountPerMarketPrice) {
liquidationAmount = liquidationAmountPerLargestSalePrice;
liquidationPrice = state.highestTokenSellPrice;
liquidationPriceDecimals = state.highestTokenSellPriceDecimals;
} else {
liquidationAmount = liquidationAmountPerMarketPrice;
liquidationPrice = assetRecord.price;
liquidationPriceDecimals = assetRecord.priceDecimals;
}
} else {
liquidationAmount = liquidationAmountPerLargestSalePrice;
liquidationPrice = state.highestTokenSellPrice;
liquidationPriceDecimals = state.highestTokenSellPriceDecimals;
}

uint256 liquidatorApprovedTokenAmount = this.allowance(msg.sender, address(this));
uint256 liquidatorApprovedTokenValue = _tokenValue(liquidatorApprovedTokenAmount, liquidationPrice);
uint256 liquidatorApprovedTokenValue = _tokenValue(
liquidatorApprovedTokenAmount,
liquidationPrice,
liquidationPriceDecimals
);
if (liquidatorApprovedTokenValue > 0) {
liquidationClaimsMap[msg.sender] += liquidatorApprovedTokenValue;
state.liquidationFundsClaimed += liquidatorApprovedTokenValue;
this.transferFrom(msg.sender, address(this), liquidatorApprovedTokenAmount);
}

uint256 liquidationFundsTotal = _tokenValue(totalSupply(), liquidationPrice);
uint256 liquidationFundsTotal = _tokenValue(
totalSupply(),
liquidationPrice,
liquidationPriceDecimals
);
uint256 liquidationFundsToPull = liquidationFundsTotal - liquidatorApprovedTokenValue;
if (liquidationFundsToPull > 0) {
_stablecoin().safeTransferFrom(msg.sender, address(this), liquidationFundsToPull);
Expand Down Expand Up @@ -348,11 +380,12 @@ contract Asset is IAsset, ERC20 {
return (campaignRecord.wallet == campaignAddress && campaignRecord.whitelisted);
}

function _tokenValue(uint256 amount, uint256 price) private view returns (uint256) {
function _tokenValue(uint256 amount, uint256 price, uint8 priceDecimals) private view returns (uint256) {
return amount
* price
* (10 ** IToken(_stablecoin_address()).decimals())
/ ((10 ** decimals()) * priceDecimalsPrecision);
/ (10 ** priceDecimals)
/ (10 ** decimals());
}

}
Loading