Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft NEP 26 and Draft NEP 27: New NEPs describing onNEPXXPayment callbacks #169

Merged
merged 13 commits into from
Jun 5, 2024
100 changes: 100 additions & 0 deletions nep-26.mediawiki
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<pre>
NEP: 26
Title: Smart contract transfer callback for non-fungible tokens
Author: Erik Zhang <erik@neo.org>, Roman Khimov <roman@nspcc.ru>, Jaime Kindelan <jaime@red4sec.com>, Jimmy Liao <jimmy@r3e.network>
Type: Informational
Status: Accepted
Created: 2024-03-03
Requires: 11
</pre>

==Abstract==

This proposal describes the callback method used by Neo smart contracts to
process transfers of non-fungible [[nep-11.mediawiki|NEP-11]] tokens to their addresses.

==Motivation==

While the original [[nep-11.mediawiki|NEP-11]] already defines this method, it
lacks some details relevant specifically to the receiver contract and it doesn't
allow for easy reference. This standard can be used in <code>supportedstandards</code>
section of [[nep-15.mediawiki|NEP-15 manifest]] or in any other situation to
refer to contracts able to receive [[nep-11.mediawiki|NEP-11]] tokens (but not
necessarily being [[nep-11.mediawiki|NEP-11]] themselves). Typical use cases
for this functionality are: depositing/staking (with contract locking and
managing tokens according to some rules) or exchange/minting (contract accepting
transfer to itself, but returning some different token to the sender).

==Specification==

Smart contracts that need to receive and own [[nep-11.mediawiki|NEP-11]] tokens
MUST implement <code>onNEP11Payment</code> method with the following signature:

<pre>
{
"name": "onNEP11Payment",
"parameters": [
{
"name": "from",
"type": "Hash160"
},
{
"name": "amount",
"type": "Integer"
},
{
"name": "tokenId",
"type": "ByteString"
},
{
"name": "data",
"type": "Any"
}
],
"returntype": "Void"
}
</pre>

A contract that doesn't implement this method will not be able to receive
Jim8y marked this conversation as resolved.
Show resolved Hide resolved
[[nep-11.mediawiki|NEP-11]] tokens (transfers to its address will fail). This
method is called by [[nep-11.mediawiki|NEP-11]] contracts which tokens are being
transferred. A particular token can be obtained via the <code>System.Runtime.GetCallingScriptHash</code>
system call. Contracts SHOULD check for this hash to match their expectations
like allowed/accepted tokens, processing this call means that contract trusts
the caller to implement [[nep-11.mediawiki|NEP-11]] correctly.

<code>from</code> parameter specifies the sender of tokens and can be <code>null</code>
for minted tokens.

<code>amount</code> is the amount of tokens transferred to the contract. It's
always 1 for non-divisible [[nep-11.mediawiki|NEP-11]] tokens.

<code>tokenId</code> is the transferred token ID.

<code>data</code> is the same <code>data</code>parameter that was given to the
[[nep-11.mediawiki|NEP-11]] <code>transfer</code> call. Contract can interpret
it in any application-specific way including imposing any restrictions on
accepted <code>data</code> contents.

If the contract implementing this NEP can't accept the transfer (for reasons like
unexpected token, wrong amount, forbidden sender, wrong <code>data</code>
parameter or any other) it MUST call <code>ABORT</code> or <code>ABORTMSG</code>
Jim8y marked this conversation as resolved.
Show resolved Hide resolved
VM opcode to fail the transaction. This is the only way to ensure contract
won't own transferred tokens, because the method doesn't return any value
and exceptions can be handled by calling contract. Contact MAY also check for
various conditions with <code>ASSERT</code> or <code>ASSERTMSG</code> opcodes.

==Rationale==

This NEP just follows and explains [[nep-11.mediawiki|NEP-11]] semantics, no
design decisions made here.

==Backwards Compatibility==

This NEP is completely compatible with [[nep-11.mediawiki|NEP-11]], there are
just two extensions to it:
* addition of <code>ABORTMSG</code> opcode that didn't exist at the time of [[nep-11.mediawiki|NEP-11]] creation, but it's the same thing as <code>ABORT</code> semantically.
* explicit <code>ASSERT</code> and <code>ASSERTMSG</code> opcode allowance that was not a part of [[nep-11.mediawiki|NEP-11]], but also has the same function as <code>ABORT</code> for failed conditions.

==Implementation==

96 changes: 96 additions & 0 deletions nep-27.mediawiki
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<pre>
NEP: 27
Title: Smart contract transfer callback for fungible tokens
Author: Erik Zhang <erik@neo.org>, Roman Khimov <roman@nspcc.ru>, Jaime Kindelan <jaime@red4sec.com>, Jimmy Liao <jimmy@r3e.network>
Type: Informational
Status: Accepted
Created: 2024-03-03
Requires: 17
</pre>

==Abstract==

This proposal describes the callback method used by Neo smart contracts to
process transfers of fungible [[nep-17.mediawiki|NEP-17]] tokens to their addresses.

==Motivation==

While the original [[nep-17.mediawiki|NEP-17]] already defines this method, it
lacks some details relevant specifically to the receiver contract and it doesn't
allow for easy reference. This standard can be used in <code>supportedstandards</code>
section of [[nep-15.mediawiki|NEP-15 manifest]] or in any other situation to
refer to contracts able to receive [[nep-17.mediawiki|NEP-17]] tokens (but not
necessarily being [[nep-17.mediawiki|NEP-17]] themselves). Typical use cases
for this functionality are: depositing/staking (with contract locking and
managing tokens according to some rules) or exchange/minting (contract accepting
transfer to itself, but returning some different token to the sender).

==Specification==

Smart contracts that need to receive and own [[nep-17.mediawiki|NEP-17]] tokens
MUST implement <code>onNEP17Payment</code> method with the following signature:

<pre>
{
"name": "onNEP17Payment",
"parameters": [
{
"name": "from",
"type": "Hash160"
},
{
"name": "amount",
"type": "Integer"
},
{
"name": "data",
"type": "Any"
}
],
"returntype": "Void"
}
</pre>

A contract that doesn't implement this method will not be able to receive
[[nep-17.mediawiki|NEP-17]] tokens (transfers to its address will fail). This
method is called by [[nep-17.mediawiki|NEP-17]] contracts which tokens are being
transferred. A particular token can be obtained via the <code>System.Runtime.GetCallingScriptHash</code>
system call. Contracts SHOULD check for this hash to match their expectations
like allowed/accepted tokens, processing this call means that contract trusts
the caller to implement [[nep-17.mediawiki|NEP-17]] correctly.

<code>from</code> parameter specifies the sender of tokens and can be <code>null</code>
for minted tokens.

<code>amount</code> is the amount of tokens transferred to the contract.

<code>data</code> is the same <code>data</code>parameter that was given to the
[[nep-17.mediawiki|NEP-17]] <code>transfer</code> call. Contract can interpret
it in any application-specific way including imposing any restrictions on
accepted <code>data</code> contents.

If the contract implementing this NEP can't accept the transfer (for reasons like
unexpected token, wrong amount, forbidden sender, wrong <code>data</code>
parameter or any other) it MUST call <code>ABORT</code> or <code>ABORTMSG</code>
VM opcode to fail the transaction. This is the only way to ensure contract
won't own transferred tokens, because the method doesn't return any value
and exceptions can be handled by calling contract. Contact MAY also check for
various conditions with <code>ASSERT</code> or <code>ASSERTMSG</code> opcodes.

==Rationale==

This NEP just follows and explains [[nep-17.mediawiki|NEP-17]] semantics, no
design decisions made here.

==Backwards Compatibility==

This NEP is completely compatible with [[nep-17.mediawiki|NEP-17]], there are
just two extensions to it:
* addition of <code>ABORTMSG</code> opcode that didn't exist at the time of [[nep-17.mediawiki|NEP-17]] creation, but it's the same thing as <code>ABORT</code> semantically.
* explicit <code>ASSERT</code> and <code>ASSERTMSG</code> opcode allowance that was not a part of [[nep-17.mediawiki|NEP-17]], but also has the same function as <code>ABORT</code> for failed conditions.

==Implementation==

* https://github.com/nspcc-dev/neo-go/blob/8d67f17943553740fe4caa31c3e9efcf7921aba2/examples/nft-nd/nft.go#L213
* https://github.com/nspcc-dev/neofs-contract/blob/91962f2c5625d8aab42adde138f2411c52393382/contracts/neofs/contract.go#L237
* https://github.com/neoburger/code/blob/beb433288bd7b0da9d2245e2a0ae5af5309da7ed/BurgerNEO.cs#L42