├── WtxEthOil ├── src │ ├── App.css │ ├── index.css │ ├── components │ │ ├── pages │ │ │ ├── NotFound.js │ │ │ ├── About.js │ │ │ └── Home.js │ │ ├── layout │ │ │ ├── Input.js │ │ │ ├── Metamask.js │ │ │ └── Navbar.js │ │ └── users │ │ │ ├── User.js │ │ │ ├── EditUser.js │ │ │ └── AddUser.js │ ├── index.js │ └── App.js ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── index.html ├── .gitignore ├── package.json └── db.json ├── backend ├── test │ ├── .gitkeep │ ├── test_access.js │ ├── test_introspection.js │ ├── test_erc721.js │ ├── test_erc1948.js │ └── test_create_bol.js ├── .gitignore ├── migrations │ ├── 1_initial_migration.js │ ├── 3_deploy_utils.js │ └── 2_deploy_bill_of_lading.js ├── contracts │ ├── DummyERC20.sol │ ├── Migrations.sol │ ├── Selector.sol │ ├── interfaces │ │ ├── IERC1948.sol │ │ └── ERC998 │ │ │ ├── IERC998ERC20BottomUp.sol │ │ │ ├── IERC998ERC20TopDown.sol │ │ │ ├── IERC998ERC721BottomUp.sol │ │ │ └── IERC998ERC721TopDown.sol │ ├── BillOfLading.sol │ ├── dBol.sol │ ├── ERC998 │ │ └── ERC998ERC721BottomUp.sol │ └── composables998 │ │ └── ComposableBottomUp.sol ├── package.json └── truffle-config.js ├── docs └── minutes │ ├── 2021-01-30.md │ └── 2021-01-18.md ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── pull_request_template.md ├── LICENSE ├── .gitignore ├── README.md └── CONTRIBUTING.md /WtxEthOil/src/App.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/test/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /WtxEthOil/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /WtxEthOil/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EthBuilders/EthGlobal-West-Texas-Oil/HEAD/WtxEthOil/public/favicon.ico -------------------------------------------------------------------------------- /WtxEthOil/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EthBuilders/EthGlobal-West-Texas-Oil/HEAD/WtxEthOil/public/logo192.png -------------------------------------------------------------------------------- /WtxEthOil/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EthBuilders/EthGlobal-West-Texas-Oil/HEAD/WtxEthOil/public/logo512.png -------------------------------------------------------------------------------- /backend/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require("Migrations"); 2 | 3 | module.exports = function (deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /WtxEthOil/src/index.css: -------------------------------------------------------------------------------- 1 | .not-found{ 2 | position: fixed; 3 | top: 0; 4 | width: 100vw; 5 | height: 100vh; 6 | background-color: #fff; 7 | display: flex; 8 | justify-content: center; 9 | align-items: center; 10 | } -------------------------------------------------------------------------------- /backend/migrations/3_deploy_utils.js: -------------------------------------------------------------------------------- 1 | const Selector = artifacts.require("Selector"); 2 | const Token = artifacts.require("Token"); 3 | 4 | module.exports = function (deployer) { 5 | deployer.deploy(Selector); 6 | deployer.deploy(Token); 7 | }; 8 | -------------------------------------------------------------------------------- /WtxEthOil/src/components/pages/NotFound.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const NotFound = () => { 4 | return ( 5 |
6 |

Page Not Found

7 |
8 | ); 9 | }; 10 | 11 | export default NotFound; 12 | -------------------------------------------------------------------------------- /WtxEthOil/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ); 12 | -------------------------------------------------------------------------------- /backend/contracts/DummyERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.5; 4 | 5 | import "@openzeppelin/contracts/presets/ERC20PresetMinterPauser.sol"; 6 | 7 | contract Token is ERC20PresetMinterPauser("", "") { 8 | 9 | /// @dev faucet function 10 | function getTokens(uint256 _value) public { 11 | _mint(msg.sender, _value); 12 | } 13 | } -------------------------------------------------------------------------------- /WtxEthOil/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /WtxEthOil/src/components/layout/Input.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function InputField(props) { 4 | const { fieldName, fieldValue, setField, type, placeholder } = props; 5 | 6 | return ( 7 | setField(e)} 14 | /> 15 | ) 16 | } 17 | 18 | export default InputField -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "truffle-config.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "@openzeppelin/contracts": "^3.3.0", 17 | "web3": "^1.3.3" 18 | }, 19 | "devDependencies": { 20 | "@openzeppelin/test-helpers": "^0.5.10" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /backend/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity >=0.4.22 <0.9.0; 4 | 5 | // @title Migrations contract generated by truffle init 6 | // @author Trufflesuite 7 | contract Migrations { 8 | address public owner = msg.sender; 9 | uint public last_completed_migration; 10 | 11 | modifier restricted() { 12 | require( 13 | msg.sender == owner, 14 | "This function is restricted to the contract's owner" 15 | ); 16 | _; 17 | } 18 | 19 | function setCompleted(uint completed) public restricted { 20 | last_completed_migration = completed; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /WtxEthOil/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /docs/minutes/2021-01-30.md: -------------------------------------------------------------------------------- 1 | # Front End Views 2 | 3 | Single Page Webapp 4 | 5 | - Create View 6 | - Form 7 | - Driver Address (Ethereum Address) 8 | - Serial Number (String) 9 | - Point of Origin 10 | - Latitude and Longitude 11 | - Degrees, Minutes, Decimal Seconds, Cardinal Direction 12 | - Point of Destination 13 | - Degrees, Minutes, Decimal Seconds, Cardinal Direction 14 | - Quantity (Metric Tonnes) 15 | - Read View 16 | - List View only (No Detailed View) 17 | - Listing of BOLs 18 | - With button to Update 19 | - Update View 20 | - Form 21 | - Textbox 22 | - Submit button 23 | -------------------------------------------------------------------------------- /backend/migrations/2_deploy_bill_of_lading.js: -------------------------------------------------------------------------------- 1 | const BillOfLading = artifacts.require("BillOfLading"); 2 | const DBol = artifacts.require("DBol"); 3 | const { default: Web3 } = require("web3"); 4 | const web3 = require("web3"); 5 | 6 | module.exports = async function (deployer, accounts) { 7 | //ganache-cli --gasLimit 0xfffffffffff -g 0x01 --allowUnlimitedContractSize 8 | deployer.deploy(DBol, "", "", "").then(() => { 9 | return deployer.deploy(BillOfLading, "", "", "", DBol.address); 10 | }).then(async() => { 11 | let instance = await DBol.deployed(); 12 | tx = instance.grantRole(web3.utils.sha3("MINTER_ROLE"), BillOfLading.address); 13 | }) 14 | }; 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEAT]" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /backend/test/test_access.js: -------------------------------------------------------------------------------- 1 | const BillOfLading = artifacts.require("BillOfLading"); 2 | 3 | contract("BillOfLading", (accounts) => { 4 | var instance; 5 | 6 | before("deploy contract", async () => { 7 | instance = await BillOfLading.deployed({ from: accounts[0] }); 8 | }); 9 | 10 | it("should set the deployer as the default admin", async () => { 11 | let DEFAULT_ADMIN_ROLE = "0x00"; 12 | let deployer_is_instance_admin = await instance.hasRole( 13 | DEFAULT_ADMIN_ROLE, 14 | accounts[0] 15 | ); 16 | let admin_count = await instance.getRoleMemberCount(DEFAULT_ADMIN_ROLE); 17 | 18 | assert.isTrue(deployer_is_instance_admin); 19 | assert.equal(admin_count, 1); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /backend/test/test_introspection.js: -------------------------------------------------------------------------------- 1 | const BillOfLading = artifacts.require("BillOfLading"); 2 | 3 | contract("BillOfLading", (accounts) => { 4 | var instance; 5 | 6 | before("deploy contract", async () => { 7 | instance = await BillOfLading.deployed(); 8 | }); 9 | 10 | describe("supportsInterface", () => { 11 | it("should return true for the ERC165 interfaceID", async () => { 12 | let result = await instance.supportsInterface("0x01ffc9a7"); 13 | assert.isTrue(result); 14 | }); 15 | 16 | it("should return false for interfaceID '0xffffffff'", async () => { 17 | let result = await instance.supportsInterface("0xffffffff"); 18 | assert.isFalse(result); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /backend/contracts/Selector.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.5; 4 | 5 | import "./interfaces/IERC1948.sol"; 6 | 7 | interface Solidity101 { 8 | function hello() external pure; 9 | 10 | function world(int256) external pure; 11 | } 12 | 13 | /// @title Contract for calculating Interface IDs 14 | contract Selector { 15 | /// @dev Calculate the interface 16 | /// @dev Requires the import of the interface 17 | function calculateSelectorReadData() public pure returns (bytes4) { 18 | IERC1948 i; 19 | return i.readData.selector; 20 | } 21 | 22 | function calculateSelectorWriteData() public pure returns (bytes4) { 23 | IERC1948 i; 24 | return i.writeData.selector; 25 | } 26 | 27 | function calculateSelector() public pure returns (bytes4) { 28 | return calculateSelectorReadData() ^ calculateSelectorWriteData(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /backend/test/test_erc721.js: -------------------------------------------------------------------------------- 1 | const BillOfLading = artifacts.require("BillOfLading"); 2 | 3 | contract("BillOfLading", (accounts) => { 4 | var instance; 5 | 6 | before("deploy contract", async () => { 7 | instance = await BillOfLading.deployed(); 8 | }); 9 | 10 | describe("supportsInterface", () => { 11 | it("should return true for the ERC721 interfaceID", async () => { 12 | let result = await instance.supportsInterface("0x80ac58cd"); 13 | assert.isTrue(result); 14 | }); 15 | 16 | it("should return true for the ERC721 Metadata extension interfaceID", async () => { 17 | let result = await instance.supportsInterface("0x5b5e139f"); 18 | assert.isTrue(result); 19 | }); 20 | 21 | it("should return true for the ERC721 Enumerable extension interfaceID", async () => { 22 | let result = await instance.supportsInterface("0x780e9d63"); 23 | assert.isTrue(result); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | **IMPORTANT: Please do not create a Pull Request without creating an issue first.** 2 | 3 | *Any change needs to be discussed before proceeding. Failure to do so may result in the rejection of the pull request.* 4 | 5 | Please provide enough information so that others can review your pull request: 6 | 7 | 8 | 9 | Explain the **details** for making this change. What existing problem does the pull request solve? 10 | 11 | 12 | 13 | **Test plan (required)** 14 | 15 | Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI. 16 | 17 | **Closing issues** 18 | 19 | Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if such). 20 | 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 EthBuilders.nyc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /WtxEthOil/src/components/layout/Metamask.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import Web3 from 'web3'; 3 | 4 | const Metamask = () => { 5 | 6 | let [login, setLogin] = useState(false); 7 | 8 | // loads the metamask extension 9 | // TODO: make it only load when we click a button 10 | const loadWeb3 = async () => { 11 | if (window.ethereum) { 12 | window.web3 = new Web3(window.ethereum) 13 | await window.ethereum.enable() 14 | } 15 | else if (window.web3) { 16 | window.web3 = new Web3(window.web3.currentProvider) 17 | } 18 | else { 19 | window.alert('Non-Ethereum browser detected. You should consider trying MetaMask!') 20 | } 21 | } 22 | 23 | // load the metamask extension 24 | useEffect(() => { 25 | if (login) { 26 | document.getElementById("metamaskLogin").style.display = "none"; 27 | } 28 | }, [login]); 29 | 30 | return ( 31 |
32 | 39 |
40 | 41 | 42 | ); 43 | } 44 | 45 | 46 | export default Metamask; -------------------------------------------------------------------------------- /WtxEthOil/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component, useEffect } from 'react'; 2 | import './App.css'; 3 | import Web3 from 'web3'; 4 | 5 | import '../node_modules/bootstrap/dist/css/bootstrap.css'; 6 | import Home from './components/pages/Home'; 7 | import About from './components/pages/About'; 8 | import Navbar from './components/layout/Navbar'; 9 | import { 10 | BrowserRouter as Router, 11 | Route, 12 | Switch, 13 | withRouter, 14 | } from 'react-router-dom'; 15 | import NotFound from './components/pages/NotFound'; 16 | import AddUser from './components/users/AddUser'; 17 | import EditUser from './components/users/EditUser'; 18 | import User from './components/users/User'; 19 | 20 | function App(props) { 21 | 22 | return ( 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
36 |
37 | ); 38 | } 39 | 40 | export default App; 41 | -------------------------------------------------------------------------------- /WtxEthOil/src/components/users/User.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { Link, useParams } from 'react-router-dom'; 3 | import axios from 'axios'; 4 | 5 | const User = () => { 6 | const [user, setUser] = useState({ 7 | driver: '', 8 | serialNumber: '', 9 | origin: '', 10 | destination: '', 11 | quantity: '', 12 | }); 13 | const { id } = useParams(); 14 | useEffect(() => { 15 | loadUser(); 16 | }, []); 17 | const loadUser = async () => { 18 | const res = await axios.get(`http://localhost:3003/users/${id}`); 19 | setUser(res.data); 20 | }; 21 | return ( 22 |
23 | 24 | back to Home 25 | 26 |

User Id: {id}

27 |
28 | 35 |
36 | ); 37 | }; 38 | 39 | export default User; 40 | -------------------------------------------------------------------------------- /backend/test/test_erc1948.js: -------------------------------------------------------------------------------- 1 | const BillOfLading = artifacts.require("BillOfLading"); 2 | 3 | const { 4 | constants, // Common constants, like the zero address and largest integers 5 | expectEvent, // Assertions for emitted events 6 | expectRevert, // Assertions for transactions that should fail 7 | } = require("@openzeppelin/test-helpers"); 8 | const { assertion } = require("@openzeppelin/test-helpers/src/expectRevert"); 9 | const { web3 } = require("@openzeppelin/test-helpers/src/setup"); 10 | 11 | contract("Bill Of Lading", (accounts) => { 12 | var instance; 13 | 14 | before("create instance", async () => { 15 | instance = await BillOfLading.deployed(); 16 | await instance.mint(accounts[0]); 17 | }); 18 | 19 | describe("writeData", () => { 20 | it("should emit a DataUpdated event", async () => { 21 | var _newData = web3.utils.asciiToHex("Hello World"); 22 | let tx = await instance.writeData(0, _newData); 23 | expectEvent(tx, "DataUpdated"); 24 | }); 25 | }); 26 | describe("readData", () => { 27 | it("should read the event in storage", async () => { 28 | let data = await instance.readData(0, { from: accounts[0] }); 29 | assert.equal( 30 | data, 31 | web3.utils.padRight(web3.utils.asciiToHex("Hello World"), 64) 32 | ); 33 | }); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /WtxEthOil/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "West-Texas-Oil", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.4", 7 | "@testing-library/react": "^11.1.0", 8 | "@testing-library/user-event": "^12.1.10", 9 | "axios": "^0.21.1", 10 | "bootstrap": "^4.5.3", 11 | "concurrently": "^5.3.0", 12 | "formik": "^2.2.6", 13 | "json-server": "^0.16.3", 14 | "react": "^17.0.1", 15 | "react-dom": "^17.0.1", 16 | "react-router-dom": "^5.2.0", 17 | "react-scripts": "4.0.1", 18 | "web-vitals": "^0.2.4", 19 | "web3": "^1.3.4" 20 | }, 21 | "scripts": { 22 | "start": "react-scripts start", 23 | "json-server": "json-server --watch db.json --port 3003", 24 | "start-dev": "concurrently \"npm start\" \"npm run json-server\"", 25 | "build": "react-scripts build", 26 | "test": "react-scripts test", 27 | "eject": "react-scripts eject" 28 | }, 29 | "eslintConfig": { 30 | "extends": [ 31 | "react-app", 32 | "react-app/jest" 33 | ] 34 | }, 35 | "browserslist": { 36 | "production": [ 37 | ">0.2%", 38 | "not dead", 39 | "not op_mini all" 40 | ], 41 | "development": [ 42 | "last 1 chrome version", 43 | "last 1 firefox version", 44 | "last 1 safari version" 45 | ] 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /backend/contracts/interfaces/IERC1948.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.5; 3 | 4 | import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; 5 | 6 | /** 7 | * @dev Interface of the ERC1948 contract. 8 | */ 9 | interface IERC1948 is IERC721 { 10 | /** 11 | * @dev Emitted when `oldData` is replaced with `newData` in storage of `tokenId`. 12 | * 13 | * Note that `oldData` or `newData` may be empty bytes. 14 | */ 15 | event DataUpdated( 16 | uint256 indexed tokenId, 17 | bytes32 oldData, 18 | bytes32 newData 19 | ); 20 | 21 | /** 22 | * @dev Reads the data of a specified token. Returns the current data in 23 | * storage of `tokenId`. 24 | * 25 | * @param tokenId The token to read the data off. 26 | * 27 | * @return A bytes32 representing the current data stored in the token. 28 | */ 29 | function readData(uint256 tokenId) external view returns (bytes32); 30 | 31 | /** 32 | * @dev Updates the data of a specified token. Writes `newData` into storage 33 | * of `tokenId`. 34 | * 35 | * @param tokenId The token to write data to. 36 | * @param newData The data to be written to the token. 37 | * 38 | * Emits a `DataUpdated` event. 39 | */ 40 | function writeData(uint256 tokenId, bytes32 newData) external; 41 | } 42 | -------------------------------------------------------------------------------- /WtxEthOil/src/components/layout/Navbar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Metamask from './Metamask'; 3 | import { Link, NavLink } from 'react-router-dom'; 4 | const Navbar = () => { 5 | return ( 6 | 46 | ); 47 | }; 48 | 49 | export default Navbar; 50 | -------------------------------------------------------------------------------- /WtxEthOil/src/components/pages/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const About = () => { 4 | return ( 5 |
6 |
7 |

About Page

8 |

9 | Description: Solving crude oil theft by reconciling Bill of Lading between mid-stream suppliers and end-stream refiners from Midland, Texas to Houston, Texas. We are using a specific test case here for a proof-of-concept. This could be a solution to putting oil and other transportable commodities on the blockchain which extends utility beyond theft prevention. 10 |

11 |

12 | How we solve this issue is by taking the industry-standard BOL(Bill of Lading) and turning it into a unique NFT that goes through state transitions. Currently, there is a significant amount of theft in the industry which costs millions in loss. This happens most frequently in the transportation phase. What this solution seeks to do is hold parties accountable for the specific amount of oil at the time of transfer. When the BOL is transfered so does accountability of the product. 13 |

14 |

15 | The application also provides tracking and all meta-data associated with the transportation and delivery of the product. This can be rolled into meaningful dashboards for the supplier, owner/operator, or fleet business. Employees could be paid in Aave which would accumulate interest while they worked and also release funds three weeks earlier than industry standard. 16 |

17 |

18 | The Team: EthGlobal Market Make Hackathon team is a multinational group of individuals who are passionate about blockchain technologies and solving real-world problems. 19 |

20 |
21 |
22 | ); 23 | }; 24 | 25 | export default About; 26 | -------------------------------------------------------------------------------- /WtxEthOil/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Project related 2 | .DS_Store 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | lerna-debug.log* 11 | 12 | # Diagnostic reports (https://nodejs.org/api/report.html) 13 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 14 | 15 | # Runtime data 16 | pids 17 | *.pid 18 | *.seed 19 | *.pid.lock 20 | 21 | # Directory for instrumented libs generated by jscoverage/JSCover 22 | lib-cov 23 | 24 | # Coverage directory used by tools like istanbul 25 | coverage 26 | *.lcov 27 | 28 | # nyc test coverage 29 | .nyc_output 30 | 31 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 32 | .grunt 33 | 34 | # Bower dependency directory (https://bower.io/) 35 | bower_components 36 | 37 | # node-waf configuration 38 | .lock-wscript 39 | 40 | # Compiled binary addons (https://nodejs.org/api/addons.html) 41 | build/Release 42 | 43 | # Dependency directories 44 | node_modules/ 45 | jspm_packages/ 46 | 47 | # TypeScript v1 declaration files 48 | typings/ 49 | 50 | # TypeScript cache 51 | *.tsbuildinfo 52 | 53 | # Optional npm cache directory 54 | .npm 55 | 56 | # Optional eslint cache 57 | .eslintcache 58 | 59 | # Microbundle cache 60 | .rpt2_cache/ 61 | .rts2_cache_cjs/ 62 | .rts2_cache_es/ 63 | .rts2_cache_umd/ 64 | 65 | # Optional REPL history 66 | .node_repl_history 67 | 68 | # Output of 'npm pack' 69 | *.tgz 70 | 71 | # Yarn Integrity file 72 | .yarn-integrity 73 | 74 | # dotenv environment variables file 75 | .env 76 | .env.test 77 | 78 | # parcel-bundler cache (https://parceljs.org/) 79 | .cache 80 | 81 | # Next.js build output 82 | .next 83 | 84 | # Nuxt.js build / generate output 85 | .nuxt 86 | dist 87 | 88 | # Gatsby files 89 | .cache/ 90 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 91 | # https://nextjs.org/blog/next-9-1#public-directory-support 92 | # public 93 | 94 | # vuepress build output 95 | .vuepress/dist 96 | 97 | # Serverless directories 98 | .serverless/ 99 | 100 | # FuseBox cache 101 | .fusebox/ 102 | 103 | # DynamoDB Local files 104 | .dynamodb/ 105 | 106 | # TernJS port file 107 | .tern-port 108 | -------------------------------------------------------------------------------- /backend/test/test_create_bol.js: -------------------------------------------------------------------------------- 1 | const BillOfLading = artifacts.require("BillOfLading"); 2 | 3 | const { 4 | BN, // Big Number support 5 | constants, // Common constants, like the zero address and largest integers 6 | expectEvent, // Assertions for emitted events 7 | expectRevert, // Assertions for transactions that should fail 8 | } = require("@openzeppelin/test-helpers"); 9 | const { assertion } = require("@openzeppelin/test-helpers/src/expectRevert"); 10 | 11 | contract("BillOfLading", (accounts) => { 12 | var instance; 13 | 14 | before("create instance", async () => { 15 | instance = await BillOfLading.deployed({ from: accounts[0] }); 16 | }); 17 | 18 | describe("createBill", () => { 19 | let [shipper, sn, origin, destination, quantity] = [ 20 | accounts[1], 21 | "13WX78KS011", 22 | // 40°39'0.36"N, 73°56'58.49"W Brooklyn, New York 23 | [ 24 | [40, 39, 00360, 0], 25 | [73, 56, 58490, 3], 26 | ], 27 | // 27°56'51.07"N, 82°27'30.35"W Tampa, Florida 28 | [ 29 | [27, 56, 51070, 0], 30 | [82, 27, 30350, 3], 31 | ], 32 | 10000, 33 | ]; 34 | it("should emit a transfer event", async () => { 35 | let tx = await instance.createBillOfLading( 36 | [shipper, sn, origin, destination, quantity], 37 | { from: accounts[0] } 38 | ); 39 | 40 | expectEvent(tx, "Transfer", { 41 | from: constants.ZERO_ADDRESS, 42 | to: accounts[0], 43 | }); 44 | }); 45 | it("should revert if sender isn't a minter", async () => { 46 | await expectRevert( 47 | instance.createBillOfLading( 48 | [shipper, sn, origin, destination, quantity], 49 | { from: accounts[1] } 50 | ), 51 | "must have minter role to mint" 52 | ); 53 | }); 54 | it("should mint a token and give to the minter", async () => { 55 | let balance_0 = await instance.balanceOf(accounts[0]); 56 | let tx = await instance.createBillOfLading( 57 | [shipper, sn, origin, destination, quantity], 58 | { from: accounts[0] } 59 | ); 60 | let balance_1 = await instance.balanceOf(accounts[0]); 61 | assert.equal(balance_0.addn(1).words[0], balance_1.words[0]); 62 | }); 63 | it("should increase the total supply by 1", async () => { 64 | let totalsupply_0 = await instance.totalSupply(); 65 | let tx = await instance.createBillOfLading( 66 | [shipper, sn, origin, destination, quantity], 67 | { from: accounts[0] } 68 | ); 69 | let totalsupply_1 = await instance.totalSupply(); 70 | assert.equal(totalsupply_0.addn(1).words[0], totalsupply_1.words[0]); 71 | }); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /WtxEthOil/src/components/users/EditUser.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import axios from 'axios'; 3 | import { useHistory, useParams } from 'react-router-dom'; 4 | 5 | const EditUser = () => { 6 | let history = useHistory(); 7 | const { id } = useParams(); 8 | const [user, setUser] = useState({ 9 | driver: '', 10 | serialNumber: '', 11 | origin: '', 12 | destination: '', 13 | quantity: '', 14 | }); 15 | 16 | const { driver, serialNumber, origin, destination, quantity } = user; 17 | const onInputChange = (e) => { 18 | setUser({ ...user, [e.target.name]: e.target.value }); 19 | }; 20 | 21 | useEffect(() => { 22 | loadUser(); 23 | }, []); 24 | 25 | const onSubmit = async (e) => { 26 | e.preventDefault(); 27 | await axios.put(`http://localhost:3003/users/${id}`, user); 28 | history.push('/'); 29 | }; 30 | 31 | const loadUser = async () => { 32 | const result = await axios.get(`http://localhost:3003/users/${id}`); 33 | setUser(result.data); 34 | }; 35 | return ( 36 |
37 |
38 |

Edit A User

39 |
onSubmit(e)}> 40 |
41 | onInputChange(e)} 48 | /> 49 |
50 |
51 | onInputChange(e)} 58 | /> 59 |
60 |
61 | onInputChange(e)} 68 | /> 69 |
70 |
71 | onInputChange(e)} 78 | /> 79 |
80 |
81 | onInputChange(e)} 88 | /> 89 |
90 | 91 |
92 |
93 |
94 | ); 95 | }; 96 | 97 | export default EditUser; 98 | -------------------------------------------------------------------------------- /backend/contracts/interfaces/ERC998/IERC998ERC20BottomUp.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.5; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | 7 | /// @title ERC998ERC20 Bottom-Up Composable Fungible Token 8 | /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-998.md 9 | /// Note: The ERC-165 identifier for this interface is 0xffafa991 10 | interface IERC998ERC20BottomUp is IERC20 { 11 | /// @dev This emits when a token is transferred to an ERC721 token 12 | /// @param _toContract The contract the token is transferred to 13 | /// @param _toTokenId The token the token is transferred to 14 | /// @param _amount The amount of tokens transferred 15 | event TransferToParent( 16 | address indexed _toContract, 17 | uint256 indexed _toTokenId, 18 | uint256 _amount 19 | ); 20 | 21 | /// @dev This emits when a token is transferred from an ERC721 token 22 | /// @param _fromContract The contract the token is transferred from 23 | /// @param _fromTokenId The token the token is transferred from 24 | /// @param _amount The amount of tokens transferred 25 | event TransferFromParent( 26 | address indexed _fromContract, 27 | uint256 indexed _fromTokenId, 28 | uint256 _amount 29 | ); 30 | 31 | /// @notice Get the balance of a non-fungible parent token 32 | /// @param _tokenContract The contract tracking the parent token 33 | /// @param _tokenId The ID of the parent token 34 | /// @return amount The balance of the token 35 | function balanceOfToken(address _tokenContract, uint256 _tokenId) 36 | external 37 | view 38 | returns (uint256 amount); 39 | 40 | /// @notice Transfer tokens from owner address to a token 41 | /// @param _from The owner address 42 | /// @param _toContract The ERC721 contract of the receiving token 43 | /// @param _toTokenId The receiving token 44 | /// @param _amount The amount of tokens to transfer 45 | function transferToParent( 46 | address _from, 47 | address _toContract, 48 | uint256 _toTokenId, 49 | uint256 _amount 50 | ) external; 51 | 52 | /// @notice Transfer token from a token to an address 53 | /// @param _fromContract The address of the owning contract 54 | /// @param _fromTokenId The owning token 55 | /// @param _to The address the token is transferred to 56 | /// @param _amount The amount of tokens to transfer 57 | function transferFromParent( 58 | address _fromContract, 59 | uint256 _fromTokenId, 60 | address _to, 61 | uint256 _amount 62 | ) external; 63 | 64 | /// @notice Transfer token from a token to an address, using ERC223 semantics 65 | /// @param _fromContract The address of the owning contract 66 | /// @param _fromTokenId The owning token 67 | /// @param _to The address the token is transferred to 68 | /// @param _amount The amount of tokens to transfer 69 | /// @param _data Additional data with no specified format, can be used to specify the sender tokenId 70 | function transferFromParentERC223( 71 | address _fromContract, 72 | uint256 _fromTokenId, 73 | address _to, 74 | uint256 _amount, 75 | bytes calldata _data 76 | ) external; 77 | 78 | /// @notice Transfer a token from a token to another token 79 | /// @param _fromContract The address of the owning contract 80 | /// @param _fromTokenId The owning token 81 | /// @param _toContract The ERC721 contract of the receiving token 82 | /// @param _toTokenId The receiving token 83 | /// @param _amount The amount tokens to transfer 84 | function transferAsChild( 85 | address _fromContract, 86 | uint256 _fromTokenId, 87 | address _toContract, 88 | uint256 _toTokenId, 89 | uint256 _amount 90 | ) external; 91 | } 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EthGlobal-West-Texas-Oil 2 | 3 | General information: 4 | Latest update: 1/21/2021 5 | Version: 0.01 6 | License: MIT 7 | 8 | 9 | 10 | The Team: 11 | EthGlobal Market Make Hackathon team is a multinational group of individuals who are passionate about blockchain technologies and solving real-world problems. 12 | 13 | 14 | 15 | [![Contributors][contributors-shield]][contributors-url] 16 | [![Forks][forks-shield]][forks-url] 17 | [![Stargazers][stars-shield]][stars-url] 18 | [![Issues][issues-shield]][issues-url] 19 | [![MIT License][license-shield]][license-url] 20 | 21 | 22 | ## About The Project 23 | 24 | [![Product Name Screen Shot][product-screenshot]](https://example.com) 25 | Solving crude oil theft by reconciling Bill of Lading between mid-stream suppliers and end-stream refiners from Midland, Texas to Houston, Texas. 26 | We are using a specific test case here for a proof-of-concept. This could be a solution to putting oil and other transportable commodities on the blockchain which extends utility beyond theft prevention. 27 | 28 | How we solve this issue is by taking the industry-standard BOL(Bill of Lading) and turning it into a unique NFT that goes through state transitions. 29 | Currently, there is a significant amount of theft in the industry which costs millions in loss. This happens most frequently in the transportation phase. What this solution seeks to do is hold parties accountable for the specific amount of oil at the time of transfer. When the BOL is transfered so does accountability of the product. 30 | 31 | The application also provides tracking and all meta-data associated with the transportation and delivery of the product. This can be rolled into meaningful dashboards for the supplier, owner/operator, or fleet business. Employees could be paid in Aave which would accumulate interest while they worked and also release funds three weeks earlier than industry standard. 32 | 33 | 34 | 35 | 36 | 37 | 38 | ## Roadmap 39 | 40 | See the [open issues](https://github.com/github_username/repo_name/issues) for a list of proposed features (and known issues). 41 | 42 | 43 | 44 | 45 | ## Contributing 46 | 47 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 48 | 49 | 1. Fork the Project 50 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 51 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 52 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 53 | 5. Open a Pull Request 54 | 55 | 56 | 57 | 58 | ## License 59 | 60 | Distributed under the MIT License. See `LICENSE` for more information. 61 | 62 | 63 | 64 | 65 | 66 | [contributors-shield]: https://img.shields.io/github/contributors/EthBuilders/EthGlobal-West-Texas-Oil.svg?style=for-the-badge 67 | [contributors-url]: https://github.com/EthBuilders/EthGlobal-West-Texas-Oil/graphs/contributors 68 | [forks-shield]: https://img.shields.io/github/forks/EthBuilders/EthGlobal-West-Texas-Oil.svg?style=for-the-badge 69 | [forks-url]: https://github.com/EthBuilders/EthGlobal-West-Texas-Oil/network/members 70 | [stars-shield]: https://img.shields.io/github/stars/EthBuilders/EthGlobal-West-Texas-Oil.svg?style=for-the-badge 71 | [stars-url]: https://github.com/EthBuilders/EthGlobal-West-Texas-Oil/stargazers 72 | [issues-shield]: https://img.shields.io/github/issues/EthBuilders/EthGlobal-West-Texas-Oil.svg?style=for-the-badge 73 | [issues-url]: https://github.com/EthBuilders/EthGlobal-West-Texas-Oil/issues 74 | [license-shield]: https://img.shields.io/github/license/EthBuilders/EthGlobal-West-Texas-Oil.svg?style=for-the-badge 75 | [license-url]: https://github.com/EthBuilders/EthGlobal-West-Texas-Oil/blob/master/LICENSE.txt 76 | -------------------------------------------------------------------------------- /WtxEthOil/src/components/pages/Home.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import axios from 'axios'; 3 | import { Link } from 'react-router-dom'; 4 | import InputField from "../layout/Input"; 5 | import Web3 from 'web3'; 6 | const bolAbi = require("../../bol.json"); 7 | 8 | 9 | 10 | const Home = () => { 11 | let [bol, setBol] = useState({ address: "0x0000000000000000000000000000000000000000", tokenContract: "0x0000000000000000000000000000000000000000" }); 12 | const [users, setUser] = useState([]); 13 | 14 | let account; 15 | 16 | useEffect(() => { 17 | loadUsers(); 18 | loadWeb3(); 19 | }, []); 20 | 21 | const loadWeb3 = async () => { 22 | if (window.ethereum) { 23 | window.web3 = new Web3(window.ethereum) 24 | await window.ethereum.enable() 25 | } 26 | else if (window.web3) { 27 | window.web3 = new Web3(window.web3.currentProvider) 28 | } 29 | else { 30 | window.alert('Non-Ethereum browser detected. You should consider trying MetaMask!') 31 | } 32 | } 33 | 34 | const loadUsers = async () => { 35 | 36 | window.web3.currentProvider.enable(); 37 | let web3 = new Web3(window.web3.currentProvider); 38 | 39 | web3.eth.getAccounts().then((e) => { 40 | 41 | let bolContract = new web3.eth.Contract(bolAbi.abi, bol.address, { from: account }); 42 | 43 | bolContract.getPastEvents("allevents", { fromBlock: 1, toBlock: "latest" }).then((events) => { 44 | console.log(events); 45 | setUser(events); 46 | }) 47 | 48 | // setUser(result.data.reverse()); 49 | }) 50 | 51 | }; 52 | 53 | const deleteUser = async (id) => { 54 | await axios.delete(`http://localhost:3003/users/${id}`); 55 | loadUsers(); 56 | }; 57 | 58 | return ( 59 |
60 |
61 |
62 |

BOL Contract Address

63 |
64 | setBol({ ...bol, [e.target.name]: e.target.value })} type="text" placeholder={"Enter an address"} /> 65 |
66 |

Token Contract Address

67 |
68 | setBol({ ...bol, [e.target.name]: e.target.value })} type="text" placeholder={"Token Contract Address"} /> 69 |
70 |
71 |
72 |
73 |

Home Page

74 | {users.map((user, index) => ( 75 |
76 |

{index}

77 |

{user}

78 |
79 | ))} 80 | {/* 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | {users.map((user, index) => ( 92 | 93 | 94 | 95 | 96 | 97 | 114 | 115 | ))} */} 116 | {/* 117 |
#NameUser NameEmailAction
{index + 1}{user.driver}{user.serialNumber}{user.origin} 98 | 99 | View 100 | 101 | 105 | Edit 106 | 107 | deleteUser(user.id)} 110 | > 111 | Delete 112 | 113 |
*/} 118 |
119 |
120 | ); 121 | }; 122 | 123 | export default Home; 124 | -------------------------------------------------------------------------------- /backend/truffle-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Use this file to configure your truffle project. It's seeded with some 3 | * common settings for different networks and features like migrations, 4 | * compilation and testing. Uncomment the ones you need or modify 5 | * them to suit your project as necessary. 6 | * 7 | * More information about configuration can be found at: 8 | * 9 | * trufflesuite.com/docs/advanced/configuration 10 | * 11 | * To deploy via Infura you'll need a wallet provider (like @truffle/hdwallet-provider) 12 | * to sign your transactions before they're sent to a remote public node. Infura accounts 13 | * are available for free at: infura.io/register. 14 | * 15 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate 16 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this 17 | * phrase from a file you've .gitignored so it doesn't accidentally become public. 18 | * 19 | */ 20 | 21 | // const HDWalletProvider = require('@truffle/hdwallet-provider'); 22 | // const infuraKey = "fj4jll3k....."; 23 | // 24 | // const fs = require('fs'); 25 | // const mnemonic = fs.readFileSync(".secret").toString().trim(); 26 | 27 | module.exports = { 28 | /** 29 | * Networks define how you connect to your ethereum client and let you set the 30 | * defaults web3 uses to send transactions. If you don't specify one truffle 31 | * will spin up a development blockchain for you on port 9545 when you 32 | * run `develop` or `test`. You can ask a truffle command to use a specific 33 | * network from the command line, e.g 34 | * 35 | * $ truffle test --network 36 | */ 37 | 38 | networks: { 39 | // Useful for testing. The `development` name is special - truffle uses it by default 40 | // if it's defined here and no other network is specified at the command line. 41 | // You should run a client (like ganache-cli, geth or parity) in a separate terminal 42 | // tab if you use this network and you must also set the `host`, `port` and `network_id` 43 | // options below to some value. 44 | // 45 | development: { 46 | host: "127.0.0.1", // Localhost (default: none) 47 | port: 8545, // Standard Ethereum port (default: none) 48 | network_id: "*", // Any network (default: none) 49 | gas: 0xfffffffffff, 50 | gasPrice: 0x01 51 | }, 52 | // Another network with more advanced options... 53 | // advanced: { 54 | // port: 8777, // Custom port 55 | // network_id: 1342, // Custom network 56 | // gas: 8500000, // Gas sent with each transaction (default: ~6700000) 57 | // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) 58 | // from:
, // Account to send txs from (default: accounts[0]) 59 | // websockets: true // Enable EventEmitter interface for web3 (default: false) 60 | // }, 61 | // Useful for deploying to a public network. 62 | // NB: It's important to wrap the provider as a function. 63 | // ropsten: { 64 | // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`), 65 | // network_id: 3, // Ropsten's id 66 | // gas: 5500000, // Ropsten has a lower block limit than mainnet 67 | // confirmations: 2, // # of confs to wait between deployments. (default: 0) 68 | // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) 69 | // skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) 70 | // }, 71 | // Useful for private networks 72 | // private: { 73 | // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), 74 | // network_id: 2111, // This network is yours, in the cloud. 75 | // production: true // Treats this network as if it was a public net. (default: false) 76 | // } 77 | }, 78 | 79 | // Set default mocha options here, use special reporters etc. 80 | mocha: { 81 | // timeout: 100000 82 | }, 83 | 84 | // Configure your compilers 85 | compilers: { 86 | solc: { 87 | version: "0.7.5", // Fetch exact version from solc-bin (default: truffle's version) 88 | // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) 89 | // settings: { // See the solidity docs for advice about optimization and evmVersion 90 | // optimizer: { 91 | // enabled: false, 92 | // runs: 200 93 | // }, 94 | // evmVersion: "byzantium" 95 | // } 96 | }, 97 | }, 98 | }; 99 | -------------------------------------------------------------------------------- /backend/contracts/interfaces/ERC998/IERC998ERC20TopDown.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.5; 4 | 5 | import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; 6 | 7 | /// @title ERC998ERC20 Top-Down Composable Non-Fungible Token 8 | /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-998.md 9 | /// Note: the ERC-165 identifier for this interface is 0x7294ffed 10 | interface IERC998ERC20TopDown is IERC721 { 11 | /// @dev This emits when a token receives ERC20 tokens. 12 | /// @param _from The prior owner of the token. 13 | /// @param _toTokenId The token that receives the ERC20 tokens. 14 | /// @param _erc20Contract The ERC20 contract. 15 | /// @param _value The number of ERC20 tokens received. 16 | event ReceivedERC20( 17 | address indexed _from, 18 | uint256 indexed _toTokenId, 19 | address indexed _erc20Contract, 20 | uint256 _value 21 | ); 22 | 23 | /// @dev This emits when a token transfers ERC20 tokens. 24 | /// @param _fromTokenId The token that owned the ERC20 tokens. 25 | /// @param _to The address that receives the ERC20 tokens. 26 | /// @param _erc20Contract The ERC20 contract. 27 | /// @param _value The number of ERC20 tokens transferred. 28 | event TransferERC20( 29 | uint256 indexed _fromTokenId, 30 | address indexed _to, 31 | address indexed _erc20Contract, 32 | uint256 _value 33 | ); 34 | 35 | /// @notice A token receives ERC20 tokens 36 | /// @param _from The prior owner of the ERC20 tokens 37 | /// @param _value The number of ERC20 tokens received 38 | /// @param _data Up to the first 32 bytes contains an integer which is the receiving tokenId. 39 | function tokenFallback( 40 | address _from, 41 | uint256 _value, 42 | bytes calldata _data 43 | ) external; 44 | 45 | /// @notice Look up the balance of ERC20 tokens for a specific token and ERC20 contract 46 | /// @param _tokenId The token that owns the ERC20 tokens 47 | /// @param _erc20Contract The ERC20 contract 48 | /// @return The number of ERC20 tokens owned by a token from an ERC20 contract 49 | function balanceOfERC20(uint256 _tokenId, address _erc20Contract) 50 | external 51 | view 52 | returns (uint256); 53 | 54 | /// @notice Transfer ERC20 tokens to address 55 | /// @param _tokenId The token to transfer from 56 | /// @param _to The address to send the ERC20 tokens to 57 | /// @param _erc20Contract The ERC20 contract 58 | /// @param _value The number of ERC20 tokens to transfer 59 | function transferERC20( 60 | uint256 _tokenId, 61 | address _to, 62 | address _erc20Contract, 63 | uint256 _value 64 | ) external; 65 | 66 | /// @notice Transfer ERC20 tokens to address or ERC20 top-down composable 67 | /// @param _tokenId The token to transfer from 68 | /// @param _to The address to send the ERC20 tokens to 69 | /// @param _erc223Contract The ERC223 token contract 70 | /// @param _value The number of ERC20 tokens to transfer 71 | /// @param _data Additional data with no specified format, can be used to specify tokenId to transfer to 72 | function transferERC223( 73 | uint256 _tokenId, 74 | address _to, 75 | address _erc223Contract, 76 | uint256 _value, 77 | bytes calldata _data 78 | ) external; 79 | 80 | /// @notice Get ERC20 tokens from ERC20 contract. 81 | /// @param _from The current owner address of the ERC20 tokens that are being transferred. 82 | /// @param _tokenId The token to transfer the ERC20 tokens to. 83 | /// @param _erc20Contract The ERC20 token contract 84 | /// @param _value The number of ERC20 tokens to transfer 85 | function getERC20( 86 | address _from, 87 | uint256 _tokenId, 88 | address _erc20Contract, 89 | uint256 _value 90 | ) external; 91 | } 92 | 93 | /// @dev The ERC-165 identifier for this interface is 0xc5fd96cd 94 | interface ERC998ERC20TopDownEnumerable { 95 | /// @notice Get the number of ERC20 contracts that token owns ERC20 tokens from 96 | /// @param _tokenId The token that owns ERC20 tokens. 97 | /// @return uint256 The number of ERC20 contracts 98 | function totalERC20Contracts(uint256 _tokenId) 99 | external 100 | view 101 | returns (uint256); 102 | 103 | /// @notice Get an ERC20 contract that token owns ERC20 tokens from by index 104 | /// @param _tokenId The token that owns ERC20 tokens. 105 | /// @param _index The index position of the ERC20 contract. 106 | /// @return address The ERC20 contract 107 | function erc20ContractByIndex(uint256 _tokenId, uint256 _index) 108 | external 109 | view 110 | returns (address); 111 | } 112 | -------------------------------------------------------------------------------- /backend/contracts/BillOfLading.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.5; 3 | pragma abicoder v2; 4 | 5 | import "@openzeppelin/contracts/presets/ERC721PresetMinterPauserAutoId.sol"; 6 | import "./interfaces/IERC1948.sol"; 7 | import "./dBol.sol"; 8 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 9 | 10 | // @title Bill of Lading NFT Contract 11 | // @author Edward Amor 12 | // @notice Users should not directly interact with this contract 13 | // @dev All ERC721 functions are inherited from Openzeppelin ERC721 contract 14 | contract BillOfLading is ERC721PresetMinterPauserAutoId, IERC1948 { 15 | enum CardinalDirections {NORTH, EAST, SOUTH, WEST} // clock-wise ordering 16 | 17 | /** 18 | @param degree ranges from 0 to 90 for latitude and 0 to 180 for longitude 19 | @param minute ranges from 0 to 60 20 | @param second ranges from 0.0 to 60.0, since this can be a decimal number 21 | and solidity only works with integers, we have to do some padding. 22 | A standardized way to do this is simply provide a number within the range 23 | of [0, 2 ^ 16 - 1], and to pad it with 3 zeros. 24 | 25 | Ex. 26 | 25.23 => 25230 27 | 59.98 => 59980 28 | 00.39 => 00390 29 | */ 30 | struct GeoPosition { 31 | uint8 degree; 32 | uint8 minute; 33 | uint16 second; 34 | CardinalDirections cardinalDirection; 35 | } 36 | 37 | /** 38 | @param quantity is the amount in MT (metric tons) since this is a decimal 39 | we pad and make sure to leave the last 2 digits as the decimal numbers. 40 | 41 | Ex. 42 | 28.1 MT => 2810 43 | 1289.32 MT => 128932 44 | */ 45 | struct MintArgs { 46 | address driver; 47 | string serialNumber; 48 | GeoPosition[2] origin; 49 | GeoPosition[2] destination; 50 | uint256 quantity; 51 | } 52 | 53 | event CreateBill(MintArgs arguments, address indexed to); 54 | 55 | struct Bill { 56 | address driver; 57 | string serialNumber; 58 | GeoPosition originLatitude; 59 | GeoPosition originLongitude; 60 | GeoPosition destinationLatitude; 61 | GeoPosition destinationLongitude; 62 | uint256 quantity; 63 | uint256 timestamp; 64 | } 65 | 66 | Bill[] public bills; 67 | mapping(uint256 => bytes32) data; 68 | 69 | DBol dBOLContract; 70 | 71 | /* 72 | * bytes4(keccak256('readData()')) == 0x70a08231 73 | * bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e 74 | * 75 | * => 0x37ebbc03 ^ 0xa983d43f == 0x9e68683c 76 | */ 77 | bytes4 private constant _INTERFACE_ID_ERC1948 = 0x9e68683c; 78 | 79 | constructor( 80 | string memory name, 81 | string memory symbol, 82 | string memory baseURI, 83 | address dbolContract 84 | ) public ERC721PresetMinterPauserAutoId(name, symbol, baseURI) { 85 | _registerInterface(_INTERFACE_ID_ERC1948); 86 | dBOLContract = DBol(dbolContract); 87 | } 88 | 89 | /// @notice Create a Bill of Lading for a shipment 90 | /// @dev User facing function which creates a bill 91 | function createBillOfLading( 92 | MintArgs memory _args, 93 | address _funding, 94 | uint256 _value 95 | ) public { 96 | require( 97 | IERC20(_funding).allowance(msg.sender, address(this)) >= _value 98 | ); 99 | bills.push( 100 | Bill({ 101 | driver: _args.driver, 102 | serialNumber: _args.serialNumber, 103 | originLatitude: _args.origin[0], 104 | originLongitude: _args.origin[1], 105 | destinationLatitude: _args.destination[0], 106 | destinationLongitude: _args.destination[1], 107 | quantity: _args.quantity, 108 | timestamp: block.timestamp 109 | }) 110 | ); 111 | mint(msg.sender); 112 | IERC20(_funding).transferFrom(msg.sender, address(this), _value); 113 | IERC20(_funding).approve(address(dBOLContract), _value); 114 | uint256 childToken = dBOLContract.createDBol(totalSupply() - 1); 115 | dBOLContract.getERC20(address(this), childToken, _funding, _value); 116 | emit CreateBill(_args, _args.driver); 117 | } 118 | 119 | /** 120 | * @dev See `IERC1948.readData`. 121 | * 122 | * Requirements: 123 | * 124 | * - `tokenId` needs to exist. 125 | */ 126 | function readData(uint256 tokenId) 127 | external 128 | view 129 | override 130 | returns (bytes32) 131 | { 132 | require(_exists(tokenId)); 133 | return data[tokenId]; 134 | } 135 | 136 | /** 137 | * @dev See `IERC1948.writeData`. 138 | * 139 | * Requirements: 140 | * 141 | * - `msg.sender` needs to be owner of `tokenId`. 142 | */ 143 | function writeData(uint256 tokenId, bytes32 newData) external override { 144 | require(msg.sender == ownerOf(tokenId)); 145 | emit DataUpdated(tokenId, data[tokenId], newData); 146 | data[tokenId] = newData; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /backend/contracts/interfaces/ERC998/IERC998ERC721BottomUp.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.5; 4 | 5 | import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; 6 | 7 | /// @title ERC998ERC721 Bottom-Up Composable Non-Fungible Token 8 | /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-998.md 9 | /// Note: the ERC-165 identifier for this interface is 0xa1b23002 10 | interface IERC998ERC721BottomUp is IERC721 { 11 | /// @dev This emits when a token is transferred to an ERC721 token 12 | /// @param _toContract The contract the token is transferred to 13 | /// @param _toTokenId The token the token is transferred to 14 | /// @param _tokenId The token that is transferred 15 | event TransferToParent( 16 | address indexed _toContract, 17 | uint256 indexed _toTokenId, 18 | uint256 _tokenId 19 | ); 20 | 21 | /// @dev This emits when a token is transferred from an ERC721 token 22 | /// @param _fromContract The contract the token is transferred from 23 | /// @param _fromTokenId The token the token is transferred from 24 | /// @param _tokenId The token that is transferred 25 | event TransferFromParent( 26 | address indexed _fromContract, 27 | uint256 indexed _fromTokenId, 28 | uint256 _tokenId 29 | ); 30 | 31 | /// @notice Get the root owner of tokenId. 32 | /// @param _tokenId The token to query for a root owner address 33 | /// @return rootOwner The root owner at the top of tree of tokens and ERC998 magic value. 34 | function rootOwnerOf(uint256 _tokenId) 35 | external 36 | view 37 | returns (bytes32 rootOwner); 38 | 39 | /// @notice Get the owner address and parent token (if there is one) of a token 40 | /// @param _tokenId The tokenId to query. 41 | /// @return tokenOwner The owner address of the token 42 | /// @return parentTokenId The parent owner of the token and ERC998 magic value 43 | /// @return isParent True if parentTokenId is a valid parent tokenId and false if there is no parent tokenId 44 | function tokenOwnerOf(uint256 _tokenId) 45 | external 46 | view 47 | returns ( 48 | bytes32 tokenOwner, 49 | uint256 parentTokenId, 50 | bool isParent 51 | ); 52 | 53 | /// @notice Transfer token from owner address to a token 54 | /// @param _from The owner address 55 | /// @param _toContract The ERC721 contract of the receiving token 56 | /// @param _toTokenId The receiving token 57 | /// @param _tokenId The token to transfer 58 | /// @param _data Additional data with no specified format 59 | function transferToParent( 60 | address _from, 61 | address _toContract, 62 | uint256 _toTokenId, 63 | uint256 _tokenId, 64 | bytes calldata _data 65 | ) external; 66 | 67 | /// @notice Transfer token from a token to an address 68 | /// @param _fromContract The address of the owning contract 69 | /// @param _fromTokenId The owning token 70 | /// @param _to The address the token is transferred to. 71 | /// @param _tokenId The token that is transferred 72 | /// @param _data Additional data with no specified format 73 | function transferFromParent( 74 | address _fromContract, 75 | uint256 _fromTokenId, 76 | address _to, 77 | uint256 _tokenId, 78 | bytes calldata _data 79 | ) external; 80 | 81 | /// @notice Transfer a token from a token to another token 82 | /// @param _fromContract The address of the owning contract 83 | /// @param _fromTokenId The owning token 84 | /// @param _toContract The ERC721 contract of the receiving token 85 | /// @param _toTokenId The receiving token 86 | /// @param _tokenId The token that is transferred 87 | /// @param _data Additional data with no specified format 88 | function transferAsChild( 89 | address _fromContract, 90 | uint256 _fromTokenId, 91 | address _toContract, 92 | uint256 _toTokenId, 93 | uint256 _tokenId, 94 | bytes calldata _data 95 | ) external; 96 | } 97 | 98 | /// @dev The ERC-165 identifier for this interface is 0x8318b539 99 | interface IERC998ERC721BottomUpEnumerable { 100 | /// @notice Get the number of ERC721 tokens owned by parent token. 101 | /// @param _parentContract The contract the parent ERC721 token is from. 102 | /// @param _parentTokenId The parent tokenId that owns tokens 103 | // @return uint256 The number of ERC721 tokens owned by parent token. 104 | function totalChildTokens(address _parentContract, uint256 _parentTokenId) 105 | external 106 | view 107 | returns (uint256); 108 | 109 | /// @notice Get a child token by index 110 | /// @param _parentContract The contract the parent ERC721 token is from. 111 | /// @param _parentTokenId The parent tokenId that owns the token 112 | /// @param _index The index position of the child token 113 | /// @return uint256 The child tokenId owned by the parent token 114 | function childTokenByIndex( 115 | address _parentContract, 116 | uint256 _parentTokenId, 117 | uint256 _index 118 | ) external view returns (uint256); 119 | } 120 | -------------------------------------------------------------------------------- /WtxEthOil/src/components/users/AddUser.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { useHistory } from 'react-router-dom'; 3 | import InputField from "../layout/Input"; 4 | import Web3 from 'web3'; 5 | const tokenAbi = require('../../token.json'); 6 | const bolAbi = require("../../bol.json"); 7 | 8 | const AddUser = () => { 9 | let history = useHistory(); 10 | let [bol, setBol] = useState({ address: "0x07E71765a2f34021AB8eCA03A8e8d8E13F9198c6", tokenContract: "0xdbAEe074eC8Af2cD1daAe455AB1d9e6a63C6F872" }); 11 | const [user, setUser] = useState({ 12 | driver: '0x96767a69aa77A8E98d926ea7900BbDBbCa2b916E', 13 | serialNumber: 'GA123AA', 14 | latitudeOriginDegrees: '0', 15 | latitudeOriginMinutes: '0', 16 | latitudeOriginSeconds: '0', 17 | latitudeOriginCardinalDirection: '0', 18 | longitudeOriginDegrees: '0', 19 | longitudeOriginMinutes: '0', 20 | longitudeOriginSeconds: '0', 21 | longitudeOriginCardinalDirection: '0', 22 | latitudeDestinationDegrees: '0', 23 | latitudeDestinationMinutes: '0', 24 | latitudeDestinationSeconds: '0', 25 | latitudeDestinationCardinalDirection: '0', 26 | longitudeDestinationDegrees: '0', 27 | longitudeDestinationMinutes: '0', 28 | longitudeDestinationSeconds: '0', 29 | longitudeDestinationCardinalDirection: '0', 30 | quantity: '1000', 31 | }); 32 | let account; 33 | 34 | useEffect(() => { 35 | loadWeb3(); 36 | }) 37 | 38 | function cleanData(data) { 39 | let result = {} 40 | let pat = /^(latitude|longitude).*/; 41 | for (const [key, value] of Object.entries(data)) { 42 | if (pat.test(key)) { 43 | result[key] = parseInt(value); 44 | } else { 45 | result[key] = value; 46 | } 47 | } 48 | return [ 49 | result.driver, 50 | result.serialNumber, 51 | [ 52 | [ 53 | result.latitudeOriginDegrees, 54 | result.latitudeOriginMinutes, 55 | result.latitudeOriginSeconds, 56 | result.latitudeOriginCardinalDirection, 57 | ], 58 | [ 59 | result.longitudeOriginDegrees, 60 | result.longitudeOriginMinutes, 61 | result.longitudeOriginSeconds, 62 | result.longitudeOriginCardinalDirection, 63 | 64 | ] 65 | ], 66 | [ 67 | [ 68 | result.latitudeDestinationDegrees, 69 | result.latitudeDestinationMinutes, 70 | result.latitudeDestinationSeconds, 71 | result.latitudeDestinationCardinalDirection, 72 | ], 73 | [ 74 | result.longitudeDestinationDegrees, 75 | result.longitudeDestinationMinutes, 76 | result.longitudeDestinationSeconds, 77 | result.longitudeDestinationCardinalDirection, 78 | 79 | ] 80 | ], 81 | result.quantity 82 | ]; 83 | } 84 | 85 | 86 | const loadWeb3 = async () => { 87 | if (window.ethereum) { 88 | window.web3 = new Web3(window.ethereum) 89 | await window.ethereum.enable() 90 | } 91 | else if (window.web3) { 92 | window.web3 = new Web3(window.web3.currentProvider) 93 | } 94 | else { 95 | window.alert('Non-Ethereum browser detected. You should consider trying MetaMask!') 96 | } 97 | } 98 | 99 | // const { driver, serialNumber, origin, destination, quantity } = user; 100 | const onInputChange = (e) => { 101 | setUser({ ...user, [e.target.name]: e.target.value }); 102 | }; 103 | 104 | const onSubmit = async (e) => { 105 | e.preventDefault(); 106 | let data = cleanData(user); 107 | 108 | window.web3.currentProvider.enable(); 109 | let web3 = new Web3(window.web3.currentProvider); 110 | 111 | web3.eth.getAccounts().then((e) => { 112 | account = e[0]; 113 | console.log(`The account is ${account}`); 114 | let bolContract = new web3.eth.Contract(bolAbi.abi, bol.address, { from: account }); 115 | 116 | console.log(bolContract.defaultAccount); 117 | 118 | bolContract.methods.createBillOfLading(data, bol.tokenContract, 100).send(); 119 | 120 | }) 121 | 122 | 123 | 124 | // history.push('/'); 125 | }; 126 | 127 | return ( 128 |
129 |
130 |
131 |

BOL Contract Address

132 |
133 | setBol({ ...bol, [e.target.name]: e.target.value })} type="text" placeholder={"Enter an address"} /> 134 |
135 |

Token Contract Address

136 |
137 | setBol({ ...bol, [e.target.name]: e.target.value })} type="text" placeholder={"Token Contract Address"} /> 138 |
139 |
140 |
141 |
142 |
143 |

Add A BOL

144 |
onSubmit(e)}> 145 | {Object.keys(user).map((key, index) => ( 146 |
147 | 148 |
149 | ))} 150 | 151 |
152 |
153 |
154 |
155 | ); 156 | }; 157 | 158 | export default AddUser; 159 | -------------------------------------------------------------------------------- /WtxEthOil/db.json: -------------------------------------------------------------------------------- 1 | { 2 | "users": [ 3 | { 4 | "id": 1, 5 | "name": "Leanne Graham", 6 | "username": "Bret", 7 | "email": "Sincere@april.biz", 8 | "address": { 9 | "street": "Kulas Light", 10 | "suite": "Apt. 556", 11 | "city": "Gwenborough", 12 | "zipcode": "92998-3874", 13 | "geo": { 14 | "lat": "-37.3159", 15 | "lng": "81.1496" 16 | } 17 | }, 18 | "phone": "1-770-736-8031 x56442", 19 | "website": "hildegard.org", 20 | "company": { 21 | "name": "Romaguera-Crona", 22 | "catchPhrase": "Multi-layered client-server neural-net", 23 | "bs": "harness real-time e-markets" 24 | } 25 | }, 26 | { 27 | "id": 2, 28 | "name": "Ervin Howell", 29 | "username": "Antonette", 30 | "email": "Shanna@melissa.tv", 31 | "address": { 32 | "street": "Victor Plains", 33 | "suite": "Suite 879", 34 | "city": "Wisokyburgh", 35 | "zipcode": "90566-7771", 36 | "geo": { 37 | "lat": "-43.9509", 38 | "lng": "-34.4618" 39 | } 40 | }, 41 | "phone": "010-692-6593 x09125", 42 | "website": "anastasia.net", 43 | "company": { 44 | "name": "Deckow-Crist", 45 | "catchPhrase": "Proactive didactic contingency", 46 | "bs": "synergize scalable supply-chains" 47 | } 48 | }, 49 | { 50 | "id": 3, 51 | "name": "Clementine Bauch", 52 | "username": "Samantha", 53 | "email": "Nathan@yesenia.net", 54 | "address": { 55 | "street": "Douglas Extension", 56 | "suite": "Suite 847", 57 | "city": "McKenziehaven", 58 | "zipcode": "59590-4157", 59 | "geo": { 60 | "lat": "-68.6102", 61 | "lng": "-47.0653" 62 | } 63 | }, 64 | "phone": "1-463-123-4447", 65 | "website": "ramiro.info", 66 | "company": { 67 | "name": "Romaguera-Jacobson", 68 | "catchPhrase": "Face to face bifurcated interface", 69 | "bs": "e-enable strategic applications" 70 | } 71 | }, 72 | { 73 | "id": 4, 74 | "name": "Patricia Lebsack", 75 | "username": "Karianne", 76 | "email": "Julianne.OConner@kory.org", 77 | "address": { 78 | "street": "Hoeger Mall", 79 | "suite": "Apt. 692", 80 | "city": "South Elvis", 81 | "zipcode": "53919-4257", 82 | "geo": { 83 | "lat": "29.4572", 84 | "lng": "-164.2990" 85 | } 86 | }, 87 | "phone": "493-170-9623 x156", 88 | "website": "kale.biz", 89 | "company": { 90 | "name": "Robel-Corkery", 91 | "catchPhrase": "Multi-tiered zero tolerance productivity", 92 | "bs": "transition cutting-edge web services" 93 | } 94 | }, 95 | { 96 | "id": 5, 97 | "name": "Chelsey Dietrich", 98 | "username": "Kamren", 99 | "email": "Lucio_Hettinger@annie.ca", 100 | "address": { 101 | "street": "Skiles Walks", 102 | "suite": "Suite 351", 103 | "city": "Roscoeview", 104 | "zipcode": "33263", 105 | "geo": { 106 | "lat": "-31.8129", 107 | "lng": "62.5342" 108 | } 109 | }, 110 | "phone": "(254)954-1289", 111 | "website": "demarco.info", 112 | "company": { 113 | "name": "Keebler LLC", 114 | "catchPhrase": "User-centric fault-tolerant solution", 115 | "bs": "revolutionize end-to-end systems" 116 | } 117 | }, 118 | { 119 | "id": 6, 120 | "name": "Mrs. Dennis Schulist", 121 | "username": "Leopoldo_Corkery", 122 | "email": "Karley_Dach@jasper.info", 123 | "address": { 124 | "street": "Norberto Crossing", 125 | "suite": "Apt. 950", 126 | "city": "South Christy", 127 | "zipcode": "23505-1337", 128 | "geo": { 129 | "lat": "-71.4197", 130 | "lng": "71.7478" 131 | } 132 | }, 133 | "phone": "1-477-935-8478 x6430", 134 | "website": "ola.org", 135 | "company": { 136 | "name": "Considine-Lockman", 137 | "catchPhrase": "Synchronised bottom-line interface", 138 | "bs": "e-enable innovative applications" 139 | } 140 | }, 141 | { 142 | "id": 7, 143 | "name": "Kurtis Weissnat", 144 | "username": "Elwyn.Skiles", 145 | "email": "Telly.Hoeger@billy.biz", 146 | "address": { 147 | "street": "Rex Trail", 148 | "suite": "Suite 280", 149 | "city": "Howemouth", 150 | "zipcode": "58804-1099", 151 | "geo": { 152 | "lat": "24.8918", 153 | "lng": "21.8984" 154 | } 155 | }, 156 | "phone": "210.067.6132", 157 | "website": "elvis.io", 158 | "company": { 159 | "name": "Johns Group", 160 | "catchPhrase": "Configurable multimedia task-force", 161 | "bs": "generate enterprise e-tailers" 162 | } 163 | }, 164 | { 165 | "id": 8, 166 | "name": "Nicholas Runolfsdottir V", 167 | "username": "Maxime_Nienow", 168 | "email": "Sherwood@rosamond.me", 169 | "address": { 170 | "street": "Ellsworth Summit", 171 | "suite": "Suite 729", 172 | "city": "Aliyaview", 173 | "zipcode": "45169", 174 | "geo": { 175 | "lat": "-14.3990", 176 | "lng": "-120.7677" 177 | } 178 | }, 179 | "phone": "586.493.6943 x140", 180 | "website": "jacynthe.com", 181 | "company": { 182 | "name": "Abernathy Group", 183 | "catchPhrase": "Implemented secondary concept", 184 | "bs": "e-enable extensible e-tailers" 185 | } 186 | }, 187 | { 188 | "id": 9, 189 | "name": "Glenna Reichert", 190 | "username": "Delphine", 191 | "email": "Chaim_McDermott@dana.io", 192 | "address": { 193 | "street": "Dayna Park", 194 | "suite": "Suite 449", 195 | "city": "Bartholomebury", 196 | "zipcode": "76495-3109", 197 | "geo": { 198 | "lat": "24.6463", 199 | "lng": "-168.8889" 200 | } 201 | }, 202 | "phone": "(775)976-6794 x41206", 203 | "website": "conrad.com", 204 | "company": { 205 | "name": "Yost and Sons", 206 | "catchPhrase": "Switchable contextually-based project", 207 | "bs": "aggregate real-time technologies" 208 | } 209 | }, 210 | { 211 | "id": 10, 212 | "name": "Clementina DuBuque", 213 | "username": "Moriah.Stanton", 214 | "email": "Rey.Padberg@karina.biz", 215 | "address": { 216 | "street": "Kattie Turnpike", 217 | "suite": "Suite 198", 218 | "city": "Lebsackbury", 219 | "zipcode": "31428-2261", 220 | "geo": { 221 | "lat": "-38.2386", 222 | "lng": "57.2232" 223 | } 224 | }, 225 | "phone": "024-648-3804", 226 | "website": "ambrose.net", 227 | "company": { 228 | "name": "Hoeger LLC", 229 | "catchPhrase": "Centralized empowering task-force", 230 | "bs": "target end-to-end models" 231 | } 232 | }, 233 | { 234 | "name": "Ashwani", 235 | "username": "ashwani65", 236 | "email": "ashwanisinghlpu66@gmail.com", 237 | "phone": "6262728300", 238 | "website": "www.ashwani.com", 239 | "id": 11 240 | } 241 | ] 242 | } -------------------------------------------------------------------------------- /backend/contracts/interfaces/ERC998/IERC998ERC721TopDown.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.5; 4 | 5 | import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; 6 | 7 | /// @title ERC998ERC721 Top-Down Composable Non-Fungible Token 8 | /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-998.md 9 | /// Note: the ERC-165 identifier for this interface is 0x1efdf36a 10 | interface IERC998ERC721TopDown is IERC721 { 11 | /// @dev This emits when a token receives a child token. 12 | /// @param _from The prior owner of the token. 13 | /// @param _toTokenId The token that receives the child token. 14 | event ReceivedChild( 15 | address indexed _from, 16 | uint256 indexed _toTokenId, 17 | address indexed _childContract, 18 | uint256 _childTokenId 19 | ); 20 | 21 | /// @dev This emits when a child token is transferred from a token to an address. 22 | /// @param _fromTokenId The parent token that the child token is being transferred from. 23 | /// @param _to The new owner address of the child token. 24 | event TransferChild( 25 | uint256 indexed _fromTokenId, 26 | address indexed _to, 27 | address indexed _childContract, 28 | uint256 _childTokenId 29 | ); 30 | 31 | /// @notice Get the root owner of tokenId. 32 | /// @param _tokenId The token to query for a root owner address 33 | /// @return rootOwner The root owner at the top of tree of tokens and ERC998 magic value. 34 | function rootOwnerOf(uint256 _tokenId) 35 | external 36 | view 37 | returns (bytes32 rootOwner); 38 | 39 | /// @notice Get the root owner of a child token. 40 | /// @param _childContract The contract address of the child token. 41 | /// @param _childTokenId The tokenId of the child. 42 | /// @return rootOwner The root owner at the top of tree of tokens and ERC998 magic value. 43 | function rootOwnerOfChild(address _childContract, uint256 _childTokenId) 44 | external 45 | view 46 | returns (bytes32 rootOwner); 47 | 48 | /// @notice Get the parent tokenId of a child token. 49 | /// @param _childContract The contract address of the child token. 50 | /// @param _childTokenId The tokenId of the child. 51 | /// @return parentTokenOwner The parent address of the parent token and ERC998 magic value 52 | /// @return parentTokenId The parent tokenId of _tokenId 53 | function ownerOfChild(address _childContract, uint256 _childTokenId) 54 | external 55 | view 56 | returns (bytes32 parentTokenOwner, uint256 parentTokenId); 57 | 58 | /// @notice A token receives a child token 59 | /// @param _operator The address that caused the transfer. 60 | /// @param _from The owner of the child token. 61 | /// @param _childTokenId The token that is being transferred to the parent. 62 | /// @param _data Up to the first 32 bytes contains an integer which is the receiving parent tokenId. 63 | function onERC721Received( 64 | address _operator, 65 | address _from, 66 | uint256 _childTokenId, 67 | bytes calldata _data 68 | ) external returns (bytes4); 69 | 70 | /// @notice Transfer child token from top-down composable to address. 71 | /// @param _fromTokenId The owning token to transfer from. 72 | /// @param _to The address that receives the child token 73 | /// @param _childContract The ERC721 contract of the child token. 74 | /// @param _childTokenId The tokenId of the token that is being transferred. 75 | function transferChild( 76 | uint256 _fromTokenId, 77 | address _to, 78 | address _childContract, 79 | uint256 _childTokenId 80 | ) external; 81 | 82 | /// @notice Transfer child token from top-down composable to address. 83 | /// @param _fromTokenId The owning token to transfer from. 84 | /// @param _to The address that receives the child token 85 | /// @param _childContract The ERC721 contract of the child token. 86 | /// @param _childTokenId The tokenId of the token that is being transferred. 87 | function safeTransferChild( 88 | uint256 _fromTokenId, 89 | address _to, 90 | address _childContract, 91 | uint256 _childTokenId 92 | ) external; 93 | 94 | /// @notice Transfer child token from top-down composable to address. 95 | /// @param _fromTokenId The owning token to transfer from. 96 | /// @param _to The address that receives the child token 97 | /// @param _childContract The ERC721 contract of the child token. 98 | /// @param _childTokenId The tokenId of the token that is being transferred. 99 | /// @param _data Additional data with no specified format 100 | function safeTransferChild( 101 | uint256 _fromTokenId, 102 | address _to, 103 | address _childContract, 104 | uint256 _childTokenId, 105 | bytes calldata _data 106 | ) external; 107 | 108 | /// @notice Transfer bottom-up composable child token from top-down composable to other ERC721 token. 109 | /// @param _fromTokenId The owning token to transfer from. 110 | /// @param _toContract The ERC721 contract of the receiving token 111 | /// @param _toTokenId The receiving token 112 | /// @param _childContract The bottom-up composable contract of the child token. 113 | /// @param _childTokenId The token that is being transferred. 114 | /// @param _data Additional data with no specified format 115 | function transferChildToParent( 116 | uint256 _fromTokenId, 117 | address _toContract, 118 | uint256 _toTokenId, 119 | address _childContract, 120 | uint256 _childTokenId, 121 | bytes calldata _data 122 | ) external; 123 | 124 | /// @notice Get a child token from an ERC721 contract. 125 | /// @param _from The address that owns the child token. 126 | /// @param _tokenId The token that becomes the parent owner 127 | /// @param _childContract The ERC721 contract of the child token 128 | /// @param _childTokenId The tokenId of the child token 129 | function getChild( 130 | address _from, 131 | uint256 _tokenId, 132 | address _childContract, 133 | uint256 _childTokenId 134 | ) external; 135 | } 136 | 137 | /// @dev The ERC-165 identifier for this interface is 0xa344afe4 138 | interface ERC998ERC721TopDownEnumerable { 139 | /// @notice Get the total number of child contracts with tokens that are owned by tokenId. 140 | /// @param _tokenId The parent token of child tokens in child contracts 141 | /// @return uint256 The total number of child contracts with tokens owned by tokenId. 142 | function totalChildContracts(uint256 _tokenId) 143 | external 144 | view 145 | returns (uint256); 146 | 147 | /// @notice Get child contract by tokenId and index 148 | /// @param _tokenId The parent token of child tokens in child contract 149 | /// @param _index The index position of the child contract 150 | /// @return childContract The contract found at the tokenId and index. 151 | function childContractByIndex(uint256 _tokenId, uint256 _index) 152 | external 153 | view 154 | returns (address childContract); 155 | 156 | /// @notice Get the total number of child tokens owned by tokenId that exist in a child contract. 157 | /// @param _tokenId The parent token of child tokens 158 | /// @param _childContract The child contract containing the child tokens 159 | /// @return uint256 The total number of child tokens found in child contract that are owned by tokenId. 160 | function totalChildTokens(uint256 _tokenId, address _childContract) 161 | external 162 | view 163 | returns (uint256); 164 | 165 | /// @notice Get child token owned by tokenId, in child contract, at index position 166 | /// @param _tokenId The parent token of the child token 167 | /// @param _childContract The child contract of the child token 168 | /// @param _index The index position of the child token. 169 | /// @return childTokenId The child tokenId for the parent token, child token and index 170 | function childTokenByIndex( 171 | uint256 _tokenId, 172 | address _childContract, 173 | uint256 _index 174 | ) external view returns (uint256 childTokenId); 175 | } 176 | -------------------------------------------------------------------------------- /backend/contracts/dBol.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.7.5; 3 | 4 | import "@openzeppelin/contracts/presets/ERC721PresetMinterPauserAutoId.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelin/contracts/utils/EnumerableSet.sol"; 7 | import "@openzeppelin/contracts/math/SafeMath.sol"; 8 | import "./interfaces/ERC998/IERC998ERC20TopDown.sol"; 9 | import "./ERC998/ERC998ERC721BottomUp.sol"; 10 | import "@openzeppelin/contracts/utils/Address.sol"; 11 | 12 | contract DBol is 13 | ERC721PresetMinterPauserAutoId, 14 | ERC998ERC721BottomUp, 15 | IERC998ERC20TopDown 16 | { 17 | using SafeMath for uint256; 18 | using EnumerableSet for EnumerableSet.AddressSet; 19 | 20 | /// @dev Keeps track of all the tokens which a tokenId owns 21 | mapping(uint256 => EnumerableSet.AddressSet) erc20Contracts; 22 | 23 | /// @dev index of a contract inside of erc20Contracts set 24 | mapping(uint256 => mapping(address => uint256)) erc20ContractIndex; 25 | 26 | /// @dev tokenId balance for contract 27 | mapping(uint256 => mapping(address => uint256)) erc20Balances; 28 | 29 | constructor( 30 | string memory name_, 31 | string memory symbol_, 32 | string memory baseURI_ 33 | ) public ERC721PresetMinterPauserAutoId(name_, symbol_, baseURI_) {} 34 | 35 | /// @dev Wrapper around the ERC721 mint function to create a new token 36 | /// @dev this function will only work if the msg.sender has the minter role 37 | /// @dev must call grantRole first to assign permission 38 | function createDBol(uint256 _parentTokenId) public returns (uint256) { 39 | require(Address.isContract(msg.sender)); 40 | // mint a ERC721 token 41 | mint(msg.sender); 42 | tokenIdToTokenOwner[totalSupply() - 1] = TokenOwner( 43 | msg.sender, 44 | _parentTokenId 45 | ); 46 | return totalSupply() - 1; 47 | } 48 | 49 | ////////////////////// Iplementation of ERC998ERC20TopDown.sol below /////////////////// 50 | 51 | /// @notice A token receives ERC20 tokens 52 | /// @param _from The prior owner of the ERC20 tokens 53 | /// @param _value The number of ERC20 tokens received 54 | /// @param _data Up to the first 32 bytes contains an integer which is the receiving tokenId. 55 | function tokenFallback( 56 | address _from, 57 | uint256 _value, 58 | bytes calldata _data 59 | ) external override { 60 | require(Address.isContract(msg.sender)); 61 | require(_data.length > 0, "must contain uint256 tokenId"); 62 | uint256 tokenId; 63 | // Already prety succinct so keeping the assembly code 64 | assembly { 65 | tokenId := calldataload(132) 66 | } 67 | if (_data.length < 32) { 68 | tokenId = tokenId >> (256 - _data.length * 8); 69 | } 70 | 71 | // if the token doesn't already have this contract in it's set 72 | if (!erc20Contracts[tokenId].contains(msg.sender)) { 73 | erc20Contracts[tokenId].add(msg.sender); 74 | erc20ContractIndex[tokenId][msg.sender] = erc20Contracts[tokenId] 75 | .length(); 76 | } 77 | // update the balance 78 | erc20Balances[tokenId][msg.sender] = erc20Balances[tokenId][msg.sender] 79 | .add(_value); 80 | 81 | ReceivedERC20(_from, tokenId, msg.sender, _value); 82 | } 83 | 84 | /// @notice Look up the balance of ERC20 tokens for a specific token and ERC20 contract 85 | /// @param _tokenId The token that owns the ERC20 tokens 86 | /// @param _erc20Contract The ERC20 contract 87 | /// @return The number of ERC20 tokens owned by a token from an ERC20 contract 88 | function _balanceOfERC20(uint256 _tokenId, address _erc20Contract) 89 | internal 90 | view 91 | returns (uint256) 92 | { 93 | return erc20Balances[_tokenId][_erc20Contract]; 94 | } 95 | 96 | /// @notice Look up the balance of ERC20 tokens for a specific token and ERC20 contract 97 | /// @param _tokenId The token that owns the ERC20 tokens 98 | /// @param _erc20Contract The ERC20 contract 99 | /// @return The number of ERC20 tokens owned by a token from an ERC20 contract 100 | function balanceOfERC20(uint256 _tokenId, address _erc20Contract) 101 | external 102 | view 103 | override 104 | returns (uint256) 105 | { 106 | return _balanceOfERC20(_tokenId, _erc20Contract); 107 | } 108 | 109 | ///////////////////////////////////////////////////////////////////////////////////////////// 110 | 111 | /// @notice Transfer ERC20 tokens to address 112 | /// @param _tokenId The token to transfer from 113 | /// @param _to The address to send the ERC20 tokens to 114 | /// @param _erc20Contract The ERC20 contract 115 | /// @param _value The number of ERC20 tokens to transfer 116 | function transferERC20( 117 | uint256 _tokenId, 118 | address _to, 119 | address _erc20Contract, 120 | uint256 _value 121 | ) external override { 122 | require(_to != address(0)); // can't burn the tokens 123 | address rootOwner = 124 | address(uint256(_rootOwnerOf(_tokenId) & ADDRESS_MASK)); 125 | require( 126 | rootOwner == msg.sender || 127 | tokenOwnerToOperators[rootOwner][msg.sender] || 128 | rootOwnerAndTokenIdToApprovedAddress[rootOwner][_tokenId] == 129 | msg.sender 130 | ); // must have the right permissions 131 | require(erc20Balances[_tokenId][_erc20Contract] >= _value); // must have enough tokens 132 | // decrease the balance the _tokenId has of _erc20Contract by _value 133 | erc20Balances[_tokenId][_erc20Contract] = erc20Balances[_tokenId][ 134 | _erc20Contract 135 | ] 136 | .sub(_value); 137 | // if the balance of _erc20Contract is now 0 138 | if (erc20Balances[_tokenId][_erc20Contract] == 0) { 139 | // remove the contract from set of contracts 140 | erc20Contracts[_tokenId].remove(_erc20Contract); 141 | // delete the value held in the index mapping 142 | delete erc20ContractIndex[_tokenId][_erc20Contract]; 143 | } 144 | require( 145 | IERC20(_erc20Contract).transfer(_to, _value), 146 | "ERC20 transfer failed" 147 | ); 148 | emit TransferERC20(_tokenId, _to, _erc20Contract, _value); 149 | } 150 | 151 | /// @notice Transfer ERC20 tokens to address or ERC20 top-down composable 152 | /// @param _tokenId The token to transfer from 153 | /// @param _to The address to send the ERC20 tokens to 154 | /// @param _erc223Contract The ERC223 token contract 155 | /// @param _value The number of ERC20 tokens to transfer 156 | /// @param _data Additional data with no specified format, can be used to specify tokenId to transfer to 157 | function transferERC223( 158 | uint256 _tokenId, 159 | address _to, 160 | address _erc223Contract, 161 | uint256 _value, 162 | bytes calldata _data 163 | ) external override { 164 | require(_to != address(0)); // can't burn the tokens 165 | address rootOwner = 166 | address(uint256(_rootOwnerOf(_tokenId) & ADDRESS_MASK)); 167 | require( 168 | rootOwner == msg.sender || 169 | tokenOwnerToOperators[rootOwner][msg.sender] || 170 | rootOwnerAndTokenIdToApprovedAddress[rootOwner][_tokenId] == 171 | msg.sender 172 | ); // must have the right permissions 173 | require(erc20Balances[_tokenId][_erc223Contract] >= _value); // must have enough tokens 174 | // decrease the balance the _tokenId has of _erc20Contract by _value 175 | erc20Balances[_tokenId][_erc223Contract] = erc20Balances[_tokenId][ 176 | _erc223Contract 177 | ] 178 | .sub(_value); 179 | // if the balance of _erc20Contract is now 0 180 | if (erc20Balances[_tokenId][_erc223Contract] == 0) { 181 | // remove the contract from set of contracts 182 | erc20Contracts[_tokenId].remove(_erc223Contract); 183 | // delete the value held in the index mapping 184 | delete erc20ContractIndex[_tokenId][_erc223Contract]; 185 | } 186 | require( 187 | IERC20(_erc223Contract).transfer(_to, _value), 188 | "ERC20 transfer failed" 189 | ); 190 | emit TransferERC20(_tokenId, _to, _erc223Contract, _value); 191 | } 192 | 193 | /// @notice Get ERC20 tokens from ERC20 contract. 194 | /// @dev Needs to be approved first by the _erc20Contract 195 | /// @param _from The current owner address of the ERC20 tokens that are being transferred. 196 | /// @param _tokenId The token to transfer the ERC20 tokens to. 197 | /// @param _erc20Contract The ERC20 token contract 198 | /// @param _value The number of ERC20 tokens to transfer 199 | function getERC20( 200 | address _from, 201 | uint256 _tokenId, 202 | address _erc20Contract, 203 | uint256 _value 204 | ) external override { 205 | require( 206 | IERC20(_erc20Contract).allowance(_from, address(this)) >= _value 207 | ); 208 | require( 209 | IERC20(_erc20Contract).transferFrom(_from, address(this), _value) 210 | ); 211 | // if the token doesn't already have this contract in it's set 212 | if (!erc20Contracts[_tokenId].contains(_erc20Contract)) { 213 | erc20Contracts[_tokenId].add(_erc20Contract); 214 | erc20ContractIndex[_tokenId][_erc20Contract] = erc20Contracts[ 215 | _tokenId 216 | ] 217 | .length(); 218 | } 219 | // update the balance 220 | erc20Balances[_tokenId][_erc20Contract] = erc20Balances[_tokenId][ 221 | msg.sender 222 | ] 223 | .add(_value); 224 | ReceivedERC20(_from, _tokenId, _erc20Contract, _value); 225 | } 226 | 227 | ///////////////////////////////////////////////////////////////////////////////////////////// 228 | function _beforeTokenTransfer( 229 | address from, 230 | address to, 231 | uint256 tokenId 232 | ) internal override(ERC721PresetMinterPauserAutoId, ERC721) { 233 | super._beforeTokenTransfer(from, to, tokenId); 234 | require(to != address(0)); // disallow burning 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to EthGlobal-West Texas Oil 2 | 3 | ## Table of Contents 4 | 5 | 6 | 7 | - [Contributing to EthGlobal-West Texas Oil](#contributing-to-ethglobal-west-texas-oil) 8 | - [Table of Contents](#table-of-contents) 9 | - [About the Project](#about-the-project) 10 | - [Project Values](#project-values) 11 | - [Sprints](#sprints) 12 | - [Project team mates](#project-team-mates) 13 | - [How to Contribute](#how-to-contribute) 14 | - [Reporting Bugs](#reporting-bugs) 15 | - [Suggesting Features / Enhancements](#suggesting-features--enhancements) 16 | - [Picking up an Issue](#picking-up-an-issue) 17 | - [Contributing Code](#contributing-code) 18 | - [Staying Up to Date](#staying-up-to-date) 19 | - [Style Guides](#style-guides) 20 | - [Naming Git Branches](#naming-git-branches) 21 | - [Creating Git Commit Messages](#creating-git-commit-messages) 22 | - [Front End Style Guide](#front-end-style-guide) 23 | - [Getting Support](#getting-support) 24 | 25 | 26 | 27 | ## About the Project 28 | 29 | ❤️ By the community, for the community. ❤️ 30 | 31 | We are building an open-source curriculum to help the community learn and build on Ethereum and related technologies. 32 | 33 | Check out our [EthBuildersNYC](https://www.meetup.com/ethbuilders/) meetup. 34 | 35 | ### Project Values 36 | 37 | Our mission is to develop social good through technology. We value initiative, collaboration, experimentation, and integrity. 38 | 39 | We are a welcoming, warm, and collaborative group of people from all over the world! Here you can experiment and take chances. 40 | 41 | To FAIL is to make your **First Attempt In Learning**, and we are here to help. We encourage developers to learn new concepts and best practices while collaborating on a project that is scaling to help anyone learn and build on Ethereum. 42 | 43 | ### Sprints 44 | 45 | TBD. 46 | 47 | The typical agenda is: 48 | 49 | 1. The Team Lead will give an update 50 | 2. Discuss project roadmap 51 | 3. Discuss project deadlines 52 | 4. Discuss action items for current goals 53 | 5. Q&A Session 54 | 55 | If you have any questions or need any additional information, please discuss it with the team [on our slack.](http://bit.ly/NYC-Blockchain-Devs-Join-Slack) 56 | 57 | ### Project team mates 58 | 59 | TBD 60 | 61 | ## How to Contribute 62 | 63 | ### Reporting Bugs 64 | 65 | This section guides you through submitting a bug report for a EthGlobal-West Texas Oil Curriculum repository. Bugs are tracked as [Github Issues](https://guides.github.com/features/issues/) and when creating a new issue you will choose from a template that gives you guidelines on what information to provide. 66 | 67 | Following these guidelines helps maintainers and the community understand your report, reproduce the behavior, and find related reports (if applicable). 68 | 69 | **Before creating a bug report**, please check the list below as you might find out that you don't need to create one. 70 | 71 | - **Check the README** for a list of common questions, abstracts, concerns, etc. 72 | - **Search the ISSUES** to see if the problem has already been reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one. 73 | 74 | > **Note:** If you find a **Closed** issue that seems like it is the same thing that you are experiencing, **open a new issue** and include a link to the closed issue in the body of your new one. 75 | 76 | **When you are creating a bug report**, please include as many details as possible and fill out the required issue template. The information it asks for helps us resolve issues faster. Use the outline in the issue template to explain the problem and include additional details for maintainers. 77 | 78 | ### Suggesting Features / Enhancements 79 | 80 | This section guides you through submitting a feature request for the EthGlobal-West Texas Oil Curriculum, including completely new features, documents, lessons or minor improvements to existing functionality. Feature requests are tracked as [Github Issues](https://guides.github.com/features/issues/) and when creating a new issue you will choose from a template that gives you guidelines on what information to provide. 81 | 82 | Following these guidelines helps maintainers and the community understand your suggestion and find related suggestions. 83 | 84 | **Before creating a feature request**, please **search the ISSUES** to see if the feature has already been requested. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one. 85 | 86 | > **Note:** If you find a **Closed** issue that seems like it is the same thing that you are experiencing, **open a new issue** and include a link to the closed issue in the body of your new one. 87 | 88 | **When you are creating a feature request**, please include as many details as possible and fill out the required issue template. The information it asks for helps us resolve issues faster. Use the outline in the issue template to explain the feature and include additional details for maintainers. 89 | 90 | ### Picking up an Issue 91 | 92 | If you wish to contribute to an issue, look through the Issues tab of the related repository and find one labeled **Help Wanted.** These are issues that we are actively looking for participation in. 93 | 94 | To pick up an issue, add a comment saying "I'm on it", and the Help Wanted label will be removed and you will be assigned to the issue. 95 | 96 | There is a time limit to finish the ticket. If you are unresponsive, the ticket assigned to you can be picked up by another person. 97 | 98 | More TBD 99 | 100 | ### Contributing Code 101 | 102 | This section describes how our workflow operates so that you can contribute code in a way that scales with a growing team and product. 103 | 104 | This represents a standard way that many open source projects operate, and more information including terminology and helpful commands can be found in the [support.md](./support.md) file. 105 | 106 | > ⚠️ **Please do not clone directly from our main repository.** This will point your local repo's [origin](https://www.git-tower.com/learn/git/glossary/origin) to our main repo, and you will not have write access. Doing so will eventually cause an error when attempting to push your changes. 107 | 108 | **When you are first setting up the repository**, follow the steps below to ensure that your changes can be merged into the project. 109 | 110 | 1. [Fork the repository](https://help.github.com/en/github/getting-started-with-github/fork-a-repo), which creates a copy of the repository under your github account to submit changes to. 111 | 2. [Clone the forked repository](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository) to your computer using the **Clone or download** button and the address from github. 112 | e.g. `git clone https://github.com/YOUR_GITHUB_NAME/EthGlobal-West-Texas-Oil` 113 | 3. Change the working directory to your cloned project. 114 | Mac/Linux: `cd ./EthGlobal-West-Texas-Oil` 115 | Windows: `cd .\EthGlobal-West-Texas-Oil` 116 | 4. [Add a remote](https://help.github.com/en/github/using-git/adding-a-remote) link to our project repository in order to track changes. \ 117 | HTTPS: `git remote add upstream https://github.com/EthBuilders/EthGlobal-West-Texas-Oil.git` \ 118 | or \ 119 | SSH: `git remote add upstream git@github.com:EthBuilders/EthGlobal-West-Texas-Oil.git` 120 | 121 | > **Pro tip:** If you prefer to [use SSH on Github](https://help.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh), remember to update the links above to the SSH versions! 122 | 123 | 1. [Create and checkout new branch](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-branches) in your local project to start making changes. 124 | e.g. `git checkout -b ` 125 | _Note: the `-b` is only required when first creating the branch (combines `git checkout` and `git branch`)_ 126 | 2. If working on the section specifically, review the documentation before adding any changes you intend to make. 127 | 3. Make changes in your local project, including adding files or updating existing code. 128 | 4. Add the changed files and create a commit message describing the changes. 129 | e.g. `git add README.md` or `git add -A` for all changed files 130 | e.g. `git commit -m "update table of contents in readme"` 131 | _Note: more information on [writing a good commit message](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) can be found in the linked article_ 132 | 5. Continue with 6 and 7 above until all changes are complete. 133 | 134 | **When the changes are complete and you are ready to submit them to our project**, please follow the additional steps below. 135 | 136 | 10. Check that all of your submissions follow the relevant [Style Guides](#style-guides). 137 | 11. Push the changes to the origin repository on Github (your fork). 138 | `git push -u origin ` 139 | _Note: the `-u` is only required when first pushing a new branch to the origin (your fork)._ 140 | 12. On github, [submit the pull request (PR)](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests) to the upstream repository when changes are complete. 141 | 13. Follow all of the instructions in the [Pull Request Template - TBD](). 142 | 143 | ### Staying Up to Date 144 | 145 | **It is important to always work from the most up to date code in our repository.** Before making any changes, always be sure that your local repository contains the most recent changes from the upstream repository (our project). 146 | 147 | To update your local repository, follow the steps below. 148 | 149 | 1. Check what branch you are currently working on. 150 | `git status` 151 | 2. Check out the master branch of your local repository. 152 | `git checkout master` 153 | 3. Fetch changes from the upstream master repository (ours). 154 | `git fetch upstream master` 155 | 4. Rebase the changes from the upstream master respository into your local repository. 156 | `git rebase upstream/master` 157 | 5. Check out a new branch to make and submit changes, following the procedures in [Contributing Code](#contributing-code) starting from Step 5. 158 | 159 | ## Style Guides 160 | 161 | 📝 TBD 162 | 163 | ### Naming Git Branches 164 | 165 | **Use the branch naming structure of "verb/intent"**, and examples are listed below. This helps make branch names easier to read, understand, and follow for the project maintainers. 166 | 167 | ``` 168 | update/resource-list 169 | feat/omni-auth 170 | fix/auth-flow 171 | ``` 172 | 173 | ### Creating Git Commit Messages 174 | 175 | **Keep git commit messages short, simple, and informative**, ideally under 50 characters. 176 | 177 | **Write commit messages in the imperative**, using "fix bug" and not "fixed bug" or "fixes bug". This convention matches up with commit messages generated by commands like `git merge` and `git revert`. 178 | 179 | `git commit -m "update contributing.md with new sections"` 180 | 181 | ### Front End Style Guide 182 | 183 | 📝 TBD 184 | 185 | ## Getting Support 186 | 187 | **We are here to help!** If you have questions or run into issues, please reach out to us on [our slack](http://bit.ly/NYC-Blockchain-Devs-Join-Slack). Please join #ethglobal-marketmake. 188 | 189 | We also have some [support documentation](SUPPORT.md) on common commands, common terminology, the general workflow, and a list of useful resources covering each topic above. 190 | 191 | _If you made it to the end of this document, thank you, and we look forward to working with you and seeing your submissions!_ 192 | -------------------------------------------------------------------------------- /docs/minutes/2021-01-18.md: -------------------------------------------------------------------------------- 1 | # Design statement 2 | 3 | > How might we improve *(problem in the domain)* for *(user)*, so that *(user-focused outcome)*? 4 | 5 | How would we solve the crude oil theft by single owner 18 wheeler truck drivers by reconciling Bill of Lading between (mid-stream suppliers) to end-stream refiners from Midland, Texas to Houston, Texas, so that (mid-stream suppliers reduce their losses). 6 | 7 | **Goal is avoid scope creep.** 8 | 9 | user: mid-stream supplier 10 | 11 | Line up incentives: 12 | 13 | Core Ideas: 14 | - Reduce the moral hazard of the truckers using the honor system to steal oil 15 | - Before: trucker user honor system, losses are higher 16 | - After: its all documented, reduced scope to cheat, less losses 17 | - use system to track fraud by reconciling BOL/reports faster 18 | - (incentives for Midstream) 19 | - Able the ability to pinpoint issues in closer real time. 20 | - Faster we can track down bad batch, faster we can stop losses 21 | - save time, save money, with more info. 22 | - Mid stream: 23 | - Tracking system to see: 24 | - driver performance 25 | - BOL status 26 | - container status 27 | - bad batch is coming from somewhere 28 | - its either container, 29 | - the driver, 30 | - or the upstream supplier with bad. 31 | - Driver: incentivize drivers to use system 32 | - Get paid fast 33 | - Low fees 34 | - Interest 35 | - Borrow if unexpected expenses 36 | - End Stream: refinery people 37 | - same incentive as mid-supplier 38 | - They want to get the full amount of oil 39 | - More materials > more product > more money 40 | - Need all the oil to get all the money 41 | - Enabled by better, faster, reliable documentation sources 42 | 43 | **Question:** 44 | How do mid-stream suppliers user our service? 45 | How do we make MONEY? 46 | How to capture? 47 | 48 | How much money does midstream supplier make? 49 | Driver gets about $115/hr 50 | 51 | **Value capture:** 52 | Figure out mid stream supplier -> end stream supplier economics 53 | Little tx over time 54 | Take Deposit fee percentage per Trip of something: 1% 55 | 1% of cost of the barrels. 56 | 57 | **Our BackLog** 58 | **Esstentials:** 59 | Back end (ETH): 60 | 61 | 1. Set up Matic: 62 | 1. Reduce gas fees to make this viable 63 | 2. Set up Biconomy (free gas via relay station) - reason no gas fees for users 64 | 1. Mid stream pays gas fees up front. 65 | 1. Who pays? 66 | 2. Off interest that is accured 67 | 2. trucker don't pay gas feees to incentivize use. 68 | 3. Crypto to Fiat integration 69 | 1. Reason: trucker want to get paid in cash - USD 70 | 4. Research crypto to fiat providers 71 | 1. Moonpay 72 | 2. Sendwyre 73 | 3. CarbonMoney 74 | 4. Transak 75 | 5. Check out Celo. 76 | 1. Pro: Also mobile 77 | 5. PWA 78 | 1. Avoid making a mobile app 79 | 2. Works offline and stores data 80 | 3. Figure out how to do it. 81 | 6. Wallet integration: 82 | 1. There is this thing that lets you choose wallets 83 | 1. Use that 84 | 2. Support Portis, MetaMask Mobile 85 | 1. Stretch goal: others Fortmatic, others. 86 | 7. ENS 87 | 1. Reason: Makes it easy to send to transfer/interact for everyone 88 | 2. Sub ticket: 89 | 1. research how many we need for contracts 90 | 1. Anthony can pay for it 91 | 2. Make sure ens domain is more than 4 characters - ~$5 92 | 8. UX/UI - address 93 | 1. Names shown not account - gets around having to setup ENS for driver 94 | 2. When clicks, show account 95 | 3. when clicked, copies account address 96 | 9. UX/UI - intersts models 97 | 1. Red button on factor. Last case scenerio 98 | 2. Green button on deposit for 90 days 99 | 10. Driver NFT 100 | 1. User OpenZeppelin ERC-721 contract 101 | 2. Use OpenZeppelin's ERC-721 contract 102 | 3. Don't roll out your own contract 103 | 4. Don't copy and paste ERC-721 contract 104 | 1. Install via NPM and use via import in Solidit 105 | 3. SPEC: 106 | 1. Array of BOLs NFTs contracts address 107 | 2. Timeliness (speed of delivery) 108 | 1. (only operate so many hours in the day) 109 | 3. Employer 110 | 1. Length of employment 111 | 4. Inspections 112 | 1. Level (1,2,3) inspections (DOT inspection) 113 | 4. Weighted Average Delta between oil onloaded and offloaded (Theft tracking) for truck 114 | 5. Location Data (?) 115 | 1. Location data to track where the oil is en route and see where oil is getting stolen(?) 116 | 6. Linked with ratio to see where stealing is happening (?) 117 | 11. UX/UI - BOL 118 | 1. Whats left in BOL? 119 | 12. create factory contract for BOL NFTs 120 | 1. Design pattern: 121 | 1. Factory contract creates BOLs 122 | 2. Each BOL is an NFT 123 | 3. Re: factory: Ask @Chan-Ho, @EthAmal, @EthanWessel, google online 124 | 13. Create BOL NFT - (Edward Amor) 125 | 1. Reason: BOL used to reconcile issues 126 | 2. Sub issue: research ERC-998 127 | 1. If not, use ERC-721 128 | 1. Create BOL and dBOL at the same time 129 | 1. Have dBOL earn interest for driver 130 | 2. dBOL is driver's work into BOL they own 131 | 3. Use Aavagacchi's ERC-998 contract 132 | 1. GIVE WARNING IN PROJECT. 133 | 2. OpenZeppelin doesn't have it yet 134 | 3. If you can find it online: add it via npm with it. 135 | 1. Don't roll out your own contract 136 | 2. Don't copy and paste ERC-998 contract 137 | 3. Install via NPM and use via import in Solidity 138 | 4. Reason: Time and security 139 | 4. Exception: 140 | 1. Trade off: complexity with two 721 contracts vs one 998 contract 141 | 2. We can copy aavagachi's ERC-998 contract. 142 | 4. Composible NFTS - ERC-998 143 | 1. ERC-998 - NFTs that own other NFTs 144 | 1. We crack open the BOL 145 | 2. Give root dBOL to driver 146 | 3. Give shell BOL to offloading specialist at end Stream 147 | 2. dBOL spec 148 | 1. How much they will get paid 149 | 2. Link to BOL 150 | 3. Funds: 151 | 1. USDC, DAI, USDT 152 | 5. Security 153 | 1. Look more into Ownable.sol if it applies 154 | 6. SPEC: 155 | 1. Struct: 156 | 1. Time 157 | 2. Date 158 | 3. Location of where Oil was taken 159 | 4. Name of driver (when superivisor meets them) 160 | 5. Truck number 161 | 6. Trailer number 162 | 7. Place where you are transporting oil to 163 | 8. Customer name 164 | 9. Reciving party 165 | 10. Market worth of Crud Oil 166 | 11. Number of barrels taken 167 | 12. link to dBOL 168 | 13. Tag with Serial number of Oil Container 169 | 1. Truck Company name taking oil (generated before meeting driver) 170 | 2. Specific amount of oil to take 171 | 3. NOTE: Each tag goes to specific BOL 172 | 4. NOTE: Serial number is in a computer, generated before meeting driver. 173 | 5. NOTE: Serial number is UNIQUE id- KEY for database(?) 174 | 14. How to create dBOL? 175 | 1. dBOL will earn money on the way to end delivery 176 | 1. earn interst while you do the work 177 | 2. dBOl is what gets collatoralized? 178 | 1. factory function for dBOL 179 | 1. Take BOL looks at struct properties 180 | 1. Spits out dBOL 181 | 15. State transition of BOL for UX/UI 182 | 1. **Reason:** track BOL to prevent theft 183 | 2. **Reason:** focus on supplier. 184 | 3. BOL flow 185 | 1. State 0 (generation of Serial Number for BOL): 186 | 1. Supervisor creates Serial Number 187 | 2. Unique BOL has unique Serial Number 188 | 1. Thats how we track it. 189 | 2. Our serial number is our KEY for "database" 190 | 3. Each Serial number is unique to each for Tank Faucetper job. 191 | 3. Supervisor tells company to get oil X days before. 192 | 2. State 1 (Creation of BOL): 193 | 1. Company will tell driver to pick up oil 194 | 2. Add tag number to BOL 195 | 3. State 2 (Driving) 196 | 1. Update state by driver 197 | 1. Flat tire, inspection, breaks by state law, any issues 198 | 4. State 3 (End of Delivery) 199 | 1. Similar to State 0 200 | 2. Driver meets Offloading specialist 201 | 1. Transition of custody: 202 | 1. Both update state 203 | 1. Add info to BOL: 204 | 1. Time of delivery 205 | 2. Date of delivery 206 | 3. Driver signs 207 | 4. Offloading specialist signs 208 | 1. Offloading specilist ONLY signs when everything is good. 209 | 2. Offloading specilist has skin in the game. 210 | 3. If oil is missing beyond negilible amount, offloading specialist makes a call. 211 | 5. Missing oil: 212 | 1. IF ANY IS MISSING then Offloading specialist writes up in BOL: 213 | 2. How much is missing (% of oil missing) 214 | 3. Who they called (supevisor) 215 | 4. Think about how to annotate in efficent manner. 216 | 5. Note ANY shenanigans 217 | 1. NOTE: use The Graph Protocol to query data and later create profiles of drivers. 218 | 6. Note: 219 | 1. No Serial number needed at End Delivery 220 | 2. Job is closed 221 | 2. Offloading specialist tells you where to drop of product 222 | 1. Its monitored and documented 223 | 3. Where are shenanagins? 224 | 5. State 4 (Get Paid) 225 | 1. BOL transformation 226 | 1. BOL is given to End Deivery Site 227 | 2. BOL generates dBOL for driver's company 228 | 1. BOL -> dBOL 229 | 2. Now dBOL becomes collateralized work the driver did. 230 | 3. dBOL = driver work, dBOL. 231 | 4. BOL = Crude Oil 232 | 5. Driver owns their work, not the oil 233 | 6. Once delivered the driver gets the stub, the dBOL 234 | 7. The dBOL goes into Aave features. 235 | 2. Aave incentives we layed out 236 | 16. Create Withdrawl function - Aave 237 | 1. Parameters: NOW, Later(at end of 90 days), Any time in-between 238 | 1. If now-> factor model. 239 | 2. If at end of 90 days -> accure interest 240 | 3. If in between -> interest pro-rated 241 | 17. Create Deposit BOL function: - Aave 242 | 1. Granular Deposit of BOLs 243 | 2. Parameters: All or portion of BOLs. 244 | 1. Boundaries: 0 to Max 245 | 2. Increments: +1. 246 | 1. Has to be whole number, not fractional. 247 | 2. Meaning truckers deposits 4/4 or 2/4. Not 2.5/4 BOL. 248 | Research and create Factor model with Aave 249 | 1. When you withdrawl immediately 250 | 2. Implement function 251 | 18. Interest model 252 | 1. When you withdrawl from n+1, 1 block after delivery to n+max (90 days) 253 | 1. Since you get interest on every block 254 | 2. Max block is ~90 days (Settlement Date) from delivery. 255 | 256 | Front End: 257 | 1. Set up Design system dependencies 258 | 1. Rimble 259 | 2. Bootstarp 260 | 3. Use npm not yarn 261 | 1. Make sure to delete yarn lock if there 262 | 2. Create container for sign-in page: 263 | 1. Create logo: 264 | 1. Worst case: 🛢️ > 🚚 > 🏭 265 | 2. If not create better one 266 | 1. Two hour max 267 | 2. Log in component 268 | 1. See easyfi.network as an example 269 | 2. Need Google Auth + Portis + MetaMask integration 270 | 3. Use/Look at Rimble design system 271 | 3. Create NavBar 272 | 1. Use/Look at Rimble design system 273 | 1. If Rimble has it, use it 274 | 2. If not use Bootstrap 275 | 4. Component for displaying address 276 | 1. First look at ENS domain name 277 | 1. If note display address 278 | 2. Use Rimble 279 | 1. If note create with Bootstrap 280 | 5. List component 281 | 1. That can hold BOLs, dBOLs 282 | 2. Use Rimble 283 | 1. If note create with Bootstrap 284 | 6. Create Item component for list 285 | 1. Summerizes BOL 286 | 1. TO 287 | 2. From 288 | 3. Amount 289 | 4. dBOL specific 290 | 1. Cash payout to driver (?) 291 | 5. BOL specific 292 | 1. Oil worth 293 | 7. BOL component 294 | 1. Own seperate page for BOL 295 | 2. Similar to item component in design 296 | 3. Use card component (?) from rimble or bootstrap 297 | 8. Form component - To create BOL 298 | 1. If you can DRY up (don't repeat yourself with code, do so) 299 | 2. Use Rimble or Bootstrap components 300 | 9. Form Component - to update BOL 301 | 1. Supervisor updates BOL 302 | 2. View for Driver to update BOL? 303 | 1. Same component different instance. 304 | 1. Calls different methods on chain 305 | 10. From component - transfer from driver to offloading specialist 306 | 1. this is where offloading specialist signs off 307 | 2. this is where offloading specialist adds any issues 308 | 1. extend form with space to write in issues. 309 | 11. table component 310 | 1. re-used for driver 311 | 1. list BOLs upcoming 312 | 1. re-use list component 313 | 2. list dBOL - completed 314 | 1. re-use list component 315 | 2. Driver dBOLs 316 | 1. Deposit 317 | 1. Deposit dBOL which has DAI, USDC, USDT 318 | 2. Withdrawl 319 | 1. get interest back accured so far 320 | 3. re-used for Mid stream suppliers for dashboard 321 | 1. all BOLS out with 322 | 1. driver info, 323 | 2. destination 324 | 3. Date 325 | 4. Time left 326 | 5. Time Arrived: DATE/ IN PROGESS 327 | 12. Profile pic 328 | 1. Use Rimble based component 329 | 13. Update Figma with state transition sketch 330 | 1. Use Edward's Sketches with state transtions 331 | 2. That becomes the updated wireframe 332 | 3. Take old sketch, new figma and state transition info 333 | 1. create new wire frame 334 | 14. Portal for mid stream provider 335 | 1. How to get money in? 336 | 2. Re use rimble 337 | 3. Form 338 | 1. Amount shipped 339 | 2. Pay button 340 | 4. Add in back end fiat -> crypto logic 341 | 15. Waiting spinner 342 | 1. User Rimble spinner for waiting 343 | 2. Output message -> Processing 344 | 345 | If we have time: 346 | **Stretch Goals:** 347 | Back End: 348 | 1. Create Deposit Cash function: 349 | 1. Reason: Deposit cash straight forward to earn interest on working captial 350 | 1. Instead of in saving account to pay credit card bill at end of month 351 | 2. Can withdrawl percentages from Witherawl function: 352 | 1. Range is 0 -> 100% 353 | 2. Reason: leave some money in aave to build up working captial and earn interest on that. 354 | 3. Problem: each BOL will have prtions left, so they will have to reconsolidate. 355 | 1. Solution: move into new "vault" and allocate. 356 | 1. Vault can be assigned a purpose 357 | 1. Working captial purpose. 358 | 3. Factoring logic 359 | 1. Can you factor? 360 | 4. Tank NFT 361 | 1. Ratio of bad oil 362 | 2. Drivers who have interacted with it (on chain or not)? 363 | 364 | Incentive holding money in aave to pay for expense b/c you earn interest 365 | vs just paying credit card bill for working capital from account in bank. 366 | 367 | **Stretch:** 368 | Focus on single driver company. 369 | Later we can expand to multi driver comapnies which require company NFT and driver NFT. Transfer of dBOL from company to driver. 370 | 371 | Company has stub pool of all drivers. They generate funds. 372 | 1. Create fiat to crypto method for mid stream to pay service 373 | 1. How do the midstream supplier pay? 374 | 1. Similar to how drivers get paid 375 | 1. on ramp and off ramp 376 | 2. Value extraction brainstorm 377 | 1. Driver: Take % of driver interest from Aave 378 | 2. Mid stream: Charge % of oil value on each trip 379 | 1. Needs to be 1 order of magnitude less than average loss per trip 380 | 1. Its literally 10x cheaper to use us. 381 | 2. upper limit to charge. 382 | 3. Pay in bulk 383 | 4. Add in margin for fiat to crypto portal we use to get paid 384 | 1. If moonpay 1%, then we add that to our margins 385 | 1. Model: portal fee + our fee 386 | 3. Strech analytics: 387 | 1. Provide analytics to see big picture 388 | 2. SASS model: Per month fee 389 | 3. Loss prevention software for crude oil shipments via trucking 390 | 1. Analyitics and real time reconsoliation. 391 | 1. AWS bill + our fee margin 392 | 393 | Ideas: 394 | How to get paid in full faster? 395 | 396 | From driver perspecting how to improve factoring? 397 | super strech goal: Market for BOL factors. 398 | Factoring means getting FUUUUCked. 399 | 400 | ??? 401 | - aavegotchi kinda 402 | - pool all the bols (staked value) to generate interest 403 | - pass interest along to participants in the chain 404 | - incentivize fair/ non theft 405 | - Performance bonus? Stretch goal 406 | 407 | - AAVE Integration 408 | - BOL = NFT 409 | - accrue interest on funds (value of delivery) 410 | - make money 411 | - get paid fast 412 | - aavegotchi kinda 413 | - pool all the bols (staked value) to generate interest 414 | - pass interest along to participants in the chain 415 | - incentivize fair/ non theft 416 | - pool together (lottery ?) gameification? 417 | - Operator Borrow Money 418 | - Unexpected expenses 419 | - Use collateralized funds (Expected payout from delivery?) 420 | - Money generated from past deliviers 421 | 422 | Features 423 | - Portis/Metamask Wallet Integration (Mobile and Web) 424 | - PWA? 425 | - AAVE Integration 426 | - BOL = NFT 427 | - accrue interest on funds (value of delivery) 428 | - make money 429 | - get paid fast 430 | - aavegotchi kinda 431 | - pool all the bols (staked value) to generate interest 432 | - pass interest along to participants in the chain 433 | - incentivize fair/ non theft 434 | - pool together (lottery ?) gameification? 435 | - Operator Borrow Money 436 | - Unexpected expenses 437 | - Use collateralized funds (Expected payout from delivery?) 438 | - Money generated from past deliviers 439 | - Matic Integration 440 | - Biconomy (free gas) (relay stations) 441 | - Low gas fees 442 | - Worried about onboarding users (only should have to worry about downloading meta) 443 | - third party crypto-fiat providers 444 | - Using the bridge system to interact with AAVE () 445 | - Crypto/Fiat exchange integration on payment view 446 | - Moonpay 447 | - Sendwyre 448 | - CarbonMoney 449 | - Transak 450 | - ENS 451 | - Names for accounts (no ethereum addresses) 452 | - 453 | 454 | Database to track meta information 455 | - The graph for querying events (indexing service) 456 | - User accounts google integration (facebook ... ) 457 | - Firebase 458 | - Docker (nosql or sql) 459 | 460 | Views 461 | - Owner/Operator (Min) 462 | - Supplier (Max) 463 | - Supervisors (Medium) 464 | 465 | How to establish credibility? 466 | - Factor into interest generation (incentivize) 467 | - Tracking ? Geolocation ? 468 | 469 | KPI 470 | 471 | **Optimize for safety**, theft, (incentivization) 472 | Second is for delivery 473 | 474 | - Timeliness (speed of delivery) (only operate so many hours in the day) 475 | - Length of employment 476 | - Weighted Average Delta between oil onloaded and offloaded (Theft tracking) 477 | - Location Data??? 478 | - Linked with ratio to see where stealing is happening 479 | - Location data to track where the oil is en route and see where oil is getting stolen 480 | - Inspections 481 | - Level (1,2,3) inspections (DOT inspection) 482 | 483 | - Driver Log 484 | - Driver has to keep track of where their at where they're going 485 | - Accounts from time you start to end 486 | 487 | - Get data for filing insurance claim collate it for supplier 488 | - Insurance smart contract 489 | 490 | 491 | # Catch phrase 492 | 493 | We establish credit so we know who to trust 494 | 495 | > I track the oil, so I can get my money 496 | 497 | 498 | # Make the claims easier 499 | 500 | - The graph querying blockchain event logs (collated into insurance claim report for supplier) 501 | - NFT certifications of driver to keep track of safety (put in insurance claim report) 502 | -------------------------------------------------------------------------------- /backend/contracts/ERC998/ERC998ERC721BottomUp.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.5; 4 | 5 | import "@openzeppelin/contracts/utils/Address.sol"; 6 | import "@openzeppelin/contracts/introspection/ERC165Checker.sol"; 7 | import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 8 | import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; 9 | import "@openzeppelin/contracts/math/SafeMath.sol"; 10 | import "@openzeppelin/contracts/utils/EnumerableSet.sol"; 11 | import "@openzeppelin/contracts/utils/EnumerableMap.sol"; 12 | import "../interfaces/ERC998/IERC998ERC721BottomUp.sol"; 13 | import "../interfaces/ERC998/IERC998ERC721TopDown.sol"; 14 | 15 | abstract contract ERC998ERC721BottomUp is 16 | ERC721, 17 | IERC998ERC721BottomUp, 18 | IERC998ERC721BottomUpEnumerable 19 | { 20 | using SafeMath for uint256; 21 | using EnumerableSet for EnumerableSet.UintSet; 22 | 23 | struct TokenOwner { 24 | address tokenOwner; 25 | uint256 parentTokenId; 26 | } 27 | 28 | // return this.rootOwnerOf.selector ^ this.rootOwnerOfChild.selector ^ 29 | // this.tokenOwnerOf.selector ^ this.ownerOfChild.selector; 30 | bytes32 constant ERC998_MAGIC_VALUE = bytes32(bytes4(0xcd740db5)) << 224; 31 | bytes32 constant ADDRESS_MASK = bytes32(type(uint256).max) >> 32; 32 | 33 | bytes4 constant ERC721_RECEIVED = 0x150b7a02; 34 | 35 | bytes4 private constant _INTERFACE_ID_ERC998ERC721TOPDOWN = 0x1efdf36a; 36 | bytes4 private constant _INTERFACE_ID_ERC998ERC721BOTTOMUP = 0xa1b23002; 37 | bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd; 38 | 39 | // tokenId => token owner 40 | mapping(uint256 => TokenOwner) tokenIdToTokenOwner; 41 | 42 | // root token owner address => (tokenId => approved address) 43 | mapping(address => mapping(uint256 => address)) 44 | internal rootOwnerAndTokenIdToApprovedAddress; 45 | 46 | // token owner => (operator address => bool) 47 | mapping(address => mapping(address => bool)) internal tokenOwnerToOperators; 48 | 49 | // token owner address => token count 50 | mapping(address => uint256) internal tokenOwnerToTokenCount; 51 | 52 | // parent address => (parent tokenId => array of child tokenIds) 53 | mapping(address => mapping(uint256 => EnumerableSet.UintSet)) 54 | private parentToChildTokenIds; 55 | 56 | // tokenId => position in childTokens set 57 | mapping(uint256 => uint256) private tokenIdToChildTokenIdsIndex; 58 | 59 | // constructor(string memory name_, string memory symbol_) 60 | // public 61 | // ERC721(name_, symbol_) 62 | // {} 63 | 64 | function addressToBytes32(address _addr) 65 | internal 66 | pure 67 | returns (bytes32 addr) 68 | { 69 | addr = bytes32(uint256(_addr)); // this is left padded 70 | return addr; 71 | } 72 | 73 | function getSupportedInterfaces( 74 | address account, 75 | bytes4[] memory interfaceIds 76 | ) internal view returns (bool[] memory) { 77 | // an array of booleans corresponding to interfaceIds and whether they're supported or not 78 | bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length); 79 | 80 | // query support of ERC165 itself 81 | if (ERC165Checker.supportsERC165(account)) { 82 | // query support of each interface in interfaceIds 83 | for (uint256 i = 0; i < interfaceIds.length; i++) { 84 | interfaceIdsSupported[i] = ERC165Checker.supportsInterface( 85 | account, 86 | interfaceIds[i] 87 | ); 88 | } 89 | } 90 | 91 | return interfaceIdsSupported; 92 | } 93 | 94 | // Use Cases handled: 95 | // Case 1: Token owner is this contract and no parent tokenId. 96 | // Case 2: Token owner is this contract and token 97 | // Case 3: Token owner is top-down composable 98 | // Case 4: Token owner is an unknown contract 99 | // Case 5: Token owner is a user 100 | // Case 6: Token owner is a bottom-up composable 101 | // Case 7: Token owner is ERC721 token owned by top-down token 102 | // Case 8: Token owner is ERC721 token owned by unknown contract 103 | // Case 9: Token owner is ERC721 token owned by user 104 | /// @notice Get the root owner of tokenId. 105 | /// @param _tokenId The token to query for a root owner address 106 | /// @return rootOwner The root owner at the top of tree of tokens and ERC998 magic value. 107 | function rootOwnerOf(uint256 _tokenId) 108 | external 109 | view 110 | override 111 | returns (bytes32 rootOwner) 112 | { 113 | return _rootOwnerOf(_tokenId); 114 | } 115 | 116 | // Use Cases handled: 117 | // Case 1: Token owner is this contract and no parent tokenId. 118 | // Case 2: Token owner is this contract and token 119 | // Case 3: Token owner is top-down composable 120 | // Case 4: Token owner is an unknown contract 121 | // Case 5: Token owner is a user 122 | // Case 6: Token owner is a bottom-up composable 123 | // Case 7: Token owner is ERC721 token owned by top-down token 124 | // Case 8: Token owner is ERC721 token owned by unknown contract 125 | // Case 9: Token owner is ERC721 token owned by user 126 | /// @notice Get the root owner of tokenId. 127 | /// @param _tokenId The token to query for a root owner address 128 | /// @return rootOwner The root owner at the top of tree of tokens and ERC998 magic value. 129 | function _rootOwnerOf(uint256 _tokenId) 130 | internal 131 | view 132 | returns (bytes32 rootOwner) 133 | { 134 | bool _isERC998ERC721TopDown; 135 | bool _isERC998ERC721BottomUp; 136 | bool _isERC721; 137 | IERC998ERC721TopDown _topDownContract; 138 | IERC998ERC721BottomUp _bottomUpContract; 139 | IERC721 _ERC721Contract; 140 | // Get token ownership information 141 | // isParent: True if parentTokenId is a valid parent tokenId and false if there is no parent tokenId 142 | // if isParent is false, no parent token, just owned by the tokenOwner 143 | (address tokenOwner, uint256 parentTokenId, bool isParent) = 144 | _tokenOwnerOf(_tokenId); 145 | 146 | // check whether owned by a contract 147 | if (Address.isContract(tokenOwner)) { 148 | // true owned by a contract 149 | // which contract is it owned by 150 | // this contract or an external contract 151 | if (tokenOwner == address(this)) { 152 | // _tokenId is owned by this contract 153 | // is it owned by another token 154 | if (isParent) { 155 | // yes owned by another token in this contract 156 | // we have to check if that token is owned by anyone 157 | do { 158 | // traverse up by overwritting the tokenOwner, parentTokenId, isParent 159 | // this way we can see who owns the _tokenID's parent token ... 160 | (tokenOwner, parentTokenId, isParent) = _tokenOwnerOf( 161 | parentTokenId 162 | ); 163 | // if the tokenOwner is still this contract repeat until 164 | // we've found that the tokenOwner is an external contract or User 165 | } while (tokenOwner == address(this)); 166 | // we need to change the _tokenId we are looking at to be the parentTokenId 167 | // because we should now inspect if the parent has a parent or if it's the root 168 | _tokenId = parentTokenId; 169 | } else { 170 | // no it isn't owned by another token in this contract 171 | // just the contract itself 172 | // essentially a dead token? 173 | return (ERC998_MAGIC_VALUE | addressToBytes32(tokenOwner)); 174 | } 175 | } 176 | 177 | // we should do check this next since we know the tokenOwner isn't this contract 178 | // and just in case we did loop through our own contract 179 | // check whether the parentTokenId is valid, or the token is owned by a Contract/EOA 180 | if (isParent == false) { 181 | // there is no parent token only a parent contract/ EOA 182 | // we have to check both branches 183 | // check if it is a contract 184 | if (Address.isContract(tokenOwner)) { 185 | // yes it is a contract but there is no parent token 186 | // owned by just the contract 187 | // since no parentToken, is this contract a TopDown composable 188 | // which receives, transfers, and manages ERC721 tokens/bottom up composables 189 | _isERC998ERC721TopDown = ERC165Checker.supportsInterface( 190 | tokenOwner, 191 | _INTERFACE_ID_ERC998ERC721TOPDOWN 192 | ); 193 | // if it is a TopDown contract we can query it for information 194 | if (_isERC998ERC721TopDown) { 195 | // true it is a top down contract 196 | // we can further query who the root owner is 197 | // by calling the rootOwnerOfChild function on the contract 198 | _topDownContract = IERC998ERC721TopDown(tokenOwner); 199 | return 200 | _topDownContract.rootOwnerOfChild( 201 | address(this), 202 | _tokenId 203 | ); 204 | } else { 205 | // this is not a Top Down composable contract 206 | return (ERC998_MAGIC_VALUE | 207 | addressToBytes32(tokenOwner)); 208 | } 209 | } else { 210 | // It is owned by a EOA account 211 | return (ERC998_MAGIC_VALUE | addressToBytes32(tokenOwner)); 212 | } 213 | } else { 214 | // _tokenId does have a parent token and it's in tokenOwner 215 | // meaning either it is a topdown/bottomup/ or regular ERC721 216 | // we have to check who the parent token is owned by 217 | // get the supported interfaces at once in a batch 218 | bytes4[] memory _interfacesLookup = new bytes4[](3); 219 | _interfacesLookup[0] = _INTERFACE_ID_ERC998ERC721TOPDOWN; 220 | _interfacesLookup[1] = _INTERFACE_ID_ERC998ERC721BOTTOMUP; 221 | _interfacesLookup[2] = _INTERFACE_ID_ERC721; 222 | 223 | bool[] memory _supportedInterfaces = 224 | getSupportedInterfaces(tokenOwner, _interfacesLookup); 225 | // assign whether they support the interface 226 | (_isERC998ERC721TopDown, _isERC998ERC721BottomUp, _isERC721) = ( 227 | _supportedInterfaces[0], 228 | _supportedInterfaces[1], 229 | _supportedInterfaces[2] 230 | ); 231 | if (_isERC998ERC721TopDown) { 232 | // yes it is a Top Down contract 233 | // this is the easiest we just call the rootOwnerOf 234 | // to see who the parent is of our token's parent 235 | _topDownContract = IERC998ERC721TopDown(tokenOwner); 236 | return _topDownContract.rootOwnerOf(parentTokenId); 237 | } else if (_isERC998ERC721BottomUp) { 238 | // the contract is a bottom up contract 239 | // similar to above we call the root owner of 240 | // to see who the parent is of our token's parent 241 | _bottomUpContract = IERC998ERC721BottomUp(tokenOwner); 242 | return _bottomUpContract.rootOwnerOf(parentTokenId); 243 | } else if (_isERC721) { 244 | // this is interesting, our token's parent token is 245 | // in an ERC721 contract, and has no awareness of having 246 | // our token attached to it 247 | // we have to see who the owner of the parent token is 248 | // the parent token can be owned by an EOA or a topdown composable contract 249 | // first we have to query who owns the parent token in the ERC721 contract 250 | _ERC721Contract = IERC721(tokenOwner); 251 | // set the new tokenOwner to be the address that owns the parent token 252 | tokenOwner = _ERC721Contract.ownerOf(parentTokenId); 253 | // now we check who owns the parent token 254 | if (Address.isContract(tokenOwner)) { 255 | // its owned by a contract 256 | // is it a top down contract? 257 | _isERC998ERC721TopDown = ERC165Checker 258 | .supportsInterface( 259 | tokenOwner, 260 | _INTERFACE_ID_ERC998ERC721TOPDOWN 261 | ); 262 | if (_isERC998ERC721TopDown) { 263 | // yes our parent token is owned by a 264 | // top down contract we can query who the 265 | // root owner is from there 266 | _topDownContract = IERC998ERC721TopDown(tokenOwner); 267 | // can't use tokenOwner because that is now the ERC998Top down 268 | // contract which we are calling 269 | return 270 | _topDownContract.rootOwnerOfChild( 271 | address(_ERC721Contract), 272 | parentTokenId 273 | ); 274 | } else { 275 | // parent token is owned by an unknown contract 276 | return (ERC998_MAGIC_VALUE | 277 | addressToBytes32(tokenOwner)); 278 | } 279 | } else { 280 | // its owned by an EOA 281 | return (ERC998_MAGIC_VALUE | 282 | addressToBytes32(tokenOwner)); 283 | } 284 | } 285 | } 286 | } else { 287 | // false owned by a contract 288 | // check openzeppelin notice 289 | // not 100% this is an EOA 290 | // Case 5: Token owner is a user 291 | return (ERC998_MAGIC_VALUE | addressToBytes32(tokenOwner)); 292 | } 293 | } 294 | 295 | function _tokenOwnerOf(uint256 _tokenId) 296 | internal 297 | view 298 | returns ( 299 | address tokenOwner, 300 | uint256 parentTokenId, 301 | bool isParent 302 | ) 303 | { 304 | tokenOwner = tokenIdToTokenOwner[_tokenId].tokenOwner; 305 | require(tokenOwner != address(0)); 306 | parentTokenId = tokenIdToTokenOwner[_tokenId].parentTokenId; 307 | if (parentTokenId > 0) { 308 | // The value in the struct is (parentTokenId + 1) 309 | isParent = true; 310 | parentTokenId = parentTokenId.sub(1); 311 | } else { 312 | isParent = false; 313 | } 314 | return (tokenOwner, parentTokenId, isParent); 315 | } 316 | 317 | /// @notice Get the owner address and parent token (if there is one) of a token 318 | /// @param _tokenId The tokenId to query. 319 | /// @return tokenOwner The owner address of the token 320 | /// @return parentTokenId The parent owner of the token and ERC998 magic value 321 | /// @return isParent True if parentTokenId is a valid parent tokenId and false if there is no parent tokenId 322 | function tokenOwnerOf(uint256 _tokenId) 323 | external 324 | view 325 | override 326 | returns ( 327 | bytes32 tokenOwner, 328 | uint256 parentTokenId, 329 | bool isParent 330 | ) 331 | { 332 | address _tokenOwner; 333 | (_tokenOwner, parentTokenId, isParent) = _tokenOwnerOf(_tokenId); 334 | return ( 335 | (ERC998_MAGIC_VALUE | addressToBytes32(_tokenOwner)), 336 | parentTokenId, 337 | isParent 338 | ); 339 | } 340 | 341 | /// @notice Transfer token from owner address to a token 342 | /// @param _from The owner address 343 | /// @param _toContract The ERC721 contract of the receiving token 344 | /// @param _toTokenId The receiving token 345 | /// @param _tokenId The token to transfer 346 | /// @param _data Additional data with no specified format 347 | function transferToParent( 348 | address _from, 349 | address _toContract, 350 | uint256 _toTokenId, 351 | uint256 _tokenId, 352 | bytes calldata _data 353 | ) external override { 354 | // require the token exists 355 | require(_exists(_tokenId)); 356 | 357 | // disallow transferring to the zero address 358 | require(_toContract != address(0)); 359 | 360 | // this function assumes we are transferring to a contract 361 | require(Address.isContract(_toContract)); 362 | 363 | // get the _tokenId's owner information 364 | (address tokenOwner, uint256 parentTokenId, bool isParent) = 365 | _tokenOwnerOf(_tokenId); 366 | 367 | // if _tokenId is owned by another token 368 | // disallow transferring because only the parent can 369 | require(isParent == false); 370 | 371 | // must be transfering from the owning parent contract/address, can't transfer a token a parent doesn't own 372 | require(tokenOwner == _from); 373 | 374 | // require the parent token exists 375 | // since we are transferring to a NFT contract 376 | require(IERC721(_toContract).ownerOf(_toTokenId) != address(0)); 377 | 378 | // get the address that is approved to move the token 379 | address approvedAddress = 380 | rootOwnerAndTokenIdToApprovedAddress[_from][_tokenId]; 381 | 382 | bool _isERC998ERC721TopDown; 383 | IERC998ERC721TopDown _topDownContract; 384 | 385 | // if the caller isn't who we are transferring the token from 386 | if (msg.sender != _from) { 387 | // require the msg.sender has permission to transfer the token 388 | // or is an operator for _from account 389 | require( 390 | tokenOwnerToOperators[_from][msg.sender] || 391 | approvedAddress == msg.sender 392 | ); 393 | 394 | // can be either self or another contract 395 | // check if it is a top down composable 396 | _isERC998ERC721TopDown = ERC165Checker.supportsInterface( 397 | _from, 398 | _INTERFACE_ID_ERC998ERC721TOPDOWN 399 | ); 400 | 401 | // if the owner address of the token is Top Down 402 | // should call that contract's transferChild function 403 | require(!_isERC998ERC721TopDown); 404 | } 405 | 406 | // clear approval 407 | // this will alse clear the approval when _from == msg.sender 408 | // which makes sense as the approval should be reset during a 409 | // transfer before the new owner gets the token 410 | if (approvedAddress != address(0)) { 411 | rootOwnerAndTokenIdToApprovedAddress[_from][_tokenId] = address(0); 412 | emit Approval(_from, address(0), _tokenId); 413 | } 414 | 415 | // remove and transfer token 416 | if (_from != _toContract) { 417 | // if the ownerAddress doesn't equal the recipient 418 | // sometimes you want to transfer a token to a new parenttoken at the same contract 419 | // if that is the case don't run this 420 | assert(tokenOwnerToTokenCount[_from] > 0); // this should never happen 421 | tokenOwnerToTokenCount[_from] = tokenOwnerToTokenCount[_from].sub( 422 | 1 423 | ); 424 | tokenOwnerToTokenCount[_toContract] = tokenOwnerToTokenCount[ 425 | _toContract 426 | ] 427 | .add(1); 428 | } 429 | 430 | // create a tokenOwner struct 431 | TokenOwner memory parentToken = 432 | TokenOwner(_toContract, _toTokenId.add(1)); 433 | // overwrite value currently held 434 | tokenIdToTokenOwner[_tokenId] = parentToken; 435 | 436 | // add _tokenId to parentToken's set of tokens 437 | parentToChildTokenIds[_toContract][_toTokenId].add(_tokenId); 438 | 439 | // set the token index in the parentToChildTokenIds mapping 440 | tokenIdToChildTokenIdsIndex[_tokenId] = 441 | parentToChildTokenIds[_toContract][_toTokenId].length() - 442 | 1; 443 | 444 | _transfer(_from, _toContract, _tokenId); 445 | emit TransferToParent(_toContract, _toTokenId, _tokenId); 446 | } 447 | 448 | /// @notice Transfer token from a token to an address 449 | /// @param _fromContract The address of the owning contract 450 | /// @param _fromTokenId The owning token 451 | /// @param _to The address the token is transferred to. 452 | /// @param _tokenId The token that is transferred 453 | /// @param _data Additional data with no specified format 454 | function transferFromParent( 455 | address _fromContract, 456 | uint256 _fromTokenId, 457 | address _to, 458 | uint256 _tokenId, 459 | bytes calldata _data 460 | ) external override { 461 | // get the _tokenId's owner information 462 | (address tokenOwner, uint256 parentTokenId, bool isParent) = 463 | _tokenOwnerOf(_tokenId); 464 | 465 | require(tokenOwner == _fromContract); 466 | require(_to != address(0)); 467 | require(isParent); 468 | require(parentTokenId == _fromTokenId); 469 | _authenticateAndClearApproval(_tokenId); 470 | 471 | // remove and transfer token 472 | // if the _fromContract isn't the recipient 473 | if (_fromContract != _to) { 474 | assert(tokenOwnerToTokenCount[_fromContract] > 0); 475 | tokenOwnerToTokenCount[_fromContract] = tokenOwnerToTokenCount[ 476 | _fromContract 477 | ] 478 | .sub(1); 479 | tokenOwnerToTokenCount[_to] = tokenOwnerToTokenCount[_to].add(1); 480 | } 481 | 482 | tokenIdToTokenOwner[_tokenId].tokenOwner = _to; 483 | tokenIdToTokenOwner[_tokenId].parentTokenId = 0; 484 | 485 | parentToChildTokenIds[_fromContract][_fromTokenId].remove(_tokenId); 486 | 487 | delete tokenIdToChildTokenIdsIndex[_tokenId]; 488 | 489 | if (Address.isContract(_to)) { 490 | bytes4 retval = 491 | IERC721Receiver(_to).onERC721Received( 492 | msg.sender, 493 | _fromContract, 494 | _tokenId, 495 | _data 496 | ); 497 | require(retval == ERC721_RECEIVED); 498 | } 499 | 500 | _transfer(_fromContract, _to, _tokenId); 501 | emit TransferFromParent(_fromContract, _fromTokenId, _tokenId); 502 | } 503 | 504 | function _authenticateAndClearApproval(uint256 _tokenId) private { 505 | // get the root owner of a token 506 | address rootOwner = 507 | address(uint256(_rootOwnerOf(_tokenId) & ADDRESS_MASK)); 508 | // get who is approved to move the token 509 | address approvedAddress = 510 | rootOwnerAndTokenIdToApprovedAddress[rootOwner][_tokenId]; 511 | require( 512 | rootOwner == msg.sender || 513 | tokenOwnerToOperators[rootOwner][msg.sender] || 514 | approvedAddress == msg.sender 515 | ); 516 | 517 | // clear approval 518 | if (approvedAddress != address(0)) { 519 | rootOwnerAndTokenIdToApprovedAddress[rootOwner][_tokenId] = address( 520 | 0 521 | ); 522 | emit Approval(rootOwner, address(0), _tokenId); 523 | } 524 | } 525 | 526 | /// @notice Transfer a token from a token to another token 527 | /// @param _fromContract The address of the owning contract 528 | /// @param _fromTokenId The owning token 529 | /// @param _toContract The ERC721 contract of the receiving token 530 | /// @param _toTokenId The receiving token 531 | /// @param _tokenId The token that is transferred 532 | /// @param _data Additional data with no specified format 533 | function transferAsChild( 534 | address _fromContract, 535 | uint256 _fromTokenId, 536 | address _toContract, 537 | uint256 _toTokenId, 538 | uint256 _tokenId, 539 | bytes calldata _data 540 | ) external override { 541 | // get the _tokenId's owner information 542 | (address tokenOwner, uint256 parentTokenId, bool isParent) = 543 | _tokenOwnerOf(_tokenId); 544 | 545 | require(tokenOwner == _fromContract); // tokenOwner must equal fromContract 546 | require(_toContract != address(0)); // can't burn the token 547 | require(isParent); // No parent token to transfer from 548 | require(parentTokenId == _fromTokenId); 549 | require(IERC721(_toContract).ownerOf(_toTokenId) != address(0)); 550 | 551 | address rootOwner = 552 | address(uint256(_rootOwnerOf(_tokenId) & ADDRESS_MASK)); // get the rootOwner of the token 553 | 554 | // get the address approved to send the token 555 | // address approvedAddress = 556 | // rootOwnerAndTokenIdToApprovedAddress[rootOwner][_tokenId]; 557 | 558 | // require the msg.sender be approved to manage the token 559 | require( 560 | rootOwner == msg.sender || 561 | tokenOwnerToOperators[rootOwner][msg.sender] || 562 | rootOwnerAndTokenIdToApprovedAddress[rootOwner][_tokenId] == 563 | msg.sender 564 | ); 565 | 566 | // clear approval ahead of transferrring the token 567 | if ( 568 | rootOwnerAndTokenIdToApprovedAddress[rootOwner][_tokenId] != 569 | address(0) 570 | ) { 571 | delete rootOwnerAndTokenIdToApprovedAddress[rootOwner][_tokenId]; 572 | emit Approval(rootOwner, address(0), _tokenId); 573 | } 574 | 575 | // remove and transfer token 576 | if (_fromContract != _toContract) { 577 | assert(tokenOwnerToTokenCount[_fromContract] > 0); 578 | tokenOwnerToTokenCount[_fromContract] = tokenOwnerToTokenCount[ 579 | _fromContract 580 | ] 581 | .sub(1); 582 | tokenOwnerToTokenCount[_toContract] = tokenOwnerToTokenCount[ 583 | _toContract 584 | ] 585 | .add(1); 586 | } 587 | 588 | TokenOwner memory _tokenOwner = TokenOwner(_toContract, _toTokenId); 589 | // set the new token owner 590 | tokenIdToTokenOwner[_tokenId] = _tokenOwner; 591 | 592 | parentToChildTokenIds[_fromContract][_fromTokenId].remove(_tokenId); 593 | 594 | delete tokenIdToChildTokenIdsIndex[_tokenId]; 595 | 596 | //add to parentToChildTokenIds 597 | parentToChildTokenIds[_toContract][_toTokenId].add(_tokenId); 598 | tokenIdToChildTokenIdsIndex[_tokenId] = 599 | parentToChildTokenIds[_toContract][_toTokenId].length() - 600 | 1; 601 | 602 | _transfer(_fromContract, _toContract, _tokenId); 603 | emit TransferFromParent(_fromContract, _fromTokenId, _tokenId); 604 | emit TransferToParent(_toContract, _toTokenId, _tokenId); 605 | } 606 | 607 | /// @notice Get the number of ERC721 tokens owned by parent token. 608 | /// @param _parentContract The contract the parent ERC721 token is from. 609 | /// @param _parentTokenId The parent tokenId that owns tokens 610 | /// @return uint256 The number of ERC721 tokens owned by parent token. 611 | function totalChildTokens(address _parentContract, uint256 _parentTokenId) 612 | external 613 | view 614 | override 615 | returns (uint256) 616 | { 617 | return parentToChildTokenIds[_parentContract][_parentTokenId].length(); 618 | } 619 | 620 | /// @notice Get a child token by index 621 | /// @param _parentContract The contract the parent ERC721 token is from. 622 | /// @param _parentTokenId The parent tokenId that owns the token 623 | /// @param _index The index position of the child token 624 | /// @return uint256 The child tokenId owned by the parent token 625 | function childTokenByIndex( 626 | address _parentContract, 627 | uint256 _parentTokenId, 628 | uint256 _index 629 | ) external view override returns (uint256) { 630 | require( 631 | parentToChildTokenIds[_parentContract][_parentTokenId].length() > 632 | _index 633 | ); 634 | return 635 | parentToChildTokenIds[_parentContract][_parentTokenId].at(_index); 636 | } 637 | } 638 | -------------------------------------------------------------------------------- /backend/contracts/composables998/ComposableBottomUp.sol: -------------------------------------------------------------------------------- 1 | // /********************************** 2 | // /* Author: Nick Mudge, , https://medium.com/@mudgen. 3 | // /**********************************/ 4 | 5 | // pragma solidity ^0.7.5; 6 | 7 | // import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 8 | // import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; 9 | // import "@openzeppelin/contracts/math/SafeMath.sol"; 10 | // import "../interfaces/ERC998/IERC998ERC721BottomUp.sol"; 11 | 12 | // contract ComposableBottomUp is 13 | // ERC721, 14 | // IERC998ERC721BottomUp, 15 | // IERC998ERC721BottomUpEnumerable 16 | // { 17 | // using SafeMath for uint256; 18 | 19 | // struct TokenOwner { 20 | // address tokenOwner; 21 | // uint256 parentTokenId; 22 | // } 23 | 24 | // // return this.rootOwnerOf.selector ^ this.rootOwnerOfChild.selector ^ 25 | // // this.tokenOwnerOf.selector ^ this.ownerOfChild.selector; 26 | // bytes32 constant ERC998_MAGIC_VALUE = bytes32(bytes4(0xcd740db5)); 27 | 28 | // // tokenId => token owner 29 | // mapping(uint256 => TokenOwner) internal tokenIdToTokenOwner; 30 | 31 | // // root token owner address => (tokenId => approved address) 32 | // mapping(address => mapping(uint256 => address)) 33 | // internal rootOwnerAndTokenIdToApprovedAddress; 34 | 35 | // // token owner address => token count 36 | // mapping(address => uint256) internal tokenOwnerToTokenCount; 37 | 38 | // // token owner => (operator address => bool) 39 | // mapping(address => mapping(address => bool)) internal tokenOwnerToOperators; 40 | 41 | // // parent address => (parent tokenId => array of child tokenIds) 42 | // mapping(address => mapping(uint256 => uint256[])) 43 | // private parentToChildTokenIds; 44 | 45 | // // tokenId => position in childTokens array 46 | // mapping(uint256 => uint256) private tokenIdToChildTokenIdsIndex; 47 | 48 | // // wrapper on minting new 721 49 | // /* 50 | // function mint721(address _to) public returns(uint256) { 51 | // _mint(_to, allTokens.length + 1); 52 | // return allTokens.length; 53 | // } 54 | // */ 55 | // //from zepellin ERC721Receiver.sol 56 | // //old version 57 | // bytes4 constant ERC721_RECEIVED = 0x150b7a02; 58 | 59 | // function isContract(address _addr) internal view returns (bool) { 60 | // uint256 size; 61 | // assembly { 62 | // size := extcodesize(_addr) 63 | // } 64 | // return size > 0; 65 | // } 66 | 67 | // function _tokenOwnerOf(uint256 _tokenId) 68 | // internal 69 | // view 70 | // returns ( 71 | // address tokenOwner, 72 | // uint256 parentTokenId, 73 | // bool isParent 74 | // ) 75 | // { 76 | // tokenOwner = tokenIdToTokenOwner[_tokenId].tokenOwner; 77 | // require(tokenOwner != address(0)); 78 | // parentTokenId = tokenIdToTokenOwner[_tokenId].parentTokenId; 79 | // if (parentTokenId > 0) { 80 | // isParent = true; 81 | // parentTokenId--; 82 | // } else { 83 | // isParent = false; 84 | // } 85 | // return (tokenOwner, parentTokenId, isParent); 86 | // } 87 | 88 | // function tokenOwnerOf(uint256 _tokenId) 89 | // external 90 | // view 91 | // returns ( 92 | // bytes32 tokenOwner, 93 | // uint256 parentTokenId, 94 | // bool isParent 95 | // ) 96 | // { 97 | // address tokenOwnerAddress = tokenIdToTokenOwner[_tokenId].tokenOwner; 98 | // require(tokenOwnerAddress != address(0)); 99 | // parentTokenId = tokenIdToTokenOwner[_tokenId].parentTokenId; 100 | // if (parentTokenId > 0) { 101 | // isParent = true; 102 | // parentTokenId--; 103 | // } else { 104 | // isParent = false; 105 | // } 106 | // return ( 107 | // (ERC998_MAGIC_VALUE << 224) | bytes32(tokenOwnerAddress), 108 | // parentTokenId, 109 | // isParent 110 | // ); 111 | // } 112 | 113 | // // Use Cases handled: 114 | // // Case 1: Token owner is this contract and no parent tokenId. 115 | // // Case 2: Token owner is this contract and token 116 | // // Case 3: Token owner is top-down composable 117 | // // Case 4: Token owner is an unknown contract 118 | // // Case 5: Token owner is a user 119 | // // Case 6: Token owner is a bottom-up composable 120 | // // Case 7: Token owner is ERC721 token owned by top-down token 121 | // // Case 8: Token owner is ERC721 token owned by unknown contract 122 | // // Case 9: Token owner is ERC721 token owned by user 123 | // function rootOwnerOf(uint256 _tokenId) 124 | // public 125 | // view 126 | // returns (bytes32 rootOwner) 127 | // { 128 | // address rootOwnerAddress = tokenIdToTokenOwner[_tokenId].tokenOwner; 129 | // require(rootOwnerAddress != address(0)); 130 | // uint256 parentTokenId = tokenIdToTokenOwner[_tokenId].parentTokenId; 131 | // bool isParent = parentTokenId > 0; 132 | // parentTokenId--; 133 | // bytes memory _calldata; 134 | // bool callSuccess; 135 | 136 | // if ((rootOwnerAddress == address(this))) { 137 | // do { 138 | // if (isParent == false) { 139 | // // Case 1: Token owner is this contract and no token. 140 | // // This case should not happen. 141 | // return 142 | // (ERC998_MAGIC_VALUE << 224) | bytes32(rootOwnerAddress); 143 | // } else { 144 | // // Case 2: Token owner is this contract and token 145 | // (rootOwnerAddress, parentTokenId, isParent) = _tokenOwnerOf( 146 | // parentTokenId 147 | // ); 148 | // } 149 | // } while (rootOwnerAddress == address(this)); 150 | // _tokenId = parentTokenId; 151 | // } 152 | 153 | // if (isParent == false) { 154 | // // success if this token is owned by a top-down token 155 | // // 0xed81cdda == rootOwnerOfChild(address, uint256) 156 | // _calldata = abi.encodeWithSelector( 157 | // 0xed81cdda, 158 | // address(this), 159 | // _tokenId 160 | // ); 161 | // assembly { 162 | // callSuccess := staticcall( 163 | // gas(), 164 | // rootOwnerAddress, 165 | // add(_calldata, 0x20), 166 | // mload(_calldata), 167 | // _calldata, 168 | // 0x20 169 | // ) 170 | // if callSuccess { 171 | // rootOwner := mload(_calldata) 172 | // } 173 | // } 174 | // if (callSuccess == true && rootOwner >> 224 == ERC998_MAGIC_VALUE) { 175 | // // Case 3: Token owner is top-down composable 176 | // return rootOwner; 177 | // } else { 178 | // // Case 4: Token owner is an unknown contract 179 | // // Or 180 | // // Case 5: Token owner is a user 181 | // return (ERC998_MAGIC_VALUE << 224) | bytes32(rootOwnerAddress); 182 | // } 183 | // } else { 184 | // // 0x43a61a8e == rootOwnerOf(uint256) 185 | // _calldata = abi.encodeWithSelector(0x43a61a8e, parentTokenId); 186 | // assembly { 187 | // callSuccess := staticcall( 188 | // gas(), 189 | // rootOwnerAddress, 190 | // add(_calldata, 0x20), 191 | // mload(_calldata), 192 | // _calldata, 193 | // 0x20 194 | // ) 195 | // if callSuccess { 196 | // rootOwner := mload(_calldata) 197 | // } 198 | // } 199 | // if (callSuccess == true && rootOwner >> 224 == ERC998_MAGIC_VALUE) { 200 | // // Case 6: Token owner is a bottom-up composable 201 | // // Or 202 | // // Case 2: Token owner is top-down composable 203 | // return rootOwner; 204 | // } else { 205 | // // token owner is ERC721 206 | // address childContract = rootOwnerAddress; 207 | // //0x6352211e == "ownerOf(uint256)" 208 | // _calldata = abi.encodeWithSelector(0x6352211e, parentTokenId); 209 | // assembly { 210 | // callSuccess := staticcall( 211 | // gas(), 212 | // rootOwnerAddress, 213 | // add(_calldata, 0x20), 214 | // mload(_calldata), 215 | // _calldata, 216 | // 0x20 217 | // ) 218 | // if callSuccess { 219 | // rootOwnerAddress := mload(_calldata) 220 | // } 221 | // } 222 | // require(callSuccess, "Call to ownerOf failed"); 223 | 224 | // // 0xed81cdda == rootOwnerOfChild(address,uint256) 225 | // _calldata = abi.encodeWithSelector( 226 | // 0xed81cdda, 227 | // childContract, 228 | // parentTokenId 229 | // ); 230 | // assembly { 231 | // callSuccess := staticcall( 232 | // gas(), 233 | // rootOwnerAddress, 234 | // add(_calldata, 0x20), 235 | // mload(_calldata), 236 | // _calldata, 237 | // 0x20 238 | // ) 239 | // if callSuccess { 240 | // rootOwner := mload(_calldata) 241 | // } 242 | // } 243 | // if ( 244 | // callSuccess == true && 245 | // rootOwner >> 224 == ERC998_MAGIC_VALUE 246 | // ) { 247 | // // Case 7: Token owner is ERC721 token owned by top-down token 248 | // return rootOwner; 249 | // } else { 250 | // // Case 8: Token owner is ERC721 token owned by unknown contract 251 | // // Or 252 | // // Case 9: Token owner is ERC721 token owned by user 253 | // return 254 | // (ERC998_MAGIC_VALUE << 224) | bytes32(rootOwnerAddress); 255 | // } 256 | // } 257 | // } 258 | // } 259 | 260 | // /** 261 | // * In a bottom-up composable authentication to transfer etc. is done by getting the rootOwner by finding the parent token 262 | // * and then the parent token of that one until a final owner address is found. If the msg.sender is the rootOwner or is 263 | // * approved by the rootOwner then msg.sender is authenticated and the action can occur. 264 | // * This enables the owner of the top-most parent of a tree of composables to call any method on child composables. 265 | // */ 266 | // // returns the root owner at the top of the tree of composables 267 | // function ownerOf(uint256 _tokenId) public view returns (address) { 268 | // address tokenOwner = tokenIdToTokenOwner[_tokenId].tokenOwner; 269 | // require(tokenOwner != address(0)); 270 | // return tokenOwner; 271 | // } 272 | 273 | // function balanceOf(address _tokenOwner) external view returns (uint256) { 274 | // require(_tokenOwner != address(0)); 275 | // return tokenOwnerToTokenCount[_tokenOwner]; 276 | // } 277 | 278 | // function approve(address _approved, uint256 _tokenId) external { 279 | // address tokenOwner = tokenIdToTokenOwner[_tokenId].tokenOwner; 280 | // require(tokenOwner != address(0)); 281 | // address rootOwner = address(rootOwnerOf(_tokenId)); 282 | // require( 283 | // rootOwner == msg.sender || 284 | // tokenOwnerToOperators[rootOwner][msg.sender] 285 | // ); 286 | 287 | // rootOwnerAndTokenIdToApprovedAddress[rootOwner][_tokenId] = _approved; 288 | // emit Approval(rootOwner, _approved, _tokenId); 289 | // } 290 | 291 | // function getApproved(uint256 _tokenId) public view returns (address) { 292 | // address rootOwner = address(rootOwnerOf(_tokenId)); 293 | // return rootOwnerAndTokenIdToApprovedAddress[rootOwner][_tokenId]; 294 | // } 295 | 296 | // function setApprovalForAll(address _operator, bool _approved) external { 297 | // require(_operator != address(0)); 298 | // tokenOwnerToOperators[msg.sender][_operator] = _approved; 299 | // emit ApprovalForAll(msg.sender, _operator, _approved); 300 | // } 301 | 302 | // function isApprovedForAll(address _owner, address _operator) 303 | // external 304 | // view 305 | // returns (bool) 306 | // { 307 | // require(_owner != address(0)); 308 | // require(_operator != address(0)); 309 | // return tokenOwnerToOperators[_owner][_operator]; 310 | // } 311 | 312 | // function removeChild( 313 | // address _fromContract, 314 | // uint256 _fromTokenId, 315 | // uint256 _tokenId 316 | // ) internal { 317 | // uint256 childTokenIndex = tokenIdToChildTokenIdsIndex[_tokenId]; 318 | // uint256 lastChildTokenIndex = 319 | // parentToChildTokenIds[_fromContract][_fromTokenId].length - 1; 320 | // uint256 lastChildTokenId = 321 | // parentToChildTokenIds[_fromContract][_fromTokenId][ 322 | // lastChildTokenIndex 323 | // ]; 324 | 325 | // if (_tokenId != lastChildTokenId) { 326 | // parentToChildTokenIds[_fromContract][_fromTokenId][ 327 | // childTokenIndex 328 | // ] = lastChildTokenId; 329 | // tokenIdToChildTokenIdsIndex[lastChildTokenId] = childTokenIndex; 330 | // } 331 | // parentToChildTokenIds[_fromContract][_fromTokenId].length--; 332 | // } 333 | 334 | // function authenticateAndClearApproval(uint256 _tokenId) private { 335 | // address rootOwner = address(rootOwnerOf(_tokenId)); 336 | // address approvedAddress = 337 | // rootOwnerAndTokenIdToApprovedAddress[rootOwner][_tokenId]; 338 | // require( 339 | // rootOwner == msg.sender || 340 | // tokenOwnerToOperators[rootOwner][msg.sender] || 341 | // approvedAddress == msg.sender 342 | // ); 343 | 344 | // // clear approval 345 | // if (approvedAddress != address(0)) { 346 | // delete rootOwnerAndTokenIdToApprovedAddress[rootOwner][_tokenId]; 347 | // emit Approval(rootOwner, address(0), _tokenId); 348 | // } 349 | // } 350 | 351 | // function transferFromParent( 352 | // address _fromContract, 353 | // uint256 _fromTokenId, 354 | // address _to, 355 | // uint256 _tokenId, 356 | // bytes calldata _data 357 | // ) external { 358 | // require(tokenIdToTokenOwner[_tokenId].tokenOwner == _fromContract); 359 | // require(_to != address(0)); 360 | // uint256 parentTokenId = tokenIdToTokenOwner[_tokenId].parentTokenId; 361 | // require(parentTokenId != 0, "Token does not have a parent token."); 362 | // require(parentTokenId - 1 == _fromTokenId); 363 | // authenticateAndClearApproval(_tokenId); 364 | 365 | // // remove and transfer token 366 | // if (_fromContract != _to) { 367 | // assert(tokenOwnerToTokenCount[_fromContract] > 0); 368 | // tokenOwnerToTokenCount[_fromContract]--; 369 | // tokenOwnerToTokenCount[_to]++; 370 | // } 371 | 372 | // tokenIdToTokenOwner[_tokenId].tokenOwner = _to; 373 | // tokenIdToTokenOwner[_tokenId].parentTokenId = 0; 374 | 375 | // removeChild(_fromContract, _fromTokenId, _tokenId); 376 | // delete tokenIdToChildTokenIdsIndex[_tokenId]; 377 | 378 | // if (isContract(_to)) { 379 | // bytes4 retval = 380 | // IERC721Receiver(_to).onERC721Received( 381 | // msg.sender, 382 | // _fromContract, 383 | // _tokenId, 384 | // _data 385 | // ); 386 | // require(retval == ERC721_RECEIVED); 387 | // } 388 | 389 | // emit Transfer(_fromContract, _to, _tokenId); 390 | // emit TransferFromParent(_fromContract, _fromTokenId, _tokenId); 391 | // } 392 | 393 | // /// @notice Transfer token from owner address to a token 394 | // /// @param _from The owner address 395 | // /// @param _toContract The ERC721 contract of the receiving token 396 | // /// @param _toTokenId The receiving token 397 | // /// @param _tokenId The token to transfer 398 | // /// @param _data Additional data with no specified format 399 | // function transferToParent( 400 | // address _from, 401 | // address _toContract, 402 | // uint256 _toTokenId, 403 | // uint256 _tokenId, 404 | // bytes calldata _data 405 | // ) external { 406 | // require(_from != address(0)); // token owner address can't be zero address (token must exist) 407 | // require(tokenIdToTokenOwner[_tokenId].tokenOwner == _from); // the token owner must equal the given token owner address 408 | // require(_toContract != address(0)); // the contract of the receiving token can't be zero address 409 | // require( // the token can't be transferred if it's already owned by another token 410 | // tokenIdToTokenOwner[_tokenId].parentTokenId == 0, 411 | // "Cannot transfer from address when owned by a token." 412 | // ); 413 | // address approvedAddress = 414 | // rootOwnerAndTokenIdToApprovedAddress[_from][_tokenId]; // get the address approved to move _tokenId 415 | // if (msg.sender != _from) { 416 | // // if the msg.sender is not the owning address 417 | // bytes32 rootOwner; 418 | // bool callSuccess; 419 | // // 0xed81cdda == rootOwnerOfChild(address,uint256) 420 | // // call the root Owner of Child function 421 | // // which is a Top Down Contract function 422 | // // which returns The root owner at the top of tree of tokens and ERC998 magic value 423 | // // this is necessary because the _tokenId could be owned by a parent token 424 | // // so we are checking to see who is the root owner 425 | // bytes memory _calldata = 426 | // abi.encodeWithSelector(0xed81cdda, address(this), _tokenId); // call data to send to Top Down Contract 427 | // // use assembly code to issue a static call to the _from address 428 | // // which may or may not be a contract 429 | // assembly { 430 | // callSuccess := staticcall( 431 | // // issue the call 432 | // gas(), 433 | // _from, 434 | // add(_calldata, 0x20), 435 | // mload(_calldata), 436 | // _calldata, 437 | // 0x20 438 | // ) 439 | // if callSuccess { 440 | // // if the call was successful load the rootOwner 441 | // rootOwner := mload(_calldata) 442 | // } 443 | // } 444 | // if (callSuccess == true) { 445 | // // if the call was successful 446 | // // require the rootOwner at the top of the tree 447 | // // is not a top Down composable contract 448 | // // i assume because then that address would need to be called 449 | // // to transfer this child token via transferChild function 450 | // require( 451 | // rootOwner >> 224 != ERC998_MAGIC_VALUE, 452 | // "Token is child of other top down composable" 453 | // ); 454 | // } 455 | // require( // regardless of whether the call is successful or not 456 | // // 457 | // tokenOwnerToOperators[_from][msg.sender] || // is the msg.sender an operator account for _from (they are allowed to call functions on behalf of owner) 458 | // approvedAddress == msg.sender // is the msg.sender an approvedAddress (they are allowed to move that token) 459 | // ); 460 | // } 461 | 462 | // // clear the approval 463 | // if (approvedAddress != address(0)) { 464 | // // only run if there is an approved address 465 | // // this exists because there may be no approved address for the token 466 | // // but the _from address may have an approved operator account 467 | // // so before transferring ownership of the token we have to remove the 468 | // // approval 469 | // delete rootOwnerAndTokenIdToApprovedAddress[_from][_tokenId]; 470 | // emit Approval(_from, address(0), _tokenId); 471 | // } 472 | 473 | // // remove and transfer token 474 | // if (_from != _toContract) { 475 | // // if the ownerAddress doesn't equal the recipient 476 | // // sometimes you want to transfer a token to a new parenttoken at the same contract 477 | // // if that is the case don't run this 478 | // assert(tokenOwnerToTokenCount[_from] > 0); // verify the _from address has a token balance 479 | // tokenOwnerToTokenCount[_from]--; // reduce the balance of the _from address 480 | // tokenOwnerToTokenCount[_toContract]++; // increase the balance of _toContract 481 | // } 482 | // TokenOwner memory parentToken = 483 | // TokenOwner(_toContract, _toTokenId.add(1)); // create a new TokenOwner structure 484 | // tokenIdToTokenOwner[_tokenId] = parentToken; // rewrite the value in the tokenIdToTokenOwner mapping 485 | // uint256 index = parentToChildTokenIds[_toContract][_toTokenId].length; // get the length of _toContract parentToken's owned tokens (only tokens from this contract) 486 | // parentToChildTokenIds[_toContract][_toTokenId].push(_tokenId); // append the _tokenId to the parentToken's set of owned tokens 487 | // tokenIdToChildTokenIdsIndex[_tokenId] = index; // set the index of _token in ParenToken's set of tokens 488 | 489 | // require( 490 | // ERC721(_toContract).ownerOf(_toTokenId) != address(0), // require the toTokenId has an owner (exists) 491 | // "_toTokenId does not exist" 492 | // ); 493 | 494 | // // emit events 495 | // emit Transfer(_from, _toContract, _tokenId); 496 | // emit TransferToParent(_toContract, _toTokenId, _tokenId); 497 | // } 498 | 499 | // function transferAsChild( 500 | // address _fromContract, 501 | // uint256 _fromTokenId, 502 | // address _toContract, 503 | // uint256 _toTokenId, 504 | // uint256 _tokenId, 505 | // bytes calldata _data 506 | // ) external { 507 | // require(tokenIdToTokenOwner[_tokenId].tokenOwner == _fromContract); 508 | // require(_toContract != address(0)); 509 | // uint256 parentTokenId = tokenIdToTokenOwner[_tokenId].parentTokenId; 510 | // require(parentTokenId > 0, "No parent token to transfer from."); 511 | // require(parentTokenId - 1 == _fromTokenId); 512 | // address rootOwner = address(rootOwnerOf(_tokenId)); 513 | // address approvedAddress = 514 | // rootOwnerAndTokenIdToApprovedAddress[rootOwner][_tokenId]; 515 | // require( 516 | // rootOwner == msg.sender || 517 | // tokenOwnerToOperators[rootOwner][msg.sender] || 518 | // approvedAddress == msg.sender 519 | // ); 520 | // // clear approval 521 | // if (approvedAddress != address(0)) { 522 | // delete rootOwnerAndTokenIdToApprovedAddress[rootOwner][_tokenId]; 523 | // emit Approval(rootOwner, address(0), _tokenId); 524 | // } 525 | 526 | // // remove and transfer token 527 | // if (_fromContract != _toContract) { 528 | // assert(tokenOwnerToTokenCount[_fromContract] > 0); 529 | // tokenOwnerToTokenCount[_fromContract]--; 530 | // tokenOwnerToTokenCount[_toContract]++; 531 | // } 532 | 533 | // TokenOwner memory parentToken = TokenOwner(_toContract, _toTokenId); 534 | // tokenIdToTokenOwner[_tokenId] = parentToken; 535 | 536 | // removeChild(_fromContract, _fromTokenId, _tokenId); 537 | 538 | // //add to parentToChildTokenIds 539 | // uint256 index = parentToChildTokenIds[_toContract][_toTokenId].length; 540 | // parentToChildTokenIds[_toContract][_toTokenId].push(_tokenId); 541 | // tokenIdToChildTokenIdsIndex[_tokenId] = index; 542 | 543 | // require( 544 | // ERC721(_toContract).ownerOf(_toTokenId) != address(0), 545 | // "_toTokenId does not exist" 546 | // ); 547 | 548 | // emit Transfer(_fromContract, _toContract, _tokenId); 549 | // emit TransferFromParent(_fromContract, _fromTokenId, _tokenId); 550 | // emit TransferToParent(_toContract, _toTokenId, _tokenId); 551 | // } 552 | 553 | // function _transferFrom( 554 | // address _from, 555 | // address _to, 556 | // uint256 _tokenId 557 | // ) private { 558 | // require(_from != address(0)); 559 | // require(tokenIdToTokenOwner[_tokenId].tokenOwner == _from); 560 | // require( 561 | // tokenIdToTokenOwner[_tokenId].parentTokenId == 0, 562 | // "Cannot transfer from address when owned by a token." 563 | // ); 564 | // require(_to != address(0)); 565 | // address approvedAddress = 566 | // rootOwnerAndTokenIdToApprovedAddress[_from][_tokenId]; 567 | // if (msg.sender != _from) { 568 | // bytes32 rootOwner; 569 | // bool callSuccess; 570 | // // 0xed81cdda == rootOwnerOfChild(address,uint256) 571 | // bytes memory _calldata = 572 | // abi.encodeWithSelector(0xed81cdda, address(this), _tokenId); 573 | // assembly { 574 | // callSuccess := staticcall( 575 | // gas(), 576 | // _from, 577 | // add(_calldata, 0x20), 578 | // mload(_calldata), 579 | // _calldata, 580 | // 0x20 581 | // ) 582 | // if callSuccess { 583 | // rootOwner := mload(_calldata) 584 | // } 585 | // } 586 | // if (callSuccess == true) { 587 | // require( 588 | // rootOwner >> 224 != ERC998_MAGIC_VALUE, 589 | // "Token is child of other top down composable" 590 | // ); 591 | // } 592 | // require( 593 | // tokenOwnerToOperators[_from][msg.sender] || 594 | // approvedAddress == msg.sender 595 | // ); 596 | // } 597 | 598 | // // clear approval 599 | // if (approvedAddress != address(0)) { 600 | // delete rootOwnerAndTokenIdToApprovedAddress[_from][_tokenId]; 601 | // emit Approval(_from, address(0), _tokenId); 602 | // } 603 | 604 | // // remove and transfer token 605 | // if (_from != _to) { 606 | // assert(tokenOwnerToTokenCount[_from] > 0); 607 | // tokenOwnerToTokenCount[_from]--; 608 | // tokenIdToTokenOwner[_tokenId].tokenOwner = _to; 609 | // tokenOwnerToTokenCount[_to]++; 610 | // } 611 | // emit Transfer(_from, _to, _tokenId); 612 | // } 613 | 614 | // function transferFrom( 615 | // address _from, 616 | // address _to, 617 | // uint256 _tokenId 618 | // ) external { 619 | // _transferFrom(_from, _to, _tokenId); 620 | // } 621 | 622 | // function safeTransferFrom( 623 | // address _from, 624 | // address _to, 625 | // uint256 _tokenId 626 | // ) external { 627 | // _transferFrom(_from, _to, _tokenId); 628 | // if (isContract(_to)) { 629 | // bytes4 retval = 630 | // IERC721Receiver(_to).onERC721Received( 631 | // msg.sender, 632 | // _from, 633 | // _tokenId, 634 | // "" 635 | // ); 636 | // require(retval == ERC721_RECEIVED); 637 | // } 638 | // } 639 | 640 | // function safeTransferFrom( 641 | // address _from, 642 | // address _to, 643 | // uint256 _tokenId, 644 | // bytes calldata _data 645 | // ) external { 646 | // _transferFrom(_from, _to, _tokenId); 647 | // if (isContract(_to)) { 648 | // bytes4 retval = 649 | // IERC721Receiver(_to).onERC721Received( 650 | // msg.sender, 651 | // _from, 652 | // _tokenId, 653 | // _data 654 | // ); 655 | // require(retval == ERC721_RECEIVED); 656 | // } 657 | // } 658 | 659 | // function totalChildTokens(address _parentContract, uint256 _parentTokenId) 660 | // public 661 | // view 662 | // returns (uint256) 663 | // { 664 | // return parentToChildTokenIds[_parentContract][_parentTokenId].length; 665 | // } 666 | 667 | // function childTokenByIndex( 668 | // address _parentContract, 669 | // uint256 _parentTokenId, 670 | // uint256 _index 671 | // ) public view returns (uint256) { 672 | // require( 673 | // parentToChildTokenIds[_parentContract][_parentTokenId].length > 674 | // _index 675 | // ); 676 | // return parentToChildTokenIds[_parentContract][_parentTokenId][_index]; 677 | // } 678 | // } 679 | --------------------------------------------------------------------------------