Skip to content

Smart Contract interactions on Partisia Platform

An interaction is an action of a contract that invokes an action in another contract. Smart contract interactions on Partisia Platform conform to a message passing paradigm with the following properties:

  • Event-driven: Users and other contracts interact with a contract through designated entry points.
  • Asynchroynous execution: It allows for the execution of events without a defined order or dependency between them.
  • Atomic events: Events within the smart contract execute atomically. In the event of a failure during event execution, the smart contract's state is reverted to its condition prior to the event's initiation.
  • Non-atomic event trees: Child events, spawned by parent events, do not inherit the parent event's atomic context.

These properties combine to a system that is incredibly scalable, but require more care in designing smart contract interactions than other blockchain architectures like Ethereum Virtual Machine (EVM).

Comparison with other systems

While smart contract blockchains are commonly compared with the Microservices architectural pattern, Partisia Platform is even more so comparable.

Feature Partisia Platfrom EVM Microservices Monolithic application
Decentralized Yes Yes Yes No
Message Parsing Yes Yes Yes No
Local State Rollback Yes Yes ? Depends
Public Ledger and public state Yes Yes No No
Scalable Yes No (Requires layer 2) Yes No
Message Call Stack Yes Yes No Yes
Global State Rollback No Yes No Depends

Partisia Platform differs from EVM-based systems in several key aspects. Notably, it does not support Global State Rollback. In EVM, interaction failures typically lead to the rollback of the entire interaction call tree. This behavior is not supported on Partisia Platform. Additionally, permissions cannot be checked retroactively on Partisia Platform, unlike in EVM environments.

Atomic events

An atomic action is a series of operations that either completes successfully in its entirety or fails completely, leaving no intermediate state. Partisia Platform guarantees that each event/action is atomic. Consequently an atomic action is either:

  • Successful: in which case the changes are committed to the smart contract state and visible for everyone.
  • Failed: in which case any changes are rolled back to before the action was attempted.

Atomicity on the Partisia Platform works in the same way as in a database system.

User-to-contract interaction model

Users interact with Partisia Blockchain by a signed transaction to a smart contract. The signature ensures that the user has authorized the transaction. Clients must cryptographically sign transactions they send to ensure authenticity and non-repudiation.

When Partisia Platform receives a signed transaction and verifies the signature it spawns an event (dotted line), which is forwarded to the smart contract. The event carries information about which action for the smart contract to perform.

Upon receiving the event, the smart contract executes the specified action. This execution involves executing the smart contract's code, resulting in an update to the smart contract's state. The action code execution occurs atomically as explained above, meaning that the action can be:

  • Successful: In which case the state changes are committed to Partisia Platform and visible for everyone.
  • Failed: In which case no state will change on Partisia Platform.

The following model illustrates a case where the user votes on a resolution using a voting contract:

SmartContractMentalModelSimple.svg

Contract-to-contract interaction model

Smart contracts can interact with other smart contracts, much like users can. Because contracts live on Partisia Platform they can interact with another smart contract without sending a signed transaction. Instead, they create an event directly, which is forwarded to the receiving contract.

Only successful actions can spawn events. Any action can spawn any number of events to other contracts, as will be discussed below.

To illustrate, consider a scenario where someone holds an NFT auction, held in USDT (or some other token). The user wants to bid on this auction.

The scenario proceeds as follows: the user sends a bid action on the auction contract. To perform the bid action, the auction contract initiates an event that triggers a transfer action on the USDT contract, which will transfer the specified USDT amount from the user's account to the auction contract. The USDT is held in the auction contract as escrow payment.

Any of these two actions (bid and transfer) can fail:

  • If the bid action fails, the auction contract will forget about the bid, and will never spawn the transfer event.
  • If the transfer action fails it will not transfer the USDT tokens. Below we will discuss ways to handle this failure case.

If the transfer action succeeds, the USDT contract will transfer the USDT tokens, and the auction contract will register the original bid.

SmartContractMentalModelcontract-to-contract.svg

Contract-to-contract with callback

Smart contracts can additionally wait for answers to their previously sent events, by declaring a callback, which is an anticipatory event for a subsequent action within the same contract.

Upon completion, a spawned event checks for a declared callback. If a callback is defined, the event sends a response event back to the originating smart contract. This mechanism enables the initial smart contract to make decisions based on the outcomes of delegated actions.

Expanding on the auction example: The auction contract must know whether the escrow transfer was successful or not, such that it can update the highest bid.

The scenario proceeds as follows: The user sends a bidaction on the auction contract. To perform the bid, the auction contract initiates a transfer action on the USDT contract to transfer the specified USDT amount from the user's account to the auction contract as an escrow payment. The auction contract registers a callback to receive the result of the transfer action. The USDT contract executes the transfer action, which may succeed or fail. Regardless of the transfer action's outcome, the auction contract's callback is invoked, allowing it to assess the transfer result and determine whether to accept the bid.

Possible results:

  • If the bid action fails, the auction contract will forget about the bid, and will never spawn the transfer event.
  • If the transfer action fails, the USDT contract will not transfer the USDT tokens. A failure callback will be sent to the auction contract, which then removes the bid.
  • If the transfer action succeed, the USDT contract will transfer the USDT tokens. A success callback will be sent to the auction contract, which then updates the best bid.

SmartContractMentalModelWithCallback.svg

Contract-to-contract in a chain

Smart contracts can continue to interact with contracts from their callbacks, allowing long chains of interactions. This can be used to check permissions and payments before performing a privileged action, like sending an NFT.

To illustrate, consider a case where someone wants to buy some COOL tokens using a shop contract made to buy COOL tokens in exchange of USDT tokens.

The scenario proceeds as follows:

  1. The customer sends a buy action on the shop contract.
  2. The shop contract requests the payment from the USDT contract using the transfer action, with a callback.
  3. The USDT contract attempts to transfer the USDT, but this may fail if the customer does not have enough USDT in their account.
  4. The shop contract callback is triggered, and the shop contract checks whether the transfer succeeded.
  5. The shop contract asks the COOL contract to transfer COOL to the customer, without a callback.
  6. The COOL contract transfers COOL to the customer.

Possible results:

  • If everything succeeds, the customer will end up being some COOL tokens richer, and some USDT poorer.
  • If the USDT transfer fails, the shop contract will refuse to send COOL tokens. No COOL or USDT will have traded hands.

Contract-to-two-contracts with one callback.

Warning

*This functionality is a footgun. Please do not use it.

Smart contracts can make several interactions at the same time, and can wait for all of the interactions to finish before a callback. This is advanced functionality with limited use cases. Using this functionality securely requires a deep understanding of the properties mentioned at the top of the article.

Pitfalls:

  • Race conditions: Interactions made at the same time do not run in order.
  • Non-atomic event tree: As explained above, individual events are atomic, but the event tree as a whole is not atomic.

Use cases where this approach may be beneficial include:

  • Permission checking across multiple contracts.
  • Payment transactions involving multiple tokens.
  • Other interactions that do not modify the system state.

Use cases where this can be inconsistent and most likely insecure:

  • Permission checking at the same time as side-effects.
  • Parallel incoming and outgoing transfers.
  • Side effects
  • Permission granting and permission checking at the same time.

Extending the shop scenario: The shop owner wants to give a 10% discount to the shoppers using the ICECOLD token.

The scenario proceeds as follows:

  1. The customer sends a buy action on the shop contract.
  2. The shop contract requests the payment from the USDT contract using the transfer action and asks the ICECOLD contract whether the user has any ICECOLD, along with a callback.
  3. The USDT contract attempts to transfer the USDT, but this may fail if the customer does not have enough USDT in their account.
  4. The ICECOLD contract checks user balance. This will fail if the user does not have any ICECOLD.
  5. The shop callback is triggered, and the shop checks that the transfer succeeded and whether the balance check succeeded.
  6. The shop asks the COOL contract to transfer COOL to the customer, without a callback.
  7. The COOL contract transfers COOL to the customer.

Possible results:

  • If everything succeeds, the customer will end up being some COOL tokens richer, and some USDT poorer.
  • If everything succeeds except the ICECOLD check, the customer will also end up being some COOL tokens richer, and some USDT poorer.
  • If the USDT transfer fails, the shop will refuse to send COOL tokens. No COOL or USDT will have traded hands.

In the case of payment systems you need to check that the payment has been sent, and is in your possession before sending the bought asset. Such functionality should not be implemented by sending events that transfer payment assets and bought assets in the same event group. Doing so may result in serious exploits.