Speedrun Ethereum guide · Web3 Solidity course style
Solidity Tokenomics DeFi
P = mS + b
P = a · e^{kS}
P = k · ln(S + c)
P = L / (1 + e^{-k(S - S0)})
contracts/LinearBondingToken.solUse case: community or access tokens with predictable price steps.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract LinearBondingToken {
uint256 public immutable m; // slope (wei per token)
uint256 public immutable b; // base price
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
event Bought(address indexed buyer, uint256 amount, uint256 cost);
event Sold(address indexed seller, uint256 amount, uint256 refund);
constructor(uint256 _m, uint256 _b) { m = _m; b = _b; }
function price(uint256 s) public view returns (uint256) { return m * s + b; }
function costToMint(uint256 amount) public view returns (uint256) {
uint256 s1 = totalSupply;
uint256 s2 = totalSupply + amount;
return m * (s1 + s2 + 1) * amount / 2 + b * amount;
}
function mint(uint256 amount) external payable {
uint256 cost = costToMint(amount);
require(msg.value >= cost, "pay cost");
totalSupply += amount;
balanceOf[msg.sender] += amount;
emit Bought(msg.sender, amount, cost);
if (msg.value > cost) payable(msg.sender).transfer(msg.value - cost);
}
function refund(uint256 amount) external {
require(balanceOf[msg.sender] >= amount, "bal");
uint256 s1 = totalSupply;
uint256 s2 = totalSupply - amount;
uint256 refundAmt = m * (s1 + s2 + 1) * amount / 2 + b * amount;
balanceOf[msg.sender] -= amount;
totalSupply -= amount;
emit Sold(msg.sender, amount, refundAmt);
payable(msg.sender).transfer(refundAmt);
}
}
contracts/ExpoBondingToken.solUse case: hype mint/NFT drop with strong early adopter reward.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {PRBMathUD60x18} from "prb-math/PRBMathUD60x18.sol";
contract ExpoBondingToken {
using PRBMathUD60x18 for uint256;
uint256 public immutable a; // base
uint256 public immutable k; // growth (scaled 1e18)
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
event Bought(address indexed buyer, uint256 amount, uint256 cost);
constructor(uint256 _a, uint256 _k) { a = _a; k = _k; }
function price(uint256 s) public view returns (uint256) {
return a.mul(k.mul(s).exp()); // a * e^{k*s}
}
function costToMint(uint256 amount) public view returns (uint256) {
uint256 cost; uint256 s = totalSupply;
for (uint256 i = 0; i < amount; i++) { cost += price(s + i); }
return cost;
}
function mint(uint256 amount) external payable {
require(amount <= 50, "cap per tx");
uint256 cost = costToMint(amount);
require(msg.value >= cost, "pay cost");
totalSupply += amount;
balanceOf[msg.sender] += amount;
emit Bought(msg.sender, amount, cost);
if (msg.value > cost) payable(msg.sender).transfer(msg.value - cost);
}
}
Use integral approximation (not loop) in production.
contracts/LogBondingToken.solUse case: stabilize price after early lift (e.g., utility token).
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {PRBMathUD60x18} from "prb-math/PRBMathUD60x18.sol";
contract LogBondingToken {
using PRBMathUD60x18 for uint256;
uint256 public immutable k;
uint256 public immutable c;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
event Bought(address indexed buyer, uint256 amount, uint256 cost);
constructor(uint256 _k, uint256 _c) { k = _k; c = _c; }
function price(uint256 s) public view returns (uint256) { return k.mul((s + c).ln()); }
function costToMint(uint256 amount) public view returns (uint256) {
uint256 cost; uint256 s = totalSupply;
for (uint256 i = 0; i < amount; i++) { cost += price(s + i); }
return cost;
}
function mint(uint256 amount) external payable {
uint256 cost = costToMint(amount);
require(msg.value >= cost, "pay cost");
totalSupply += amount;
balanceOf[msg.sender] += amount;
emit Bought(msg.sender, amount, cost);
if (msg.value > cost) payable(msg.sender).transfer(msg.value - cost);
}
}
contracts/SigmoidBondingToken.solUse case: memberships/credits with soft ceiling and mid-growth ramp.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {PRBMathUD60x18} from "prb-math/PRBMathUD60x18.sol";
contract SigmoidBondingToken {
using PRBMathUD60x18 for uint256;
uint256 public immutable L; // ceiling price
uint256 public immutable k; // steepness (1e18)
uint256 public immutable S0; // inflection
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
event Bought(address indexed buyer, uint256 amount, uint256 cost);
constructor(uint256 _L, uint256 _k, uint256 _S0) { L=_L; k=_k; S0=_S0; }
function price(uint256 s) public view returns (uint256) {
if (s >= S0) {
uint256 expPos = k.mul(s - S0).exp();
return L.mul(expPos).div(1e18 + expPos);
} else {
uint256 expPos = k.mul(S0 - s).exp();
return L.div(1e18 + expPos);
}
}
function costToMint(uint256 amount) public view returns (uint256) {
uint256 cost; uint256 s = totalSupply;
for (uint256 i = 0; i < amount; i++) cost += price(s + i);
return cost;
}
function mint(uint256 amount) external payable {
uint256 cost = costToMint(amount);
require(msg.value >= cost, "pay cost");
totalSupply += amount;
balanceOf[msg.sender] += amount;
emit Bought(msg.sender, amount, cost);
if (msg.value > cost) payable(msg.sender).transfer(msg.value - cost);
}
}
Use sigmoid to simulate saturation/ceiling pricing for memberships or bandwidth credits.
Ask about curve selection, fees, edge cases, or demo steps.