Introduction
In the current ecosystem, Deny mode is the safest way to interact with smart
contracts, and users should be in the habit of never signing a transaction in
Allow mode. The reality is that some applications, e.g. DeFi apps, move assets
between a variety of contracts as part of one contract call. The movements may
be dynamic and difficult or impossible to predict in advance, so users end up
reverting to signing Allow mode transactions, just to make it work. This is a
very bad habit to teach our users, and we can do better. What the user really
cares about is something like, “When I call contract X, at most m STX and n
USDCx may be moved from my wallet.” They don’t care if contract X sends sBTC to
contract Y, which sends USDCx to contract Z, etc. Originator mode allows the
user to specify only the restrictions on their own assets, allowing any
movements of assets amongst other principals.
MAY SEND fills an obvious gap in the existing post-condition implementation,
and does not require any more introduction or motivation.
Specification
The specification for both of these is based on and builds off of the original
post-condition specification in
SIP-005.
Originator Mode
All post-conditions continue to be evaluated in the same way, with the addition
that in Originator mode, the transaction’s origin account’s assets are
protected as in Deny mode, while other principals are protected as in Allow
mode.
The origin account is the account identified by the origin spending condition in
the transaction authorization structure (SIP-005). It is fixed for the
transaction and does not change during execution. It is distinct from
tx-sender, and does not change under as-contract?. In sponsored
transactions, it is the account that signs first (the origin).
In Originator mode, all post-conditions MUST hold. Additionally, when
enforcing allowlist coverage, a transaction MUST be rejected only if there
exists an asset transfer not covered by any post-condition whose sending
principal is the origin account. Uncovered transfers from any other principal
are permitted.
The transaction encoding reserves 1-byte for the post-condition mode.
Originator mode uses the value 0x03.
MAY SEND Condition
This new MAY SEND condition shall be implemented as a check on the NFT asset
movement upon completion of the transaction. It is always satisfied regardless
of whether the specified NFT is sent, and it is treated as covering that
specific NFT instance for the specified principal when enforcing allowlist
coverage in Deny or Originator mode. In Allow mode, it is redundant but
valid.
The transaction encoding reserves 1-byte for the non-fungible condition code.
MAY SEND adds a new acceptable value for this byte, 0x12.
Updates to the SIP-005 Specification
Adding these changes into SIP-005’s spec produces the following diff:
diff --git a/sips/sip-005/sip-005-blocks-and-transactions.md b/sips/sip-005/sip-005-blocks-and-transactions.md
index 64d3a5d..253164b 100644
--- a/sips/sip-005/sip-005-blocks-and-transactions.md
+++ b/sips/sip-005/sip-005-blocks-and-transactions.md
@@ -305,10 +305,12 @@ The Stacks blockchain supports the following two types of comparators:
* **Non-fungible asset state** -- that is, a question of _whether or not_ an
account sent a non-fungible asset when the transaction ran.
-In addition, the Stacks blockchain supports an "allow" or "deny" mode for
-evaluating post-conditions: in "allow" mode, other asset transfers not covered
-by the post-conditions are permitted, but in "deny" mode, no other asset
-transfers are permitted besides those named in the post-conditions.
+In addition, the Stacks blockchain supports an "allow", "deny", or "originator"
+mode for evaluating post-conditions: in "allow" mode, other asset transfers
+not covered by the post-conditions are permitted, but in "deny" mode, no other
+asset transfers are permitted besides those named in the post-conditions, and
+in "originator" mode, no other asset transfers from the transaction's origin
+account are permitted besides those named in the post-conditions.
Post-conditions are meant to be added by the user (or by the user's wallet
software) at the moment they sign with their origin account. Because the
@@ -359,6 +361,8 @@ encoded as big-endian.
post-conditions.
* `0x02`: This transaction may NOT affect other assets besides those listed
in the post-conditions.
+ * `0x03`: This transaction may NOT affect other assets from the origin
+ account besides those listed in the post-conditions.
* A length-prefixed list of **post-conditions**, describing properties that must be true of the
originating account's assets once the transaction finishes executing. It is encoded as follows:
* A 4-byte length, indicating the number of post-conditions.
@@ -625,6 +629,7 @@ non-fungible token, with respect to whether or not the particular non-fungible
token is owned by the account. It can take the following values:
* `0x10`: "The account will SEND this non-fungible token"
* `0x11`: "The account will NOT SEND this non-fungible token"
+* `0x12`: "The account may SEND this non-fungible token"
Post-conditions are defined in terms of which assets each account sends or
does not send during the transaction's execution. To enforce post-conditions,
Related Work
- This SIP adds to the existing specification for post-conditions in
SIP-005 - This SIP will activate in epoch 3.4 together with
SIP-clarity5