Created
January 20, 2018 07:11
-
-
Save goastoman/620649d027f274f040d4d643399b984c to your computer and use it in GitHub Desktop.
Moneybox contract Tutorial. Not for production.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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