diff --git a/Declarator.sol b/Declarator.sol new file mode 100644 index 0000000..c4a78d3 --- /dev/null +++ b/Declarator.sol @@ -0,0 +1,15 @@ +pragma solidity >=0.6.12; + +// Author: Louis Holbrook 0826EDA1702D1E87C6E2875121D2E7BB88C2A746 +// SPDX-License-Identifier: GPL-3.0-or-later +// File-version: 1 +// Description: The ERC20 standard interface as specified in EIP20 (sha256:9f843cbb25a737c9351b0b6a6f54b86864490d0d5284f6877b4929d481d34312) + +interface AddressDeclarator { + function addDeclaration(address _address, bytes32 _proof) external returns ( bytes32 ); + function declaratorCount(address _objectAddress) external view returns ( uint256 ); + function declaratorAddressAt(address _targetAddress, uint256 _idx) external view returns ( address ); + function declaration(address _subjectAddress, address _objectAddress) external view returns ( bytes32[] memory ); + function declarationCount(address _subjectAddress) external view returns ( uint256 ); + function declarationAddressAt(address _subjectAddress, uint256 _idx) external view returns ( address ); +} diff --git a/ERC20.txt b/ERC20.txt new file mode 100644 index 0000000..a9cf372 --- /dev/null +++ b/ERC20.txt @@ -0,0 +1,6 @@ +totalSupply() +balanceOf(address) +allowance(address,address) +transfer(address,uint) +approve(address,uint) +transferFrom(address,address,uint) diff --git a/Faucet.sol b/Faucet.sol index faa123e..a2ad188 100644 --- a/Faucet.sol +++ b/Faucet.sol @@ -2,16 +2,17 @@ pragma solidity >=0.6.12; // Author: Louis Holbrook 0826EDA1702D1E87C6E2875121D2E7BB88C2A746 // SPDX-License-Identifier: GPL-3.0-or-later -// File-version: 3 +// File-version: 4 interface Faucet { event FaucetUsed(address indexed _recipient, address indexed _token, uint256 _value); event FaucetFail(address indexed _recipient, address indexed _token, uint256 _value); + event FaucetAmountChange(address indexed _token, uint256 _value); function amount() external view returns (uint256); function token() external view returns (address); function setAmount(uint256 _amount) external returns (bool); function giveTo(address _recipient) external returns (bool); - function gimme() external returns (bool); + function cooldown(address _recipient) external returns (uint256); } diff --git a/RegistryClient.sol b/RegistryClient.sol index 279a1dc..bcf5798 100644 --- a/RegistryClient.sol +++ b/RegistryClient.sol @@ -8,4 +8,5 @@ pragma solidity >=0.6.12; interface RegistryClient { function registryCount() external view returns (uint256); function addressOf(bytes32) external view returns (address); + function entry(uint256) external view returns (address); } diff --git a/RegistryStandard.sol b/RegistryStandard.sol index e85507e..a7472b2 100644 --- a/RegistryStandard.sol +++ b/RegistryStandard.sol @@ -9,4 +9,5 @@ interface RegistryStandard { function registryCount() external view returns (uint256); function addressOf(bytes32) external view returns (address); function register(bytes32,address) external view returns (bool); + function entry(uint256) external view returns (address); } diff --git a/TransferApproval.sol b/TransferApproval.sol deleted file mode 100644 index b11ef9e..0000000 --- a/TransferApproval.sol +++ /dev/null @@ -1,19 +0,0 @@ -pragma solidity >=0.6.12; - -// Author: Louis Holbrook 0826EDA1702D1E87C6E2875121D2E7BB88C2A746 -// SPDX-License-Identifier: GPL-3.0-or-later -// File-version: 2 - - -interface TransferApproval { - event NewRequest(address indexed _sender, address indexed _recipient, address indexed _token, uint256 _value, uint256 _serial); - event NewExecution(uint256 serial); - event NewRejection(uint256 serial); - - function serial() external view returns (uint256); - function requests(uint256) external view returns (address); - function approvers(address) external view returns(bool); - function request(address _recipient, address _token, uint256 _value) external returns (uint256); - function execute(uint256 _serial) external returns (bool); - function reject(uint256 _serial) external returns (bool); -} diff --git a/TransferAuthorization.sol b/TransferAuthorization.sol new file mode 100644 index 0000000..fe3d27c --- /dev/null +++ b/TransferAuthorization.sol @@ -0,0 +1,31 @@ +pragma solidity >=0.6.12; + +// Author: Louis Holbrook 0826EDA1702D1E87C6E2875121D2E7BB88C2A746 +// SPDX-License-Identifier: GPL-3.0-or-later +// File-version: 2 + + +interface TransferAuthorization { + event NewRequest(address indexed _sender, address indexed _recipient, address indexed _token, uint256 _value, uint256 _serial); + event NewExecution(uint256 serial); + event NewRejection(uint256 serial); + event TransferFail(uint256 serial); + + struct Transaction { + uint256 serial; + address sender; + address recipient; + address token; + uint256 value; + } + + function approvers() external view returns (address[] memory); + function count() external view returns (uint256); + function lastSerial() external view returns (uint256); + function nextSerial() external view returns (uint256); + function getSerialAt(uint256 _idx) external view returns (uint256); + function requests(uint256 _serial) external view returns (Transaction memory); + function createRequest(address _recipient, address _token, uint256 _value) external returns (uint256); + function executeRequest(uint256 _serial) external returns (bool); + function rejectRequest(uint256 _serial) external returns (bool); +} diff --git a/abis/AddressDeclarator.json b/abis/AddressDeclarator.json new file mode 100644 index 0000000..1fd5f31 --- /dev/null +++ b/abis/AddressDeclarator.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_address","type":"address"},{"internalType":"bytes32","name":"_proof","type":"bytes32"}],"name":"addDeclaration","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_subjectAddress","type":"address"},{"internalType":"address","name":"_objectAddress","type":"address"}],"name":"declaration","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_subjectAddress","type":"address"},{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"declarationAddressAt","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_subjectAddress","type":"address"}],"name":"declarationCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_targetAddress","type":"address"},{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"declaratorAddressAt","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_objectAddress","type":"address"}],"name":"declaratorCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}] diff --git a/abis/Declarator.json b/abis/Declarator.json new file mode 100644 index 0000000..1fd5f31 --- /dev/null +++ b/abis/Declarator.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_address","type":"address"},{"internalType":"bytes32","name":"_proof","type":"bytes32"}],"name":"addDeclaration","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_subjectAddress","type":"address"},{"internalType":"address","name":"_objectAddress","type":"address"}],"name":"declaration","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_subjectAddress","type":"address"},{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"declarationAddressAt","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_subjectAddress","type":"address"}],"name":"declarationCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_targetAddress","type":"address"},{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"declaratorAddressAt","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_objectAddress","type":"address"}],"name":"declaratorCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}] diff --git a/abis/RegistryStandard.json b/abis/RegistryStandard.json new file mode 100644 index 0000000..07b39bd --- /dev/null +++ b/abis/RegistryStandard.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"addressOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"entry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"address","name":"","type":"address"}],"name":"register","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"registryCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}] diff --git a/abis/TransferAuthorization.json b/abis/TransferAuthorization.json new file mode 100644 index 0000000..a6a76c2 --- /dev/null +++ b/abis/TransferAuthorization.json @@ -0,0 +1 @@ +[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"serial","type":"uint256"}],"name":"NewExecution","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"serial","type":"uint256"}],"name":"NewRejection","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_sender","type":"address"},{"indexed":true,"internalType":"address","name":"_recipient","type":"address"},{"indexed":true,"internalType":"address","name":"_token","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_serial","type":"uint256"}],"name":"NewRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"serial","type":"uint256"}],"name":"TransferFail","type":"event"},{"inputs":[],"name":"approvers","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"count","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"createRequest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_serial","type":"uint256"}],"name":"executeRequest","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_idx","type":"uint256"}],"name":"getSerialAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastSerial","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextSerial","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_serial","type":"uint256"}],"name":"rejectRequest","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_serial","type":"uint256"}],"name":"requests","outputs":[{"components":[{"internalType":"uint256","name":"serial","type":"uint256"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"internalType":"struct TransferAuthorization.Transaction","name":"","type":"tuple"}],"stateMutability":"view","type":"function"}] diff --git a/build_docker.sh b/build_docker.sh deleted file mode 100644 index 4187202..0000000 --- a/build_docker.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -t grassrootseconomics:cic . diff --git a/contracts_overview.txt b/contracts_overview.txt new file mode 100644 index 0000000..0cb6663 --- /dev/null +++ b/contracts_overview.txt @@ -0,0 +1,47 @@ +Contract interfaces overview + + +AccountRegistry + +* interface: +* append-only list of custodial addresses +* only writers may add addresses +* only owner may add and remove writers + + +AddressDeclarator + +* interface: +* enables addresses to link one or more 32 byte entries to another address +* used for: + - kyc proofs to user account addresses + - token metadata (eg. tokenlists.org std), endorsements, warnings +* freely writable + + +Faucet + +* interface: +* allows transfer of an amount of one particular token to a given address +* transfer amount can be changed at any time. invocation will used the transfer amount set at the time. +* anyone can invoke the function for anyone +* implements a function to provide cooldown info, max(uint256) means cooldown disabled (one-shot only) + + +Token Symbol Registry + +* interface: + +* simple key-value store to enforce unique token symbols +* only owner can write +* will check that token symbol reported by ERC20 interface matches provided key (case sensitive) + + +Transfer Approvals + +* interface: + +* counter-signing of transfers from custodial addresses invoked by third-party (staff) +* custodial address must create an allowance for contract +* only "approvers" may reject/execute transfers +* provides lists of open requests diff --git a/doc/texinfo/Makefile b/doc/texinfo/Makefile new file mode 100644 index 0000000..49c9710 --- /dev/null +++ b/doc/texinfo/Makefile @@ -0,0 +1,3 @@ +all: + makeinfo --html index.texi + diff --git a/doc/texinfo/index.texi b/doc/texinfo/index.texi new file mode 100644 index 0000000..dcdc431 --- /dev/null +++ b/doc/texinfo/index.texi @@ -0,0 +1 @@ +@include overview.texi diff --git a/doc/texinfo/index/Smart-contract-interfaces.html b/doc/texinfo/index/Smart-contract-interfaces.html new file mode 100644 index 0000000..e2763d3 --- /dev/null +++ b/doc/texinfo/index/Smart-contract-interfaces.html @@ -0,0 +1,117 @@ + + + + + +Smart contract interfaces (Untitled Document) + + + + + + + + + + + + + +

1 Smart contract interfaces

+ + +

1.1 Smart Contracts in the CIC Network

+ +

1.1.1 Technology

+ +

The long-term aim of the CIC network is to be agnostic of consensus engines. However, since we have to start somewhere, the current state of development only deals with Smart Contracts on the (Ethereum Virtual Machine (EVM). +

+ +

1.1.2 The registry contract

+ +

The CICRegistry contract defines the entry-point to the entire CIC network. All other CIC network resources can be discovered through this contract. +

+

Its implementation is contained in the cic-registry repository. Details about it are documented further in that section. +

+

1.1.3 Auxiliary contracts

+ +

All other Smart Contracts in the network other than CICRegistry are essentially optional, and each applies at least one the interfaces defined in the cic-contracts repository. +

+ +

1.1.4 Contract interfaces

+ +

All contracts interfaces also implement the EIP165 Standard Interface Detection. +

+ +

1.1.4.1 Registry

+ +

A key-value store, which resolves arbitrary 32-byte pointers to Ethereum addresses. Typically, this is used to resolve an address from a well-known identifier, either as a UTF-8 value or a 256-bit hash. +

+

It also provides numerical index access to all registered values in order of insertion. +

+ +

1.1.4.2 Registry Client

+ +

A subset of the Registry interface, which defines only the non-transactional methods of the contract. The Registry Client interface is implemented by the CICRegistry contract. +

+ +

1.1.4.3 Faucet

+ +

Enables disbursement of a set amount of tokens to a requesting address. +

+

Allows privileged accounts to adjust the amount of tokens to disburse. +

+

Can be implemented as a periodic or a one-time service per account. +

+ +

1.1.4.4 Transfer approval

+ +

Enables a third-party approval to spend an ERC20 token allowance. +

+

This is useful in the case of a custodial key store, where a transaction has been initiated by another entity than the owner of the key. +

+

The contract allows the third-party address to either allow or reject the transfer. +

+ +

1.1.4.5 Declarator

+ +

Stores one or more 32-byte entries as a description of an Ethereum address, signed by another Ethereum address. +

+

This can be used to describe network resources, be it tokens, contracts or user accounts. Examples of uses are KYC documentation proofs for users, token metadata used for display, or fraud alerts. +

+

Entries are stored by the transacting address that adds address/description pairs. In other words, any address may have different "opinions" registered about it, depending on which signing address it is queried in the context of. This, in turn, allows the quering entity to compile its own "opinion" of an address by combining data from signatures it trusts. +

+

Entries typically are text strings or content hashes. +

+ +

1.1.4.6 Address Index

+ +

A simple append-only list of addresses. Used to check whether an address is part of a particular group. +

+ +

1.1.4.7 DEX Index

+ +

Methods required to convert between tokens. +


+ + + + + diff --git a/doc/texinfo/overview.texi b/doc/texinfo/overview.texi new file mode 100644 index 0000000..c2e5ce5 --- /dev/null +++ b/doc/texinfo/overview.texi @@ -0,0 +1,76 @@ +@node Smart contract interfaces +@chapter Smart contract interfaces + + +@section Smart Contracts in the CIC Network + +@subsection Technology + +The long-term aim of the CIC network is to be agnostic of consensus engines. However, since we have to start @emph{somewhere}, the current state of development only deals with Smart Contracts on the (Ethereum Virtual Machine (EVM). + + +@subsection The registry contract + +The CICRegistry contract defines the entry-point to the entire CIC network. All other CIC network resources can be discovered through this contract. + +Its implementation is contained in the @file{cic-registry} repository. Details about it are documented further in that section. + +@subsection Auxiliary contracts + +All other Smart Contracts in the network other than CICRegistry are essentially optional, and each applies @emph{at least one} the interfaces defined in the @code{cic-contracts} repository. + + +@subsection Contract interfaces + +All contracts interfaces also implement the @strong{EIP165} Standard Interface Detection. + + +@subsubsection Registry + +A key-value store, which resolves arbitrary 32-byte pointers to Ethereum addresses. Typically, this is used to resolve an address from a well-known identifier, either as a UTF-8 value or a 256-bit hash. + +It also provides numerical index access to all registered values in order of insertion. + + +@subsubsection Registry Client + +A subset of the @code{Registry} interface, which defines only the non-transactional methods of the contract. The @code{Registry Client} interface is implemented by the @code{CICRegistry} contract. + + +@subsubsection Faucet + +Enables disbursement of a set amount of tokens to a requesting address. + +Allows privileged accounts to adjust the amount of tokens to disburse. + +Can be implemented as a periodic or a one-time service per account. + + +@subsubsection Transfer approval + +Enables a third-party approval to spend an ERC20 token allowance. + +This is useful in the case of a custodial key store, where a transaction has been initiated by another entity than the owner of the key. + +The contract allows the third-party address to either allow or reject the transfer. + + +@subsubsection Declarator + +Stores one or more 32-byte entries as a description of an Ethereum address, @emph{signed} by another Ethereum address. + +This can be used to @emph{describe} network resources, be it tokens, contracts or user accounts. Examples of uses are KYC documentation proofs for users, token metadata used for display, or fraud alerts. + +Entries are stored by the transacting address that adds address/description pairs. In other words, any address may have different "opinions" registered about it, depending on which signing address it is queried in the context of. This, in turn, allows the quering entity to compile its own "opinion" of an address by combining data from signatures it trusts. + +Entries typically are text strings or content hashes. + + +@subsubsection Address Index + +A simple append-only list of addresses. Used to check whether an address is part of a particular group. + + +@subsubsection DEX Index + +Methods required to convert between tokens. diff --git a/scripts/registry_list.py b/scripts/registry_list.py new file mode 100644 index 0000000..8e32aaa --- /dev/null +++ b/scripts/registry_list.py @@ -0,0 +1,44 @@ +import sys + +from chainlib.eth.constant import ZERO_ADDRESS +from chainlib.eth.gas import DefaultGasOracle +from chainlib.eth.nonce import DefaultNonceOracle +from chainlib.eth.tx import TxFactory +from chainlib.eth.connection import HTTPConnection +from chainlib.eth.hash import keccak256_string_to_hex +from chainlib.eth.rpc import jsonrpc_template +from chainlib.eth.error import EthException +import eth_abi +from hexathon import ( + strip_0x, + add_0x, + ) + +i = 0 +while True: + conn = HTTPConnection('http://localhost:63545') + gas_oracle = DefaultGasOracle(conn) + nonce_oracle = DefaultNonceOracle(ZERO_ADDRESS, conn) + + # Get Token registry address + txf = TxFactory(signer=None, gas_oracle=gas_oracle, nonce_oracle=nonce_oracle, chain_id=8996) + tx = txf.template(ZERO_ADDRESS, sys.argv[1]) + identifiers_method = keccak256_string_to_hex('identifiers(uint256)')[:8] + data = add_0x(identifiers_method) + data += eth_abi.encode_single('uint256', i).hex() + txf.set_code(tx, data) + + o = jsonrpc_template() + o['method'] = 'eth_call' + tx = txf.normalize(tx) + o['params'].append(tx) + o['params'].append('latest') + try: + r = conn.do(o) + except EthException: + break + id_bytes = bytes.fromhex(strip_0x(r)) + id_str = id_bytes[:id_bytes.find(b'\x00')] + print(id_str.decode('utf-8̈́')) + i += 1 + diff --git a/util/calculate_erc165.py b/util/calculate_erc165.py new file mode 100644 index 0000000..f93f84d --- /dev/null +++ b/util/calculate_erc165.py @@ -0,0 +1,17 @@ +import sys +import web3 + +f = open(sys.argv[1], 'r') +z = b'' +for i in range(32): + z += b'\x00' +while True: + l = f.readline() + if l == '': + break + #print('line {}'.format(l)) + h = web3.Web3.keccak(text=l) + z = bytes([a ^ b for a, b in zip(h, z)]) + #print(h.hex(), z.hex()) +f.close() +print(h[:4].hex())