Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simple spread #1

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions contracts/EthBondingCurvedToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@ contract EthBondingCurvedToken is DetailedERC20, StandardToken {

event Minted(uint256 amount, uint256 totalCost);
event Burned(uint256 amount, uint256 reward);
event Withdrawn(uint256 amount);


using SafeMath for uint256;
using SafeMath for uint8;

uint256 public poolBalance;
uint256 public ownerFund;
address public creator;

/// @dev constructor Initializes the bonding curve
/// @param name The name of the token
Expand All @@ -23,7 +28,9 @@ contract EthBondingCurvedToken is DetailedERC20, StandardToken {
string name,
string symbol,
uint8 decimals
) DetailedERC20(name, symbol, decimals) public {}
) DetailedERC20(name, symbol, decimals) public {
creator = msg.sender;
}

/// @dev Get the price in ether to mint tokens
/// @param numTokens The number of tokens to calculate price for
Expand All @@ -33,20 +40,30 @@ contract EthBondingCurvedToken is DetailedERC20, StandardToken {
/// @param numTokens The number of tokens to calculate reward for
function rewardForBurn(uint256 numTokens) public returns(uint256);


/// @dev Get the amount of ether to send to reserve (rest goes to ownerFund)
/// @param numTokens The number of tokens to calculate reserve contribution for
function priceToReserve(uint256 numTokens) public returns(uint256);

/// @dev Mint new tokens with ether
/// @param numTokens The number of tokens you want to mint
function mint(uint256 numTokens) public payable {
uint256 priceForTokens = priceToMint(numTokens);
uint256 contributionToReserve = priceToReserve(numTokens);
uint256 ownerFundContribution = priceForTokens - contributionToReserve;
require(msg.value >= priceForTokens);

totalSupply_ = totalSupply_.add(numTokens);
balances[msg.sender] = balances[msg.sender].add(numTokens);
poolBalance = poolBalance.add(priceForTokens);
poolBalance = poolBalance.add(contributionToReserve);
ownerFund = ownerFund.add(ownerFundContribution);
if (msg.value > priceForTokens) {
msg.sender.transfer(msg.value - priceForTokens);
}


emit Minted(numTokens, priceForTokens);

}

/// @dev Burn tokens to receive ether
Expand All @@ -62,4 +79,13 @@ contract EthBondingCurvedToken is DetailedERC20, StandardToken {

emit Burned(numTokens, ethToReturn);
}

/// @dev Withdraw ether from ownerFund
function withdraw() public {
require(ownerFund > 0, "No ether available in ownerFund");
creator.transfer(ownerFund);

emit Withdrawn(ownerFund);
ownerFund = 0;
}
}
15 changes: 12 additions & 3 deletions contracts/EthPolynomialCurvedToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,24 @@ contract EthPolynomialCurvedToken is EthBondingCurvedToken {
uint256 constant private PRECISION = 10000000000;

uint8 public exponent;
uint8 public margin;

/// @dev constructor Initializes the bonding curve
/// @param name The name of the token
/// @param decimals The number of decimals to use
/// @param symbol The symbol of the token
/// @param _exponent The exponent of the curve
/// @param _margin The percentage difference between buy and sell curve
constructor(
string name,
string symbol,
uint8 decimals,
uint8 _exponent
uint8 _exponent,
uint8 _margin
) EthBondingCurvedToken(name, symbol, decimals) public {
require(margin < 100, "Margin needs to be a valid percentage");
exponent = _exponent;
margin = _margin;
}

/// @dev Calculate the integral from 0 to t
Expand All @@ -33,10 +38,14 @@ contract EthPolynomialCurvedToken is EthBondingCurvedToken {
}

function priceToMint(uint256 numTokens) public returns(uint256) {
return curveIntegral(totalSupply_.add(numTokens)).sub(poolBalance);
return curveIntegral(totalSupply_.add(numTokens)).sub(curveIntegral(totalSupply_));
}

function rewardForBurn(uint256 numTokens) public returns(uint256) {
return poolBalance.sub(curveIntegral(totalSupply_.sub(numTokens)));
return poolBalance.sub((100-margin).mul(curveIntegral(totalSupply_.sub(numTokens))).div(100));
}

function priceToReserve(uint256 numTokens) public returns(uint256) {
return (100-margin).mul(curveIntegral(totalSupply_.add(numTokens))).div(100).sub(poolBalance);
}
}
176 changes: 123 additions & 53 deletions test/EthPolynomialCurvedToken-test.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,39 @@
const EthPolynomialCurvedToken = artifacts.require("EthPolynomialCurvedToken");


contract("EthPolynomialCurvedToken", accounts => {
let polyBondToken1;
const creator = accounts[0];
const user1 = accounts[1];
const user2 = accounts[2];


const getBalance = address => {
return new Promise((resolve, reject) => {
web3.eth.getBalance(address, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
})
})
}

before(async () => {
polyBondToken1 = await EthPolynomialCurvedToken.new(
"oed curve",
"OCU",
18,
2
2,
10
);
const hash = polyBondToken1.transactionHash;
let receipt;
await web3.eth.getTransactionReceipt(hash, (err, res) => {
receipt = res;
console.log('Gas for deployment: ', receipt.gasUsed)
});
});

it("Is initiated correcly", async () => {
Expand All @@ -32,7 +53,8 @@ contract("EthPolynomialCurvedToken", accounts => {
"oed curve",
"OCU",
18,
exponent
exponent,
10
);
let res;
let jsres;
Expand Down Expand Up @@ -61,10 +83,13 @@ contract("EthPolynomialCurvedToken", accounts => {
});
});

it("Can mint tokens with ether", async function() {
it("Can mint tokens with ether", async function () {
let balance = await polyBondToken1.balanceOf(user1);
assert.equal(balance.toNumber(), 0);

let contractBalance = await getBalance(polyBondToken1.address);
console.log('contract holds: ', contractBalance.toString());

const priceToMint1 = await polyBondToken1.priceToMint.call(50);
let tx = await polyBondToken1.mint(50, {
value: priceToMint1,
Expand All @@ -80,10 +105,13 @@ contract("EthPolynomialCurvedToken", accounts => {
const poolBalance1 = await polyBondToken1.poolBalance.call();
assert.equal(
poolBalance1.toNumber(),
priceToMint1.toNumber(),
Math.round(priceToMint1.toNumber() * 0.9),
"poolBalance should be correct"
);

contractBalance = await getBalance(polyBondToken1.address);
console.log('contract holds: ', contractBalance.toString(), 'priceToMint1: ', priceToMint1.toString());

const priceToMint2 = await polyBondToken1.priceToMint.call(50);
assert.isAbove(priceToMint2.toNumber(), priceToMint1.toNumber());
tx = await polyBondToken1.mint(50, { value: priceToMint2, from: user2 });
Expand All @@ -93,16 +121,23 @@ contract("EthPolynomialCurvedToken", accounts => {
"amount minted should be 50"
);
assert.equal(tx.logs[0].args.totalCost.toNumber(), priceToMint2.toNumber());

console.log('Gas for buying: ', tx.receipt.gasUsed);

const poolBalance2 = await polyBondToken1.poolBalance.call();
assert.equal(
poolBalance2.toNumber(),
priceToMint1.toNumber() + priceToMint2.toNumber(),
Math.round(0.9 * (priceToMint1.toNumber())) + Math.round(0.9 * priceToMint2.toNumber()),
"poolBalance should be correct"
);

contractBalance = await getBalance(polyBondToken1.address);
console.log('contract holds: ', contractBalance.toString(), 'priceToMint2: ', priceToMint2.toString());

const totalSupply = await polyBondToken1.totalSupply.call();
assert.equal(totalSupply.toNumber(), 100);

// should not mint when value sent is too low
let didThrow = false;
const priceToMint3 = await polyBondToken1.priceToMint.call(50);
try {
Expand All @@ -117,54 +152,89 @@ contract("EthPolynomialCurvedToken", accounts => {
});

it("should not be able to burn tokens user dont have", async () => {
let didThrow = false;
try {
tx = await polyBondToken1.burn(80, { from: user2 });
} catch (e) {
didThrow = true;
}
assert.isTrue(didThrow);
});
let didThrow = false;
try {
tx = await polyBondToken1.burn(80, { from: user2 });
} catch (e) {
didThrow = true;
}
assert.isTrue(didThrow);
});

it("Can burn tokens and receive ether", async () => {
const poolBalance1 = await polyBondToken1.poolBalance.call();
const totalSupply1 = await polyBondToken1.totalSupply.call();

let reward1 = await polyBondToken1.rewardForBurn.call(50);
let tx = await polyBondToken1.burn(50, { from: user1 });
assert.equal(
tx.logs[0].args.amount.toNumber(),
50,
"amount burned should be 50"
);
assert.equal(tx.logs[0].args.reward.toNumber(), reward1);
let balance = await polyBondToken1.balanceOf(user1);
assert.equal(balance.toNumber(), 0);

const poolBalance2 = await polyBondToken1.poolBalance.call();
assert.equal(
poolBalance2.toNumber(),
poolBalance1.toNumber() - reward1.toNumber()
);
const totalSupply2 = await polyBondToken1.totalSupply.call();
assert.equal(totalSupply2.toNumber(), totalSupply1.toNumber() - 50);

let reward2 = await polyBondToken1.rewardForBurn.call(50);
tx = await polyBondToken1.burn(50, { from: user2 });
assert.equal(
tx.logs[0].args.amount.toNumber(),
50,
"amount burned should be 50"
);
assert.equal(tx.logs[0].args.reward.toNumber(), reward2);
balance = await polyBondToken1.balanceOf(user2);
assert.equal(balance.toNumber(), 0);
assert.isBelow(reward2.toNumber(), reward1.toNumber());

const poolBalance3 = await polyBondToken1.poolBalance.call();
assert.equal(poolBalance3.toNumber(), 0);
const totalSupply3 = await polyBondToken1.totalSupply.call();
assert.equal(totalSupply3.toNumber(), 0);
});
it("Can burn tokens and receive ether", async () => {
const poolBalance1 = await polyBondToken1.poolBalance.call();
const totalSupply1 = await polyBondToken1.totalSupply.call();

contractBalance = await getBalance(polyBondToken1.address);

let reward1 = await polyBondToken1.rewardForBurn.call(50);
console.log('contract holds: ', contractBalance.toString(), 'reward1: ', reward1.toString());
let tx = await polyBondToken1.burn(50, { from: user1 });
assert.equal(
tx.logs[0].args.amount.toNumber(),
50,
"amount burned should be 50"
);
assert.equal(tx.logs[0].args.reward.toNumber(), reward1);
let balance = await polyBondToken1.balanceOf(user1);
assert.equal(balance.toNumber(), 0);

console.log('Gas for selling: ', tx.receipt.gasUsed);

const poolBalance2 = await polyBondToken1.poolBalance.call();
assert.equal(
poolBalance2.toNumber(),
poolBalance1.toNumber() - reward1.toNumber()
);
const totalSupply2 = await polyBondToken1.totalSupply.call();
assert.equal(totalSupply2.toNumber(), totalSupply1.toNumber() - 50);

contractBalance = await getBalance(polyBondToken1.address);

let reward2 = await polyBondToken1.rewardForBurn.call(50);
console.log('contract holds: ', contractBalance.toString(), 'reward2: ', reward2.toString());
tx = await polyBondToken1.burn(50, { from: user2 });
assert.equal(
tx.logs[0].args.amount.toNumber(),
50,
"amount burned should be 50"
);
assert.equal(tx.logs[0].args.reward.toNumber(), reward2);
balance = await polyBondToken1.balanceOf(user2);
assert.equal(balance.toNumber(), 0);
assert.isBelow(reward2.toNumber(), reward1.toNumber());

contractBalance = await getBalance(polyBondToken1.address);
console.log('contract holds: ', contractBalance.toString());

const poolBalance3 = await polyBondToken1.poolBalance.call();
assert.equal(poolBalance3.toNumber(), 0);
const totalSupply3 = await polyBondToken1.totalSupply.call();
assert.equal(totalSupply3.toNumber(), 0);
});


it("Can withdraw ether from ownerFund", async function () {
contractBalance = await getBalance(polyBondToken1.address);
console.log('contract holds: ', contractBalance.toString());
let ownerBalancePre;
web3.eth.getBalance(creator, (err, res) => {
ownerBalancePre = res.toNumber();
});
tx = await polyBondToken1.withdraw({ from: creator });
await web3.eth.getTransaction(tx.tx, (err, res) => {
console.log('Gas for withdrawing: ', tx.receipt.gasUsed, 'Gas Price: ', res.gasPrice.toNumber(), 'Gas cost: ', tx.receipt.gasUsed * res.gasPrice.toNumber() );
})
let ownerBalancePost;
web3.eth.getBalance(creator, (err, res) => {
ownerBalancePost = res.toNumber();
console.log('Post withdraw: ', ownerBalancePost.toString(), 'Pre withdraw: ', ownerBalancePre.toString() )
// Somehow the ownerFund is rounded up / after subtracting gasCosts,
// the owner is given a bit more than whats in the ownerFund?!
});
// assert.isAbove(ownerBalancePost.toNumber(), ownerBalancePre.toNumber()) // this is not necessarily the case because of gas costs!
contractBalance = await getBalance(polyBondToken1.address);
console.log('contract holds: ', contractBalance.toString());
});

});
Loading