Skip to content

Instantly share code, notes, and snippets.

@stephenlb
Last active April 14, 2023 13:38
Show Gist options
  • Save stephenlb/e5067fe724f83362c030fccc04238a3d to your computer and use it in GitHub Desktop.
Save stephenlb/e5067fe724f83362c030fccc04238a3d to your computer and use it in GitHub Desktop.
Solidity Smart Contract all-in-one that allows the crowd-sale of custom tokens for as long as the owner wallet balance is above zero. See readme for more details.

Simplified Modifiable ERC20 Token Details

Controllable Crowd-sale and transferable ownership allows you to change ownership and change exchange rates.

  • Owner Wallet receives ETH funds.
  • Owner Wallet holds TOKEN's for sale.
  • Moving tokens from owner wallet to another removes publicly purchaseable token inventory.
  • Custom Exchange Rate ETH for YOUR TOKEN. Default is 1:10 One ETH = 10 YOUR TOKEN.
  • Changable Exchange Rate at any time!
  • Exchange ETH for YOURTOKEN at a custom exchange rate.
  • An investor sends ETH to your Contract Address. Investor receives YOURTOKEN and you receive ETH.
  • Modify your exchange rate at any time.
  • You can stop the crowd-sale by moving the tokens out of the owner's account into a different account.
  • You can re-start the crowd-sale by adding tokens back to the owner account.
  • You can cap the total volume of token sales by placing only the desired amount of tokens in the owner's balance.
  • To change the owner, call transferOwnership(address newOwner) as the owner.
  • You also may want to transfer to the new owner's address as well by calling transfer(newOwner, XXXX) as the original owner balance holder.
  • To change the Exchange Rate, call changeExhangeRate(uint256 newRate) as the owner.
  • To change the Ticker Symbol, call changeTokenSymbol(string newSymbol) as the owner.
  • To change the Ticker Name, call changeTokenName(string newName) as the owner.

How to use (test mode)

  1. Download MetaMask https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn
  2. Select KOVAN Network "Kovan Test Net".
  3. Copy your Account Address from MetaMask
  4. Paste your Account Address into the Kovan Chat https://gitter.im/kovan-testnet/faucet (sign in using Twitter/GitHub)
  5. Copy controllable-crowd-sale-token.sol (find lower down on this page!)
  6. Paste into Ethereum Editor https://remix.ethereum.org/
  7. Click CREATE Button
  8. Done!

You will see some new buttons after the contract was deployed (after clicking CREATE).
Paste in your address "0x0000" make sure to use "Quotes". Click the balanceOf button and see your balance.

You can change the COIN name using the other methods.

pragma solidity ^0.4.19;
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a / b;
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function Ownable() public {
owner = msg.sender;
}
modifier isOwner() {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) public isOwner {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
contract StandardToken {
using SafeMath for uint256;
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
mapping (address => uint256) balances;
mapping (address => mapping (address => uint256)) allowed;
uint256 public totalSupply;
function totalSupply() public constant returns (uint256 supply) {
return totalSupply;
}
function transfer(address _to, uint256 _value) public returns (bool success) {
if (balances[msg.sender] >= _value && _value > 0) {
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
Transfer(msg.sender, _to, _value);
return true;
} else { return false; }
}
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && _value > 0) {
balances[_to] = balances[_to].add(_value);
balances[_from] = balances[_from].sub(_value);
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
Transfer(_from, _to, _value);
return true;
} else { return false; }
}
function balanceOf(address _owner) public constant returns (uint256 balance) {
return balances[_owner];
}
function approve(address _spender, uint256 _value) public returns (bool success) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
function allowance(address _owner, address _spender) public constant returns (uint256 remaining) {
return allowed[_owner][_spender];
}
}
contract ERC20Token is StandardToken, Ownable {
using SafeMath for uint256;
string public name;
string public symbol;
string public version = '1.0';
uint256 public totalCoin;
uint8 public decimals;
uint8 public exchangeRate;
event TokenNameChanged(string indexed previousName, string indexed newName);
event TokenSymbolChanged(string indexed previousSymbol, string indexed newSymbol);
event ExhangeRateChanged(uint8 indexed previousRate, uint8 indexed newRate);
function ERC20Token() public {
decimals = 18;
totalCoin = 10000000; // Total Supply of Coin
totalSupply = totalCoin * 10**uint(decimals); // Total Supply of Coin
balances[owner] = totalSupply; // Total Supply sent to Owner's Address
exchangeRate = 100; // 100 Coins per ETH (changable)
symbol = "TICKER"; // Your Ticker Symbol (changable)
name = "YourCoinNameHere"; // Your Coin Name (changable)
}
function changeTokenName(string newName) public isOwner returns (bool success) {
TokenNameChanged(name, newName);
name = newName;
return true;
}
function changeTokenSymbol(string newSymbol) public isOwner returns (bool success) {
TokenSymbolChanged(symbol, newSymbol);
symbol = newSymbol;
return true;
}
function changeExhangeRate(uint8 newRate) public isOwner returns (bool success) {
ExhangeRateChanged(exchangeRate, newRate);
exchangeRate = newRate;
return true;
}
function () public payable {
fundTokens();
}
function fundTokens() public payable {
require(msg.value > 0);
uint256 tokens = msg.value.mul(exchangeRate);
require(balances[owner].sub(tokens) > 0);
balances[msg.sender] = balances[msg.sender].add(tokens);
balances[owner] = balances[owner].sub(tokens);
Transfer(msg.sender, owner, msg.value);
forwardFunds();
}
function forwardFunds() internal {
owner.transfer(msg.value);
}
function approveAndCall(
address _spender,
uint256 _value,
bytes _extraData
) public returns (bool success) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
if(!_spender.call(
bytes4(bytes32(keccak256("receiveApproval(address,uint256,address,bytes)"))),
msg.sender,
_value,
this,
_extraData
)) { revert(); }
return true;
}
}
@hassadem
Copy link

Yeah you really tried by explain it better

@bitbytemaster
Copy link

function forwardFunds() internal {        owner.transfer(msg.value);    }

owner.transfer has no to address and I suppose it is a bug?

@stephenlb
Copy link
Author

Sure does look like it! Good find. Look like it could be. It seems to be working in production. However the code you highlighted suggests it shouldn't even work because it's a syntax error, it's not using the method parameters needed for transfer.

@stephenlb
Copy link
Author

Strangely this still works. Would be great if the code would have a comment that describes why it is working.

@bitbytemaster
Copy link

bitbytemaster commented May 27, 2021

where does the funds go? it goes to the owner address? I suppose if it works it means the funds come into the contract address first and then it gets redirected to the owner address/account thru owner.transfer usage which is supported by solidity language.

@bitbytemaster
Copy link

bitbytemaster commented May 27, 2021

found this:

https://docs.soliditylang.org/en/develop/units-and-global-variables.html?highlight=transfer#address-related

But I really don't like Solidity syntax. It seems illogical and ugly to me. By contrast, WASM C++ is the best smart contract language to me. It's just sad to see so many devs are picking up Solidity for the sake of DeFi on EVM.

@stephenlb
Copy link
Author

Good find! Agreed.

@stephenlb
Copy link
Author

This explains it. Agreed on the illogical syntax. It's confusing like you mentioned. The syntax should be descriptive of what is happening, rather than hide or obscure.

@bitbytemaster
Copy link

bitbytemaster commented May 31, 2021

At least they should rename it as address.receive(uint256 amount) LoL
But it also happens that there's also address.send(uint256 amount) which has similar but somewhat diff usage and that explains why they chose transfer to make it look diff and neat but rather confusing.

@stephenlb
Copy link
Author

stephenlb commented Jun 1, 2021

👍 Good points. Yes agreed. It is neat but rather confusing.

@BigMadCode
Copy link

Question,
Were you able to deploy this?

Also, in this scenario, can you or do you approve the sale contract to spend from owner wallet and then just call transfer from upon receiving ETH?

@stephenlb
Copy link
Author

Yes this has been deployed several times. Creating quite a few ERC-20 tokens.

@stephenlb
Copy link
Author

@BigMadCode you'll need to add those methods for additional capabilities beyond the basics. Approve the sale contract to spend from owner wallet is a bit more solidity.

@aphrodite999
Copy link

aphrodite999 commented May 26, 2022

Hi Nice to meet you
This is Danilo
Senior Blockchain and Full Stack Developer
Hope to discuss more about your project
Please send me a message to @Gold_Strategist to my telegram

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment