Weekly design meeting #30

Two different namespaces

Let's first assume an example a token, with an attribute:

<ts:token>
    <ts:attribute-type name="balance" …>
        <ts:origins>
           <ethereum:call function="getBalance"…/>
        </ts:origins>
    </ts:attribute-type>
</ts:token>

Let's say that there is a current price for those who wish to purchase the token. The price is irrelevant until the user enters the 'buy' action so it is defined in the action card:

<ts:cards>
  <ts:card type="action" name="sell">
     <ts:attribute-type name="price" syntax="1.3.6.1.4.1.1466.115.121.1.15">
        <ts:origins>
            <ethereum:call function="getPrice"…/>
        </ts:origins>
     </ts:attribute-type>
     <ts:view>…</ts:view>
  </ts:card>
</ts:cards>

Careful: namespace

The attribute defined in this action card is local to this card. The user agent does not source value from the smart contract Note that price is not in the same namespace as balance. That means if later there is an attribute typed outside of the token card called price as well, there isn't a name conflict. The following example shows no namespace conflict.

<ts:token>
  <ts:attribute-type name="price" …>
      <ts:origins>
	 <ethereum:call function="getPrice"…/>
      </ts:origins>
  </ts:attribute-type>
  <ts:cards>
    <ts:card type="action" name="sell">
      <ts:attribute-type name="price" syntax="1.3.6.1.4.1.1466.115.121.1.15">
	  <ts:origins>
	      <ethereum:call function="getHighestBid"…/>
	  </ts:origins>
      </ts:attribute-type>
      <ts:view>…</ts:view>
    </ts:card>
  </ts:cards>
</ts:token>

token attributes and local card's attributes are referred to differently

Let's say, the smart contract function is enriched with an order book. With it, price is a function of amount thanks to the way order book works. The more the purchasing amount, the worse the price.

Let's look at 2 pieces of TokenScript.

The first one gets the price from "balance", that is, in this action, the asking price is calculated as if the token owner wishes to sell their entire balance:

<ts:card type="action" name="sell">
    <ts:attribute-type id="price" syntax="1.3.6.1.4.1.1466.115.121.1.15">
        <ts:origins>
            <ethereum:call function="getSellPrice"…>
                <ts:data>
                    <ts:uint ref="balance"></ts:uint>
                </ts:data>
            </ethereum:call>
        </ts:origins>
    </ts:attribute-type>
    <ts:view>…</view>
</ts:card>

The second piece has only one line of slight change:

<ts:card type="action" name="sell">
    <ts:attribute-type id="price" syntax="1.3.6.1.4.1.1466.115.121.1.15">
        <ts:origins>
            <ethereum:call function="getSellPrice"…>
                <ts:data>
                    <ts:uint local-ref="amount"></ts:uint>
                </ts:data>
            </ethereum:call>
        </ts:origins>
    </ts:attribute-type>
    <ts:view>…</view>
</ts:card>

So it refers to amount instead of balance, and it is done through local-ref instead of ref. What it means here is this value comes from the amount in this action card only.

Since amount is not defined in the local card, this value is unattainable unless it is defined in the view.

Getting/setting attribute values from view

The naïve solution

So how does a TokenScript engine knows how to get the amount value from the view?

Naïvely, we can design it in such a way, that the developer can put a form element with the name amount, whose value is to be used. The TokenScript engine can monitor the change of this value and fetch price accordingly.

<ts:view xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
    <body>
        <form id="buy">
            <input type="text" name="amount"/>
        </form>
    </body>
</ts:view>

But that means the developer can't use JavaScript to control what the amount value is to be used in getting the price (it is always the value this input element holds), nor can it control when to get the price (e.g. if the amount is "0" don't bother getting the price).

The elaborate solution

To return control to the developers we create a new JavaScript object to hold attributes local to the card, like price and amount in this example, similar to how token object holds token attributes.

web3.token.data.curreninstance.balance // this holds the token balance
web3.card.props.amount // this holds the action attribute amount
web3.card.props.price  // this holds the action attribute price

To update, the JavaScript:

web3.card.setProps({
    'amount' : document.getElementById("buy").amount.value;
})

Such a line of JavaScript, typically used in the onChange event handler of that form element, reads the value of amount form element and assign it to the amount action attribute. Consequently, it triggers the TokenScript engine to fetch a new value for the price action attribute.

As a consequence of this new design, the action attribute's definition should also change to this:

<ts:card type="action" name="sell">
    <ts:attribute-type name="price" syntax="1.3.6.1.4.1.1466.115.121.1.15">
        <ts:origins>
            <ethereum:call function="getAskPrice"…>
                <ts:data>
                    <ts:uint local-ref="amount"></ts:uint>
                </ts:data>
            </ethereum:call>
        </ts:origins>
    </ts:attribute-type>
    <ts:view>…</ts:view>
</ts:card>

The change is in the middle where local-ref is used in place of ref, meaning he value is referred from local action, not from the token.

There is no difference if this is on an action card or token card

Take the example and apply it for the token card (the card that displays a token), the logic is the same. The following token card has no action to perform, but you can still put an input element on the Token card and update the price.

<ts:cards>
  <ts:card type="token">
    <ts:attribute-type name="price" syntax="1.3.6.1.4.1.1466.115.121.1.15">
        <ts:origins>
            <ethereum:call function="getAskPrice"…>
                <ts:data>
                    <ts:uint local-ref="amount"></ts:uint>
                </ts:data>
            </ethereum:call>
        </ts:origins>
    </ts:attribute-type>
    <ts:view>…</ts:view>
  </ts:card>
</ts:cards>

Careful: namespace

The amount and the price in this example are in the same namespace. If they are the same, it creates an unresolvable looping reference.

Perhaps there could be two forms of this possible, the 'naïve' version which is easy to implement but updates the whole view on receiving an update which differs from the previous value:

		web3.card.setProps({
			nodeRefs : namehash.hash(this.props.fullName)
		})

And the react variant, where the developer can specify an action that takes place once the attributes have been updated:

		web3.card.setProps({
			nodeRefs : namehash.hash(this.props.fullName)
		}, function(error, tokenProps) {
                ... view update code ...
               });

I'm presuming that the intention for the callback is to provide an opportunity for the view code to update the view. This isn't needed because the dataChanged() callback is called when the data managed by the TokenScript engine is updated.

But, had a call with Weiwu about something else and we do require a callback here, but in the form of:

web3.card.setProps({
			nodeRefs : namehash.hash(this.props.fullName)
		}, function(error) {
                 //View code should check: If error != null, the prop update has failed. This is (for now), because the prop being set has the same name as a card-level attribute
               });

Yes, as setProps might fail with 2 reasons. One is that the value violated the syntax rule (e.g. setting "$20" as the value for price); the other is that the card attribute already have <origins> which makes them readonly to the JavaScript

But one can argue that in order to give errors, you don't need to use a callback but should throw up. I propose we do-away the 2nd parameter of setProps and throw either with "Syntax Error" or " defined for ××× can't set new values to it".

But I understand it can't be a syntax error for now because setProps can't override attributes.

No. You can still define a typed attribute without <origins>. You type it so it can be used in attestation (user-signed message) or in smart contract transactions. Recall that one of the action the user can do is to produce a signed message (in the up-coming Alice-send-ether-to-Bob-by-Email project).

If it happens the attribute might get further complicated there since the user might use rules like e10 which should be encoded as long integer in Ethereum but probably not in attestation - a topic for future TS design sessions.

No. You can still define a typed attribute without <origins> . You type it so it can be used in attestation (user-signed message) or in smart contract transactions. Recall that one of the action the user can do is to produce a signed message (in the up-coming Alice-send-ether-to-Bob-by-Email project).

Right. Is that (to support such typed attribute, not for attestation, etc) to be immediately available? If so, we might also want to formalise type conversion rules. Remember we loosely modelled:

origin type -> (optional) map -> attribute (syntax)

as a kind of pipeline when thinking about the flow of values and types (conversions).

So if we want to support typed attribute without <origin>s, we should decide on type conversion rules, at least so JavaScript -> TokenScript syntax (type) works?

Or can we put this off for later?

I hope we put this off for later but if you come up with something you can write down and ask us to go over it in the next TS meeting to formalise it.

I have some ideas based on how it works currently before we added setProps, but let's give James Sangalli some time to work with it first and see if he has suggestions later.