My Crowdsale Deployer

Hello! Tell me please.

How to do it right?

I use the GSN and initialize functions

What should a deployment contract look like?

    pragma solidity ^0.5.0;

    import "@openzeppelin/upgrades/contracts/Initializable.sol";
    import "@openzeppelin/contracts-ethereum-package/contracts/GSN/GSNRecipient.sol";
    import "./GLDToken.sol";
    import "./MyCrowdsale.sol";

    contract MyCrowdsaleDeployer is Initializable, GSNRecipient {

        function initialize()
            public initializer
        {
            GSNRecipient.initialize();
            
            // create a mintable token
            GLDToken token = new GLDToken();
            token.initialize(0);

            // create the crowdsale and tell it about the token
            MyCrowdsale crowdsale = new MyCrowdsale();
            crowdsale.initialize(1, msg.sender, token.address);

            // transfer the minter role from this contract (the default)
            // to the crowdsale, so it can mint tokens
            token.addMinter(crowdsale.address);
            token.renounceMinter();
        }
    }
1 Like

Hi @casper,

Currently your deployment contract is upgradeable, and could potentially allow relayed transactions via the GSN (though extra code is required), whilst the token and crowdsale are not upgradeable.

Assuming that you want to create an upgradeable token and an upgradeable crowdsale, then I would suggest deploying them using the OpenZeppelin CLI.

If required you can create the contracts programmatically (see the documentation on Upgrading Contracts Programmatically)

See the following simple example of creating a crowdsale using the OpenZeppelin CLI:

SimpleToken.sol

pragma solidity ^0.5.0;

import "@openzeppelin/upgrades/contracts/Initializable.sol";

import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Detailed.sol";

/**
 * @title SimpleToken
 * @dev Very simple ERC20 Token example, where all tokens are pre-assigned to the creator.
 * Note they can later distribute these tokens as they wish using `transfer` and other
 * `ERC20` functions.
 */
contract SimpleToken is Initializable, ERC20, ERC20Detailed {
    /**
     * @dev Constructor that gives sender all of existing tokens.
     */
    function initialize(address sender) public initializer {
        ERC20Detailed.initialize("SimpleToken", "SIM", 18);

        _mint(sender, 10000 * (10 ** uint256(decimals())));
    }
}

SimpleCrowdsale.sol

pragma solidity ^0.5.0;

import "@openzeppelin/upgrades/contracts/Initializable.sol";

import "@openzeppelin/contracts-ethereum-package/contracts/crowdsale/Crowdsale.sol";

contract SimpleCrowdsale is Initializable, Crowdsale {
 
    function initialize(
        uint256 rate,    // rate in TKNbits
        address payable wallet,
        IERC20 token
    ) public initializer {
        Crowdsale.initialize(rate, wallet, token);
    }
}

Create the token

Tokens given to the sender

$ npx oz create
Nothing to compile, all contracts are up to date.
? Pick a contract to instantiate SimpleToken
? Pick a network development
All contracts are up to date
? Call a function to initialize the instance after creating it? Yes
? Select which function * initialize(sender: address)
? sender (address): 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1
✓ Setting everything up to create contract instances
✓ Instance created at 0xCfEB869F69431e42cdB54A4F4f105C19C080A601
0xCfEB869F69431e42cdB54A4F4f105C19C080A601

Create the crowdsale

Rate of 1

$ npx oz create
Nothing to compile, all contracts are up to date.
? Pick a contract to instantiate SimpleCrowdsale
? Pick a network development
✓ Added contract SimpleCrowdsale
✓ Contract SimpleCrowdsale deployed
All contracts have been deployed
? Call a function to initialize the instance after creating it? Yes
? Select which function initialize(rate: uint256, wallet: address, token: address)
? rate (uint256): 1
? wallet (address): 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1
? token (address): 0xCfEB869F69431e42cdB54A4F4f105C19C080A601
✓ Instance created at 0xC89Ce4735882C9F0f0FE26686c53074E09B0D550
0xC89Ce4735882C9F0f0FE26686c53074E09B0D550

Transfer the total supply to the crowdsale

Get the total supply

$ npx oz call
? Pick a network development
? Pick an instance SimpleToken at 0xCfEB869F69431e42cdB54A4F4f105C19C080A601
? Select which function totalSupply()
✓ Method 'totalSupply()' returned: 10000000000000000000000
10000000000000000000000

Transfer the total supply

$ npx oz send-tx
? Pick a network development
? Pick an instance SimpleToken at 0xCfEB869F69431e42cdB54A4F4f105C19C080A601
? Select which function transfer(recipient: address, amount: uint256)
? recipient (address): 0xC89Ce4735882C9F0f0FE26686c53074E09B0D550
? amount (uint256): 10000000000000000000000
✓ Transaction successful. Transaction hash: 0x6e088af3e5e142cbdf64df3942a2bc8f6437e84ccc4fb4659bb2ec1cbc664390
Events emitted:
 - Transfer(0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1, 0xC89Ce4735882C9F0f0FE26686c53074E09B0D550, 10000000000000000000000)

Buy Tokens

$ npx oz send-tx --value "1000000000000000000"
? Pick a network development
? Pick an instance SimpleCrowdsale at 0xC89Ce4735882C9F0f0FE26686c53074E09B0D550
? Select which function buyTokens(beneficiary: address)
? beneficiary (address): 0xd03ea8624C8C5987235048901fB614fDcA89b117
✓ Transaction successful. Transaction hash: 0x30cb6158224ab3d43651ff3cce9af0d4b0fc14cc89700d8adc1d38a6dbde5ca3
Events emitted:
 - TokensPurchased(0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1, 0xd03ea8624C8C5987235048901fB614fDcA89b117, 1000000000000000000, 1000000000000000000)
1 Like

How can I call methods to continue?
These

token.addMinter(address(crowdsale));
token.renounceMinter();

They become unavailable

Error: Error: Returned error: VM Exception while processing transaction: revert MinterRole: caller does not have the Minter role

1 Like

Hi @casper,

The example I showed was for a token that minted all it’s tokens at time of creation rather than a mintable token.

Changing to a Mintable Token and Mintable Crowdsale you can do this through the CLI:

Note: This is sample code that hasn’t been tested, any such solution would need appropriate testing and auditing.

SimpleToken

pragma solidity ^0.5.0;

import "@openzeppelin/upgrades/contracts/Initializable.sol";

import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Detailed.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Mintable.sol";

/**
 * @title SimpleToken
 */
contract SimpleToken is Initializable, ERC20, ERC20Detailed, ERC20Mintable {
    /**
     * @dev Constructor that gives msg.sender all of existing tokens.
     */
    function initialize(address minter) public initializer {
        ERC20Detailed.initialize("SimpleToken", "SIM", 18);
        ERC20Mintable.initialize(minter);
    }
}

SimpleCrowdsale.sol

pragma solidity ^0.5.0;

import "@openzeppelin/upgrades/contracts/Initializable.sol";

import "@openzeppelin/contracts-ethereum-package/contracts/crowdsale/Crowdsale.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/crowdsale/emission/MintedCrowdsale.sol";

contract SimpleCrowdsale is Initializable, MintedCrowdsale {
 
    function initialize(
        uint256 rate,    // rate in TKNbits
        address payable wallet,
        IERC20 token
    ) public initializer {
        Crowdsale.initialize(rate, wallet, token);
    }
}

Create SimpleToken

$ npx oz create
✓ Compiled contracts with solc 0.5.16 (commit.9c3226ce)
? Pick a contract to instantiate SimpleToken
? Pick a network development
✓ Contract SimpleToken deployed
✓ Contract SimpleCrowdsale deployed
All contracts have been deployed
? Call a function to initialize the instance after creating it? Yes
? Select which function * initialize(minter: address)
? minter (address): 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1
✓ Setting everything up to create contract instances
✓ Instance created at 0x254dffcd3277C0b1660F6d42EFbB754edaBAbC2B
0x254dffcd3277C0b1660F6d42EFbB754edaBAbC2B

Create SimpleCrowdsale

$ npx oz create
Nothing to compile, all contracts are up to date.
? Pick a contract to instantiate SimpleCrowdsale
? Pick a network development
All contracts are up to date
? Call a function to initialize the instance after creating it? Yes
? Select which function initialize(rate: uint256, wallet: address, token: address)
? rate (uint256): 1
? wallet (address): 0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0
? token (address): 0x254dffcd3277C0b1660F6d42EFbB754edaBAbC2B
✓ Instance created at 0xC89Ce4735882C9F0f0FE26686c53074E09B0D550
0xC89Ce4735882C9F0f0FE26686c53074E09B0D550

Add the crowdsale as a minter

$ npx oz send-tx
? Pick a network development
? Pick an instance SimpleToken at 0x254dffcd3277C0b1660F6d42EFbB754edaBAbC2B
? Select which function addMinter(account: address)
? account (address): 0xC89Ce4735882C9F0f0FE26686c53074E09B0D550
✓ Transaction successful. Transaction hash: 0x4f96f599171060e2d7ee2e924136a0f4c794bb70f51e65f1c82acfdbbee04d48
Events emitted:
 - MinterAdded(0xC89Ce4735882C9F0f0FE26686c53074E09B0D550)

Renounce the contract creator as a minter

$ npx oz send-tx
? Pick a network development
? Pick an instance SimpleToken at 0x254dffcd3277C0b1660F6d42EFbB754edaBAbC2B
? Select which function renounceMinter()
✓ Transaction successful. Transaction hash: 0xe16e1fec53e30f58079b07e65e50e048136a1ea4acb23c7d9a5f6d77478c2070
Events emitted:
 - MinterRemoved(0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1)

Purchase tokens

$ npx oz send-tx --value "1000000000000000000"
? Pick a network development
? Pick an instance SimpleCrowdsale at 0xC89Ce4735882C9F0f0FE26686c53074E09B0D550
? Select which function buyTokens(beneficiary: address)
? beneficiary (address): 0xE11BA2b4D45Eaed5996Cd0823791E0C93114882d
✓ Transaction successful. Transaction hash: 0x158002bc03c98c9f8c5916056e1a39139a78ae034d2024559d3e87f1818d72be
Events emitted:
 - TokensPurchased(0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1, 0xE11BA2b4D45Eaed5996Cd0823791E0C93114882d, 1000000000000000000, 1000000000000000000)
1 Like

A post was split to a new topic: Error: Cannot send funds via the GSN

Hi @casper,

The CLI now supports deploying regular (non-upgradeable) contracts in the release candidate of OpenZeppelin CLI 2.8

Would appreciate if you could give the release candidate a try and let us know what you think!

1 Like