Skip to main content
All articles

StateCell Ownership: Who Can Change the State?

· 11 min read
Bankware Global Engineering

A has authorized S to spend up to 30 units of A's asset. S wants to send 20 of those units to B. S signs this transaction. But the asset being spent belongs to A.

If the ledger only checks whether the asset's owner signed the transaction, it cannot process this request. It cannot simply accept S's claim to be spending A's asset, either. It must also consider what A approved and whether S's request falls within that approval.

Whose state is it, and who can change it under what conditions? Introducing ownership into StateCell meant addressing both questions together.

A StateCell is a small unit of recorded state in the ledger. Part 1 explained why assets and smart contract state share this common unit. Part 2 explored how to describe the structure of the values it holds. This post follows the spending approval example to explain ownership, permission to change state, and the roles of programs and the ledger.

When someone else can spend my asset

First, consider a ledger that directly manages asset types, holders, and quantities, and permits transfers with the owner's signature. A holds 100 units of this asset, and B holds none. The example excludes fees and any additional issuance or burning.

A transfer made directly by A is easy to understand. The ledger checks that the asset record names A as the owner and verifies A's request through the signature. StateCell records this ownership information in owner, an address identifying the entity that owns the cell.

Authorizing someone else to spend requires another record. When A signs an approval allowing S to spend up to 30, the approval is recorded as separate state alongside the asset record.

Asset record
Owner: A
Quantity: 100

Spending approval record
Granted by: A
Spender: S
Allowance: 30

Both records refer to the same asset. An allowance is not a separate deposit of that asset, so granting approval does not reduce A's 100 or give S a balance of 30. S receives permission to spend A's asset within a defined limit.

If S now sends 20 to B, the result should be:

RecordBefore spendingAfter spending
A's asset quantity10080
B's asset quantity020
S's remaining allowance3010

The total asset quantity is still 100. S uses the approved permission to transfer 20 to B without first taking ownership of A's asset.

This makes the role of owner clearer. The ledger must identify A's asset as the subject of the request while allowing S to request a transfer when the conditions are met. Ownership information is a starting point for deciding who may spend. The approval conditions must also be considered to determine whether this change is allowed.

Programs check spending conditions; the ledger enforces common rules

To allow this transaction, someone must read the approval record and answer a few questions. Is this really A's approval for S? Is the 20 that S wants to send within the allowance of 30? Will the remaining allowance decrease to exactly 10?

These conditions can differ between assets and services. If adding an expiration period or changing the approval process required modifying the ledger itself each time, introducing new rules would be difficult. Programs therefore take responsibility for checking individual spending conditions.

In the example, the transaction presents the asset and approval records it will use, along with the records that should remain after the change. A program checks this proposed change against the approval conditions. The rules for permitted changes are expressed in code, and the ledger considers the validation result together with its common rules.

Should the ledger then accept everything a program approves? Even if the allowance is respected, a transaction must not create assets out of nothing or reuse an asset record that has already been spent. A program checking approval conditions must not be allowed to change another program's state, either.

The responsibilities can be divided as follows:

  • The program checks the allowance A granted to S, the conditions for this use, and how the approval record should change afterward.
  • The ledger checks that a program authorized for this asset performed the validation and that the validation covers the records the transaction will actually use. It also checks record validity, conservation of asset quantities, and that changes stay within the permitted state.

In Nigo, a program that validates changes proposed by a transaction in this way is called a Native Program. The part that enforces the ledger's common rules is the protocol core (Core).

A separate post will explore the design and operation of Native Programs in more detail.

This distinction also applies to the owner of an approval record. Nigo's basic asset approval record names A, the person who granted approval, as its owner. The ledger also distinguishes which program's rules must govern changes to that record. Separately from ownership information, the state's ID carries information identifying the area and program that manage it.

A's asset record and A's approval record therefore do not need to change in the same way. A direct transfer request is checked using the owner's signature and the ledger's rules. A request to change the approval record also requires validation by the program that manages it. A's signature alone does not allow the request to bypass that program's rules and set the approval record to an arbitrary value.

This allows programs to extend the rules for changing state while preserving its ownership information. The ledger has less need to understand every service's approval process, and programs can focus on their own conditions on top of the common rules the ledger provides.

Whose state is my balance inside a smart contract?

Now consider expressing the same approval and transfer through a token smart contract. This is another way to handle the same situation, sending 20 from A's 100 to B; it is not an additional transfer of 20 after the earlier transaction. Call the contract T.

T records A's balance and the allowance granted to S in its own storage. When S calls the transfer function, T's code reads the balance and allowance, checks the conditions, and changes A's balance to 80, B's balance to 20, and the remaining allowance to 10.

Two perspectives overlap here. To someone using the token, this is A's balance. From the ledger's perspective of storage and execution, it is data managed by T. A cannot directly change the stored value to 1000 just because it represents A's balance. The change must go through T's code, which defines what the balance means and when it can change.

In the EVM, the virtual machine that executes smart contracts, a storage location for this kind of data is called a slot. When Nigo represents the slot as a StateCell, the cell's owner contains the address of T, the contract to which the storage belongs.

Record stored in the ledgerCell's owner
A's asset record, handled directly as an asset by the ledgerA
The slot holding A's balance in T's storageT

In the second row, A still holds the tokens. The relationship between A and the balance is expressed through T's data and code. Setting the slot's owner to T identifies the contract whose execution rules govern that state. It does not refer to the personal address of the deployer or contract administrator.

The approval validation program and this smart contract also handle state changes differently. The validation program checks whether the before and after states proposed by the transaction satisfy the conditions. An ordinary EVM contract reads state and computes changes while executing a function. Native Programs can also be written in Solidity, but their role in execution is different.

There is also a boundary to the meaning the ledger understands. For assets it handles directly, the ledger knows which asset a quantity belongs to and checks that quantity's conservation. T's code determines whether a number in its storage is a balance or an allowance. The ledger does not interpret every number in every contract as an asset balance.

StateCell holds these states in a common record format. On top of that format, user assets follow their transfer rules, and contract state follows the contract's execution rules. The common storage unit leaves the meaning of each state and responsibility for changing it with the appropriate component.

The transfer and allowance reduction must happen together

Now follow the original transaction through to its final state. Of A's 100, B receives 20 and A keeps 80. S's allowance must decrease from 30 to 10.

Saving each record separately could leave a problematic intermediate result. If B receives 20 but the allowance remains 30, the same permission can be used again. If only the allowance drops to 10 and B receives nothing, permission has been lost without being used.

Therefore, even when state is stored in separate pieces, the changes belonging to one transaction must be applied together. This is called atomicity. The asset transfer and approval record change must both succeed, or both of those application changes must be discarded.

Nigo collects changes in a temporary workspace while processing the transaction. Once the asset and approval records have been validated, it applies the changes as a group. If application execution fails, the group is discarded. In this example, the asset quantities and allowance remain at their previous values: A=100, B=0, allowance=30. Execution costs are settled separately from these application state changes.

This is where the different execution approaches meet again. Whether a transaction proposed the changes or a contract function computed them, the storage layer can receive a group of cells to consume and cells to create. An asset record is consumed and new records are created. A contract slot's cell is replaced with a new value at the same location. Both approaches treat the related changes as one group.

A cell is a unit for representing state. A transaction is a unit for grouping changes that must succeed together. Distinguishing the two makes it possible to identify small pieces of state individually while preserving the relationships between them.

Preserving the path to the current state

Seeing only that A holds 80 after the transaction does not explain how that quantity came about. Looking at the state the transaction used and what it left behind reveals the path of the change.

Before
A's asset: 100, allowance: 30

S's transaction sending 20 to B

After
A's asset: 80, B's asset: 20
Remaining allowance: 10

From B's newly created asset record of 20, it is possible to trace back to the transaction that created it, then to A's asset and approval records used by that transaction. If B spends the received asset in a later transaction, that transaction is connected through the same records. These links between state and transactions are called transaction lineage. Here, lineage means the path of creation and change that led to the current state.

The links begin with recording which cells were consumed and which were created in a transaction's execution result. They can be followed by preserving and querying the change records for each transaction. Even when a value at the same location is replaced, as with a contract slot, these records identify which transaction changed that state.

Common records show which cells were created, consumed, or changed by which transactions. Each program's rules explain what those changes mean for the application. Asset transfers and allowance changes can thus be read through the same relationships.

This also connects to the reason Part 1 linked holding-period research to ledger design. A common way to trace state changes provides a foundation for interpreting ownership history and changes in rights. Programs determine how that history is used to calculate periods or rights. I wanted the ledger to hold current state and also support the relationships between the transactions that produced it.

Building usage rules on ownership information

Returning to the opening question, S can spend A's asset because A approved the use and the transaction stays within that approval. Expressing this relationship requires ownership information, the spending conditions checked by the program, and the ledger's common validation rules together.

Connecting these roles was central to the design of StateCell: record who owns the state, let programs define the rules for changing it, apply validated changes together, and preserve the path of those changes. Representing user assets and smart contract state in a common format requires deciding where responsibility for changing a value belongs, just as much as deciding what value to store.

A question about shared state remains. If several transactions try to reduce the same allowance at once, they are difficult to process independently. Dividing state into small cells and independently allocating quantities available for use are separate design problems. A future standalone post on Para-cell will continue that question.


Part 3 of the StateCell design series. The people, assets, and quantities in the example are assumptions used to explain ownership and permission to change state.