Skip to content

Instantly share code, notes, and snippets.

@goastoman
Created January 20, 2018 07:11
Show Gist options
  • Save goastoman/620649d027f274f040d4d643399b984c to your computer and use it in GitHub Desktop.
Save goastoman/620649d027f274f040d4d643399b984c to your computer and use it in GitHub Desktop.
Moneybox contract Tutorial. Not for production.
// Creates secret key and balance for test net. Outputs account.json.
const fs = require('fs');
const Web3 = require('web3');
const crypto = require('crypto');
const {run} = require('./lib/utils.js');
const prompt = require('./lib/prompt.js');
async function main() {
// Ask user to enter password and amount of balance in Ether
const {
password,
balance,
} = await prompt();
// Create 32 byte length hash from password
let secretKey;
if (password) {
secretKey = Web3.utils.soliditySha3({
type: 'string',
value: password,
});
}
else {
secretKey = '0x' + crypto.randomBytes(32).toString('hex');
}
// Convert ethers to inner coin
const weis = Web3.utils.toInn(balance.toString(), 'ether');
const account = {
secretKey,
balance: weis,
};
fs.writeFileSync('./account.json', JSON.stringify(account, null, 4));
}
run(main);
// Compiles contract and write result to code.json
const fs = require('fs');
const solc = require('solc');
const {run} = require('./lib/utils');
async function main() {
console.log('Read contract source');
const source = fs.readFileSync('./contract.sol', 'utf-8');
console.log('Compile contract code');
const result = solc.compile(source);
// Extract bytecode and program interface from compiled data
const compiled = result.contracts[':moneybox'];
const contract = {
interface: JSON.parse(compiled.interface),
bytecode: compiled.bytecode,
};
console.log('Write "code.json"');
fs.writeFileSync('./code.json', JSON.stringify(contract, null, 4));
console.log('Complete');
}
run(main);
// Deploy contract into testnet and write it's address to contract.json
const Web3 = require('web3');
const fs = require('fs');
const {run} = require('./lib/utils.js');
const code = require('./code.json');
async function main() {
// Create web3 instance and connect to local net
const web3 = new Web3(
new Web3.providers.HttpProvider('http://localhost:8545')
);
const coinbase = await web3.eth.getCoinbase();
const moneybox = new web3.eth.Contract(code.interface, {
from: coinbase,
gas: 5000000,
});
const limit = process.argv[2] || 2;
// Deploy contract in the testnet
const contract = await moneybox.deploy({
// Conract body
data: code.bytecode,
// Constructor arguments
arguments: [limit],
})
.send();
// Remember contract's address and save with interface
fs.writeFileSync('contract.json', JSON.stringify({
address: contract.options.address,
interface: code.interface,
}, null, 4));
}
run(main);
// Destroy contract and remove it from blockchain.
const Web3 = require('web3');
const {run} = require('./lib/utils.js');
async function main() {
const contract = require('./contract.json');
// Initialize web3 instance with local network
const web3 = new Web3(
new Web3.providers.HttpProvider('http://localhost:8545')
);
// Get current account address
const coinbase = await web3.eth.getCoinbase();
// Initialize contract
const moneyb = new web3.eth.Contract(contract.interface, contract.address, {
// Set default from address
from: coinbase,
// Set default gas amount
gas: 5000000,
});
await moneyb.methods.kill().send();
};
run(main);
pragma solidity ^0.4.0;
contract moneybox {
address public owner;
//set up the limit
uint public limit;
//exchange Eth for the inner coin
uint decimals = (10 * 100);
//checking is the owner
modifier isOwner() {
require(msg.sender == owner);
_;
}
event Deposit(address indexed from, uint value);
function deposit() public payable {
Deposit(msg.sender, msg.value);
}
function moneybox(uint _limit) public {
require(_limit > 0);
owner = msg.sender;
limit = _limit * decimals;
}
//checking balance
function isDone() public constant returns (bool) {
return this.balance >= limit;
}
//if balance >= limit, owner can withdraw the money
function withdraw() public isOwner {
require(isDone());
msg.sender.transfer(this.balance);
}
//kill contract id balance == 0
function kill() public isOwner {
require(this.balance == 0);
selfdestruct(owner);
}
}
// Run HTTP Server with simple UI to interact with contract.
const http = require('http');
const Web3 = require('web3');
const Plant = require('@plant/plant');
const fs = require('fs');
const path = require('path');
const {run, forever, indent} = require('./lib/utils.js');
const toEther = (value) => Web3.utils.fromWei(String(value), 'ether');
const toWei = (value) => Web3.utils.toWei(String(value), 'ether');
// BigNumber
const BigNum = Web3.utils.BN;
const originHandler = require('./lib/origin-handler.js');
const errorHandler = require('./lib/error-handler.js');
const indexPage = fs.readFileSync(
path.join(__dirname, 'ui/index.html'), 'utf8'
);
const PORT = process.env.PORT || 8080;
const HOST = process.env.HOST || 'localhost';
async function main() {
const contract = require('./contract.json');
// Initialize web3 instance with local network
const web3 = new Web3(
new Web3.providers.HttpProvider('http://localhost:8545')
);
// Get current account address
const coinbase = await web3.eth.getCoinbase();
// Initialize contract
const moneyb = new web3.eth.Contract(contract.interface, contract.address, {
// Set default from address
from: coinbase,
// Set default gas amount
gas: 5000000,
});
// Helper method to get current state
const getState = async () => {
// Get contract balance
const contractBalance = await web3.eth.getBalance(contract.address);
// Get current user balance
const balance = await web3.eth.getBalance(coinbase);
// Get PiggyBank.limit variable
const limit = await moneyb.methods.limit().call();
return {
balance: toEther(balance),
contract: {
address: contract.address,
balance: toEther(contractBalance),
limit: toEther(limit),
},
};
};
// Create API Router
const router = new Plant.Router();
// Route to get state of balance and contract.
router.get('/state', async ({res}) => {
const state = await getState();
res.json(state);
});
// Route to deposit ethers
router.post('/deposit', async ({req, res}) => {
let value;
if ('value' in req.query) {
value = parseInt(req.query.value);
if (value <= 0) {
res.status(400);
res.json({
error: 'Value should be greater then 0',
});
return;
}
}
else {
value = 1;
}
const balance = await web3.eth.getBalance(coinbase);
// Convert balance and value to BigNumbers to allow comparision in weis.
const bnValue = web3.utils.toWei(new BigNum(value), 'ether');
const bnBalance = new BigNum(balance);
// Check if we have enough ethers on a balance.
if (bnValue.gt(bnBalance)) {
res.status(400);
res.json({
error: `You have no ${value} ethers on your balance`,
});
return;
}
// Send value to contract with payable method deposit
// with method PiggyBank.deposit
await moneyb.methods.deposit().send({
value: bnValue,
});
const state = await getState();
res.json(state);
});
// Route to withdraw
router.post('/withdraw', async ({res}) => {
// Call canWithdraw method to determine is contract's `limit` reached.
const canWithdraw = await moneyb.methods.canWithdraw().call();
if (! canWithdraw) {
res.status(400)
.json({
error: 'Limited funds',
});
return;
}
// Withdraw ethers from contact to users' contract
// with method moneybox.withdraw
await moneyb.methods.withdraw().send();
const state = await getState();
res.json(state);
});
// Get UI page
router.get('/', async ({res}) => {
res.html(indexPage);
});
// Create server
const plant = new Plant();
plant.use(originHandler);
plant.use(errorHandler);
plant.use(router);
const server = http.createServer(plant.handler());
// Run server
server.listen(PORT, HOST, () => {
console.log(indent(`
Server is started at port http://localhost:8080.
GET /state - returns current balance of contract and account.
POST /deposit - calls deposit() and sends 1 ether from account to contract.
POST /withdraw - calls withdraw().
`));
});
// Prevent from termination
await forever;
}
run(main);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment