Skip to content
This repository has been archived by the owner on Jun 18, 2018. It is now read-only.

WingsDao/wings-bridge

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DEPRECATED


Wings Bridge

This is Wings bridge, based on custom crowdsale contract and integration that allowing only to provide collected amount and automatically move rewards on bridge smart contract.

It makes integration much simple and easy to do.

Requirements

  • Nodejs v8
  • Truffle
  • Testrpc

Flow

This Wings bridge contract works like communication contract, allows to message to Wings amount that ICO collecting during crowdsale.

What you need to do step by step:

  • Install this package as dependency for your smart contracts.
  • Inherit your main Crowdsale contract from Connector contract.
  • Call method notifySale when sale/exchange of your tokens happen.
  • Call method closeBridge and impelement movement of rewards to bridge contract.
  • Deploy your token/crowdsale etc contracts, deploy bridge contract, call method changeBridge on your crowdsale contract and pass there address of deployed bridge contract.
  • Create project on Wings, as 3rd party crowdsale contract provide address of bridge contract.
  • Call transferManager method on bridge contract and pass there DAO contract address generated by Wings.
  • Call start for bridge before crowdsale start.

For more details read next part of this tutorial.

Integration

On example of standard crowdsale contract we will do bridge integration. Important: this is just tutorial and shows one of the many ways to integrate bridge contract, everything based on your own crowdsale contract. Don't use this example on mainnet.

Let's take a standard token and crowdsale contract. Token contract is mintable, so we will use token to issue new tokens, manage issue will be done by crowdsale contract (that will be owner of token contract).

Token contract:

pragma solidity ^0.4.18;

import "zeppelin-solidity/contracts/token/ERC20/StandardToken.sol";
import "zeppelin-solidity/contracts/ownership/Ownable.sol";

// Minimal crowdsale token for custom contracts
contract Token is Ownable, StandardToken {
    // ERC20 requirements
    string public name;
    string public symbol;
    uint8 public decimals;

    // how many tokens was created (i.e. minted)
    uint256 public totalSupply;

    // here are 2 states: mintable (initial) and transferrable
    bool public releasedForTransfer;

    // Ctor. Hardcodes names in this example
    function Token() public {
        name = "CustomTokenExample";
        symbol = "CTE";
        decimals = 18;
    }

// override these 2 functions to prevent from transferring tokens before it was released

    function transfer(address _to, uint256 _value) public returns (bool) {
        require(releasedForTransfer);
        return super.transfer(_to, _value);
    }

    function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
        require(releasedForTransfer);
        return super.transferFrom(_from, _to, _value);
    }

    // transfer the state from intable to transferrable
    function release() public
        onlyOwner() // only owner can do it
    {
        releasedForTransfer = true;
    }

    // creates new amount of the token from a thin air
    function issue(address _recepient, uint256 _amount) public
        onlyOwner() // only owner can do it
    {
        // the owner can mint until released
        require (!releasedForTransfer);

        // total token supply increases here.
        // Note that the recepient is not able to transfer anything until release() is called
        balances[_recepient] += _amount;
        totalSupply += _amount;
    }
}

Crowdsale contract:

pragma solidity ^0.4.18;

import "zeppelin-solidity/contracts/ownership/Ownable.sol";
import "./Token.sol";

// Example of crowdsale. NOT FOR REAL USAGE
contract Crowdsale is Ownable {
  modifier onlyActive() {
    require(active);
    _;
  }

  modifier finished() {
    require(!active);
    _;
  }

  // tokens per ETH fixed price
  uint256 public tokensPerEthPrice = 500;

  // Crowdsale token
  Token public crowdsaleToken;

  // hard cap
  uint256 public hardCap = 1000 ether;

  // total collected
  uint256 public totalCollected = 0;

  bool public active;

  function Crowdsale(
    address _token
  )
    public
  {
    owner = msg.sender;
    crowdsaleToken = Token(_token);

    active = true;
  }

  // just for tests
  function finish() onlyOwner() onlyActive() {
    active = false;
  }

  // if there is ETH rewards and all ETH already withdrawn
  function deposit() public payable finished() {
  }

  // transfers crowdsale token from mintable to transferrable state
  function releaseTokens()
    public
    onlyOwner()
    finished()
  {
    crowdsaleToken.release();
  }


  // default function allows for ETH transfers to the contract
  function () payable public {
    require(msg.value > 0);

    // and it sells the token
    sellTokens(msg.sender, msg.value);
  }

  // sels the project's token to buyers
  function sellTokens(address _recepient, uint256 _value)
    internal
    onlyActive()
  {
    require(totalCollected < hardCap);
    uint256 newTotalCollected = totalCollected + _value;

    if (hardCap < newTotalCollected) {
      // don't sell anything above the hard cap

      uint256 refund = newTotalCollected - hardCap;
      uint256 diff = _value - refund;

      // send the ETH part which exceeds the hard cap back to the buyer
      _recepient.transfer(refund);
      _value = diff;
    }

    // token amount as per price (fixed in this example)
    uint256 tokensSold = _value * tokensPerEthPrice;

    // create new tokens for this buyer
    crowdsaleToken.issue(_recepient, tokensSold);

    // update total ETH collected
    totalCollected += _value;
  }

  // project's owner withdraws ETH funds to the funding address upon successful crowdsale
  function withdraw(
    uint256 _amount // can be done partially
  )
    public
  {
    require(_amount <= this.balance);
    owner.transfer(_amount);
  }
}

Now let's add support of Connector contract to crowdsale contract.

npm i @wings_platform/wings-bridge --save
import "@wings_platform/wings-bridge/contracts/Connector.sol";

And inherit Crowdsale from Connector.

contract Crowdsale is Connector {

Connector is already inherits from Ownable so we don't need to inherit again.

Now our goal to call notifySale on each call, method looks so in Connector contract:

function notifySale(uint256 _ethAmount, uint256 _tokenAmount) internal bridgeInitialized {
    bridge.notifySale(_ethAmount, _tokenAmount);
}
  • uint256 _ethAmount - is amount of ETH that was sent to buy tokens.
  • uint256 _tokenAmount - is amount of tokens that bought.

So we have method sellTokens in Crowdsale, where we usually sell tokens, in the end of this method we will add call of notifySale:

// call notifySale, _value - ETH amount, tokensSold - tokens amount
notifySale(_value, tokensSold);

To see how method looks now, see example.

And last thing we should in crowdsale source code, it's adding issue of rewards tokens, and closing bridge. Let's add it to releaseTokens method.

// send rewards
uint256 ethReward = 0;
uint256 tokenReward = 0;

(ethReward, tokenReward) = bridge.calculateRewards();

if (ethReward > 0) {
   bridge.transfer(ethReward);
}

if (tokenReward > 0) {
   crowdsaleToken.issue(bridge, tokenReward);
}

// close bridge
closeBridge();

So, with method calculateRewards we can get amount of rewards we have to pay, important to call this method when crowdsale completed, returns two value: reward in ETH and reward in tokens. If you don't have reward in ETH, amount to send will be zero.

Another method is closeBridge, that report to bridge smart contract, that crowdsale completed.

See how it looks now in examples.

At this stage no need more changes to source code.

We should deploy our contracts right, before we start forecasting on Wings platform. So let's make a migration script, that will deploy token/crowdsale and bridge contract, and meanwhile make right ownership logic on our contracts.

Let's require contracts first:

const Crowdsale = artifacts.require('Crowdsale')
const Token = artifacts.require('Token')
const Bridge = artifacts.require('Bridge')

Deploying token and crowdsale:

// Deploying token
await deployer.deploy(Token)
const token = await Token.deployed()

// Deploying Crowdsale
await deployer.deploy(Crowdsale, token.address)
const crowdsale = await Crowdsale.deployed()

// Move token owner to crowdsale
await token.transferOwnership(crowdsale.address)

Now let's deploy bridge and direct crowdsale to bridge:

await deployer.deploy(Bridge, web3.toWei(10, 'ether'), web3.toWei(100, 'ether'), token.address, crowdsale.address)
const bridge = await Bridge.deployed()

If we look at Bridge constructor:

function Bridge(
    uint256 _minimalGoal,
    uint256 _hardCap,
    address _token,
    address _crowdsaleAddress
)
  • uint256 _minimalGoal - minimal goal in wei, should be great then 10% of hard cap.
  • uint256 _hardCap - hard cap in wei.
  • address _token - token address.
  • address _crowdsaleAddress - crowdsale address.

And then call to crowdsale to change bridge to deployed one:

await crowdsale.changeBridge(bridge.address)

Now need to create project on Wings platform. We go to (Wings)[https://wings.ai], fill project details, and on Smart contract path we should choose Custom Contract and put there Bridge Contract Address to Contract address filed.

Like on image:

contract address

Once you created you project and forecasting started, you have time to move bridge manager under control of DAO contract address.

To do it, just take URL of your project, like:

https://wings.ai/project/0x28e7f296570498f1182cf148034e818df723798a

As you see - 0x28e7f296570498f1182cf148034e818df723798a, it's your DAO contract address. You can check it via parity or some other ethereum client, your account that you used to project on wings.ai is owner of this smart contract.

So we take this address, and move manager of bridge to this address, we use transferManager method for it.

IMPORTANT: all this steps can go during you forecasting period.

Like:

await bridge.transferManager('0x28e7f296570498f1182cf148034e818df723798a')

Ok, it's done, and last small step, you should start your bridge. For this, you should call few methods on DAO contract, indeed createCustomCrowdsale method and start method on your crowdsale controller contract, that will be generated by createCustomCrowdsale call.

Here is ABI for contracts and we recommend to use truffle contract library to make calls.

IMPORTANT: use same account that you used to create project on wings.ai

Like:

const dao = await DAO.at('0x28e7f296570498f1182cf148034e818df723798a') // change with your DAO address
await dao.createCustomCrowdsale()

And:

const ccAddress = await dao.crowdsaleController.call() 
const crowdsaleController = await CrowdsaleController.at(ccAddress)
await crowdsaleController.start(0, 0, '0x0')

IMPORTANT: values like 0, 0, '0x0' for start works fine only if you using bridge, if you done full integration, do it in another way.

It's all. You can start you crowdsale!

Developing

We recommend to make pull requests to current repository. Each pull request should be covered with tests.

Fetch current repository, install dependencies:

npm install

We strongly recommend to develop using testrpc to save time and cost.

Tests

To run tests fetch current repository, install dependencies and run:

truffle test

Authors

Wings Stiftung

License

See in license file.