0

For a Smart Contract, in Solidity, we want to implement ranges.

The reason is that many interactors do so on "ranges". Our Contract holds batches of coupons which we don't need, nor want to, track individually. Interactors simply say "give me the next 200 coupons" after which we update the state of Range. Which allows interactors to check for the state of "individual" Coupons as well.

A pseudo-interface would look like:

contract.issue(10000)
contract.amountAvailable() #=> 10.000
contract.reserve(200, { from: A })
contract.reserve(800, { from: B })
contract.reserve(9001, { from: C }) #=> Fails, not enough available.
contract.amountAvailable() #=> 9.000
contract.amountReserved(A) #=> 200
contract.amountReserved(B) #=> 800
contract.stateOf(16) #=>  1 (reserved)
contract.stateOf(9001) #=> 0 (free)

From here on, an interactor can perform an action on a single coupon, for the example, coupon with ID 16. Which changes the state of this coupon. This is where it becomes hairy.

contract.claim(16)
contract.amountReserved(A) #=> 199
contract.amountReserved(B) #=> 800
contract.claim(16) #=> Fails.

Because 16 is within the batch/range "reserved" by A, the mapping that maintains reserved-batches per address must be updated; most probably, it needs to be split into two ranges: 1-15 and 17-200. But this is an implementation detail that I'm not certain of.

What I'm trying to show, is that operations work on ranges, but that an interface allows for the checking of singular items. Those singular items could be considered "virtual" in the sense that they are not stored in some mapping or array.

A Range would be a pair of integers: [bottom, top] and have associated helper methods such as memberOf(item, range), length(range) or pop(amount, range).

We chose not to manage individual items, because (i) we don't need to and (ii) we found that e.g. ERC721 as provided by e.g. Solidity, is above all expensive here. E.g. in order to mint (issue) 100 unique tokens with ERC721 from Solidity, we already needed 14.164.312 gas. So we investigate operating on ranges instead.

So, is there a library, standard or common practice on how to implement a Range? Is this maybe covered in an ERC that I overlooked?

1

To support the test you describe, you don't need ranges at all:

pragma solidity ^0.4.24;

contract Coupons {
    uint256 public totalSupply;
    uint256 public amountAvailable;
    mapping(address => uint256) public amountReserved;

    function issue(uint256 howMany) external {
        // overflow test
        require(totalSupply + howMany >= totalSupply);

        totalSupply += howMany;
        amountAvailable += howMany;
    }

    function reserve(uint256 howMany) external {
        require(amountAvailable >= howMany);

        amountReserved[msg.sender] += howMany;
        amountAvailable -= howMany;
    }

    function stateOf(uint256 which) external view returns (bool) {
        return which <= totalSupply - amountAvailable;
    }
}

Perhaps you have additional requirements that lead you to needing ranges. If so, please edit your question.

  • Thanks. This makes sense and in simplifying my question, indeed I overlooked this smart solution. I'll update my question. In reality there are four states and one action might be performed on single items. – berkes Sep 13 '18 at 15:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.