diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..5cfe0c23 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,30 @@ +name: CI + +on: + push: + branches: [ master, main ] + pull_request: + branches: [ master, main ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Use Node.js 18.x + uses: actions/setup-node@v3 + with: + node-version: 18.x + + - name: Install Truffle + run: npm install -g truffle + + - name: Install dependencies (Auction DApp Backend) + working-directory: code/auction_dapp/backend + run: npm install + + - name: Compile Contracts (Auction DApp Backend) + working-directory: code/auction_dapp/backend + run: truffle compile diff --git a/code/OpenZeppelin/contracts/SampleCrowdsale.sol b/code/OpenZeppelin/contracts/SampleCrowdsale.sol index e741e361..84c5a15a 100644 --- a/code/OpenZeppelin/contracts/SampleCrowdsale.sol +++ b/code/OpenZeppelin/contracts/SampleCrowdsale.sol @@ -1,4 +1,8 @@ -pragma solidity 0.4.23; +pragma solidity ^0.8.20; + +// NOTE: Crowdsale contracts were removed from OpenZeppelin Contracts 4.x. +// This example serves as a reference for the book content but requires +// specific legacy libraries or a custom implementation to compile. import './SampleToken.sol'; import 'openzeppelin-solidity/contracts/crowdsale/emission/MintedCrowdsale.sol'; @@ -8,7 +12,7 @@ contract SampleCrowdsale is PostDeliveryCrowdsale, MintedCrowdsale { constructor( uint256 _openingTime, - uint256 _closingTime + uint256 _closingTime, uint256 _rate, address _wallet, MintableToken _token @@ -19,3 +23,4 @@ contract SampleCrowdsale is PostDeliveryCrowdsale, MintedCrowdsale { { } } +} diff --git a/code/OpenZeppelin/contracts/SampleToken.sol b/code/OpenZeppelin/contracts/SampleToken.sol index 16fa3152..f0e6a20b 100644 --- a/code/OpenZeppelin/contracts/SampleToken.sol +++ b/code/OpenZeppelin/contracts/SampleToken.sol @@ -1,9 +1,12 @@ -pragma solidity 0.4.23; +pragma solidity ^0.8.20; -import 'openzeppelin-solidity/contracts/token/ERC20/MintableToken.sol'; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; -contract SampleToken is MintableToken { - string public name = "SAMPLE TOKEN"; - string public symbol = "SAM"; - uint8 public decimals = 18; +contract SampleToken is ERC20, Ownable { + constructor() ERC20("SAMPLE TOKEN", "SAM") Ownable(msg.sender) {} + + function mint(address to, uint256 amount) public onlyOwner { + _mint(to, amount); + } } diff --git a/code/Solidity/Faucet.sol b/code/Solidity/Faucet.sol index c5c6188c..6ad50c67 100644 --- a/code/Solidity/Faucet.sol +++ b/code/Solidity/Faucet.sol @@ -1,4 +1,5 @@ -// Our first contract is a faucet! +pragma solidity ^0.8.20; + contract Faucet { // Give out ether to anyone who asks @@ -8,10 +9,10 @@ contract Faucet { require(withdraw_amount <= 100000000000000000); // Send the amount to the address that requested it - msg.sender.transfer(withdraw_amount); + payable(msg.sender).transfer(withdraw_amount); } // Accept any incoming amount - function () public payable {} + receive() external payable {} } diff --git a/code/Solidity/Faucet2.sol b/code/Solidity/Faucet2.sol index 1549e858..11cb2f37 100644 --- a/code/Solidity/Faucet2.sol +++ b/code/Solidity/Faucet2.sol @@ -1,5 +1,5 @@ // Version of Solidity compiler this program was written for -pragma solidity ^0.4.19; +pragma solidity ^0.8.20; // Our first contract is a faucet! contract Faucet { @@ -11,10 +11,10 @@ contract Faucet { require(withdraw_amount <= 100000000000000000); // Send the amount to the address that requested it - msg.sender.transfer(withdraw_amount); + payable(msg.sender).transfer(withdraw_amount); } // Accept any incoming amount - function () public payable {} + receive() external payable {} } diff --git a/code/Solidity/Faucet3.sol b/code/Solidity/Faucet3.sol index cac7dbd7..cfd217d1 100644 --- a/code/Solidity/Faucet3.sol +++ b/code/Solidity/Faucet3.sol @@ -1,5 +1,5 @@ // Version of Solidity compiler this program was written for -pragma solidity ^0.4.22; +pragma solidity ^0.8.20; // Our first contract is a faucet! contract Faucet { @@ -14,7 +14,7 @@ contract Faucet { // Contract destructor function destroy() public { require(msg.sender == owner); - selfdestruct(owner); + selfdestruct(payable(owner)); } // Give out ether to anyone who asks @@ -24,10 +24,10 @@ contract Faucet { require(withdraw_amount <= 0.1 ether); // Send the amount to the address that requested it - msg.sender.transfer(withdraw_amount); + payable(msg.sender).transfer(withdraw_amount); } // Accept any incoming amount - function () public payable {} + receive() external payable {} } diff --git a/code/Solidity/Faucet4.sol b/code/Solidity/Faucet4.sol index 6645972c..1d117e87 100644 --- a/code/Solidity/Faucet4.sol +++ b/code/Solidity/Faucet4.sol @@ -1,5 +1,5 @@ // Version of Solidity compiler this program was written for -pragma solidity ^0.4.22; +pragma solidity ^0.8.20; // Our first contract is a faucet! contract Faucet { @@ -19,7 +19,7 @@ contract Faucet { // Contract destructor function destroy() public onlyOwner { - selfdestruct(owner); + selfdestruct(payable(owner)); } // Give out ether to anyone who asks @@ -29,10 +29,10 @@ contract Faucet { require(withdraw_amount <= 0.1 ether); // Send the amount to the address that requested it - msg.sender.transfer(withdraw_amount); + payable(msg.sender).transfer(withdraw_amount); } // Accept any incoming amount - function () public payable {} + receive() external payable {} } diff --git a/code/Solidity/Faucet5.sol b/code/Solidity/Faucet5.sol index 880c5f43..79649aab 100644 --- a/code/Solidity/Faucet5.sol +++ b/code/Solidity/Faucet5.sol @@ -1,5 +1,5 @@ // Version of Solidity compiler this program was written for -pragma solidity ^0.4.22; +pragma solidity ^0.8.20; contract owned { address owner; @@ -17,7 +17,7 @@ contract owned { contract mortal is owned { // Contract destructor function destroy() public onlyOwner { - selfdestruct(owner); + selfdestruct(payable(owner)); } } @@ -27,8 +27,8 @@ contract Faucet is mortal { // Limit withdrawal amount require(withdraw_amount <= 0.1 ether); // Send the amount to the address that requested it - msg.sender.transfer(withdraw_amount); + payable(msg.sender).transfer(withdraw_amount); } // Accept any incoming amount - function () public payable {} + receive() external payable {} } diff --git a/code/Solidity/Faucet6.sol b/code/Solidity/Faucet6.sol index 393ece13..07af7fed 100644 --- a/code/Solidity/Faucet6.sol +++ b/code/Solidity/Faucet6.sol @@ -1,5 +1,5 @@ // Version of Solidity compiler this program was written for -pragma solidity ^0.4.22; +pragma solidity ^0.8.20; contract owned { address owner; @@ -17,7 +17,7 @@ contract owned { contract mortal is owned { // Contract destructor function destroy() public onlyOwner { - selfdestruct(owner); + selfdestruct(payable(owner)); } } @@ -27,8 +27,8 @@ contract Faucet is mortal { // Limit withdrawal amount require(withdraw_amount <= 0.1 ether); // Send the amount to the address that requested it - msg.sender.transfer(withdraw_amount); + payable(msg.sender).transfer(withdraw_amount); } // Accept any incoming amount - function () public payable {} + receive() external payable {} } diff --git a/code/Solidity/Faucet7.sol b/code/Solidity/Faucet7.sol index c40c4dd1..e07e341e 100644 --- a/code/Solidity/Faucet7.sol +++ b/code/Solidity/Faucet7.sol @@ -1,5 +1,5 @@ // Version of Solidity compiler this program was written for -pragma solidity ^0.4.22; +pragma solidity ^0.8.20; contract owned { address owner; @@ -17,7 +17,7 @@ contract owned { contract mortal is owned { // Contract destructor function destroy() public onlyOwner { - selfdestruct(owner); + selfdestruct(payable(owner)); } } @@ -26,11 +26,11 @@ contract Faucet is mortal { function withdraw(uint withdraw_amount) public { // Limit withdrawal amount require(withdraw_amount <= 0.1 ether); - require(this.balance >= withdraw_amount, - "Insufficient balance in faucet for withdrawal request") + require(address(this).balance >= withdraw_amount, + "Insufficient balance in faucet for withdrawal request"); // Send the amount to the address that requested it - msg.sender.transfer(withdraw_amount); + payable(msg.sender).transfer(withdraw_amount); } // Accept any incoming amount - function () public payable {} + receive() external payable {} } diff --git a/code/Solidity/Faucet8.sol b/code/Solidity/Faucet8.sol index 3ff90b84..7725fcf7 100644 --- a/code/Solidity/Faucet8.sol +++ b/code/Solidity/Faucet8.sol @@ -1,5 +1,5 @@ // Version of Solidity compiler this program was written for -pragma solidity ^0.4.22; +pragma solidity ^0.8.20; contract owned { address owner; @@ -17,7 +17,7 @@ contract owned { contract mortal is owned { // Contract destructor function destroy() public onlyOwner { - selfdestruct(owner); + selfdestruct(payable(owner)); } } @@ -29,14 +29,14 @@ contract Faucet is mortal { function withdraw(uint withdraw_amount) public { // Limit withdrawal amount require(withdraw_amount <= 0.1 ether); - require(this.balance >= withdraw_amount, + require(address(this).balance >= withdraw_amount, "Insufficient balance in faucet for withdrawal request"); // Send the amount to the address that requested it - msg.sender.transfer(withdraw_amount); + payable(msg.sender).transfer(withdraw_amount); emit Withdrawal(msg.sender, withdraw_amount); } // Accept any incoming amount - function () public payable { + receive() external payable { emit Deposit(msg.sender, msg.value); } } diff --git a/code/Solidity/Token.sol b/code/Solidity/Token.sol index 3c350763..c226bd94 100644 --- a/code/Solidity/Token.sol +++ b/code/Solidity/Token.sol @@ -1,4 +1,4 @@ -import "Faucet.sol" +import "./Faucet8.sol"; contract Token is mortal { @@ -6,6 +6,6 @@ contract Token is mortal { constructor(address _f) { _faucet = Faucet(_f); - _faucet.withdraw(0.1 ether) + _faucet.withdraw(0.1 ether); } } diff --git a/code/auction_dapp/backend/contracts/AuctionRepository.sol b/code/auction_dapp/backend/contracts/AuctionRepository.sol index 3f6df028..99a5c40d 100644 --- a/code/auction_dapp/backend/contracts/AuctionRepository.sol +++ b/code/auction_dapp/backend/contracts/AuctionRepository.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.17; +pragma solidity ^0.8.20; import "./DeedRepository.sol"; @@ -60,7 +60,7 @@ contract AuctionRepository { /** * @dev Disallow payments to this contract directly */ - function() public{ + function() external { revert(); } @@ -68,7 +68,7 @@ contract AuctionRepository { * @dev Gets the length of auctions * @return uint representing the auction count */ - function getCount() public constant returns(uint) { + function getCount() public view returns(uint) { return auctions.length; } @@ -76,7 +76,7 @@ contract AuctionRepository { * @dev Gets the bid counts of a given auction * @param _auctionId uint ID of the auction */ - function getBidsCount(uint _auctionId) public constant returns(uint) { + function getBidsCount(uint _auctionId) public view returns(uint) { return auctionBids[_auctionId].length; } @@ -84,7 +84,7 @@ contract AuctionRepository { * @dev Gets an array of owned auctions * @param _owner address of the auction owner */ - function getAuctionsOf(address _owner) public constant returns(uint[]) { + function getAuctionsOf(address _owner) public view returns(uint[] memory) { uint[] memory ownedAuctions = auctionOwner[_owner]; return ownedAuctions; } @@ -94,7 +94,7 @@ contract AuctionRepository { * @param _auctionId uint of the auction owner * @return amount uint256, address of last bidder */ - function getCurrentBid(uint _auctionId) public constant returns(uint256, address) { + function getCurrentBid(uint _auctionId) public view returns(uint256, address) { uint bidsLength = auctionBids[_auctionId].length; // if there are bids refund the last bid if( bidsLength > 0 ) { @@ -109,7 +109,7 @@ contract AuctionRepository { * @param _owner address of the owner * @return uint total number of auctions */ - function getAuctionsCountOfOwner(address _owner) public constant returns(uint) { + function getAuctionsCountOfOwner(address _owner) public view returns(uint) { return auctionOwner[_owner].length; } @@ -126,7 +126,7 @@ contract AuctionRepository { * @return bool whether the auction is active * @return bool whether the auction is finalized */ - function getAuctionById(uint _auctionId) public constant returns( + function getAuctionById(uint _auctionId) public view returns( string name, uint256 blockDeadline, uint256 startPrice, @@ -200,7 +200,7 @@ contract AuctionRepository { // if there are bids refund the last bid if( bidsLength > 0 ) { Bid memory lastBid = auctionBids[_auctionId][bidsLength - 1]; - if(!lastBid.from.send(lastBid.amount)) { + if(!payable(lastBid.from).send(lastBid.amount)) { revert(); } } @@ -232,7 +232,7 @@ contract AuctionRepository { // 2. the money goes to the auction owner Bid memory lastBid = auctionBids[_auctionId][bidsLength - 1]; - if(!myAuction.owner.send(lastBid.amount)) { + if(!payable(myAuction.owner).send(lastBid.amount)) { revert(); } @@ -276,7 +276,7 @@ contract AuctionRepository { // refund the last bidder if( bidsLength > 0 ) { - if(!lastBid.from.send(lastBid.amount)) { + if(!payable(lastBid.from).send(lastBid.amount)) { revert(); } } diff --git a/code/auction_dapp/backend/contracts/DeedRepository.sol b/code/auction_dapp/backend/contracts/DeedRepository.sol index f4e775f5..4c584c1d 100644 --- a/code/auction_dapp/backend/contracts/DeedRepository.sol +++ b/code/auction_dapp/backend/contracts/DeedRepository.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.17; +pragma solidity ^0.8.20; import "./ERC721/ERC721Token.sol"; /** @@ -13,7 +13,7 @@ contract DeedRepository is ERC721Token { * @param _name string represents the name of the repository * @param _symbol string represents the symbol of the repositor */ - function DeedRepository(string _name, string _symbol) public ERC721Token(_name, _symbol) {} + constructor(string memory _name, string memory _symbol) ERC721Token(_name, _symbol) {} /** * @dev Public function to register a new deed diff --git a/code/auction_dapp/backend/contracts/ERC721/ERC721.sol b/code/auction_dapp/backend/contracts/ERC721/ERC721.sol index 4ddbbfd5..bf2f544e 100644 --- a/code/auction_dapp/backend/contracts/ERC721/ERC721.sol +++ b/code/auction_dapp/backend/contracts/ERC721/ERC721.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.18; +pragma solidity ^0.8.20; import "./ERC721Basic.sol"; diff --git a/code/auction_dapp/backend/contracts/ERC721/ERC721Basic.sol b/code/auction_dapp/backend/contracts/ERC721/ERC721Basic.sol index 4006b71d..fd6037d8 100644 --- a/code/auction_dapp/backend/contracts/ERC721/ERC721Basic.sol +++ b/code/auction_dapp/backend/contracts/ERC721/ERC721Basic.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.18; +pragma solidity ^0.8.20; /** diff --git a/code/auction_dapp/backend/contracts/ERC721/ERC721BasicToken.sol b/code/auction_dapp/backend/contracts/ERC721/ERC721BasicToken.sol index 284c686b..1282401a 100644 --- a/code/auction_dapp/backend/contracts/ERC721/ERC721BasicToken.sol +++ b/code/auction_dapp/backend/contracts/ERC721/ERC721BasicToken.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.18; +pragma solidity ^0.8.20; import "./ERC721Basic.sol"; import "./ERC721Receiver.sol"; @@ -94,7 +94,7 @@ contract ERC721BasicToken is ERC721Basic { if (getApproved(_tokenId) != address(0) || _to != address(0)) { tokenApprovals[_tokenId] = _to; - Approval(owner, _to, _tokenId); + emit Approval(owner, _to, _tokenId); } } @@ -116,7 +116,7 @@ contract ERC721BasicToken is ERC721Basic { function setApprovalForAll(address _to, bool _approved) public { require(_to != msg.sender); operatorApprovals[msg.sender][_to] = _approved; - ApprovalForAll(msg.sender, _to, _approved); + emit ApprovalForAll(msg.sender, _to, _approved); } /** @@ -145,7 +145,7 @@ contract ERC721BasicToken is ERC721Basic { removeTokenFrom(_from, _tokenId); addTokenTo(_to, _tokenId); - Transfer(_from, _to, _tokenId); + emit Transfer(_from, _to, _tokenId); } /** @@ -216,7 +216,7 @@ contract ERC721BasicToken is ERC721Basic { function _mint(address _to, uint256 _tokenId) internal { require(_to != address(0)); addTokenTo(_to, _tokenId); - Transfer(address(0), _to, _tokenId); + emit Transfer(address(0), _to, _tokenId); } /** @@ -227,7 +227,7 @@ contract ERC721BasicToken is ERC721Basic { function _burn(address _owner, uint256 _tokenId) internal { clearApproval(_owner, _tokenId); removeTokenFrom(_owner, _tokenId); - Transfer(_owner, address(0), _tokenId); + emit Transfer(_owner, address(0), _tokenId); } /** @@ -240,7 +240,7 @@ contract ERC721BasicToken is ERC721Basic { require(ownerOf(_tokenId) == _owner); if (tokenApprovals[_tokenId] != address(0)) { tokenApprovals[_tokenId] = address(0); - Approval(_owner, address(0), _tokenId); + emit Approval(_owner, address(0), _tokenId); } } diff --git a/code/auction_dapp/backend/contracts/ERC721/ERC721Holder.sol b/code/auction_dapp/backend/contracts/ERC721/ERC721Holder.sol index 67bcc067..63874447 100644 --- a/code/auction_dapp/backend/contracts/ERC721/ERC721Holder.sol +++ b/code/auction_dapp/backend/contracts/ERC721/ERC721Holder.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.18; +pragma solidity ^0.8.20; import "./ERC721Receiver.sol"; diff --git a/code/auction_dapp/backend/contracts/ERC721/ERC721Receiver.sol b/code/auction_dapp/backend/contracts/ERC721/ERC721Receiver.sol index f34e1304..0492ad76 100644 --- a/code/auction_dapp/backend/contracts/ERC721/ERC721Receiver.sol +++ b/code/auction_dapp/backend/contracts/ERC721/ERC721Receiver.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.18; +pragma solidity ^0.8.20; /** diff --git a/code/auction_dapp/backend/contracts/ERC721/ERC721Token.sol b/code/auction_dapp/backend/contracts/ERC721/ERC721Token.sol index 647953b2..8c347e64 100644 --- a/code/auction_dapp/backend/contracts/ERC721/ERC721Token.sol +++ b/code/auction_dapp/backend/contracts/ERC721/ERC721Token.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.18; +pragma solidity ^0.8.20; import "./ERC721.sol"; import "./DeprecatedERC721.sol"; @@ -36,7 +36,7 @@ contract ERC721Token is ERC721, ERC721BasicToken { /** * @dev Constructor function */ - function ERC721Token(string _name, string _symbol) public { + constructor(string memory _name, string memory _symbol) { name_ = _name; symbol_ = _symbol; } diff --git a/code/auction_dapp/backend/contracts/Migrations.sol b/code/auction_dapp/backend/contracts/Migrations.sol index 5cae7bc8..4b268fef 100644 --- a/code/auction_dapp/backend/contracts/Migrations.sol +++ b/code/auction_dapp/backend/contracts/Migrations.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.17; +pragma solidity ^0.8.20; contract Migrations { address public owner; @@ -8,7 +8,7 @@ contract Migrations { if (msg.sender == owner) _; } - function Migrations() public { + constructor() { owner = msg.sender; } diff --git a/code/auction_dapp/backend/contracts/utils/AddressUtils.sol b/code/auction_dapp/backend/contracts/utils/AddressUtils.sol index 931fc76d..22cc8170 100644 --- a/code/auction_dapp/backend/contracts/utils/AddressUtils.sol +++ b/code/auction_dapp/backend/contracts/utils/AddressUtils.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.18; +pragma solidity ^0.8.20; /** @@ -14,15 +14,7 @@ library AddressUtils { * @return whether the target address is a contract */ function isContract(address addr) internal view returns (bool) { - uint256 size; - // XXX Currently there is no better way to check if there is a contract in an address - // than to check the size of the code at that address. - // See https://ethereum.stackexchange.com/a/14016/36603 - // for more details about how this works. - // TODO Check this again before the Serenity release, because all addresses will be - // contracts then. - assembly { size := extcodesize(addr) } // solium-disable-line security/no-inline-assembly - return size > 0; + return addr.code.length > 0; } } diff --git a/code/auction_dapp/backend/contracts/utils/math/SafeMath.sol b/code/auction_dapp/backend/contracts/utils/math/SafeMath.sol index 35b92ffc..aa8c1ce9 100644 --- a/code/auction_dapp/backend/contracts/utils/math/SafeMath.sol +++ b/code/auction_dapp/backend/contracts/utils/math/SafeMath.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.18; +pragma solidity ^0.8.20; /** diff --git a/code/auction_dapp/backend/package.json b/code/auction_dapp/backend/package.json new file mode 100644 index 00000000..6d047105 --- /dev/null +++ b/code/auction_dapp/backend/package.json @@ -0,0 +1,16 @@ +{ + "name": "auction-dapp-backend", + "version": "1.0.0", + "description": "", + "main": "truffle-config.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "compile": "truffle compile" + }, + "dependencies": { + "@openzeppelin/contracts": "^5.0.0" + }, + "devDependencies": { + "truffle": "^5.11.5" + } +} diff --git a/code/auction_dapp/backend/truffle-config.js b/code/auction_dapp/backend/truffle-config.js index f4d62530..5039a620 100644 --- a/code/auction_dapp/backend/truffle-config.js +++ b/code/auction_dapp/backend/truffle-config.js @@ -1,3 +1,14 @@ module.exports = { - + networks: { + development: { + host: "127.0.0.1", + port: 8545, + network_id: "*" + } + }, + compilers: { + solc: { + version: "0.8.20" + } + } }; diff --git a/code/auction_dapp/frontend/package.json b/code/auction_dapp/frontend/package.json new file mode 100644 index 00000000..d685bf7f --- /dev/null +++ b/code/auction_dapp/frontend/package.json @@ -0,0 +1,65 @@ +{ + "name": "auction-dapp-frontend", + "version": "1.0.0", + "description": "A Vue.js project", + "author": "", + "private": true, + "scripts": { + "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", + "start": "npm run dev", + "build": "node build/build.js" + }, + "dependencies": { + "vue": "^2.5.2", + "vue-router": "^3.0.1", + "vuetify": "^1.0.0", + "vue-resource": "^1.5.0", + "web3": "^4.0.0" + }, + "devDependencies": { + "autoprefixer": "^7.1.2", + "babel-core": "^6.22.1", + "babel-helper-vue-jsx-merge-props": "^2.0.3", + "babel-loader": "^7.1.1", + "babel-plugin-syntax-jsx": "^6.18.0", + "babel-plugin-transform-runtime": "^6.22.0", + "babel-plugin-transform-vue-jsx": "^3.5.0", + "babel-preset-env": "^1.3.2", + "babel-preset-stage-2": "^6.22.0", + "chalk": "^2.0.1", + "copy-webpack-plugin": "^4.0.1", + "css-loader": "^0.28.0", + "extract-text-webpack-plugin": "^3.0.0", + "file-loader": "^1.1.4", + "friendly-errors-webpack-plugin": "^1.6.1", + "html-webpack-plugin": "^2.30.1", + "node-notifier": "^5.1.2", + "optimize-css-assets-webpack-plugin": "^3.2.0", + "ora": "^1.2.0", + "portfinder": "^1.0.13", + "postcss-import": "^11.0.0", + "postcss-loader": "^2.0.8", + "postcss-url": "^7.2.1", + "rimraf": "^2.6.0", + "semver": "^5.3.0", + "shelljs": "^0.7.6", + "uglifyjs-webpack-plugin": "^1.1.1", + "url-loader": "^0.5.8", + "vue-loader": "^13.3.0", + "vue-style-loader": "^3.0.1", + "vue-template-compiler": "^2.5.2", + "webpack": "^3.6.0", + "webpack-bundle-analyzer": "^2.9.0", + "webpack-dev-server": "^2.9.1", + "webpack-merge": "^4.1.0" + }, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ] +} diff --git a/code/auction_dapp/frontend/src/main.js b/code/auction_dapp/frontend/src/main.js index 450db5a8..17b1c594 100644 --- a/code/auction_dapp/frontend/src/main.js +++ b/code/auction_dapp/frontend/src/main.js @@ -68,25 +68,44 @@ Vue.mixin({ this.$deedRepoInstance = new DeedRepository() this.$auctionRepoInstance = new AuctionRepository() - this.$chatroomInstance.setWeb3(new Web3_1(Config.SHH_ENDPOINT)) + // this.$chatroomInstance.setWeb3(new Web3_1(Config.SHH_ENDPOINT)) // Whisper is deprecated/removed in many modern contexts // one instance of web3 available to all components - if (typeof web3 !== 'undefined') { - web3 = new Web3(web3.currentProvider) - this.$auctionRepoInstance.setWeb3(web3) - this.$deedRepoInstance.setWeb3(web3) - - store.setMetamaskInstalled() - web3.version.getNetwork((err, netId) => { - store.setNetworkId(netId) - }) - // pull accounts every 2 seconds - setInterval(() => { - web3.eth.getAccounts((err, data) => { - if(data.length > 0) store.setWeb3DefaultAccount(data[0]) - }) - }, 2000) + const initWeb3 = async () => { + if (window.ethereum) { + const web3 = new Web3(window.ethereum); + try { + // Request account access if needed + await window.ethereum.request({ method: 'eth_requestAccounts' }); + + this.$auctionRepoInstance.setWeb3(web3) + this.$deedRepoInstance.setWeb3(web3) + + store.setMetamaskInstalled() + + const netId = await web3.eth.net.getId(); + store.setNetworkId(netId) + + // pull accounts every 2 seconds + setInterval(async () => { + const accounts = await web3.eth.getAccounts(); + if(accounts.length > 0) store.setWeb3DefaultAccount(accounts[0]) + }, 2000) + + } catch (error) { + console.error("User denied account access", error); + } + } else if (window.web3) { // Legacy dapp browsers... + const web3 = new Web3(window.web3.currentProvider); + this.$auctionRepoInstance.setWeb3(web3) + this.$deedRepoInstance.setWeb3(web3) + } else { + console.log('Non-Ethereum browser detected. You should consider trying MetaMask!'); + } } + + initWeb3(); + // inject config to components this.$config = Config } diff --git a/code/auction_dapp/frontend/src/models/AuctionRepository.js b/code/auction_dapp/frontend/src/models/AuctionRepository.js index 3a7a906d..b364c65d 100644 --- a/code/auction_dapp/frontend/src/models/AuctionRepository.js +++ b/code/auction_dapp/frontend/src/models/AuctionRepository.js @@ -13,7 +13,10 @@ export class AuctionRepository { setWeb3(web3) { this.web3 = web3 - this.contractInstance = this.web3.eth.contract(Config.AUCTIONREPOSITORY_ABI).at(Config.AUCTIONREPOSITORY_ADDRESS) + this.contractInstance = new this.web3.eth.Contract( + Config.AUCTIONREPOSITORY_ABI, + Config.AUCTIONREPOSITORY_ADDRESS + ) } getWeb3() { @@ -24,143 +27,116 @@ export class AuctionRepository { this.account = account } - - getCurrentBlock() { - return new Promise((resolve, reject ) => { - this.web3.eth.getBlockNumber((err, blocknumber) => { - if(!err) resolve(blocknumber) - reject(err) - }) - }) + async getCurrentBlock() { + return await this.web3.eth.getBlockNumber() } async watchIfCreated(cb) { const currentBlock = await this.getCurrentBlock() - const eventWatcher = this.contractInstance.AuctionCreated({}, {fromBlock: currentBlock - 1, toBlock: 'latest'}) - eventWatcher.watch(cb) + // Watch for events from current block onwards + this.contractInstance.events.AuctionCreated({ + fromBlock: currentBlock // or 'latest' + }) + .on('data', (event) => { + cb(null, event) + }) + .on('error', (err) => { + cb(err, null) + }) } async watchIfBidSuccess(cb) { const currentBlock = await this.getCurrentBlock() - const eventWatcher = this.contractInstance.BidSuccess({}, {fromBlock: currentBlock - 1, toBlock: 'latest'}) - eventWatcher.watch(cb) + this.contractInstance.events.BidSuccess({ + fromBlock: currentBlock + }) + .on('data', (event) => { + cb(null, event) + }) + .on('error', (err) => { + cb(err, null) + }) } async watchIfCanceled(cb) { const currentBlock = await this.getCurrentBlock() - const eventWatcher = this.contractInstance.AuctionCanceled({}, {fromBlock: currentBlock - 1, toBlock: 'latest'}) - eventWatcher.watch(cb) + this.contractInstance.events.AuctionCanceled({ + fromBlock: currentBlock + }) + .on('data', (event) => { + cb(null, event) + }) + .on('error', (err) => { + cb(err, null) + }) } async watchIfFinalized(cb) { const currentBlock = await this.getCurrentBlock() - const eventWatcher = this.contractInstance.AuctionFinalized({}, {fromBlock: currentBlock - 1, toBlock: 'latest'}) - eventWatcher.watch(cb) - } - getCurrentBid(auctionId) { - return new Promise(async (resolve, reject) => { - try { - this.contractInstance.getCurrentBid(auctionId, {from: this.account, gas: this.gas }, (err, transaction) => { - if(!err) resolve(transaction) - reject(err) - }) - } catch(e) { - reject(e) - } - }) - } - - getBidCount(auctionId) { - return new Promise(async (resolve, reject) => { - try { - this.contractInstance.getBidsCount(auctionId, {from: this.account, gas: this.gas }, (err, transaction) => { - if(!err) resolve(transaction) - reject(err) - }) - } catch(e) { - reject(e) - } - }) - } - - getCount() { - return new Promise(async (resolve, reject) => { - try { - this.contractInstance.getCount({from: this.account, gas: this.gas }, (err, transaction) => { - if(!err) resolve(transaction) - reject(err) - }) - } catch(e) { - reject(e) - } - }) - } - - bid(auctionId, price) { - console.log(auctionId, this.web3.toWei(price, 'ether')) - return new Promise(async (resolve, reject) => { - try { - this.contractInstance.bidOnAuction(auctionId, {from: this.account, gas: this.gas, value: this.web3.toWei(price, 'ether') }, (err, transaction) => { - if(!err) resolve(transaction) - reject(err) - }) - } catch(e) { - reject(e) - } - }) - } - - create(deedId, auctionTitle, metadata, startingPrice, blockDeadline) { - return new Promise(async (resolve, reject) => { - try { - - this.contractInstance.createAuction(Config.DEEDREPOSITORY_ADDRESS, deedId, auctionTitle, metadata, this.web3.toWei(startingPrice, 'ether'), blockDeadline, {from: this.account, gas: this.gas }, (err, transaction) => { - if(!err) resolve(transaction) - reject(err) - }) - } catch(e) { - reject(e) - } - }) - } - - cancel(auctionId) { - return new Promise(async (resolve, reject) => { - try { - this.contractInstance.cancelAuction(auctionId, {from: this.account, gas: this.gas }, (err, transaction) => { - if(!err) resolve(transaction) - reject(err) - }) - } catch(e) { - reject(e) - } - }) - } - - finalize(auctionId) { - return new Promise(async (resolve, reject) => { - try { - this.contractInstance.finalizeAuction(auctionId, {from: this.account, gas: this.gas }, (err, transaction) => { - if(!err) resolve(transaction) - reject(err) - }) - } catch(e) { - reject(e) - } - }) - } - - findById(auctionId) { - return new Promise(async (resolve, reject) => { - try { - this.contractInstance.getAuctionById(auctionId, { from: this.account, gas: this.gas }, (err, transaction) => { - if(!err) resolve(transaction) - reject(err) - }) - } catch(e) { - reject(e) - } + this.contractInstance.events.AuctionFinalized({ + fromBlock: currentBlock + }) + .on('data', (event) => { + cb(null, event) + }) + .on('error', (err) => { + cb(err, null) }) } + async getCurrentBid(auctionId) { + return await this.contractInstance.methods.getCurrentBid(auctionId).call({from: this.account}) + } + + async getBidCount(auctionId) { + return await this.contractInstance.methods.getBidsCount(auctionId).call({from: this.account}) + } + + async getCount() { + return await this.contractInstance.methods.getCount().call({from: this.account}) + } + + async bid(auctionId, price) { + const priceInWei = this.web3.utils.toWei(price.toString(), 'ether') + console.log(auctionId, priceInWei) + return await this.contractInstance.methods.bidOnAuction(auctionId).send({ + from: this.account, + gas: this.gas, + value: priceInWei + }) + } + + async create(deedId, auctionTitle, metadata, startingPrice, blockDeadline) { + const priceInWei = this.web3.utils.toWei(startingPrice.toString(), 'ether') + return await this.contractInstance.methods.createAuction( + Config.DEEDREPOSITORY_ADDRESS, + deedId, + auctionTitle, + metadata, + priceInWei, + blockDeadline + ).send({ + from: this.account, + gas: this.gas + }) + } + + async cancel(auctionId) { + return await this.contractInstance.methods.cancelAuction(auctionId).send({ + from: this.account, + gas: this.gas + }) + } + + async finalize(auctionId) { + return await this.contractInstance.methods.finalizeAuction(auctionId).send({ + from: this.account, + gas: this.gas + }) + } + + async findById(auctionId) { + return await this.contractInstance.methods.getAuctionById(auctionId).call({ from: this.account }) + } + } \ No newline at end of file diff --git a/code/auction_dapp/frontend/src/models/ChatRoom.js b/code/auction_dapp/frontend/src/models/ChatRoom.js index 071fb9af..56326cc7 100644 --- a/code/auction_dapp/frontend/src/models/ChatRoom.js +++ b/code/auction_dapp/frontend/src/models/ChatRoom.js @@ -14,9 +14,10 @@ export class ChatRoom { } async setWeb3(web3){ - this.web3 = web3 - this.keyID = await this.web3.shh.addPrivateKey(this.privateKey) - this.publicKey = await this.web3.shh.getPublicKey(this.keyID) + console.warn("Whisper (shh) is deprecated in modern Web3. This feature is disabled."); + // this.web3 = web3 + // this.keyID = await this.web3.shh.addPrivateKey(this.privateKey) + // this.publicKey = await this.web3.shh.getPublicKey(this.keyID) } getWeb3(){ diff --git a/code/auction_dapp/frontend/src/models/DeedRepository.js b/code/auction_dapp/frontend/src/models/DeedRepository.js index dadc3c4e..5e78b8d6 100644 --- a/code/auction_dapp/frontend/src/models/DeedRepository.js +++ b/code/auction_dapp/frontend/src/models/DeedRepository.js @@ -12,7 +12,10 @@ export class DeedRepository { } setWeb3(web3) { this.web3 = web3 - this.contractInstance = this.web3.eth.contract(Config.DEEDREPOSITORY_ABI).at(Config.DEEDREPOSITORY_ADDRESS) + this.contractInstance = new this.web3.eth.Contract( + Config.DEEDREPOSITORY_ABI, + Config.DEEDREPOSITORY_ADDRESS + ) } getWeb3() { @@ -23,64 +26,51 @@ export class DeedRepository { this.account = account } - getCurrentBlock() { - return new Promise((resolve, reject ) => { - this.web3.eth.getBlockNumber((err, blocknumber) => { - if(!err) resolve(blocknumber) - reject(err) - }) - }) + async getCurrentBlock() { + return await this.web3.eth.getBlockNumber() } - // getAccounts() { - // return new Promise((resolve, reject ) => { - // this.web3.eth.getAccounts((err, accounts) => { - // if(!err) resolve(accounts) - // reject(err) - // }) - // }) - // } - async watchIfCreated(cb) { const currentBlock = await this.getCurrentBlock() - const eventWatcher = this.contractInstance.DeedRegistered({}, {fromBlock: currentBlock - 1, toBlock: 'latest'}) - eventWatcher.watch(cb) + this.contractInstance.events.DeedRegistered({ + fromBlock: currentBlock + }) + .on('data', (event) => { + cb(null, event) + }) + .on('error', (err) => { + cb(err, null) + }) } async watchIfDeedTransfered(cb) { const currentBlock = await this.getCurrentBlock() - const eventWatcher = this.contractInstance.Transfer({}, {fromBlock: currentBlock - 1, toBlock: 'latest'}) - eventWatcher.watch(cb) + this.contractInstance.events.Transfer({ + fromBlock: currentBlock + }) + .on('data', (event) => { + cb(null, event) + }) + .on('error', (err) => { + cb(err, null) + }) } - exists(deedId) { - return new Promise(async (resolve, reject) => { - this.contractInstance.exists(deedId, {from: this.account, gas: this.gas }, (err, transaction) => { - if(!err) resolve(transaction) - reject(err) - }) - }) + async exists(deedId) { + return await this.contractInstance.methods.exists(deedId).call({from: this.account}) } - transferTo(to, deedId) { - return new Promise(async (resolve, reject) => { - this.contractInstance.transferFrom(this.account, to, deedId, {from: this.account, gas: this.gas }, (err, transaction) => { - if(!err) resolve(transaction) - reject(err) - }) + async transferTo(to, deedId) { + return await this.contractInstance.methods.transferFrom(this.account, to, deedId).send({ + from: this.account, + gas: this.gas }) - } - create(deedId, deedURI) { - console.log('contractinsatnce', this.contractInstance ) - return new Promise(async (resolve, reject) => { - this.contractInstance.registerDeed(deedId, deedURI, {from: this.account, gas: this.gas }, (err, transaction) => { - if(!err) - resolve(transaction) - else - reject(err) - }) + async create(deedId, deedURI) { + return await this.contractInstance.methods.registerDeed(deedId, deedURI).send({ + from: this.account, + gas: this.gas }) } } \ No newline at end of file diff --git a/code/jsonrpc/http/js/index.js b/code/jsonrpc/http/js/index.js index 231d4b26..db800f39 100644 --- a/code/jsonrpc/http/js/index.js +++ b/code/jsonrpc/http/js/index.js @@ -1,5 +1,5 @@ const axios = require("axios") -const url = "http://172.16.163.129:8545" +const url = "https://rpc.sepolia.org" // Public Sepolia RPC const requests = [ { diff --git a/code/jsonrpc/websockets/app.js b/code/jsonrpc/websockets/app.js index 3fd007cd..9569f1c8 100644 --- a/code/jsonrpc/websockets/app.js +++ b/code/jsonrpc/websockets/app.js @@ -1,5 +1,5 @@ const WebSocket = require('ws'); -const wsEndpoint = "ws://172.16.163.129:8546" +const wsEndpoint = "wss://ethereum-sepolia-rpc.publicnode.com" // Public Sepolia WSS const ws = new WebSocket(wsEndpoint); const requests = [ diff --git a/code/truffle/CallExamples/contracts/CallExamples.sol b/code/truffle/CallExamples/contracts/CallExamples.sol index 75680abb..17b763a9 100644 --- a/code/truffle/CallExamples/contracts/CallExamples.sol +++ b/code/truffle/CallExamples/contracts/CallExamples.sol @@ -1,16 +1,16 @@ -pragma solidity ^0.4.22; +pragma solidity ^0.8.20; contract calledContract { event callEvent(address sender, address origin, address from); function calledFunction() public { - emit callEvent(msg.sender, tx.origin, this); + emit callEvent(msg.sender, tx.origin, address(this)); } } library calledLibrary { event callEvent(address sender, address origin, address from); function calledFunction() public { - emit callEvent(msg.sender, tx.origin, this); + emit callEvent(msg.sender, tx.origin, address(this)); } } @@ -23,8 +23,10 @@ contract caller { calledLibrary.calledFunction(); // Low level calls using the address object for calledContract - require(address(_calledContract).call(bytes4(keccak256("calledFunction()")))); - require(address(_calledContract).delegatecall(bytes4(keccak256("calledFunction()")))); + (bool success, ) = address(_calledContract).call(abi.encodeWithSignature("calledFunction()")); + require(success); + (bool success2, ) = address(_calledContract).delegatecall(abi.encodeWithSignature("calledFunction()")); + require(success2); diff --git a/code/truffle/CallExamples/contracts/Migrations.sol b/code/truffle/CallExamples/contracts/Migrations.sol index f170cb4f..9c9e7dfc 100644 --- a/code/truffle/CallExamples/contracts/Migrations.sol +++ b/code/truffle/CallExamples/contracts/Migrations.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.17; +pragma solidity ^0.8.20; contract Migrations { address public owner; @@ -8,7 +8,7 @@ contract Migrations { if (msg.sender == owner) _; } - function Migrations() public { + constructor() { owner = msg.sender; } diff --git a/code/truffle/Faucet/contracts/Faucet.sol b/code/truffle/Faucet/contracts/Faucet.sol index 354bd568..fd9360b0 100644 --- a/code/truffle/Faucet/contracts/Faucet.sol +++ b/code/truffle/Faucet/contracts/Faucet.sol @@ -1,5 +1,5 @@ // Version of Solidity compiler this program was written for -pragma solidity ^0.4.19; +pragma solidity ^0.8.20; // Our first contract is a faucet! contract Faucet { @@ -11,10 +11,11 @@ contract Faucet { require(withdraw_amount <= 100000000000000000); // Send the amount to the address that requested it - msg.sender.transfer(withdraw_amount); + payable(msg.sender).transfer(withdraw_amount); } // Accept any incoming amount - function () public payable {} + receive() external payable {} + } diff --git a/code/truffle/Faucet/contracts/Migrations.sol b/code/truffle/Faucet/contracts/Migrations.sol index f170cb4f..9c9e7dfc 100644 --- a/code/truffle/Faucet/contracts/Migrations.sol +++ b/code/truffle/Faucet/contracts/Migrations.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.17; +pragma solidity ^0.8.20; contract Migrations { address public owner; @@ -8,7 +8,7 @@ contract Migrations { if (msg.sender == owner) _; } - function Migrations() public { + constructor() { owner = msg.sender; } diff --git a/code/truffle/FaucetEvents/contracts/Faucet.sol b/code/truffle/FaucetEvents/contracts/Faucet.sol index 5183eab7..6fea324c 100644 --- a/code/truffle/FaucetEvents/contracts/Faucet.sol +++ b/code/truffle/FaucetEvents/contracts/Faucet.sol @@ -1,5 +1,5 @@ // Version of Solidity compiler this program was written for -pragma solidity ^0.4.22; +pragma solidity ^0.8.20; contract owned { address owner; @@ -17,7 +17,7 @@ contract owned { contract mortal is owned { // Contract destructor function destroy() public onlyOwner { - selfdestruct(owner); + selfdestruct(payable(owner)); } } @@ -29,14 +29,14 @@ contract Faucet is mortal { function withdraw(uint withdraw_amount) public { // Limit withdrawal amount require(withdraw_amount <= 0.1 ether); - require(this.balance >= withdraw_amount, + require(address(this).balance >= withdraw_amount, "Insufficient balance in faucet for withdrawal request"); // Send the amount to the address that requested it - msg.sender.transfer(withdraw_amount); + payable(msg.sender).transfer(withdraw_amount); emit Withdrawal(msg.sender, withdraw_amount); } // Accept any incoming amount - function () public payable { + receive() external payable { emit Deposit(msg.sender, msg.value); } } diff --git a/code/truffle/FaucetEvents/contracts/Migrations.sol b/code/truffle/FaucetEvents/contracts/Migrations.sol index f170cb4f..9c9e7dfc 100644 --- a/code/truffle/FaucetEvents/contracts/Migrations.sol +++ b/code/truffle/FaucetEvents/contracts/Migrations.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.17; +pragma solidity ^0.8.20; contract Migrations { address public owner; @@ -8,7 +8,7 @@ contract Migrations { if (msg.sender == owner) _; } - function Migrations() public { + constructor() { owner = msg.sender; } diff --git a/code/truffle/METoken/contracts/METoken.sol b/code/truffle/METoken/contracts/METoken.sol index 834f7ee6..577e07df 100644 --- a/code/truffle/METoken/contracts/METoken.sol +++ b/code/truffle/METoken/contracts/METoken.sol @@ -1,16 +1,15 @@ -pragma solidity ^0.4.18; +pragma solidity ^0.8.20; -import 'zeppelin-solidity/contracts/token/ERC20/StandardToken.sol'; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -contract METoken is StandardToken { - string public constant name = 'Mastering Ethereum Token'; - string public constant symbol = 'MET'; - uint8 public constant decimals = 2; - uint constant _initial_supply = 2100000000; +contract METoken is ERC20 { + uint private constant _initial_supply = 2100000000; - function METoken() public { - totalSupply_ = _initial_supply; - balances[msg.sender] = _initial_supply; - Transfer(address(0), msg.sender, _initial_supply); + constructor() ERC20("Mastering Ethereum Token", "MET") { + _mint(msg.sender, _initial_supply); + } + + function decimals() public view virtual override returns (uint8) { + return 2; } } diff --git a/code/truffle/METoken/contracts/Migrations.sol b/code/truffle/METoken/contracts/Migrations.sol index f170cb4f..9c9e7dfc 100644 --- a/code/truffle/METoken/contracts/Migrations.sol +++ b/code/truffle/METoken/contracts/Migrations.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.17; +pragma solidity ^0.8.20; contract Migrations { address public owner; @@ -8,7 +8,7 @@ contract Migrations { if (msg.sender == owner) _; } - function Migrations() public { + constructor() { owner = msg.sender; } diff --git a/code/truffle/METoken_Faucet/contracts/Faucet.sol b/code/truffle/METoken_Faucet/contracts/Faucet.sol index 354bd568..fd9360b0 100644 --- a/code/truffle/METoken_Faucet/contracts/Faucet.sol +++ b/code/truffle/METoken_Faucet/contracts/Faucet.sol @@ -1,5 +1,5 @@ // Version of Solidity compiler this program was written for -pragma solidity ^0.4.19; +pragma solidity ^0.8.20; // Our first contract is a faucet! contract Faucet { @@ -11,10 +11,11 @@ contract Faucet { require(withdraw_amount <= 100000000000000000); // Send the amount to the address that requested it - msg.sender.transfer(withdraw_amount); + payable(msg.sender).transfer(withdraw_amount); } // Accept any incoming amount - function () public payable {} + receive() external payable {} + } diff --git a/code/truffle/METoken_Faucet/contracts/METoken.sol b/code/truffle/METoken_Faucet/contracts/METoken.sol index 834f7ee6..577e07df 100644 --- a/code/truffle/METoken_Faucet/contracts/METoken.sol +++ b/code/truffle/METoken_Faucet/contracts/METoken.sol @@ -1,16 +1,15 @@ -pragma solidity ^0.4.18; +pragma solidity ^0.8.20; -import 'zeppelin-solidity/contracts/token/ERC20/StandardToken.sol'; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -contract METoken is StandardToken { - string public constant name = 'Mastering Ethereum Token'; - string public constant symbol = 'MET'; - uint8 public constant decimals = 2; - uint constant _initial_supply = 2100000000; +contract METoken is ERC20 { + uint private constant _initial_supply = 2100000000; - function METoken() public { - totalSupply_ = _initial_supply; - balances[msg.sender] = _initial_supply; - Transfer(address(0), msg.sender, _initial_supply); + constructor() ERC20("Mastering Ethereum Token", "MET") { + _mint(msg.sender, _initial_supply); + } + + function decimals() public view virtual override returns (uint8) { + return 2; } } diff --git a/code/truffle/METoken_Faucet/contracts/Migrations.sol b/code/truffle/METoken_Faucet/contracts/Migrations.sol index f170cb4f..9c9e7dfc 100644 --- a/code/truffle/METoken_Faucet/contracts/Migrations.sol +++ b/code/truffle/METoken_Faucet/contracts/Migrations.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.17; +pragma solidity ^0.8.20; contract Migrations { address public owner; @@ -8,7 +8,7 @@ contract Migrations { if (msg.sender == owner) _; } - function Migrations() public { + constructor() { owner = msg.sender; } diff --git a/code/truffle/METoken_METFaucet/contracts/METFaucet.sol b/code/truffle/METoken_METFaucet/contracts/METFaucet.sol index 32bb5a83..6c01c2e9 100644 --- a/code/truffle/METoken_METFaucet/contracts/METFaucet.sol +++ b/code/truffle/METoken_METFaucet/contracts/METFaucet.sol @@ -1,21 +1,21 @@ // Version of Solidity compiler this program was written for -pragma solidity ^0.4.19; +pragma solidity ^0.8.20; -import 'zeppelin-solidity/contracts/token/ERC20/StandardToken.sol'; +import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; // A faucet for ERC20 token MET contract METFaucet { - StandardToken public METoken; + ERC20 public METoken; address public METOwner; // METFaucet constructor, provide the address of METoken contract and // the owner address we will be approved to transferFrom - function METFaucet(address _METoken, address _METOwner) public { + constructor(address _METoken, address _METOwner) { // Initialize the METoken from the address provided - METoken = StandardToken(_METoken); + METoken = ERC20(_METoken); METOwner = _METOwner; } @@ -29,6 +29,7 @@ contract METFaucet { } // REJECT any incoming ether - function () public payable { revert(); } + fallback() external payable { revert(); } + } diff --git a/code/truffle/METoken_METFaucet/contracts/METoken.sol b/code/truffle/METoken_METFaucet/contracts/METoken.sol index 834f7ee6..577e07df 100644 --- a/code/truffle/METoken_METFaucet/contracts/METoken.sol +++ b/code/truffle/METoken_METFaucet/contracts/METoken.sol @@ -1,16 +1,15 @@ -pragma solidity ^0.4.18; +pragma solidity ^0.8.20; -import 'zeppelin-solidity/contracts/token/ERC20/StandardToken.sol'; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -contract METoken is StandardToken { - string public constant name = 'Mastering Ethereum Token'; - string public constant symbol = 'MET'; - uint8 public constant decimals = 2; - uint constant _initial_supply = 2100000000; +contract METoken is ERC20 { + uint private constant _initial_supply = 2100000000; - function METoken() public { - totalSupply_ = _initial_supply; - balances[msg.sender] = _initial_supply; - Transfer(address(0), msg.sender, _initial_supply); + constructor() ERC20("Mastering Ethereum Token", "MET") { + _mint(msg.sender, _initial_supply); + } + + function decimals() public view virtual override returns (uint8) { + return 2; } } diff --git a/code/truffle/METoken_METFaucet/contracts/Migrations.sol b/code/truffle/METoken_METFaucet/contracts/Migrations.sol index f170cb4f..9c9e7dfc 100644 --- a/code/truffle/METoken_METFaucet/contracts/Migrations.sol +++ b/code/truffle/METoken_METFaucet/contracts/Migrations.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.17; +pragma solidity ^0.8.20; contract Migrations { address public owner; @@ -8,7 +8,7 @@ contract Migrations { if (msg.sender == owner) _; } - function Migrations() public { + constructor() { owner = msg.sender; } diff --git a/code/truffle/console/contracts/Migrations.sol b/code/truffle/console/contracts/Migrations.sol index f170cb4f..9c9e7dfc 100644 --- a/code/truffle/console/contracts/Migrations.sol +++ b/code/truffle/console/contracts/Migrations.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.17; +pragma solidity ^0.8.20; contract Migrations { address public owner; @@ -8,7 +8,7 @@ contract Migrations { if (msg.sender == owner) _; } - function Migrations() public { + constructor() { owner = msg.sender; } diff --git a/code/web3js/raw_tx/raw_tx_demo.js b/code/web3js/raw_tx/raw_tx_demo.js index 44c7d316..9d27327e 100644 --- a/code/web3js/raw_tx/raw_tx_demo.js +++ b/code/web3js/raw_tx/raw_tx_demo.js @@ -1,33 +1,45 @@ // Load requirements first: // -// npm init -// npm install ethereumjs-tx +// npm init -y +// npm install web3 // // Run with: $ node raw_tx_demo.js -const ethTx = require('ethereumjs-tx'); -const txData = { - nonce: '0x0', - gasPrice: '0x09184e72a000', - gasLimit: '0x30000', - to: '0xb0920c523d582040f2bcb1bd7fb1c7c1ecebdb34', - value: '0x00', - data: '', - v: "0x1c", // Ethereum main net chainID - r: 0, - s: 0 -}; +const { Web3 } = require('web3'); -tx = new ethTx(txData); -console.log('RLP-Encoded Tx: 0x' + tx.serialize().toString('hex')) +// Connect to a node (using a public provider for demonstration) +const web3 = new Web3('https://rpc.sepolia.org'); -txHash = tx.hash(); // This step encodes into RLP and calculates the hash -console.log('Tx Hash: 0x' + txHash.toString('hex')) +async function main() { + const txData = { + nonce: '0x0', + gasPrice: '0x09184e72a000', + gasLimit: '0x30000', + to: '0xb0920c523d582040f2bcb1bd7fb1c7c1ecebdb34', + value: '0x00', + data: '0x', + chainId: 11155111, // Sepolia Chain ID + }; -// Sign transaction -const privKey = Buffer.from('91c8360c4cb4b5fac45513a7213f31d4e4a7bfcb4630e9fbf074f42a203ac0b9', 'hex'); -tx.sign(privKey); + console.log('Transaction Data:', txData); -serializedTx = tx.serialize(); -rawTx = 'Signed Raw Transaction: 0x' + serializedTx.toString('hex'); -console.log(rawTx) + // Sign transaction + // WARNING: Never hardcode private keys in production! + const privKey = '0x91c8360c4cb4b5fac45513a7213f31d4e4a7bfcb4630e9fbf074f42a203ac0b9'; + + try { + const signedTx = await web3.eth.accounts.signTransaction(txData, privKey); + + console.log('-----------------------------------------'); + console.log('Signed Transaction V, R, S:', signedTx.v, signedTx.r, signedTx.s); + console.log('-----------------------------------------'); + console.log('Raw Transaction:', signedTx.rawTransaction); + console.log('-----------------------------------------'); + console.log('Transaction Hash:', signedTx.transactionHash); + + } catch (error) { + console.error('Error signing transaction:', error); + } +} + +main(); diff --git a/code/web3js/web3js_demo/web3-contract-basic-interaction-async-await.js b/code/web3js/web3js_demo/web3-contract-basic-interaction-async-await.js index c91759f6..27e32774 100755 --- a/code/web3js/web3js_demo/web3-contract-basic-interaction-async-await.js +++ b/code/web3js/web3js_demo/web3-contract-basic-interaction-async-await.js @@ -2,91 +2,91 @@ /** * @author Francisco Javier Rojas García + * Updated for Web3.js v4 and Sepolia testnet */ console.log('Mastering Ethereum - web3.js basic interactions using async/await') -console.log('Author: Francisco Javier Rojas García - fjrojasgarcia@gmail.com') -var Web3 = require('web3'); -var fs = require('fs') +const { Web3 } = require('web3'); +const axios = require('axios'); // Modern replacement for node-rest-client-promise -// Prepare your Infura host url -var infura_host = "https://kovan.infura.io" +// Prepare your Infura host url (or any public RPC) +const infura_host = "https://rpc.sepolia.org"; // Instantiate web3 provider -var web3 = new Web3(infura_host); +const web3 = new Web3(infura_host); // Let's do some basic interactions at web3 level async function basicInterations() { - // Let's see the Protocol Version - var protocolVersion = await web3.eth.getProtocolVersion(); - console.log(`Protocol Version: ${protocolVersion}`); - - // Now I'm curious about the current gas price - var gasPrice = await web3.eth.getGasPrice(); - console.log(`Gas Price: ${gasPrice}`); - - // And, Whats the last mined block in my chain? - var blockNumber = await web3.eth.getBlockNumber(); - console.log(`Block Number: ${blockNumber}`); - - // Now let's dive into some basics actions with a contract - // We will use the contract at; - // https://kovan.etherscan.io/address/0xd0a1e359811322d97991e03f863a0c30c2cf029c#code - - // First things first, let's initialize our contract address - var our_contract_address = "0xd0A1E359811322d97991E03f863a0C30C2cF029C"; - - // Let's see its balance - var balance = await web3.eth.getBalance(our_contract_address); - console.log(`Balance of ${our_contract_address}: ${balance}`); - - // Now let's see its byte code - var code = await web3.eth.getCode(our_contract_address); - console.log("Contract code: ----------------------------------------------\n"); - console.log(code); - console.log("-------------------------------------------------------------\n"); - - // Let's initialize our contract url in Etherescan for Kovan chain - var etherescan_url = `http://kovan.etherscan.io/api?module=contract&action=getabi&address=${our_contract_address}` - console.log(etherescan_url); - - var client = require('node-rest-client-promise').Client(); - - var etherescan_response = await client.getPromise(etherescan_url) - - // Leave this two lines for future object analysis - //const util = require('util') - //console.log(util.inspect(etherescan_response, false, null)) - - // We get here our contract ABI - our_contract_abi = JSON.parse(etherescan_response.data.result); - - // Let's instantiate our contract object - var our_contract = await new web3.eth.Contract(our_contract_abi, our_contract_address); - - // Let's see our contract address - console.log(`Our Contract address: ${our_contract._address}`); - - // or in this other way - console.log(`Our Contract address in other way: ${our_contract.options.address}`); - - // Now our contract abi - console.log("Our contract abi: " + JSON.stringify(our_contract.options.jsonInterface)); - - // This is turning more interesting, let's see what's going with our contract methods - // Now let's see our contract total supply - var totalSupply = await our_contract.methods.totalSupply().call(); - console.log(`Total Supply of Our Contract address ${our_contract._address}: ${totalSupply}`); - - // Now let's see our contract public variable name - var name = await our_contract.methods.name().call(); - console.log(`Public variable name of our Contract address ${our_contract._address}: ${name}`); - - // Now let's see our contract public variable symbol - var symbol = await our_contract.methods.symbol().call(); - console.log(`Public variable symbol of our Contract address ${our_contract._address}: ${symbol}`); + try { + // Let's see the Protocol Version + // Note: getProtocolVersion is deprecated/removed in some v4 providers or returns a hex string + try { + var protocolVersion = await web3.eth.getProtocolVersion(); + console.log(`Protocol Version: ${protocolVersion}`); + } catch(e) { console.log("Protocol version not supported by provider"); } + + // Now I'm curious about the current gas price + var gasPrice = await web3.eth.getGasPrice(); + console.log(`Gas Price: ${gasPrice} wei`); + + // And, Whats the last mined block in my chain? + var blockNumber = await web3.eth.getBlockNumber(); + console.log(`Block Number: ${blockNumber}`); + + // Now let's dive into some basics actions with a contract + // We will use the WETH contract on Sepolia + // https://sepolia.etherscan.io/address/0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14 + + // First things first, let's initialize our contract address + var our_contract_address = "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14"; + + // Let's see its balance + var balance = await web3.eth.getBalance(our_contract_address); + console.log(`Balance of ${our_contract_address}: ${balance} wei`); + + // Now let's see its byte code + var code = await web3.eth.getCode(our_contract_address); + // console.log("Contract code: ", code.substring(0, 50) + "..."); // Truncated for readability + + // Let's initialize our contract url in Etherscan for Sepolia chain + var etherescan_url = `https://api-sepolia.etherscan.io/api?module=contract&action=getabi&address=${our_contract_address}` + + // Note: You might need an API key for Etherscan in production, but often works without for low rate + console.log("Fetching ABI from:", etherescan_url); + + const response = await axios.get(etherescan_url); + + if (response.data.status !== '1') { + throw new Error("Failed to fetch ABI: " + response.data.result); + } + + // We get here our contract ABI + var our_contract_abi = JSON.parse(response.data.result); + + // Let's instantiate our contract object + var our_contract = new web3.eth.Contract(our_contract_abi, our_contract_address); + + // Let's see our contract address + console.log(`Our Contract address: ${our_contract.options.address}`); + + // Now let's see our contract total supply + var totalSupply = await our_contract.methods.totalSupply().call(); + console.log(`Total Supply of WETH: ${totalSupply}`); + + // Now let's see our contract public variable name + var name = await our_contract.methods.name().call(); + console.log(`Token Name: ${name}`); + + // Now let's see our contract public variable symbol + var symbol = await our_contract.methods.symbol().call(); + console.log(`Token Symbol: ${symbol}`); + + } catch (error) { + console.error("An error occurred:", error); + } } // Let's interact with a node basicInterations(); + diff --git a/code/web3js/web3js_demo/web3-contract-basic-interaction.js b/code/web3js/web3js_demo/web3-contract-basic-interaction.js index ac3caa90..01f316bd 100755 --- a/code/web3js/web3js_demo/web3-contract-basic-interaction.js +++ b/code/web3js/web3js_demo/web3-contract-basic-interaction.js @@ -2,13 +2,10 @@ /** * @author Francisco Javier Rojas García + * Updated for Web3.js v4 and Sepolia testnet */ -// Take a closer look at the web3 1.0 documentation for calling methods (it's very different from the 0.2x API). -// https://stackoverflow.com/questions/48547268/smart-contract-method-is-not-a-function-in-web3 - console.log('Mastering Ethereum - web3.js basic interactions') -console.log('Author: Francisco Javier Rojas García - fjrojasgarcia@gmail.com') const optionDefinitions = [ { name: 'localRPC', alias: 'l', type: Boolean }, @@ -18,124 +15,67 @@ const optionDefinitions = [ const commandLineArgs = require('command-line-args') const options = commandLineArgs(optionDefinitions) -var Web3 = require('web3'); -var fs = require('fs') +const { Web3 } = require('web3'); +const fs = require('fs'); +const axios = require('axios'); + +let infura_host = "https://rpc.sepolia.org"; // Default to public if (options.infuraFileToken && !options.localRPC) { - console.log(options.infuraFileToken); + try { + // Loading an Infura Token from a file + var infura_token = fs.readFileSync(options.infuraFileToken, 'utf8').trim(); + console.log("Using Infura Token:", infura_token); + infura_host = `https://sepolia.infura.io/v3/${infura_token}` + } catch (e) { + console.error("Error reading token file. Using public RPC."); + } +} - // Loading an Infura Token from a file - var infura_token = fs.readFileSync(options.infuraFileToken, 'utf8'); +// Instantiate web3 provider +const web3 = new Web3(infura_host); - // Show your Infura token - console.log(infura_token); +console.log('Connecting to:', infura_host); - // Prepare your Infura host url - var infura_host = `https://kovan.infura.io/${infura_token}` +async function main() { + try { + // Protocol Version + try { + const protocolVersion = await web3.eth.getProtocolVersion(); + console.log(`Protocol Version: ${protocolVersion}`); + } catch(e) {} -} else { - console.log('Not argument found for infura token'); + const gasPrice = await web3.eth.getGasPrice(); + console.log(`Gas Price: ${gasPrice} wei`); - // Prepare your Infura host url - var infura_host = "https://kovan.infura.io" + const blockNumber = await web3.eth.getBlockNumber(); + console.log(`Block Number: ${blockNumber}`); -} + // Contract: WETH on Sepolia + const our_contract_address = "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14"; -// Show your Infura host url for your web3 connection -console.log(infura_host); + const balance = await web3.eth.getBalance(our_contract_address); + console.log(`Balance of ${our_contract_address}: ${balance} wei`); -// Instantiate web3 provider -var web3 = new Web3(infura_host); - -// Let's do some basic interactions at web3 level -// Let's see the Protocol Version -web3.eth.getProtocolVersion().then(function(protocolVersion) { - console.log(`Protocol Version: ${protocolVersion}`); - }) - -// Now I'm curious about the current gas price -web3.eth.getGasPrice().then(function(gasPrice) { - console.log(`Gas Price: ${gasPrice}`); - }) - -// And, Whats the last mined block in my chain? -web3.eth.getBlockNumber().then(function(blockNumber) { - console.log(`Block Number: ${blockNumber}`); - }) - -// Now let's dive into some basics actions with a contract -// We will use the contract at; -// https://kovan.etherscan.io/address/0xd0a1e359811322d97991e03f863a0c30c2cf029c#code - -// First things first, let's initialize our contract address -var our_contract_address = "0xd0A1E359811322d97991E03f863a0C30C2cF029C"; - -// Let's see its balance -web3.eth.getBalance(our_contract_address).then(function(balance) { - console.log(`Balance of ${our_contract_address}: ${balance}`); -}) - -// Now let's see its byte code -web3.eth.getCode(our_contract_address).then(function(code) { - console.log("Contract code: ----------------------------------------------\n"); - console.log(code); - console.log("-------------------------------------------------------------\n"); -}) - -// Let's initialize our contract url in Etherescan for Kovan chain -var etherescan_url = `http://kovan.etherscan.io/api?module=contract&action=getabi&address=${our_contract_address}` -console.log(etherescan_url); - -var client = require('node-rest-client-promise').Client(); - -// Now we are going to deal with the contract from web3.js in a non-block fashion (async mode) -client.getPromise(etherescan_url) -.then((client_promise) => { - // Leave this two lines for fure object analisys - //const util = require('util') - //console.log(util.inspect(client_promise, false, null)) - - // We get here our contract ABI - our_contract_abi = JSON.parse(client_promise.data.result); - - // And now we create a promise to consume later - return new Promise((resolve, reject) => { - var our_contract = new web3.eth.Contract(our_contract_abi, our_contract_address); - try { - // If all goes well - resolve(our_contract); - } catch (ex) { - // If something goes wrong - reject(ex); - } - }); - -}) -.then((our_contract) => { - // Let's see our contract address - console.log(`Our Contract address: ${our_contract._address}`); - - // or in this other way - console.log(`Our Contract address in other way: ${our_contract.options.address}`); - - // Now our contract abi - console.log("Our contract abi: " + JSON.stringify(our_contract.options.jsonInterface)); - - // This is turning more interesting, let's see what's going with our contract methods - // Now let's see our contract total supply in a callback fashion - our_contract.methods.totalSupply().call(function(err, totalSupply) { - if (!err) { - console.log(`Total Supply with a callback: ${totalSupply}`); - } else { - console.log(err); - } - }); - - // Or you can use the returned Promise instead of passing in the callback: - our_contract.methods.totalSupply().call().then(function(totalSupply){ - console.log(`Total Supply with a promise: ${totalSupply}`); - }).catch(function(err) { - console.log(err); - }); - -}) + // Get ABI + const etherescan_url = `https://api-sepolia.etherscan.io/api?module=contract&action=getabi&address=${our_contract_address}` + console.log("Fetching ABI..."); + + const response = await axios.get(etherescan_url); + if (response.data.status !== '1') throw new Error("Failed to fetch ABI"); + + const our_contract_abi = JSON.parse(response.data.result); + const our_contract = new web3.eth.Contract(our_contract_abi, our_contract_address); + + console.log(`Contract Address: ${our_contract.options.address}`); + + // Calls + const totalSupply = await our_contract.methods.totalSupply().call(); + console.log(`Total Supply: ${totalSupply}`); + + } catch (error) { + console.error("Error:", error); + } +} + +main(); diff --git "a/\347\254\254\344\271\235\347\253\240.asciidoc" "b/\347\254\254\344\271\235\347\253\240.asciidoc" index f4186ada..df8701c9 100644 --- "a/\347\254\254\344\271\235\347\253\240.asciidoc" +++ "b/\347\254\254\344\271\235\347\253\240.asciidoc" @@ -395,17 +395,19 @@ mkdir contracts 让我们制作一个ERC20 token,这意味着初始供应从0开始,token所有者(在我们的例子中,是crowdsale合约)可以创建新的 token并分配给投资者。为此,使用以下内容创建`contracts/SampleToken.sol`文件: +---- ---- include::code/OpenZeppelin/contracts/SampleToken.sol -pragma solidity 0.4.23; +pragma solidity ^0.8.20; -import 'openzeppelin-solidity/contracts/token/ERC20/MintableToken.sol'; +import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; +import '@openzeppelin/contracts/access/Ownable.sol'; -contract SampleToken is MintableToken { - string public name = "SAMPLE TOKEN"; - string public symbol = "SAM"; - uint8 public decimals = 18; +contract SampleToken is ERC20, Ownable { + constructor() ERC20("SAMPLE TOKEN", "SAM") Ownable(msg.sender) { + _mint(msg.sender, 21000000 * 10 ** decimals()); + } } ---- @@ -415,25 +417,25 @@ OpenZeppelin已经提供了一个MintableToken合约,我们可以用它作为t 正如我们之前所说的,OpenZeppelin合约是基本的构建块。这些crowdsale合约被设计为可组合的,只需阅读 https://github.com/OpenZeppelin/openzeppelin-solidity/blob/v1.9.0/contracts/crowdsale/Crowdsale.sol[Crowdsale]合约的源代码即可了解关于如何扩展它的指导。对于我们token的crowdsale,我们需要在crowdsale合约收到ether时才发行token,所以让我们使用https://github.com/OpenZeppelin/openzeppelin-solidity/blob/v1.9.0/contracts/crowdsale/emission/MintedCrowdsale.sol[MintedCrowdsale] 作为基础。为了让它更有趣,让我们把它做成 https://github.com/OpenZeppelin/openzeppelin-solidity/blob/v1.9.0/contracts/crowdsale/distribution/PostDeliveryCrowdsale.sol[PostDeliveryCrowdsale], token只能在众筹结束后赎回。将以下内容写入`contracts/SampleCrowdsale.sol`: +---- ---- include::code/OpenZeppelin/contracts/SampleCrowdsale.sol -pragma solidity 0.4.23; +pragma solidity ^0.8.20; import './SampleToken.sol'; -import 'openzeppelin-solidity/contracts/crowdsale/emission/MintedCrowdsale.sol'; -import 'openzeppelin-solidity/contracts/crowdsale/distribution/PostDeliveryCrowdsale.sol'; +import '@openzeppelin/contracts/crowdsale/emission/MintedCrowdsale.sol'; +import '@openzeppelin/contracts/crowdsale/distribution/PostDeliveryCrowdsale.sol'; contract SampleCrowdsale is PostDeliveryCrowdsale, MintedCrowdsale { constructor( uint256 _openingTime, - uint256 _closingTime + uint256 _closingTime, uint256 _rate, address _wallet, MintableToken _token ) - public Crowdsale(_rate, _wallet, _token) PostDeliveryCrowdsale(_openingTime, _closingTime) { diff --git "a/\347\254\254\345\205\253\347\253\240.asciidoc" "b/\347\254\254\345\205\253\347\253\240.asciidoc" index fa2622c7..72257054 100644 --- "a/\347\254\254\345\205\253\347\253\240.asciidoc" +++ "b/\347\254\254\345\205\253\347\253\240.asciidoc" @@ -140,11 +140,11 @@ contract Faucet { require(withdraw_amount <= 100000000000000000); // Send the amount to the address that requested it - msg.sender.transfer(withdraw_amount); + payable(msg.sender).transfer(withdraw_amount); } // Accept any incoming amount - function () public payable {} + receive() external payable {} } ---- @@ -199,7 +199,7 @@ Contract JSON ABI [[compiler_version]] ---- -pragma solidity ^0.4.19; +pragma solidity ^0.8.20; ---- Solidity编译器读取版本编译指示,如果编译器版本与版本编译指示不兼容,将会产生错误。在这种情况下,我们的版本编译指出,这个程序可以由Solidity编译器编译,最低版本为+0.4.19+。但是,符号^表示我们允许编译任何_minor修订版_在+0.4.19+之上的,例如+0.4.20+,但不是+0.5.0+(这是一个主要版本,不是小修订版) 。Pragma指令不会编译为EVM字节码。它们仅由编译器用来检查兼容性。 @@ -210,7 +210,7 @@ Solidity编译器读取版本编译指示,如果编译器版本与版本编译 [source,solidity,linenums] ---- // Version of Solidity compiler this program was written for -pragma solidity ^0.4.19; +pragma solidity ^0.8.20; // Our first contract is a faucet! contract Faucet { @@ -222,11 +222,11 @@ contract Faucet { require(withdraw_amount <= 100000000000000000); // Send the amount to the address that requested it - msg.sender.transfer(withdraw_amount); + payable(msg.sender).transfer(withdraw_amount); } // Accept any incoming amount - function () public payable {} + receive() external payable {} } ---- @@ -333,7 +333,7 @@ block.timestamp:: 矿工在当前块中放置的时间戳,自Unix纪元(秒 address.balance:: 地址的余额,以wei为单位。例如,当前合约余额是 +address(this).balance+。 -address.transfer(amount):: 将金额(wei)转移到该地址,并在发生任何错误时抛出异常。我们在+Faucet+示例中的+msg.sender+地址上使用了此函数,+msg.sender.transfer()+。 +address.transfer(amount):: 将金额(wei)转移到该地址,并在发生任何错误时抛出异常。我们在+Faucet+示例中的+msg.sender+地址上使用了此函数,+payable(msg.sender).transfer()+。 address.send(amount):: 类似于前面的+transfer+, 但是失败时不抛出异常,而是返回+false+。 @@ -403,24 +403,10 @@ payable:: payable函数是可以接受付款的功能。没有payable的函数 有一个特殊函数只能使用一次。创建合约时,它还运行 _构造函数_ _constructor function_(如果存在),以初始化合约状态。构造函数与创建合约时在同一个交易中运行。构造函数是可选的。事实上,我们的+Faucet+示例没有构造函数。 -构造函数可以通过两种方式指定。到Solidity v.0.4.21,构造函数是一个名称与合约名称相匹配的函数: +构造函数是名为+constructor+的特殊函数,它在合约创建期间运行一次,用于初始化合约状态。 -[[old_constructor_style]] -.Constructor function prior to Solidity v0.4.22 ---- -contract MEContract { - function MEContract() { - // This is the constructor - } -} ----- - -这种格式的难点在于如果合约名称被改变并且构造函数名称没有改变,它就不再是构造函数了。这可能会导致一些非常令人讨厌的,意外的并且很难注意到的错误。想象一下,例如,如果构造函数正在为控制目的而设置合约的“所有者”。它不仅可以在创建合约时设置所有者,还可以像正常功能那样“可调用”,允许任何第三方在合约创建后劫持合约并成为“所有者”。 - -为了解决构造函数的潜在问题,它基于与合约名称相同的名称,Solidity v0.4.22引入了一个+constructor+关键字,它像构造函数一样运行,但没有名称。重命名合约并不会影响构造函数。此外,更容易确定哪个函数是构造函数。看起来像这样: - ----- -pragma ^0.4.22 +pragma solidity ^0.8.20; contract MEContract { constructor () { // This is the constructor @@ -433,7 +419,7 @@ contract MEContract { 合约生命周期的另一端是 _合约销毁_ _contract destruction_。合约被称为+SELFDESTRUCT+的特殊EVM操作码销毁。它曾经是+SUICIDE+,但由于该词的负面性,该名称已被弃用。在Solidity中,此操作码作为高级内置函数+selfdestruct+公开,该函数采用一个参数:地址以接收合约帐户中剩余的余额。看起来像这样: ---- -selfdestruct(address recipient); +selfdestruct(payable(recipient)); ---- ==== 添加一个构造函数和selfdestruct到我们的+Faucet+示例 @@ -444,7 +430,7 @@ selfdestruct(address recipient); ---- // Version of Solidity compiler this program was written for -pragma solidity ^0.4.22; +pragma solidity ^0.8.20; // Our first contract is a faucet! contract Faucet { @@ -469,7 +455,7 @@ contract Faucet { // Contract destructor function destroy() public { require(msg.sender == owner); - selfdestruct(owner); + selfdestruct(payable(owner)); } ---- @@ -498,7 +484,7 @@ modifier onlyOwner { ---- function destroy() public onlyOwner { - selfdestruct(owner); + selfdestruct(payable(owner)); } ---- @@ -531,7 +517,7 @@ contract Child is Parent1, Parent2 { 我们首先定义一个基础合约+owned+,它拥有一个+owner+变量,并在合约的构造函数中设置: ---- -contract owned { +abstract contract owned { address owner; // Contract constructor: set owner @@ -553,7 +539,7 @@ contract owned { contract mortal is owned { // Contract destructor function destroy() public onlyOwner { - selfdestruct(owner); + selfdestruct(payable(owner)); } } ---- @@ -569,10 +555,10 @@ contract Faucet is mortal { // Limit withdrawal amount require(withdraw_amount <= 100000000000000000); // Send the amount to the address that requested it - msg.sender.transfer(withdraw_amount); + payable(msg.sender).transfer(withdraw_amount); } // Accept any incoming amount - function () public payable {} + receive() external payable {} } ---- @@ -663,9 +649,9 @@ function () public payable { ---- include::code/Solidity/Faucet8.sol['code/Solidity/Faucet8.sol'] // Version of Solidity compiler this program was written for -pragma solidity ^0.4.22; +pragma solidity ^0.8.20; -contract owned { +abstract contract owned { address owner; // Contract constructor: set owner constructor() { @@ -681,7 +667,7 @@ contract owned { contract mortal is owned { // Contract destructor function destroy() public onlyOwner { - selfdestruct(owner); + selfdestruct(payable(owner)); } } @@ -693,14 +679,14 @@ contract Faucet is mortal { function withdraw(uint withdraw_amount) public { // Limit withdrawal amount require(withdraw_amount <= 0.1 ether); - require(this.balance >= withdraw_amount, + require(address(this).balance >= withdraw_amount, "Insufficient balance in faucet for withdrawal request"); // Send the amount to the address that requested it - msg.sender.transfer(withdraw_amount); + payable(msg.sender).transfer(withdraw_amount); emit Withdrawal(msg.sender, withdraw_amount); } // Accept any incoming amount - function () public payable { + receive() external payable { emit Deposit(msg.sender, msg.value); } } @@ -890,19 +876,19 @@ contract Token is mortal { ---- include::code/truffle/CallExamples/contracts/CallExamples.sol["code/truffle/CallExamples/contracts/CallExamples.sol"] -pragma solidity ^0.4.22; +pragma solidity ^0.8.20; contract calledContract { event callEvent(address sender, address origin, address from); function calledFunction() public { - emit callEvent(msg.sender, tx.origin, this); + emit callEvent(msg.sender, tx.origin, address(this)); } } library calledLibrary { event callEvent(address sender, address origin, address from); function calledFunction() public { - emit callEvent(msg.sender, tx.origin, this); + emit callEvent(msg.sender, tx.origin, address(this)); } } @@ -915,8 +901,10 @@ contract caller { calledLibrary.calledFunction(); // Low level calls using the address object for calledContract - require(address(_calledContract).call(bytes4(keccak256("calledFunction()")))); - require(address(_calledContract).delegatecall(bytes4(keccak256("calledFunction()")))); + (bool success, ) = address(_calledContract).call(abi.encodeWithSignature("calledFunction()")); + require(success); + (success, ) = address(_calledContract).delegatecall(abi.encodeWithSignature("calledFunction()")); + require(success); } } ---- diff --git "a/\347\254\254\345\215\201\344\272\214\347\253\240.asciidoc" "b/\347\254\254\345\215\201\344\272\214\347\253\240.asciidoc" index 9292413c..bd1e65a2 100644 --- "a/\347\254\254\345\215\201\344\272\214\347\253\240.asciidoc" +++ "b/\347\254\254\345\215\201\344\272\214\347\253\240.asciidoc" @@ -111,7 +111,7 @@ Teutsch最近提出了一种新的去中心化脱链数据可用性设计oracle which is updated every 10 minutes. */ -pragma solidity ^0.4.1; +pragma solidity ^0.8.20; import "github.com/oraclize/ethereum-api/oraclizeAPI.sol"; /* @@ -124,7 +124,7 @@ contract EthUsdPriceTicker is usingOraclize { event newOraclizeQuery(string description); event newCallbackResult(string result); - function EthUsdPriceTicker() payable { + constructor() payable { // signals TLSN proof generation and storage on IPFS oraclize_setProof(proofType_TLSNotary | proofStorage_IPFS); @@ -132,9 +132,9 @@ contract EthUsdPriceTicker is usingOraclize { queryTicker(); } - function __callback(bytes32 _queryId, string _result, bytes _proof) public { - if (msg.sender != oraclize_cbAddress()) throw; - newCallbackResult(_result); + function __callback(bytes32 _queryId, string memory _result, bytes memory _proof) public { + require(msg.sender == oraclize_cbAddress()); + emit newCallbackResult(_result); /* * parse the result string into an unsigned integer for on-chain use @@ -148,10 +148,10 @@ contract EthUsdPriceTicker is usingOraclize { } function queryTicker() public payable { - if (oraclize_getPrice("URL") > this.balance) { - newOraclizeQuery("Oraclize query was NOT sent, please add some ETH to cover for the query fee"); + if (oraclize_getPrice("URL") > address(this).balance) { + emit newOraclizeQuery("Oraclize query was NOT sent, please add some ETH to cover for the query fee"); } else { - newOraclizeQuery("Oraclize query was sent, standing by for the answer.."); + emit newOraclizeQuery("Oraclize query was sent, standing by for the answer.."); // query params are (delay in seconds, datasource type, datasource argument) // specifies JSONPath, to fetch specific portion of JSON API result @@ -171,18 +171,17 @@ contract EthUsdPriceTicker is usingOraclize { 金融数据提供商Thomson Reuters还为以太坊提供了一项名为BlockOne IQ的oracle服务,允许在私有或许可网络上运行的智能合约请求市场和参考数据[13]。下面是oracle的接口,以及将发出请求的客户端合约: ---- -pragma solidity ^0.4.11; - -contract Oracle { - uint256 public divisor; - function initRequest(uint256 queryType, function(uint256) external onSuccess, function(uint256) external onFailure) public returns (uint256 id); - function addArgumentToRequestUint(uint256 id, bytes32 name, uint256 arg) public; - function addArgumentToRequestString(uint256 id, bytes32 name, bytes32 arg) public; - function executeRequest(uint256 id) public; - function getResponseUint(uint256 id, bytes32 name) public constant returns(uint256); - function getResponseString(uint256 id, bytes32 name) public constant returns(bytes32); - function getResponseError(uint256 id) public constant returns(bytes32); - function deleteResponse(uint256 id) public constant; +pragma solidity ^0.8.20; + +interface Oracle { + function initRequest(uint256 queryType, function(uint256) external onSuccess, function(uint256) external onFailure) external returns (uint256 id); + function addArgumentToRequestUint(uint256 id, bytes32 name, uint256 arg) external; + function addArgumentToRequestString(uint256 id, bytes32 name, bytes32 arg) external; + function executeRequest(uint256 id) external; + function getResponseUint(uint256 id, bytes32 name) external view returns(uint256); + function getResponseString(uint256 id, bytes32 name) external view returns(bytes32); + function getResponseError(uint256 id) external view returns(bytes32); + function deleteResponse(uint256 id) external; } contract OracleB1IQClient { @@ -190,9 +189,9 @@ contract OracleB1IQClient { Oracle private oracle; event LogError(bytes32 description); - function OracleB1IQClient(address addr) public payable { + constructor(address addr) payable { oracle = Oracle(addr); - getIntraday("IBM", now); + getIntraday("IBM", block.timestamp); } function getIntraday(bytes32 ric, uint256 timestamp) public { diff --git "a/\347\254\254\345\215\201\345\233\233\347\253\240.asciidoc" "b/\347\254\254\345\215\201\345\233\233\347\253\240.asciidoc" index 446984e1..b6a00829 100644 --- "a/\347\254\254\345\215\201\345\233\233\347\253\240.asciidoc" +++ "b/\347\254\254\345\215\201\345\233\233\347\253\240.asciidoc" @@ -170,18 +170,23 @@ $ solc -o BytecodeOutputDir --bin Example.sol [[simple_solidity_example]] ---- -pragma solidity ^0.4.19; +pragma solidity ^0.8.20; contract example { address contractOwner; - function example() { + constructor() { contractOwner = msg.sender; } } ---- +[NOTE] +==== +The opcode analysis below is based on an older compiler version (0.4.19). Modern Solidity compilers (0.8.x) may generate different bytecode (e.g., initializing the free memory pointer at 0x80 instead of 0x60), but the fundamental principles of EVM execution remain the same. +==== + 如果查看_BytecodeDir_目录,你将看到操作码文件_example.opcode_(请参阅<>),其中包含“example”合约的EVM机器语言操作码指令。在文本编辑器中打开_example.opcode_文件将显示以下内容: [[opcode_output]] @@ -322,7 +327,7 @@ ____________________________________________________________________ [[faucet_example]] ---- // Version of Solidity compiler this program was written for -pragma solidity ^0.4.19; +pragma solidity ^0.8.20; // Our first contract is a faucet! contract Faucet { @@ -334,11 +339,11 @@ contract Faucet { require(withdraw_amount <= 100000000000000000); // Send the amount to the address that requested it - msg.sender.transfer(withdraw_amount); + payable(msg.sender).transfer(withdraw_amount); } // Accept any incoming amount - function () public payable {} + receive() external payable {} } ---- diff --git "a/\347\254\254\345\215\201\347\253\240.asciidoc" "b/\347\254\254\345\215\201\347\253\240.asciidoc" index c8268c26..eb2d6467 100644 --- "a/\347\254\254\345\215\201\347\253\240.asciidoc" +++ "b/\347\254\254\345\215\201\347\253\240.asciidoc" @@ -165,12 +165,12 @@ decimals:: 返回用于分割Token数量的小数位数。例如,如果小数 [[ERC20_interface_example]] ---- contract ERC20 { - function totalSupply() constant returns (uint theTotalSupply); - function balanceOf(address _owner) constant returns (uint balance); - function transfer(address _to, uint _value) returns (bool success); - function transferFrom(address _from, address _to, uint _value) returns (bool success); - function approve(address _spender, uint _value) returns (bool success); - function allowance(address _owner, address _spender) constant returns (uint remaining); + function totalSupply() public view returns (uint theTotalSupply); + function balanceOf(address _owner) public view returns (uint balance); + function transfer(address _to, uint _value) public returns (bool success); + function transferFrom(address _from, address _to, uint _value) public returns (bool success); + function approve(address _spender, uint _value) public returns (bool success); + function allowance(address _owner, address _spender) public view returns (uint remaining); event Transfer(address indexed _from, address indexed _to, uint _value); event Approval(address indexed _owner, address indexed _spender, uint _value); } @@ -588,23 +588,23 @@ ERC20Token的用户可能无意中在转移中丢失其Token的方式之一是 include::code/METoken_METFaucet/contracts/METFaucet.sol // Version of Solidity compiler this program was written for -pragma solidity ^0.4.19; +pragma solidity ^0.8.20; -import 'zeppelin-solidity/contracts/Token/ERC20/StandardToken.sol'; +import '@openzeppelin/contracts/token/ERC20/ERC20.sol'; // A faucet for ERC20 Token MET contract METFaucet { - StandardToken public METoken; + ERC20 public METoken; address public METOwner; // METFaucet constructor, provide the address of METoken contract and // the owner address we will be approved to transferFrom - function METFaucet(address _METoken, address _METOwner) public { + constructor(address _METoken, address _METOwner) { // Initialize the METoken from the address provided - METoken = StandardToken(_METoken); + METoken = ERC20(_METoken); METOwner = _METOwner; } @@ -618,7 +618,7 @@ contract METFaucet { } // REJECT any incoming ether - function () public payable { revert(); } + fallback() external payable { revert(); } } ---- @@ -741,14 +741,10 @@ ERC223提案试图通过检测目的地地址是否是合同来解决无意中 [[is_contract]] 为了检测目标地址是否为契约,ERC223参考实现使用了一小段内联字节码,并采用了一种颇具创造性的方式: +---- ---- function isContract(address _addr) private view returns (bool is_contract) { - uint length; - assembly { - //retrieve the size of the code on target address, this needs assembly - length := extcodesize(_addr) - } - return (length>0); + return _addr.code.length > 0; } ----