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>
Note on the namespace
The attribute defined in this action card is local to this card. Note that
price
is not in the same namespace asbalance
. That means if later there is an attribute typed outside of the token card calledprice
as well, there isn't a name conflict. The following example shows no namespace conflict, since theprice
defined under<token>
is not the sameprice
defined under<action>
.
<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:card type="action">
<!-- This is ypothetical code; doesn't really work -->
<ts:attribute name="amount">
<ts:type><ts:syntax>Integer</ts:syntax></ts:type>
<ts:origins>
<ts:user-entry as="e18" form="purchase_order" element="amount"/>
</ts:origins>
</ts:attribute>
<ts:view xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
<body>
<form id="purchase_order">
<input type="text" name="amount"/>
</form>
</body>
</ts:view>
</ts:card>
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("purchase_order").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.
In TokenScript, we can define price
to depend on the value of amount
, therefore, the said JavaScript triggers the TokenScript engine to fetch a new value for the price
action attribute. The entire card section is:
<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 xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
<script type="text/javascript">
function updatePrice() {
web3.card.setProps({
'amount' : document.getElementById("purchase_order").amount.value;
})
</script>
<body>
<form id="purchase_order">
<input type="text" name="amount" onChange="updatePrice()"/>
</form>
</body>
</ts:view>
</ts:card>
Observe how local-ref="amount"
refers to a value set in JavaScript. Also, observe that since JavaScript does the value update at runtime, there is no need to declare it as an attribute (unless the engine wishes to enforce syntax or other origination rules). Such is the design proposed in this meeting.
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>
Note on the namespace
The
amount
and theprice
in this example are in the same namespace.