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.
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:
notifySale
when sale/exchange of your tokens happen.closeBridge
and impelement movement of rewards to bridge contract.changeBridge
on your crowdsale contract and pass there address of deployed bridge contract.transferManager
method on bridge contract and pass there DAO contract address generated by Wings. start
for bridge before crowdsale start.For more details read next part of this tutorial.
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:
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!
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.
To run tests fetch current repository, install dependencies and run:
truffle test
Wings Stiftung
See in license file.