Wondering what’s next for npm?Check out our public roadmap! »

    @wings_platform/wings-bridge

    1.0.5 • Public • Published

    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.

    Install

    npm i @wings_platform/wings-bridge

    DownloadsWeekly Downloads

    1

    Version

    1.0.5

    License

    SEE LICENSE IN LICENSE

    Unpacked Size

    3.83 MB

    Total Files

    48

    Last publish

    Collaborators

    • avatar
    • avatar