Skip to content

Context Variables

Stuart Farmer edited this page Aug 4, 2018 · 4 revisions

Detailed Explanation

For access control and many other applications, you will want to know certain attributes about the individual or smart contract that is invoking the methods on your own contract. We call these variables Context variables.

Because every transaction on the Seneca system is technically a smart contract itself, we create a list of the contracts that are called at runtime that you can access with special variable names.

For example, assume that currency.sen.py exists on the blockchain. You write an inline smart contract such as:

import currency
currency.send('carl', 100)

and submit it to the network.

When your smart contract is executed, it imports currency and runs the method send with the arguments 'carl', 100. However, you did not send your own address. If you were able to do this, then you could pretend to be anyone and send currency on other peoples' behalves. This is where context variables come into play.

Along with the arguments 'carl' and 100, a Context object is passed to the smart contract executer. From this object, a number of variables are exposed.

At a Glance

Each Context object has the following properties and methods:

  • call_stack
    • a list of Context object primitives where index 0 is the current context and the final index is the invoker of the runtime execution.
  • contract_address
    • the address of the contract, which is determined by the system when it is first published. For smart contracts that will only ever be invoked once, this is generally unneccessary information.
  • author
    • the address of the original creator of the smart contract. This is good for basic access control.

The Context objects available at runtime are:

  • this
    • the current Context where this.author is the author of the current smart contract. if user_id == this.author would check if some user is the same as the original creator of this smart contract and then continue. This is helpful for access control.
  • sender
    • the address of the invoker at the end of the call stack. This allows us to access who is invoking our smart contract methods. if sender == this.author only continues if the sender made the contract originally.
  • upstream
    • the Context of the next contract up the call stack. This is useful if you want to only allow certain smart contracts to have access to your own smart contract. Imagine a currency contract that only allows trading at a certain exchange. You can create the conditional like this: if upstream.contract_address == 'accepted_exchange'

Example

from seneca.runtime import *
from seneca.storage import tabular

ledger = tabular.create_table('ledger', [
    ('wallet_id', st.str_len(200), True),
    ('balance', int),
])

blacklist = [
    'scammer_1',
    'scammer_2',
    'scammer_3'
]

@export
def mint(wallet_id, amount_to_add):
    assert sender == this.author, "Only the contract author can mint tokens."
    assert wallet_exists(wallet_id), "Wallet id is not present in ledger"
    assert amount_to_add >= 0, "It's not possible to 'add' a negative balance"

    old_balance = get_balance(wallet_id)
    ledger.update({'balance':old_balance + amount_to_add}) \
          .where(ledger.wallet_id==wallet_id).run()

@export
def transfer_coins(receiver_id, amount):
    assert sender not in blacklist, "Blacklisted wallets cannot transfer"
    assert upstream.contract_address not in blacklist, "Blacklisted contracts cannot transfer"

    burn(sender, amount) # omitted for brevity purposes
    mint(receiver_id, amount)

Clone this wiki locally