Coder Social home page Coder Social logo

ethereum / sourcify Goto Github PK

View Code? Open in Web Editor NEW
761.0 38.0 375.0 29.92 MB

Decentralized Solidity contract source code verification service

Home Page: https://sourcify.dev

License: MIT License

JavaScript 11.20% Shell 1.72% Dockerfile 0.19% HTML 0.12% TypeScript 60.43% Solidity 25.62% CSS 0.72%
verification contracts

sourcify's Introduction

 

  sourcify logo

codecov

Sourcify (sourcify.dev) is a Solidity source code verification service for Ethereum smart contracts.

Different than other verification services, Sourcify leverages the Solidity metadata file to "fully verify" the contracts.

Sourcify mainly consists of:

  • sourcify-server - an HTTP server to do verifications and store the verified contracts for supported chains through an API
  • sourcify-ui - a web UI to interact with the server, lookup, and verify contracts
  • sourcify-monitor - a standalone service that listens to various EVM chains for new contract creations and automatically submits them to a Sourcify API for verification.
  • Packages:

The project aims to serve as a public good infrastructure with fully open-source development and an open and accessible contract repository of verified contracts. Anyone can easily run their own Sourcify server and monitor to verify contracts on their own. We also aim to provide tooling to verify contracts easier on different platforms e.g. browers.

ℹ️ This monorepo the main modules. The sourcifyeth Github organization contains all other auxiliary services and components.

Documentation

For more details refer to docs.sourcify.dev

Questions?

🔍 Check out docs F.A.Q. and use search in docs.

💬 Chat with us on Matrix chat or Discord

🐦 Follow us and help us spread the word on Twitter.

Adding a new chain

If you'd like to add a new chain support to Sourcify please follow the chain support instructions in docs.

sourcify's People

Contributors

acuarica avatar aswanth-c avatar bertux avatar cgewecke avatar charliemc0 avatar chriseth avatar connorch avatar defiyaco avatar devli13 avatar dfenstermaker avatar edisinovcic avatar ethgr0wth avatar fabijanc avatar fedekunze avatar franzihei avatar kharabet avatar kuzdogan avatar ligi avatar manuelwedler avatar marcocastignoli avatar matevz avatar ogwurujohnson avatar orenyomtov avatar renovate-bot avatar rimrakhimov avatar roman-tik avatar scream4ik avatar symplexialabs avatar unchase avatar uttam-singhh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sourcify's Issues

Run IPNS publishing via docker

currently outside docker - should be part of it. Command would be:

ipfs name publish `ipfs add -Q -r <contract_path>`

should run like once per hour or so

View in Huly HI-86

Support Metadata generated from solc with option "useLiteralContent": true

example metadata generated via useLiteralContent": true

This is for contract : 0x3845badAde8e6dFF049820680d1F14bD3903a5d0

{
  "compiler": {
    "version": "0.5.9+commit.e560f70d"
  },
  "language": "Solidity",
  "output": {
    "abi": [
      {
        "constant": true,
        "inputs": [],
        "name": "name",
        "outputs": [
          {
            "name": "",
            "type": "string"
          }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
      },
      {
        "constant": false,
        "inputs": [
          {
            "name": "newAdmin",
            "type": "address"
          }
        ],
        "name": "changeExecutionAdmin",
        "outputs": [],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
      },
      {
        "constant": false,
        "inputs": [
          {
            "name": "spender",
            "type": "address"
          },
          {
            "name": "amount",
            "type": "uint256"
          }
        ],
        "name": "approve",
        "outputs": [
          {
            "name": "success",
            "type": "bool"
          }
        ],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
      },
      {
        "constant": true,
        "inputs": [],
        "name": "totalSupply",
        "outputs": [
          {
            "name": "",
            "type": "uint256"
          }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
      },
      {
        "constant": false,
        "inputs": [
          {
            "name": "owner",
            "type": "address"
          },
          {
            "name": "amount",
            "type": "uint256"
          }
        ],
        "name": "burnFor",
        "outputs": [
          {
            "name": "",
            "type": "bool"
          }
        ],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
      },
      {
        "constant": false,
        "inputs": [
          {
            "name": "from",
            "type": "address"
          },
          {
            "name": "to",
            "type": "address"
          },
          {
            "name": "amount",
            "type": "uint256"
          }
        ],
        "name": "transferFrom",
        "outputs": [
          {
            "name": "success",
            "type": "bool"
          }
        ],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
      },
      {
        "constant": false,
        "inputs": [
          {
            "name": "owner",
            "type": "address"
          },
          {
            "name": "spender",
            "type": "address"
          },
          {
            "name": "amount",
            "type": "uint256"
          }
        ],
        "name": "approveFor",
        "outputs": [
          {
            "name": "success",
            "type": "bool"
          }
        ],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
      },
      {
        "constant": true,
        "inputs": [],
        "name": "decimals",
        "outputs": [
          {
            "name": "",
            "type": "uint8"
          }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
      },
      {
        "constant": false,
        "inputs": [
          {
            "name": "owner",
            "type": "address"
          },
          {
            "name": "spender",
            "type": "address"
          },
          {
            "name": "amountNeeded",
            "type": "uint256"
          }
        ],
        "name": "addAllowanceIfNeeded",
        "outputs": [
          {
            "name": "success",
            "type": "bool"
          }
        ],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
      },
      {
        "constant": false,
        "inputs": [
          {
            "name": "amount",
            "type": "uint256"
          }
        ],
        "name": "burn",
        "outputs": [
          {
            "name": "",
            "type": "bool"
          }
        ],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
      },
      {
        "constant": true,
        "inputs": [
          {
            "name": "who",
            "type": "address"
          }
        ],
        "name": "isExecutionOperator",
        "outputs": [
          {
            "name": "",
            "type": "bool"
          }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
      },
      {
        "constant": true,
        "inputs": [
          {
            "name": "who",
            "type": "address"
          }
        ],
        "name": "isSuperOperator",
        "outputs": [
          {
            "name": "",
            "type": "bool"
          }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
      },
      {
        "constant": false,
        "inputs": [
          {
            "name": "executionOperator",
            "type": "address"
          },
          {
            "name": "enabled",
            "type": "bool"
          }
        ],
        "name": "setExecutionOperator",
        "outputs": [],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
      },
      {
        "constant": true,
        "inputs": [],
        "name": "getAdmin",
        "outputs": [
          {
            "name": "",
            "type": "address"
          }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
      },
      {
        "constant": true,
        "inputs": [
          {
            "name": "owner",
            "type": "address"
          }
        ],
        "name": "balanceOf",
        "outputs": [
          {
            "name": "",
            "type": "uint256"
          }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
      },
      {
        "constant": false,
        "inputs": [
          {
            "name": "from",
            "type": "address"
          },
          {
            "name": "to",
            "type": "address"
          },
          {
            "name": "amount",
            "type": "uint256"
          },
          {
            "name": "gasLimit",
            "type": "uint256"
          },
          {
            "name": "data",
            "type": "bytes"
          }
        ],
        "name": "approveAndExecuteWithSpecificGas",
        "outputs": [
          {
            "name": "success",
            "type": "bool"
          },
          {
            "name": "returnData",
            "type": "bytes"
          }
        ],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
      },
      {
        "constant": false,
        "inputs": [
          {
            "name": "newAdmin",
            "type": "address"
          }
        ],
        "name": "changeAdmin",
        "outputs": [],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
      },
      {
        "constant": true,
        "inputs": [],
        "name": "symbol",
        "outputs": [
          {
            "name": "",
            "type": "string"
          }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
      },
      {
        "constant": false,
        "inputs": [
          {
            "name": "to",
            "type": "address"
          },
          {
            "name": "amount",
            "type": "uint256"
          }
        ],
        "name": "transfer",
        "outputs": [
          {
            "name": "success",
            "type": "bool"
          }
        ],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
      },
      {
        "constant": false,
        "inputs": [
          {
            "name": "superOperator",
            "type": "address"
          },
          {
            "name": "enabled",
            "type": "bool"
          }
        ],
        "name": "setSuperOperator",
        "outputs": [],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
      },
      {
        "constant": true,
        "inputs": [],
        "name": "getExecutionAdmin",
        "outputs": [
          {
            "name": "",
            "type": "address"
          }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
      },
      {
        "constant": false,
        "inputs": [
          {
            "name": "target",
            "type": "address"
          },
          {
            "name": "amount",
            "type": "uint256"
          },
          {
            "name": "data",
            "type": "bytes"
          }
        ],
        "name": "paidCall",
        "outputs": [
          {
            "name": "",
            "type": "bytes"
          }
        ],
        "payable": true,
        "stateMutability": "payable",
        "type": "function"
      },
      {
        "constant": false,
        "inputs": [
          {
            "name": "target",
            "type": "address"
          },
          {
            "name": "amount",
            "type": "uint256"
          },
          {
            "name": "data",
            "type": "bytes"
          }
        ],
        "name": "approveAndCall",
        "outputs": [
          {
            "name": "",
            "type": "bytes"
          }
        ],
        "payable": true,
        "stateMutability": "payable",
        "type": "function"
      },
      {
        "constant": false,
        "inputs": [
          {
            "name": "from",
            "type": "address"
          },
          {
            "name": "to",
            "type": "address"
          },
          {
            "name": "amount",
            "type": "uint256"
          },
          {
            "name": "gasLimit",
            "type": "uint256"
          },
          {
            "name": "tokenGasPrice",
            "type": "uint256"
          },
          {
            "name": "baseGasCharge",
            "type": "uint256"
          },
          {
            "name": "tokenReceiver",
            "type": "address"
          },
          {
            "name": "data",
            "type": "bytes"
          }
        ],
        "name": "approveAndExecuteWithSpecificGasAndChargeForIt",
        "outputs": [
          {
            "name": "success",
            "type": "bool"
          },
          {
            "name": "returnData",
            "type": "bytes"
          }
        ],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
      },
      {
        "constant": false,
        "inputs": [
          {
            "name": "to",
            "type": "address"
          },
          {
            "name": "gasLimit",
            "type": "uint256"
          },
          {
            "name": "data",
            "type": "bytes"
          }
        ],
        "name": "executeWithSpecificGas",
        "outputs": [
          {
            "name": "success",
            "type": "bool"
          },
          {
            "name": "returnData",
            "type": "bytes"
          }
        ],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
      },
      {
        "constant": true,
        "inputs": [
          {
            "name": "owner",
            "type": "address"
          },
          {
            "name": "spender",
            "type": "address"
          }
        ],
        "name": "allowance",
        "outputs": [
          {
            "name": "remaining",
            "type": "uint256"
          }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
      },
      {
        "constant": false,
        "inputs": [
          {
            "name": "from",
            "type": "address"
          },
          {
            "name": "to",
            "type": "address"
          },
          {
            "name": "amount",
            "type": "uint256"
          },
          {
            "name": "gasLimit",
            "type": "uint256"
          },
          {
            "name": "tokenGasPrice",
            "type": "uint256"
          },
          {
            "name": "baseGasCharge",
            "type": "uint256"
          },
          {
            "name": "tokenReceiver",
            "type": "address"
          }
        ],
        "name": "transferAndChargeForGas",
        "outputs": [
          {
            "name": "",
            "type": "bool"
          }
        ],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
      },
      {
        "inputs": [
          {
            "name": "sandAdmin",
            "type": "address"
          },
          {
            "name": "executionAdmin",
            "type": "address"
          },
          {
            "name": "beneficiary",
            "type": "address"
          }
        ],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "constructor"
      },
      {
        "anonymous": false,
        "inputs": [
          {
            "indexed": true,
            "name": "from",
            "type": "address"
          },
          {
            "indexed": true,
            "name": "to",
            "type": "address"
          },
          {
            "indexed": false,
            "name": "value",
            "type": "uint256"
          }
        ],
        "name": "Transfer",
        "type": "event"
      },
      {
        "anonymous": false,
        "inputs": [
          {
            "indexed": true,
            "name": "owner",
            "type": "address"
          },
          {
            "indexed": true,
            "name": "spender",
            "type": "address"
          },
          {
            "indexed": false,
            "name": "value",
            "type": "uint256"
          }
        ],
        "name": "Approval",
        "type": "event"
      },
      {
        "anonymous": false,
        "inputs": [
          {
            "indexed": false,
            "name": "superOperator",
            "type": "address"
          },
          {
            "indexed": false,
            "name": "enabled",
            "type": "bool"
          }
        ],
        "name": "SuperOperator",
        "type": "event"
      },
      {
        "anonymous": false,
        "inputs": [
          {
            "indexed": false,
            "name": "oldAdmin",
            "type": "address"
          },
          {
            "indexed": false,
            "name": "newAdmin",
            "type": "address"
          }
        ],
        "name": "AdminChanged",
        "type": "event"
      },
      {
        "anonymous": false,
        "inputs": [
          {
            "indexed": false,
            "name": "oldAdmin",
            "type": "address"
          },
          {
            "indexed": false,
            "name": "newAdmin",
            "type": "address"
          }
        ],
        "name": "ExecutionAdminAdminChanged",
        "type": "event"
      },
      {
        "anonymous": false,
        "inputs": [
          {
            "indexed": false,
            "name": "executionOperator",
            "type": "address"
          },
          {
            "indexed": false,
            "name": "enabled",
            "type": "bool"
          }
        ],
        "name": "ExecutionOperator",
        "type": "event"
      }
    ],
    "devdoc": {
      "methods": {
        "allowance(address,address)": {
          "params": {
            "owner": "address whose token is allowed.",
            "spender": "address allowed to transfer."
          },
          "return": "the amount of token `spender` is allowed to transfer on behalf of `owner`."
        },
        "approve(address,uint256)": {
          "params": {
            "amount": "the number of tokens allowed.",
            "spender": "address to be given rights to transfer."
          },
          "return": "true if success."
        },
        "approveAndCall(address,uint256,bytes)": {
          "params": {
            "amount": "the number of tokens allowed.",
            "data": "bytes for the call.",
            "target": "address to be given rights to transfer and destination of the call."
          },
          "return": "data of the call."
        },
        "approveAndExecuteWithSpecificGas(address,address,uint256,uint256,bytes)": {
          "params": {
            "amount": "number of tokens allowed that can be transfer by the code at `to`.",
            "data": "the bytes sent to the destination address.",
            "from": "address of which token will be transfered.",
            "gasLimit": "exact amount of gas to be passed to the call.",
            "to": "destination address fo the call."
          },
          "return": "success whether the execution was successful.returnData data resulting from the execution."
        },
        "approveAndExecuteWithSpecificGasAndChargeForIt(address,address,uint256,uint256,uint256,uint256,address,bytes)": {
          "details": "the reason for this function is that charging for gas here is more gas-efficient than doing it in the caller.",
          "params": {
            "amount": "number of tokens allowed that can be transfer by the code at `to`.",
            "baseGasCharge": "amount of gas charged on top of the gas used for the call.",
            "data": "the bytes sent to the destination address.",
            "from": "address of which token will be transfered.",
            "gasLimit": "exact amount of gas to be passed to the call.",
            "to": "destination address fo the call.",
            "tokenGasPrice": "price in token for the gas to be charged.",
            "tokenReceiver": "recipient address of the token charged for the gas used."
          },
          "return": "success whether the execution was successful.returnData data resulting from the execution."
        },
        "approveFor(address,address,uint256)": {
          "params": {
            "amount": "the number of tokens allowed.",
            "owner": "address whose token is allowed.",
            "spender": "address to be given rights to transfer."
          },
          "return": "true if success."
        },
        "balanceOf(address)": {
          "params": {
            "owner": "The address to query the balance of."
          },
          "return": "The amount owned by `owner`."
        },
        "burn(uint256)": {
          "params": {
            "amount": "the number of tokens to burn."
          },
          "return": "true if success."
        },
        "burnFor(address,uint256)": {
          "params": {
            "amount": "the number of token to burn.",
            "owner": "address whose token is to burn."
          },
          "return": "true if success."
        },
        "changeAdmin(address)": {
          "params": {
            "newAdmin": "address of the new administrator."
          }
        },
        "changeExecutionAdmin(address)": {
          "params": {
            "newAdmin": "address of the new administrator."
          }
        },
        "decimals()": {
          "return": "the number of decimals."
        },
        "executeWithSpecificGas(address,uint256,bytes)": {
          "params": {
            "data": "the bytes sent to the destination address.",
            "gasLimit": "exact amount of gas to be passed to the call.",
            "to": "destination address fo the call."
          },
          "return": "success whether the execution was successful.returnData data resulting from the execution."
        },
        "getAdmin()": {
          "return": "the current administrator of this contract."
        },
        "getExecutionAdmin()": {
          "return": "address of the execution administrator."
        },
        "isExecutionOperator(address)": {
          "params": {
            "who": "The address to query."
          },
          "return": "whether the address has executionOperator rights."
        },
        "isSuperOperator(address)": {
          "params": {
            "who": "The address to query."
          },
          "return": "whether the address has superOperator rights."
        },
        "name()": {
          "return": "name of the tokens"
        },
        "paidCall(address,uint256,bytes)": {
          "params": {
            "amount": "the number of tokens allowed to spend.",
            "data": "bytes for the call.",
            "target": "destination of the call, allowed to spend the amount specified"
          },
          "return": "data of the call."
        },
        "setExecutionOperator(address,bool)": {
          "params": {
            "enabled": "set whether the executionOperator is enabled or disabled.",
            "executionOperator": "address that will be given/removed executionOperator right."
          }
        },
        "setSuperOperator(address,bool)": {
          "params": {
            "enabled": "set whether the superOperator is enabled or disabled.",
            "superOperator": "address that will be given/removed superOperator right."
          }
        },
        "symbol()": {
          "return": "symbol of the tokens"
        },
        "totalSupply()": {
          "return": "the total number of tokens in existence."
        },
        "transfer(address,uint256)": {
          "params": {
            "amount": "the number of tokens transfered.",
            "to": "the recipient address of the tokens transfered."
          },
          "return": "true if success."
        },
        "transferAndChargeForGas(address,address,uint256,uint256,uint256,uint256,address)": {
          "params": {
            "amount": "number of tokens allowed that can be transfer by the code at `to`.",
            "baseGasCharge": "amount of gas charged on top of the gas used for the call.",
            "from": "address of which token will be transfered.",
            "gasLimit": "exact amount of gas to be passed to the call.",
            "to": "destination address fo the call.",
            "tokenGasPrice": "price in token for the gas to be charged.",
            "tokenReceiver": "recipient address of the token charged for the gas used."
          },
          "return": "whether the transfer was successful."
        },
        "transferFrom(address,address,uint256)": {
          "params": {
            "amount": "the number of tokens transfered.",
            "from": "whose token it is transferring from.",
            "to": "the recipient address of the tokens transfered."
          },
          "return": "true if success."
        }
      }
    },
    "userdoc": {
      "methods": {
        "allowance(address,address)": {
          "notice": "gets allowance of `spender` for `owner`'s tokens."
        },
        "approve(address,uint256)": {
          "notice": "approve `spender` to transfer `amount` tokens."
        },
        "approveAndCall(address,uint256,bytes)": {
          "notice": "approve `target` to spend `amount` and call it with data."
        },
        "approveAndExecuteWithSpecificGas(address,address,uint256,uint256,bytes)": {
          "notice": "approve a specific amount of token for `from` and execute on behalf of the contract."
        },
        "approveAndExecuteWithSpecificGasAndChargeForIt(address,address,uint256,uint256,uint256,uint256,address,bytes)": {
          "notice": "approve a specific amount of token for `from` and execute on behalf of the contract. Plus charge the gas required to perform it."
        },
        "approveFor(address,address,uint256)": {
          "notice": "approve `spender` to transfer `amount` tokens from `owner`."
        },
        "balanceOf(address)": {
          "notice": "Gets the balance of `owner`."
        },
        "burn(uint256)": {
          "notice": "burn `amount` tokens."
        },
        "burnFor(address,uint256)": {
          "notice": "burn `amount` tokens from `owner`."
        },
        "changeAdmin(address)": {
          "notice": "change the administrator to be `newAdmin`."
        },
        "changeExecutionAdmin(address)": {
          "notice": "change the execution adminstrator to be `newAdmin`."
        },
        "decimals()": {
          "notice": "returns the number of decimals for that token."
        },
        "executeWithSpecificGas(address,uint256,bytes)": {
          "notice": "execute on behalf of the contract."
        },
        "getAdmin()": {
          "notice": "gives the current administrator of this contract."
        },
        "getExecutionAdmin()": {
          "notice": "give the address responsible for adding execution rights."
        },
        "isExecutionOperator(address)": {
          "notice": "check whether address `who` is given executionOperator rights."
        },
        "isSuperOperator(address)": {
          "notice": "check whether address `who` is given superOperator rights."
        },
        "name()": {
          "notice": "A descriptive name for the tokens"
        },
        "paidCall(address,uint256,bytes)": {
          "notice": "temporarly approve `target` to spend `amount` and call it with data. Previous approvals remains unchanged."
        },
        "setExecutionOperator(address,bool)": {
          "notice": "set `executionOperator` as executionOperator: `enabled`."
        },
        "setSuperOperator(address,bool)": {
          "notice": "Enable or disable the ability of `superOperator` to transfer tokens of all (superOperator rights)."
        },
        "symbol()": {
          "notice": "An abbreviated name for the tokens"
        },
        "totalSupply()": {
          "notice": "Gets the total number of tokens in existence."
        },
        "transfer(address,uint256)": {
          "notice": "Transfer `amount` tokens to `to`."
        },
        "transferAndChargeForGas(address,address,uint256,uint256,uint256,uint256,address)": {
          "notice": "transfer 1amount1 token from `from` to `to` and charge the gas required to perform that transfer."
        },
        "transferFrom(address,address,uint256)": {
          "notice": "Transfer `amount` tokens from `from` to `to`."
        }
      }
    }
  },
  "settings": {
    "compilationTarget": {
      "src/Sand.sol": "Sand"
    },
    "evmVersion": "petersburg",
    "libraries": {},
    "metadata": {
      "useLiteralContent": true
    },
    "optimizer": {
      "enabled": true,
      "runs": 2000
    },
    "remappings": []
  },
  "sources": {
    "contracts_common/src/BaseWithStorage/Admin.sol": {
      "content": "pragma solidity ^0.5.2;\n\ncontract Admin {\n\n    address internal _admin;\n\n    event AdminChanged(address oldAdmin, address newAdmin);\n\n    /// @notice gives the current administrator of this contract.\n    /// @return the current administrator of this contract.\n    function getAdmin() external view returns (address) {\n        return _admin;\n    }\n\n    /// @notice change the administrator to be `newAdmin`.\n    /// @param newAdmin address of the new administrator.\n    function changeAdmin(address newAdmin) external {\n        require(msg.sender == _admin, \"only admin can change admin\");\n        emit AdminChanged(_admin, newAdmin);\n        _admin = newAdmin;\n    }\n\n    modifier onlyAdmin() {\n        require (msg.sender == _admin, \"only admin allowed\");\n        _;\n    }\n\n}\n",
      "keccak256": "0xf3763fa108235379b48cf8fe9a708692fcb077250d9974351fd636ff0baf84bf"
    },
    "contracts_common/src/BaseWithStorage/SuperOperators.sol": {
      "content": "pragma solidity ^0.5.2;\n\nimport \"./Admin.sol\";\n\ncontract SuperOperators is Admin {\n\n    mapping(address => bool) internal _superOperators;\n\n    event SuperOperator(address superOperator, bool enabled);\n\n    /// @notice Enable or disable the ability of `superOperator` to transfer tokens of all (superOperator rights).\n    /// @param superOperator address that will be given/removed superOperator right.\n    /// @param enabled set whether the superOperator is enabled or disabled.\n    function setSuperOperator(address superOperator, bool enabled) external {\n        require(\n            msg.sender == _admin,\n            \"only admin is allowed to add super operators\"\n        );\n        _superOperators[superOperator] = enabled;\n        emit SuperOperator(superOperator, enabled);\n    }\n\n    /// @notice check whether address `who` is given superOperator rights.\n    /// @param who The address to query.\n    /// @return whether the address has superOperator rights.\n    function isSuperOperator(address who) public view returns (bool) {\n        return _superOperators[who];\n    }\n}\n",
      "keccak256": "0x22354cf60ccf77a6de61c13bdaf4e3094d115b960ae563b0527622c846a12abb"
    },
    "contracts_common/src/Interfaces/ERC20Events.sol": {
      "content": "pragma solidity ^0.5.2;\n\n/* interface */\ncontract ERC20Events {\n    event Transfer(address indexed from, address indexed to, uint256 value);\n    event Approval(\n        address indexed owner,\n        address indexed spender,\n        uint256 value\n    );\n}\n",
      "keccak256": "0xee09c10a3f9adc913227efdcd94ce44e05b59b04717f8f868153d356bd03f5d2"
    },
    "contracts_common/src/Libraries/BytesUtil.sol": {
      "content": "pragma solidity ^0.5.2;\n\nlibrary BytesUtil {\n    function memcpy(uint256 dest, uint256 src, uint256 len) internal pure {\n        // Copy word-length chunks while possible\n        for (; len >= 32; len -= 32) {\n            assembly {\n                mstore(dest, mload(src))\n            }\n            dest += 32;\n            src += 32;\n        }\n\n        // Copy remaining bytes\n        uint256 mask = 256**(32 - len) - 1;\n        assembly {\n            let srcpart := and(mload(src), not(mask))\n            let destpart := and(mload(dest), mask)\n            mstore(dest, or(destpart, srcpart))\n        }\n    }\n\n    function pointerToBytes(uint256 src, uint256 len)\n        internal\n        pure\n        returns (bytes memory)\n    {\n        bytes memory ret = new bytes(len);\n        uint256 retptr;\n        assembly {\n            retptr := add(ret, 32)\n        }\n\n        memcpy(retptr, src, len);\n        return ret;\n    }\n\n    function addressToBytes(address a) internal pure returns (bytes memory b) {\n        assembly {\n            let m := mload(0x40)\n            mstore(\n                add(m, 20),\n                xor(0x140000000000000000000000000000000000000000, a)\n            )\n            mstore(0x40, add(m, 52))\n            b := m\n        }\n    }\n\n    function uint256ToBytes(uint256 a) internal pure returns (bytes memory b) {\n        assembly {\n            let m := mload(0x40)\n            mstore(add(m, 32), a)\n            mstore(0x40, add(m, 64))\n            b := m\n        }\n    }\n\n    function doFirstParamEqualsAddress(bytes memory data, address _address)\n        internal\n        pure\n        returns (bool)\n    {\n        if (data.length < (36 + 32)) {\n            return false;\n        }\n        uint256 value;\n        assembly {\n            value := mload(add(data, 36))\n        }\n        return value == uint256(_address);\n    }\n\n    function doParamEqualsUInt256(bytes memory data, uint256 i, uint256 value)\n        internal\n        pure\n        returns (bool)\n    {\n        if (data.length < (36 + (i + 1) * 32)) {\n            return false;\n        }\n        uint256 offset = 36 + i * 32;\n        uint256 valuePresent;\n        assembly {\n            valuePresent := mload(add(data, offset))\n        }\n        return valuePresent == value;\n    }\n\n    function overrideFirst32BytesWithAddress(\n        bytes memory data,\n        address _address\n    ) internal pure returns (bytes memory) {\n        uint256 dest;\n        assembly {\n            dest := add(data, 48)\n        } // 48 = 32 (offset) + 4 (func sig) + 12 (address is only 20 bytes)\n\n        bytes memory addressBytes = addressToBytes(_address);\n        uint256 src;\n        assembly {\n            src := add(addressBytes, 32)\n        }\n\n        memcpy(dest, src, 20);\n        return data;\n    }\n\n    function overrideFirstTwo32BytesWithAddressAndInt(\n        bytes memory data,\n        address _address,\n        uint256 _value\n    ) internal pure returns (bytes memory) {\n        uint256 dest;\n        uint256 src;\n\n        assembly {\n            dest := add(data, 48)\n        } // 48 = 32 (offset) + 4 (func sig) + 12 (address is only 20 bytes)\n        bytes memory bbytes = addressToBytes(_address);\n        assembly {\n            src := add(bbytes, 32)\n        }\n        memcpy(dest, src, 20);\n\n        assembly {\n            dest := add(data, 68)\n        } // 48 = 32 (offset) + 4 (func sig) + 32 (next slot)\n        bbytes = uint256ToBytes(_value);\n        assembly {\n            src := add(bbytes, 32)\n        }\n        memcpy(dest, src, 32);\n\n        return data;\n    }\n}\n",
      "keccak256": "0xb5c236938e1f71524127371a015836e83103c5ac1b4ed7b7f22892a8c0429e10"
    },
    "src/Sand.sol": {
      "content": "pragma solidity 0.5.9;\n\nimport \"./Sand/erc20/ERC20ExecuteExtension.sol\";\nimport \"./Sand/erc20/ERC20BaseToken.sol\";\nimport \"./Sand/erc20/ERC20BasicApproveExtension.sol\";\n\ncontract Sand is ERC20ExecuteExtension, ERC20BasicApproveExtension, ERC20BaseToken {\n\n    constructor(address sandAdmin, address executionAdmin, address beneficiary) public {\n        _admin = sandAdmin;\n        _executionAdmin = executionAdmin;\n        _mint(beneficiary, 3000000000000000000000000000);\n    }\n\n    /// @notice A descriptive name for the tokens\n    /// @return name of the tokens\n    function name() public view returns (string memory) {\n        return \"SAND\";\n    }\n\n    /// @notice An abbreviated name for the tokens\n    /// @return symbol of the tokens\n    function symbol() public view returns (string memory) {\n        return \"SAND\";\n    }\n\n}\n",
      "keccak256": "0xe05c8232fddc05f34648fa1db29cbfff2d163357e613425a925d80dbefff75eb"
    },
    "src/Sand/erc20/ERC20BaseToken.sol": {
      "content": "pragma solidity 0.5.9;\n\nimport \"../../../contracts_common/src/Interfaces/ERC20Events.sol\";\nimport \"../../../contracts_common/src/BaseWithStorage/SuperOperators.sol\";\n\ncontract ERC20BaseToken is SuperOperators, ERC20Events {\n\n    uint256 internal _totalSupply;\n    mapping(address => uint256) internal _balances;\n    mapping(address => mapping(address => uint256)) internal _allowances;\n\n    /// @notice Gets the total number of tokens in existence.\n    /// @return the total number of tokens in existence.\n    function totalSupply() public view returns (uint256) {\n        return _totalSupply;\n    }\n\n    /// @notice Gets the balance of `owner`.\n    /// @param owner The address to query the balance of.\n    /// @return The amount owned by `owner`.\n    function balanceOf(address owner) public view returns (uint256) {\n        return _balances[owner];\n    }\n\n    /// @notice gets allowance of `spender` for `owner`'s tokens.\n    /// @param owner address whose token is allowed.\n    /// @param spender address allowed to transfer.\n    /// @return the amount of token `spender` is allowed to transfer on behalf of `owner`.\n    function allowance(address owner, address spender)\n        public\n        view\n        returns (uint256 remaining)\n    {\n        return _allowances[owner][spender];\n    }\n\n    /// @notice returns the number of decimals for that token.\n    /// @return the number of decimals.\n    function decimals() public view returns (uint8) {\n        return uint8(18);\n    }\n\n    /// @notice Transfer `amount` tokens to `to`.\n    /// @param to the recipient address of the tokens transfered.\n    /// @param amount the number of tokens transfered.\n    /// @return true if success.\n    function transfer(address to, uint256 amount)\n        public\n        returns (bool success)\n    {\n        _transfer(msg.sender, to, amount);\n        return true;\n    }\n\n    /// @notice Transfer `amount` tokens from `from` to `to`.\n    /// @param from whose token it is transferring from.\n    /// @param to the recipient address of the tokens transfered.\n    /// @param amount the number of tokens transfered.\n    /// @return true if success.\n    function transferFrom(address from, address to, uint256 amount)\n        public\n        returns (bool success)\n    {\n        if (msg.sender != from && !_superOperators[msg.sender]) {\n            uint256 currentAllowance = _allowances[from][msg.sender];\n            if (currentAllowance != (2**256) - 1) {\n                // save gas when allowance is maximal by not reducing it (see https://github.com/ethereum/EIPs/issues/717)\n                require(currentAllowance >= amount, \"Not enough funds allowed\");\n                _allowances[from][msg.sender] = currentAllowance - amount;\n            }\n        }\n        _transfer(from, to, amount);\n        return true;\n    }\n\n    /// @notice burn `amount` tokens.\n    /// @param amount the number of tokens to burn.\n    /// @return true if success.\n    function burn(uint256 amount) external returns (bool) {\n        _burn(msg.sender, amount);\n        return true;\n    }\n\n    /// @notice burn `amount` tokens from `owner`.\n    /// @param owner address whose token is to burn.\n    /// @param amount the number of token to burn.\n    /// @return true if success.\n    function burnFor(address owner, uint256 amount) external returns (bool) {\n        _burn(owner, amount);\n        return true;\n    }\n\n    /// @notice approve `spender` to transfer `amount` tokens.\n    /// @param spender address to be given rights to transfer.\n    /// @param amount the number of tokens allowed.\n    /// @return true if success.\n    function approve(address spender, uint256 amount)\n        public\n        returns (bool success)\n    {\n        _approveFor(msg.sender, spender, amount);\n        return true;\n    }\n\n    /// @notice approve `spender` to transfer `amount` tokens from `owner`.\n    /// @param owner address whose token is allowed.\n    /// @param spender  address to be given rights to transfer.\n    /// @param amount the number of tokens allowed.\n    /// @return true if success.\n    function approveFor(address owner, address spender, uint256 amount)\n        public\n        returns (bool success)\n    {\n        require(\n            msg.sender == owner || _superOperators[msg.sender],\n            \"msg.sender != owner && !superOperator\"\n        );\n        _approveFor(owner, spender, amount);\n        return true;\n    }\n\n    function addAllowanceIfNeeded(address owner, address spender, uint256 amountNeeded)\n        public\n        returns (bool success)\n    {\n        require(\n            msg.sender == owner || _superOperators[msg.sender],\n            \"msg.sender != owner && !superOperator\"\n        );\n        _addAllowanceIfNeeded(owner, spender, amountNeeded);\n        return true;\n    }\n\n    function _addAllowanceIfNeeded(address owner, address spender, uint256 amountNeeded)\n        internal\n    {\n        if(amountNeeded > 0 && !isSuperOperator(spender)) {\n            uint256 currentAllowance = _allowances[owner][spender];\n            if(currentAllowance < amountNeeded) {\n                _approveFor(owner, spender, amountNeeded);\n            }\n        }\n    }\n\n    function _approveFor(address owner, address spender, uint256 amount)\n        internal\n    {\n        require(\n            owner != address(0) && spender != address(0),\n            \"Cannot approve with 0x0\"\n        );\n        _allowances[owner][spender] = amount;\n        emit Approval(owner, spender, amount);\n    }\n\n    function _transfer(address from, address to, uint256 amount) internal {\n        require(to != address(0), \"Cannot send to 0x0\");\n        uint256 currentBalance = _balances[from];\n        require(currentBalance >= amount, \"not enough fund\");\n        _balances[from] = currentBalance - amount;\n        _balances[to] += amount;\n        emit Transfer(from, to, amount);\n    }\n\n    function _mint(address to, uint256 amount) internal {\n        require(to != address(0), \"Cannot mint to 0x0\");\n        require(amount > 0, \"cannot mint 0 tokens\");\n        uint256 currentTotalSupply = _totalSupply;\n        uint256 newTotalSupply = currentTotalSupply + amount;\n        require(newTotalSupply > currentTotalSupply, \"overflow\");\n        _totalSupply = newTotalSupply;\n        _balances[to] += amount;\n        emit Transfer(address(0), to, amount);\n    }\n\n    function _burn(address from, uint256 amount) internal {\n        require(amount > 0, \"cannot burn 0 tokens\");\n        if (msg.sender != from && !_superOperators[msg.sender]) {\n            uint256 currentAllowance = _allowances[from][msg.sender];\n            require(\n                currentAllowance >= amount,\n                \"Not enough funds allowed\"\n            );\n            if (currentAllowance != (2**256) - 1) {\n                // save gas when allowance is maximal by not reducing it (see https://github.com/ethereum/EIPs/issues/717)\n                _allowances[from][msg.sender] = currentAllowance - amount;\n            }\n        }\n\n        uint256 currentBalance = _balances[from];\n        require(currentBalance >= amount, \"Not enough funds\");\n        _balances[from] = currentBalance - amount;\n        _totalSupply -= amount;\n        emit Transfer(from, address(0), amount);\n    }\n}\n",
      "keccak256": "0xc70b1452852cbe8f437ba3fdcff911ed3fd7820b0d853e5fe147a90989cc1085"
    },
    "src/Sand/erc20/ERC20BasicApproveExtension.sol": {
      "content": "pragma solidity 0.5.9;\n\nimport \"../../../contracts_common/src/Libraries/BytesUtil.sol\";\n\ncontract ERC20BasicApproveExtension {\n\n    /// @notice approve `target` to spend `amount` and call it with data.\n    /// @param target address to be given rights to transfer and destination of the call.\n    /// @param amount the number of tokens allowed.\n    /// @param data bytes for the call.\n    /// @return data of the call.\n    function approveAndCall(\n        address target,\n        uint256 amount,\n        bytes calldata data\n    ) external payable returns (bytes memory) {\n        require(\n            BytesUtil.doFirstParamEqualsAddress(data, msg.sender),\n            \"first param != sender\"\n        );\n\n        _approveFor(msg.sender, target, amount);\n\n        // solium-disable-next-line security/no-call-value\n        (bool success, bytes memory returnData) = target.call.value(msg.value)(data);\n        require(success, string(returnData));\n        return returnData;\n    }\n\n    /// @notice temporarly approve `target` to spend `amount` and call it with data. Previous approvals remains unchanged.\n    /// @param target destination of the call, allowed to spend the amount specified\n    /// @param amount the number of tokens allowed to spend.\n    /// @param data bytes for the call.\n    /// @return data of the call.\n    function paidCall(\n        address target,\n        uint256 amount,\n        bytes calldata data\n    ) external payable returns (bytes memory) {\n        require(\n            BytesUtil.doFirstParamEqualsAddress(data, msg.sender),\n            \"first param != sender\"\n        );\n\n        if (amount > 0) {\n            _addAllowanceIfNeeded(msg.sender, target, amount);\n        }\n\n        // solium-disable-next-line security/no-call-value\n        (bool success, bytes memory returnData) = target.call.value(msg.value)(data);\n        require(success, string(returnData));\n\n        return returnData;\n    }\n\n    function _approveFor(address owner, address target, uint256 amount) internal;\n    function _addAllowanceIfNeeded(address owner, address spender, uint256 amountNeeded) internal;\n}\n",
      "keccak256": "0xbd8c882843acf82adb849222068490302925e31ac66305b75f1663a719c7fc37"
    },
    "src/Sand/erc20/ERC20ExecuteExtension.sol": {
      "content": "pragma solidity 0.5.9;\n\n\ncontract ERC20ExecuteExtension {\n\n    /// @dev _executionAdmin != _admin so that this super power can be disabled independently\n    address internal _executionAdmin;\n\n    event ExecutionAdminAdminChanged(address oldAdmin, address newAdmin);\n\n    /// @notice give the address responsible for adding execution rights.\n    /// @return address of the execution administrator.\n    function getExecutionAdmin() external view returns (address) {\n        return _executionAdmin;\n    }\n\n    /// @notice change the execution adminstrator to be `newAdmin`.\n    /// @param newAdmin address of the new administrator.\n    function changeExecutionAdmin(address newAdmin) external {\n        require(msg.sender == _executionAdmin, \"only executionAdmin can change executionAdmin\");\n        emit ExecutionAdminAdminChanged(_executionAdmin, newAdmin);\n        _executionAdmin = newAdmin;\n    }\n\n    mapping(address => bool) internal _executionOperators;\n    event ExecutionOperator(address executionOperator, bool enabled);\n\n    /// @notice set `executionOperator` as executionOperator: `enabled`.\n    /// @param executionOperator address that will be given/removed executionOperator right.\n    /// @param enabled set whether the executionOperator is enabled or disabled.\n    function setExecutionOperator(address executionOperator, bool enabled) external {\n        require(\n            msg.sender == _executionAdmin,\n            \"only execution admin is allowed to add execution operators\"\n        );\n        _executionOperators[executionOperator] = enabled;\n        emit ExecutionOperator(executionOperator, enabled);\n    }\n\n    /// @notice check whether address `who` is given executionOperator rights.\n    /// @param who The address to query.\n    /// @return whether the address has executionOperator rights.\n    function isExecutionOperator(address who) public view returns (bool) {\n        return _executionOperators[who];\n    }\n\n    /// @notice execute on behalf of the contract.\n    /// @param to destination address fo the call.\n    /// @param gasLimit exact amount of gas to be passed to the call.\n    /// @param data the bytes sent to the destination address.\n    /// @return success whether the execution was successful.\n    /// @return returnData data resulting from the execution.\n    function executeWithSpecificGas(address to, uint256 gasLimit, bytes calldata data) external returns (bool success, bytes memory returnData) {\n        require(_executionOperators[msg.sender], \"only execution operators allowed to execute on SAND behalf\");\n        (success, returnData) = to.call.gas(gasLimit)(data);\n        assert(gasleft() > gasLimit / 63); // not enough gas provided, assert to throw all gas // TODO use EIP-1930\n    }\n\n    /// @notice approve a specific amount of token for `from` and execute on behalf of the contract.\n    /// @param from address of which token will be transfered.\n    /// @param to destination address fo the call.\n    /// @param amount number of tokens allowed that can be transfer by the code at `to`.\n    /// @param gasLimit exact amount of gas to be passed to the call.\n    /// @param data the bytes sent to the destination address.\n    /// @return success whether the execution was successful.\n    /// @return returnData data resulting from the execution.\n    function approveAndExecuteWithSpecificGas(\n        address from,\n        address to,\n        uint256 amount,\n        uint256 gasLimit,\n        bytes calldata data\n    ) external returns (bool success, bytes memory returnData) {\n        require(_executionOperators[msg.sender], \"only execution operators allowed to execute on SAND behalf\");\n        return _approveAndExecuteWithSpecificGas(from, to, amount, gasLimit, data);\n    }\n\n    /// @dev the reason for this function is that charging for gas here is more gas-efficient than doing it in the caller.\n    /// @notice approve a specific amount of token for `from` and execute on behalf of the contract. Plus charge the gas required to perform it.\n    /// @param from address of which token will be transfered.\n    /// @param to destination address fo the call.\n    /// @param amount number of tokens allowed that can be transfer by the code at `to`.\n    /// @param gasLimit exact amount of gas to be passed to the call.\n    /// @param tokenGasPrice price in token for the gas to be charged.\n    /// @param baseGasCharge amount of gas charged on top of the gas used for the call.\n    /// @param tokenReceiver recipient address of the token charged for the gas used.\n    /// @param data the bytes sent to the destination address.\n    /// @return success whether the execution was successful.\n    /// @return returnData data resulting from the execution.\n    function approveAndExecuteWithSpecificGasAndChargeForIt(\n        address from,\n        address to,\n        uint256 amount,\n        uint256 gasLimit,\n        uint256 tokenGasPrice,\n        uint256 baseGasCharge,\n        address tokenReceiver,\n        bytes calldata data\n    ) external returns (bool success, bytes memory returnData) {\n        uint256 initialGas = gasleft();\n        require(_executionOperators[msg.sender], \"only execution operators allowed to execute on SAND behalf\");\n        (success, returnData) = _approveAndExecuteWithSpecificGas(from, to, amount, gasLimit, data);\n        if (tokenGasPrice > 0) {\n            _charge(from, gasLimit, tokenGasPrice, initialGas, baseGasCharge, tokenReceiver);\n        }\n    }\n\n    /// @notice transfer 1amount1 token from `from` to `to` and charge the gas required to perform that transfer.\n    /// @param from address of which token will be transfered.\n    /// @param to destination address fo the call.\n    /// @param amount number of tokens allowed that can be transfer by the code at `to`.\n    /// @param gasLimit exact amount of gas to be passed to the call.\n    /// @param tokenGasPrice price in token for the gas to be charged.\n    /// @param baseGasCharge amount of gas charged on top of the gas used for the call.\n    /// @param tokenReceiver recipient address of the token charged for the gas used.\n    /// @return whether the transfer was successful.\n    function transferAndChargeForGas(\n        address from,\n        address to,\n        uint256 amount,\n        uint256 gasLimit,\n        uint256 tokenGasPrice,\n        uint256 baseGasCharge,\n        address tokenReceiver\n    ) external returns (bool) {\n        uint256 initialGas = gasleft();\n        require(_executionOperators[msg.sender], \"only execution operators allowed to perfrom transfer and charge\");\n        _transfer(from, to, amount);\n        if (tokenGasPrice > 0) {\n            _charge(from, gasLimit, tokenGasPrice, initialGas, baseGasCharge, tokenReceiver);\n        }\n        return true;\n    }\n\n    function _charge(\n        address from,\n        uint256 gasLimit,\n        uint256 tokenGasPrice,\n        uint256 initialGas,\n        uint256 baseGasCharge,\n        address tokenReceiver\n    ) internal {\n        uint256 gasCharge = initialGas - gasleft();\n        if(gasCharge > gasLimit) {\n            gasCharge = gasLimit;\n        }\n        gasCharge += baseGasCharge;\n        uint256 tokensToCharge = gasCharge * tokenGasPrice;\n        require(tokensToCharge / gasCharge == tokenGasPrice, \"overflow\");\n        _transfer(from, tokenReceiver, tokensToCharge);\n    }\n\n    function _approveAndExecuteWithSpecificGas(\n        address from,\n        address to,\n        uint256 amount,\n        uint256 gasLimit,\n        bytes memory data\n    ) internal returns (bool success, bytes memory returnData) {\n\n        if (amount > 0) {\n            _addAllowanceIfNeeded(from, to, amount);\n        }\n        (success, returnData) = to.call.gas(gasLimit)(data);\n        assert(gasleft() > gasLimit / 63); // not enough gas provided, assert to throw all gas // TODO use EIP-1930\n    }\n\n\n    function _transfer(address from, address to, uint256 amount) internal;\n    function _addAllowanceIfNeeded(address owner, address spender, uint256 amountNeeded) internal;\n}",
      "keccak256": "0x4b9a75299ab80e84aa287f2ecf2ef175e0feb074764e4a670f9397a2d2d4bda8"
    }
  },
  "version": 1
}

View in Huly HI-8

Better error reporting

The server-side component should report why something went wrong and how much it was able to do so far.

View in Huly HI-29

Create "health" endpoint on server

Create new endpoint inside server.js For example: app.get('/health', (req, res) => res.send("I'm alive"))

This endpoint will be pinged every minute or two.

Also, create docker-compose for monitor.js that automatically restarts monitor if it fails.

View in Huly HI-10

Offer sync interface

We should offer an interface so that other people can clone and host the same data via ipfs.

View in Huly HI-408

use chainId instead of network name

currently it looks like this:

image

it would be better if chainId's are used here instead of network names. Because if a wallet wants to look up contracts here it then needs a mapping between chainId's and the network names used here. Would be nice to not need this mapping.

View in Huly HI-46

Add test for monitor.js

Would be nice to have a check of this before restructuring the project and converting to typescript. Might look like:

  1. Launch testrpc
  2. Launch monitor, watching localhost
  3. Launch local ipfs node --offline
  4. Deploy contracts to testrpc
  5. Publish contracts to local ipfs node
  6. Verify address is put in addressDB
  7. Verify sources are published to repository

View in Huly HI-15

UI crashes on error

Server:

Error: Error: Given address "0x00000000000000000000" is not a valid Ethereum address.

UI:

react-error-crash

View in Huly HI-14

Update README

Update README with steps on how to deploy locally with and without docker and docker-compose

View in Huly HI-19

UI CSS

Tracking issue to look over ui css and identify any small things to normalize.

View in Huly HI-48

Improve queues

The various components here move data around in queues. We should have a generic architecture for that.

View in Huly HI-265

Restructure repo

One way the project could be restructured prior to converting to TS might be:

.circleci
Dockerfile
public /
src/
  address-db.js
  injector.js
  monitor.js
  server.js
  utils.js  # Duplicate fns
scripts/
  run.sh
  run_monitor.sh
  run_server.sh
test /
  .. etc ..

Some files seem experimental (or incidental) and might be removed / stored away.

  • index.js (?)
  • loop.sh
  • verify.js

Shell scripts could be aliased to npm scripts

"scripts": {
    "docker:all": "./scripts/run.sh",
    "monitor": "./scripts/run_monitor.sh",
    "server": "./scripts/run_server.sh",
}

View in Huly HI-16

Creation bytecode

Mention that because we do not compare with the creation bytecode, the constructor code could be "fake". When only dealing with abi and natspec, we are anyway trusting the developer / deployer, so that is not such a big deal, it is only important to know when we check the source code.

View in Huly HI-218

Updateable code hash database

The service currently uses a static dump of the code hash -> addresses mapping made at some point in time. This database should update itself and work on mainnet and all testnets.

View in Huly HI-135

Handle simultaneous file writes

In a special case where both monitor and server try to write to the same file simultaneously unpredictable stuff could happen with both server and monitor containers

View in Huly HI-34

Improve multi-file upload

The file upload mechanism should be done in client-side javascript to allow for the following:

  • select multiple files for upload
  • determine client-side which files are metadata files
  • try to discover the linked source files in the list
  • report which source files are missing
  • be able to work with multiple metadata files at the same time

Some of these tasks could also be done server-side, but the state should be kept on the client side.

It should still be possible to work with "dumb clients", i.e. external file uploads not using the client-side javascript, so some functionality should be put in modules that can be used both on the client and on the server side.

View in Huly HI-24

Organize documentation

At some point it could make sense to have a simple ReadTheDocs explaining

  • What metadata is
  • How the services / endpoints work
  • What tools are available to help upload sources & metadata to the repository
  • How to access and consume data in the repository
    • perfect matches vs. partial_matches
  • How to deploy your own version of the service

View in Huly HI-266

Refactor code

This repository currently contains quite some code repetition. It should be refactored.

This includes extracting the shared components in the monitor and the injector and rewriting them in typescript.

View in Huly HI-23

Metamask plugin

(Following up from discussion in the Jan 8 source-verify meeting)

In the 'trust gap' slides there's a mockup of a metamask confirmation screen as an example of how source-verify could be used.

Screen Shot 2020-01-08 at 3 51 56 PM

Below are notes about Metamask's plugin system (in beta).

Resources

The wallet currently decodes some transaction data using Parity's 4Byte registry and an API at eth-method-registry. It is

  • rendered in the transaction confirmation window's data tab. (code ref).
  • a JSON stringified object placed in <pre></pre> code block - e.g can be anything.
  • supplied to the wallet by an api method called addKnownMethod, exposed with user permission to the plugin here

A source-verify plugin would call the backend with the transaction data of a proposed tx and its target address. Maybe something like:

const data = await verifier.parseData(tx.data, address, networkInfo)

{
  name: register,
  function: "register(string name, uint duration)",
  arguments: {
    name: "meetup.berlin.eth",
    duration: "1 year"
  },
  description: "Registers the name meetup.berlin.eth for 1 year"
}

_metamask.addKnownMethod(data.name, data)

Other

  • No sources link, but it's possible to open pull requests on the beta to add UI features.
  • Small api for "recipient address auditing" with special rendering described here.

View in Huly HI-321

Nicer file upload

The look and feel of the file upload page should be improved. We should have a drag and drop area and in general, better input fields.

View in Huly HI-11

Stable multi-file upload

The multi-file upload is currently rather unstable, it works on some browsers sometimes, but not always. It might be necessary to run custom ajax posts.

View in Huly HI-12

Improve file browser

We should offer a better way to browse the source code. It should not be too fancy, but currently, the directory listing is not really nice.

  • improve directory listing
  • display source code with syntax highlighting

At the same time, a raw interface should also be supported, so that people can retrieve the unmodified source or ABI file, for example.

View in Huly HI-20

Receiving "Error when using http://komputing.org:8081

Issue: "Error: Metadata file not found. Did you include "metadata.json"?" when using the UI

Screen shot of inputs:

sourceverify

System : Tried on both Chrome and Firefox same issue

metadata.json was generated using solc --metadata --metadata-literal

as well as passed it into source-verify/index.js < metadata.json with output

Metadata MATCHES!
Bytecode comparison NOT performed - has to be done manually.

Sample contract used was 0xd2006b3452ff12ce48d018ff48e0df76bca6ee04 where we've already uploaded the source to ipfs here: https://ipfs.infura.io/ipfs/Qmbu6TMyKCH9HJVvhG6BFA3UkrJuGVdZjhFQqKRRKmsNyD

View in Huly HI-7

CLI

(Following up on discussion in the Jan 8 source-verify meeting)

Source submission could be simplified by publishing a cli utility. Buidler and Truffle both support plugins and collecting the sources / generating the metadata would be easy to implement in that format.

Install

$ npm install --save-dev source-verify

Configure

// truffle-config.js
module.exports = {
  plugins: ['source-verify'],
  networks: {...}
}

Run

$ truffle run source-verify

> Generating metadata... 
> ======================
> - A.sol
> - B.sol
> 
> Uploading
> =========
> Your sources should be verified in a few minutes. Check at:
> Metadata: https://gateway.pinata.cloud/ipfs/QmYVd8qstdXtTd1quwv4nJen6XprykxQRLo67Jy7WyiLMB
> Sources: https://gateway.pinata.cloud/ipfs/QmYVd8qiwendlgdsfnJen6XprykxQRLo67Jy(WyiLMC

$ truffle run source-verify --fetch QmYVd8qstdXtTd1quwv4nJen6XprykxQRLo67Jy7WyiLMB

> {
>  ...metadata
> }

Other

Check if the local version of a contract matches one on chain (to validate a simulation).

truffle run source-verify \
  --diff \
  --local build/contracts/ENSRegistry.json \
  --network mainnet \
  --address 0xabc....de \

> - 0.4.23+commit.124ca40d // Mainnet
> + 0.5.12+commit.7709ece9 // Local

View in Huly HI-85

Paths in metadata

(Following up on discussion in the Jan 8 source-verify meeting)

Am wondering about how the metadata is hashed and the importance of the source file paths. (Apologies in advance if I've misunderstood this...)

In the solidity docs there is a warning:

Since the bytecode of the resulting contract contains the metadata hash by default, any change to the metadata might result in a change of the bytecode. This includes changes to a filename or path, and since the metadata includes a hash of all the sources used, a single whitespace change results in different metadata, and different bytecode.

For example, A.sol, compiled with truffle compile and deployed using the data in the truffle artifact, has metadata source keys like:

"sources": {
  "/Users/cgewecke/code/ef/sv-truffle/contracts/A.sol": {
   "keccak256": "0xfc68a9ffdb0d7d3ec5456f5e2d2c4f855e684892f6c78fcb0dc1e482696d9bb7",
   "urls": [
    "bzz-raw://4d22457fe6215d956a57fcd0cb63f745d8fd718ba8d85a20d65c72232038779f",
    "dweb:/ipfs/QmPmUahXAjtq5mJTW9Qc6oNUVGEuocxb1un5371zLJAAb5"
   ]
  }
},

Running solc --metadata contracts/A.sol over the same source produces

"sources": {
  "contracts/A.sol": {
   "keccak256": "0xfc68a9ffdb0d7d3ec5456f5e2d2c4f855e684892f6c78fcb0dc1e482696d9bb7",
   "urls": [
    "bzz-raw://4d22457fe6215d956a57fcd0cb63f745d8fd718ba8d85a20d65c72232038779f",
    "dweb:/ipfs/QmPmUahXAjtq5mJTW9Qc6oNUVGEuocxb1un5371zLJAAb5"
   ]
  }
},

It seems like the file system context and tooling of the deployment and metadata compilations are not allowed to differ even when the sources are identical.

Not sure this would work but perhaps if the file's keccak hash was used as a key instead, metadata could be matched to sources from anywhere as long as paths were associated with the files when uploading to the service for JSON-IO compilation.

If metadata was pathless, you could:

  • have a source-verify cli tool set up to run in CI on merge to a release branch
  • have a bot that searched Github for Solidity projects, cloning and compiling them, uploading all the metadata to SV to build a large sources DB.

View in Huly HI-209

Add download from ipfs

Currently, files are only downloaded from swarm by default. We should also support ipfs links.

View in Huly HI-21

Make "next" branch

It might be useful to have a base branch that new work is targeted at, preserving the current project state until it's safe to merge.

View in Huly HI-9

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.