Skip to content

Commit

Permalink
Merge pull request #1024 from poanetwork/proxy-finalize
Browse files Browse the repository at this point in the history
(Feature) Enable proxy for both strategies
  • Loading branch information
vbaranov committed Jul 13, 2018
2 parents c1f3c2f + 6772136 commit 2f3a1ba
Show file tree
Hide file tree
Showing 21 changed files with 625 additions and 185 deletions.
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ REACT_APP_MINTED_CAPPED_TOKEN_MANAGER_ADDRESS='{"3":"0xbcb59c250cd3b33573ac61336
REACT_APP_DUTCH_IDX_ADDRESS='{"3":"0x9e691aa80c516c7d92de3171b650c8e38ab54de5"}'
REACT_APP_DUTCH_CROWDSALE_ADDRESS='{"3":"0x5db537686cea5a252996bec21ff4755cf604574c"}'
REACT_APP_DUTCH_TOKEN_ADDRESS='{"3":"0x59068cb9a592b9f29a7ded22c5caff5d268fec72"}'
REACT_APP_TW_PROXIES_REGISTRY_ADDRESS='{"3":"0x8eb31cf60ebc201a11eebd6e43a60bba0605c826"}'
REACT_APP_PROXY_PROVIDER_ADDRESS='{"3":"0x5eadd1456ce64247b48bac2e53605b4a934c53fd"}'
REACT_APP_REGISTRY_EXEC_ID='0x116fea137db6e131133e7f2bab296045d8f41cc5607279db17b218cab0929a51'
REACT_APP_MINTED_CAPPED_APP_NAME='MintedCappedCrowdsale'
REACT_APP_DUTCH_APP_NAME='DutchCrowdsale'
Expand Down
14 changes: 14 additions & 0 deletions public/contracts/DutchProxy.abi
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,20 @@
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getCrowdsaleUniqueBuyers",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
Expand Down
2 changes: 1 addition & 1 deletion public/contracts/DutchProxy.bin

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions public/contracts/DutchProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface IAdmin {
function getTokensSold() external view returns (uint);
function getCrowdsaleWhitelist() external view returns (uint, address[]);
function getWhitelistStatus(address) external view returns (uint, uint);
function getCrowdsaleUniqueBuyers() external view returns (uint);
}

interface AdminIdx {
Expand All @@ -22,6 +23,7 @@ interface AdminIdx {
function getTokensSold(address, bytes32) external view returns (uint);
function getCrowdsaleWhitelist(address, bytes32) external view returns (uint, address[]);
function getWhitelistStatus(address, bytes32, address) external view returns (uint, uint);
function getCrowdsaleUniqueBuyers(address, bytes32) external view returns (uint);
}

// File: contracts/classes/sale/ISale.sol
Expand Down Expand Up @@ -255,6 +257,15 @@ contract AdminProxy is IAdmin, SaleProxy {
function getWhitelistStatus(address _buyer) external view returns (uint, uint) {
return AdminIdx(app_index).getWhitelistStatus(app_storage, app_exec_id, _buyer);
}

/*
Returns the number of unique addresses that have participated in the crowdsale
@return uint: The number of unique addresses that have participated in the crowdsale
*/
function getCrowdsaleUniqueBuyers() external view returns (uint) {
return AdminIdx(app_index).getCrowdsaleUniqueBuyers(app_storage, app_exec_id);
}
}

contract TokenProxy is IToken, AdminProxy {
Expand Down
142 changes: 142 additions & 0 deletions public/contracts/ProxiesRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
pragma solidity ^0.4.24;

// File: openzeppelin-solidity/contracts/ownership/Ownable.sol

/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;


event OwnershipRenounced(address indexed previousOwner);
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);


/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor() public {
owner = msg.sender;
}

/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}

/**
* @dev Allows the current owner to relinquish control of the contract.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipRenounced(owner);
owner = address(0);
}

/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param _newOwner The address to transfer ownership to.
*/
function transferOwnership(address _newOwner) public onlyOwner {
_transferOwnership(_newOwner);
}

/**
* @dev Transfers control of the contract to a newOwner.
* @param _newOwner The address to transfer ownership to.
*/
function _transferOwnership(address _newOwner) internal {
require(_newOwner != address(0));
emit OwnershipTransferred(owner, _newOwner);
owner = _newOwner;
}
}

// File: contracts/ProxiesRegistry.sol

contract AbstractProxy {
bytes32 public app_exec_id;
function getAdmin() external view returns (address);
}

contract AbstractIdx {
function getAdmin(address, bytes32) external view returns (address);
}

/**
* Registry of Proxy smart-contracts deployed from Token Wizard 2.0.
*/
contract TokenWizardProxiesRegistry is Ownable {
address public abstractStorageAddr;
address public mintedCappedIdxAddr;
address public dutchIdxAddr;
mapping (address => Crowdsale[]) private deployedCrowdsalesByUser;
event Added(address indexed sender, address indexed proxyAddress, bytes32 appExecID);
struct Crowdsale {
address proxyAddress;
bytes32 execID;
}

constructor (
address _abstractStorage,
address _mintedCappedIdx,
address _dutchIdx
) public {
require(_abstractStorage != address(0));
require(_mintedCappedIdx != address(0));
require(_dutchIdx != address(0));
require(_abstractStorage != _mintedCappedIdx && _abstractStorage != _dutchIdx && _mintedCappedIdx != _dutchIdx);
abstractStorageAddr = _abstractStorage;
mintedCappedIdxAddr = _mintedCappedIdx;
dutchIdxAddr = _dutchIdx;
}

function changeAbstractStorage(address newAbstractStorageAddr) public onlyOwner {
abstractStorageAddr = newAbstractStorageAddr;
}

function changeMintedCappedIdx(address newMintedCappedIdxAddr) public onlyOwner {
mintedCappedIdxAddr = newMintedCappedIdxAddr;
}

function changeDutchIdxAddr(address newDutchIdxAddr) public onlyOwner {
dutchIdxAddr = newDutchIdxAddr;
}

function trackCrowdsale(address proxyAddress) public {
AbstractProxy proxy = AbstractProxy(proxyAddress);
require(proxyAddress != address(0));
require(msg.sender == proxy.getAdmin());
bytes32 appExecID = proxy.app_exec_id();
AbstractIdx mintedCappedIdx = AbstractIdx(mintedCappedIdxAddr);
AbstractIdx dutchIdx = AbstractIdx(dutchIdxAddr);
require(mintedCappedIdx.getAdmin(abstractStorageAddr, appExecID) == msg.sender || dutchIdx.getAdmin(abstractStorageAddr, appExecID) == msg.sender);
for (uint i = 0; i < deployedCrowdsalesByUser[msg.sender].length; i++) {
require(deployedCrowdsalesByUser[msg.sender][i].proxyAddress != proxyAddress);
require(deployedCrowdsalesByUser[msg.sender][i].execID != appExecID);
}
deployedCrowdsalesByUser[msg.sender].push(Crowdsale({proxyAddress: proxyAddress, execID: appExecID}));
emit Added(msg.sender, proxyAddress, appExecID);
}

function countCrowdsalesForUser(address deployer) public view returns (uint) {
return deployedCrowdsalesByUser[deployer].length;
}

function getCrowdsalesForUser(address deployer) public view returns (address[]) {
address[] memory proxies = new address[](deployedCrowdsalesByUser[deployer].length);
for (uint k = 0; k < deployedCrowdsalesByUser[deployer].length; k++) {
proxies[k] = deployedCrowdsalesByUser[deployer][k].proxyAddress;
}
return proxies;
}
}
23 changes: 7 additions & 16 deletions src/components/contribute/QRPaymentProcess.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,30 @@ import React from 'react'
import QRCode from 'qrcode.react'
import { CopyToClipboard } from 'react-copy-to-clipboard'

const QRPaymentProcess = ({ registryExecAddr, txData }) => {
const QRPaymentProcess = ({ crowdsaleProxyAddr, registryExecAddr, txData }) => {
const targetContractStr = crowdsaleProxyAddr ? 'Proxy' : 'RegistryExec'
const targetAddr = crowdsaleProxyAddr ? crowdsaleProxyAddr : registryExecAddr
return (
<div>
<div className="payment-process">
<div className="payment-process-qr">
<QRCode value={registryExecAddr} />
<QRCode value={targetAddr} />
</div>
<p className="payment-process-hash">{registryExecAddr}</p>
<p className="payment-process-hash">{targetAddr}</p>

<CopyToClipboard text={registryExecAddr}>
<CopyToClipboard text={targetAddr}>
<a href="" onClick={e => e.preventDefault()} className="payment-process-copy">
Copy Address
</a>
</CopyToClipboard>

{/* <div className="payment-process-loader">Waiting for payment</div> */}
<div className="payment-process-notation">
<p className="payment-process-notation-title">Important</p>
<p className="payment-process-notation-description">
Send ethers to the Auth-os RegistryExec smart-contract address with a data: {txData}
Send ethers to the Auth-os {targetContractStr} smart-contract address with a data: {txData}
</p>
</div>
</div>
{/* <div className="payment-process">
<div className="payment-process-success"></div>
<p className="payment-process-description">
Your Project tokens were sent to
</p>
<p className="payment-process-hash">
0x6b0770d930bB22990c83fBBfcba6faB129AD7E385
</p>
<a href="#" className="payment-process-see">See it on the blockchain</a>
</div> */}
</div>
)
}
Expand Down
25 changes: 19 additions & 6 deletions src/components/contribute/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import { ContributeForm } from './ContributeForm'
import { CopyToClipboard } from 'react-copy-to-clipboard'
import ReactTooltip from 'react-tooltip'
import logdown from 'logdown'
import { DEPLOYMENT_VALUES } from '../../utils/constants'

const logger = logdown('TW:contribute')

Expand Down Expand Up @@ -350,6 +351,8 @@ export class Contribute extends React.Component {

const userLimits = await getUserMaxLimits(addr, execID, methods, account)

logger.log('userLimits:', userLimits.toString())

return tokensToContribute.gt(userLimits) ? userLimits : tokensToContribute
}

Expand Down Expand Up @@ -403,7 +406,13 @@ export class Contribute extends React.Component {
const targetContractName = execID ? 'registryExec' : crowdsaleStore.proxyName
const method = methodToExec(targetContractName, `buy()`, this.getBuyParams, paramsToExec)

const estimatedGas = await method.estimateGas(opts)
let estimatedGas
try {
estimatedGas = await method.estimateGas(opts)
} catch (e) {
logger.error(e)
estimatedGas = DEPLOYMENT_VALUES.GAS_REQUIRED.BUY
}
logger.log('estimatedGas:', estimatedGas)

opts.gasLimit = calculateGasLimit(estimatedGas)
Expand Down Expand Up @@ -453,7 +462,7 @@ export class Contribute extends React.Component {
const { proxyName } = crowdsaleStore

const { curAddr, contributeThrough, web3Available, toNextTick, nextTick, minimumContribution } = this.state
const crowdsaleExecID = contractStore.crowdsale && contractStore.crowdsale.execID
const crowdsaleExecID = crowdsale && crowdsale.execID
const { days, hours, minutes, seconds } = toNextTick

const { decimals, ticker, name } = tokenStore
Expand All @@ -477,13 +486,17 @@ export class Contribute extends React.Component {
//min contribution
const minimumContributionDisplay =
minimumContribution >= 0 ? `${minimumContribution} ${tokenTicker}` : 'You are not allowed'

const registryExecAddr =
contractStore.registryExec && contractStore.registryExec.addr ? contractStore.registryExec.addr : ''

const crowdsaleProxyAddr = contractStore[proxyName] && contractStore[proxyName].addr
const QRPaymentProcessElement =
contributeThrough === CONTRIBUTION_OPTIONS.QR &&
(crowdsaleExecID || (contractStore[proxyName] && contractStore[proxyName].addr)) ? (
<QRPaymentProcess registryExecAddr={registryExecAddr} txData={getExecBuyCallData(crowdsaleExecID)} />
contributeThrough === CONTRIBUTION_OPTIONS.QR && (crowdsaleExecID || crowdsaleProxyAddr) ? (
<QRPaymentProcess
crowdsaleProxyAddr={crowdsaleProxyAddr}
registryExecAddr={registryExecAddr}
txData={getExecBuyCallData(crowdsaleExecID)}
/>
) : null

const rightColumnClasses = classNames('contribute-table-cell', 'contribute-table-cell_right', {
Expand Down
39 changes: 28 additions & 11 deletions src/components/crowdsale/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,26 @@ export let getCrowdsaleData = async (initCrowdsaleContract, execID) => {
if (crowdsaleInfo && !crowdsaleInfo.hasOwnProperty('team_wallet')) {
crowdsaleInfo.team_wallet = crowdsaleInfo[1]
}
if (crowdsaleInfo && !crowdsaleInfo.hasOwnProperty('is_initialized')) {
crowdsaleInfo.is_initialized = crowdsaleInfo[2]
}
if (crowdsaleInfo && !crowdsaleInfo.hasOwnProperty('is_finalized')) {
crowdsaleInfo.is_finalized = crowdsaleInfo[3]
if (crowdsaleStore.isMintedCappedCrowdsale) {
if (crowdsaleInfo && !crowdsaleInfo.hasOwnProperty('is_initialized')) {
crowdsaleInfo.is_initialized = crowdsaleInfo[2]
}
if (crowdsaleInfo && !crowdsaleInfo.hasOwnProperty('is_finalized')) {
crowdsaleInfo.is_finalized = crowdsaleInfo[3]
}
} else if (crowdsaleStore.isDutchAuction) {
if (crowdsaleInfo && !crowdsaleInfo.hasOwnProperty('is_initialized')) {
crowdsaleInfo.is_initialized = crowdsaleInfo[3]
}
if (crowdsaleInfo && !crowdsaleInfo.hasOwnProperty('is_finalized')) {
crowdsaleInfo.is_finalized = crowdsaleInfo[4]
}
}
const wei_raised = crowdsaleInfo.wei_raised ? crowdsaleInfo.wei_raised : crowdsaleInfo[0]
const wei_raised = crowdsaleInfo.wei_raised
let tokensSold = await getTokensSold(...params).call()
logger.log('tokensSold:', tokensSold)
const contributors = await getCrowdsaleUniqueBuyers(...params).call()
const { fromWei } = web3Store.web3.utils

crowdsalePageStore.setProperty('weiRaised', wei_raised)
crowdsalePageStore.setProperty('ethRaised', fromWei(wei_raised, 'ether'))
crowdsalePageStore.setProperty('tokensSold', tokensSold)
Expand All @@ -130,13 +139,19 @@ export let getCrowdsaleData = async (initCrowdsaleContract, execID) => {

if (crowdsaleStore.isDutchAuction) {
const crowdsaleStatus = await getCrowdsaleStatus(...params).call()
const { max_sellable } = await isCrowdsaleFull(...params).call()
crowdsaleStatus.start_rate = crowdsaleStatus.start_rate || crowdsaleStatus[0]
crowdsaleStatus.end_rate = crowdsaleStatus.end_rate || crowdsaleStatus[1]
crowdsaleStatus.current_rate = crowdsaleStatus.current_rate || crowdsaleStatus[2]
crowdsaleStatus.time_remaining = crowdsaleStatus.time_remaining || crowdsaleStatus[4]
const tokens_remaining = crowdsaleStatus.tokens_remaining || crowdsaleStatus[5]
const _isCrowdsaleFull = await isCrowdsaleFull(...params).call()
const max_sellable = _isCrowdsaleFull.max_sellable || _isCrowdsaleFull[1]
const current_rate = chooseRateForDutchAuction(crowdsaleStatus)

crowdsalePageStore.setProperty('rate', current_rate) //should be one token in wei
crowdsalePageStore.setProperty('maximumSellableTokens', max_sellable)

const tokenRemainingBN = toBigNumber(crowdsaleStatus.tokens_remaining)
const tokenRemainingBN = toBigNumber(tokens_remaining)
const curRateBN = toBigNumber(current_rate) //one token in wei
const remainingWEI = curRateBN.gt(0)
? tokenRemainingBN
Expand Down Expand Up @@ -219,6 +234,8 @@ export let getCrowdsaleTargetDates = (initCrowdsaleContract, execID) => {
.getCrowdsaleStartAndEndTimes(...params)
.call()
.then(crowdsaleStartAndEndTimes => {
crowdsaleStartAndEndTimes.start_time = crowdsaleStartAndEndTimes.start_time || crowdsaleStartAndEndTimes[0]
crowdsaleStartAndEndTimes.end_time = crowdsaleStartAndEndTimes.end_time || crowdsaleStartAndEndTimes[1]
const tierDates = setTierDates(crowdsaleStartAndEndTimes.start_time, crowdsaleStartAndEndTimes.end_time)

fillCrowdsalePageStoreDates(tierDates.startsAtMilliseconds, tierDates.endsAtMilliseconds)
Expand Down Expand Up @@ -319,7 +336,7 @@ export const getUserMaxLimits = async (addr, execID, methods, account) => {

const whitelistStatus = await getWhitelistStatus(...params, tier_index, account).call()
const max_tokens_remaining = whitelistStatus.max_tokens_remaining || whitelistStatus[1]
const maxTokensRemaining = toBigNumber(max_tokens_remaining)
const maxTokensRemaining = toBigNumber(max_tokens_remaining).times(currentRate)

return tierTokensRemaining.lt(maxTokensRemaining) ? tierTokensRemaining : maxTokensRemaining
} else if (crowdsaleStore.isDutchAuction) {
Expand All @@ -337,7 +354,7 @@ export const getUserMaxLimits = async (addr, execID, methods, account) => {

const whitelistStatus = await getWhitelistStatus(...params, account).call()
const max_tokens_remaining = whitelistStatus.max_tokens_remaining || whitelistStatus[1]
const maxTokensRemaining = toBigNumber(max_tokens_remaining)
const maxTokensRemaining = toBigNumber(max_tokens_remaining).times(currentRate)

return crowdsaleTokensRemaining.lt(maxTokensRemaining) ? crowdsaleTokensRemaining : maxTokensRemaining
}
Expand Down
Loading

0 comments on commit 2f3a1ba

Please sign in to comment.