Dharma action scripts checked token balance increases against their declared outputs

Dharma used dharmaOS action scripts to check whether contract calls produced the required token balance increases. Each declared output paired a token with a required raw amount. The consumer wallet retired in 2022, ending the app workflow that had presented these actions to users.

Last updated:

Scripts connected contract calls with token expectations

An action script combined an ordered set of contract calls with declarations that described its token inputs and outputs. The dharmaOS format used YAML, a structured text format, and typed variables for supplied arguments. Token definitions identified the contracts involved. This let an integration describe the operation and the quantities that mattered to its outcome in the same file.

Those declarations had different meanings. An input amount concerned tokens that the action could approve or transfer. An output amount concerned a required balance increase. Permission to spend an input token did not establish receipt of an output token. The distinction remained relevant when a contract call completed without an exception, because execution status alone did not describe the resulting token ownership.

The receiving balance determined what the output check measured

The outputs field specified the raw amount by which a defined token balance had to increase from its pre-execution balance.

For ERC-20 tokens, the standard interface included balanceOf, which returned the balance of a specified account. A meaningful comparison used the same token contract and account for both readings. Reading a different recipient changed the question that the balance query answered. A transfer to one account did not increase another account's balance merely because both appeared in the same application.

The subtraction was final balance - initial balance. This measured a net change between the selected readings. It excluded holdings that already existed at the starting point. If an operation both consumed and received the same token, its net increase differed from the gross amount transferred in.

For actions using escrowed inputs, output tokens inherited the inputs' escrow period. Their appearance in a balance did not establish unrestricted withdrawal.

Raw units kept balance arithmetic consistent

Token decimals controlled how an interface displayed an integer amount; the underlying balance arithmetic still used raw units.

If a token used a decimal precision represented by d, its display scale was 10^d. Dividing a raw amount by that scale produced the corresponding human-readable token quantity. Applying the scale twice would distort the comparison. So would taking the input token's precision and applying it to a different output token. Both readings and the required increase needed the output token's own units, without inserting a currency conversion into the calculation. Rounding belonged to presentation. A shortened display could conceal a small difference that remained visible in the underlying integers. Retaining those integers preserved the precision needed to assess the output requirement.

A decision checklist for testing the required increase

For a hypothetical test of an archived script, keep the output requirement fixed and compare a matching increase with a smaller increase.

On the matching path, the balance change supported receipt of the declared output quantity. On the shortfall path, an existing holding could leave the final balance looking sufficient while the action added too little. Changing the amount actually received changed the increase, even when the token, account and required output stayed fixed. The next investigation concerned the recipient and call results, not a larger pre-existing balance.

What did a successful contract call actually confirm?

A successful low-level call confirmed that execution at the called address had not reverted, which was narrower than token receipt.

Solidity distinguished that execution flag from the data returned by the function. An ERC-20 method could return a Boolean result that its caller needed to interpret. The token standard explicitly required callers to handle a returned false. A low-level success flag alone did not replace that interpretation. Reading the returned data required the correct function signature and return types.

Visual outline: Dharma: What did a successful contract call actually confirm?

View image file

Transaction status also concerned the outer execution. The SDK's test wallet could handle an inner call failure and report its results without making the outer transaction fail. This made an outer success status insufficient to establish that every intended inner action had persisted.

A reported token amount carried the meaning that its function assigned to it. The relevant balance query addressed a separate question: which account actually held that token amount.

Simulation previewed execution without preserving the token changes

The SDK's test wallet simulated calls by executing them internally and rolling back their state changes while returning call results. A simulation therefore could describe a projected outcome without leaving the corresponding tokens in the account. The app's action descriptions used simulated results until the transaction mined, then used realized results. Changes to contract state between a preview and execution could affect the later outcome.

A failed call and a balance shortfall pointed to different causes

A reverted contract call concerned execution failure, while an insufficient balance increase concerned the quantity required by an output declaration. The SDK's test wallet rolled back earlier calls when a later call in the same batch failed. It also stopped before subsequent calls. Its success events included a rollback indicator, so a successful earlier call record could describe work that the batch subsequently undid.

An output shortfall did not, by itself, establish that a contract had reverted. Incorrect recipient selection or inconsistent amount units could also make an output comparison wrong. These conditions required examination of the affected balance and arguments. Error data needed similar care: a nested contract could originate an error that another contract forwarded. Repeating unchanged arguments would leave an address or unit mistake in place.

Reusable scripts needed maintenance beyond an earlier passing test

A retained integration depended on the contract interfaces and token behavior that its calls actually used. Changing a target contract or function signature could alter how the arguments and returned data needed interpretation. Keeping the call definitions aligned with those interfaces mattered whenever someone maintained or adapted the integration. A previous test covered the conditions under which it ran; it did not settle compatibility with a different contract implementation.

Historical script files still described that design, including its relationship with smart wallet balances and token integrations. Compared with an approval-only record, an action script stated an intended token outcome. An approval specified spending permission; the output requirement concerned the resulting balance increase.

Details worth knowing about Dharma

Could a token allowance expire automatically after a Dharma action?

The standard ERC-20 approval method did not include an expiry parameter. It specified a spender and an allowance amount, while individual token implementations could impose additional behavior. Receiving an output token did not itself establish that an input allowance had expired or disappeared.

Were imported Dharma action scripts covered only by the parent script's checks?

Imported action scripts enforced their own input and output checks independently of the importing script. The parent therefore did not replace every nested requirement with its final output declaration. Each script retained the conditions that applied to its own inputs and outputs.

Which token metadata could be absent from a Dharma action's balance display?

ERC-20 treated token names, symbols and decimals as optional metadata methods. The balance query remained part of the standard interface. A display that expected those metadata values needed appropriate handling when they were absent, rather than assigning an assumed name or decimal precision to a raw balance.

How did token associations clarify results in a Dharma action script?

Token associations identified the token in which a variable or result should be denominated. They supplied meaning for an amount that otherwise appeared as a bare value. An association did not itself transfer tokens or change the balance that the output declaration measured.

Could an ERC-20 balance query consume gas inside a Dharma action?

A balance query consumed execution gas when a contract called it inside a transaction. Reading a view function through an off-chain call did not require a mined transaction or an on-chain gas payment. The difference concerned how the query ran, rather than whether it changed the token balance.