From 6712688268d7e9e378aad44e950e7463328e5beb Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Sat, 30 Aug 2025 10:36:57 -0400 Subject: [PATCH 01/12] Init Draft --- nep-33.mediawiki | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 nep-33.mediawiki diff --git a/nep-33.mediawiki b/nep-33.mediawiki new file mode 100644 index 00000000..f7a1bf3f --- /dev/null +++ b/nep-33.mediawiki @@ -0,0 +1,38 @@ +
+  NEP: 33
+  Title: Fixed/Dynamic Non-native Contract Method Pricing
+  Author: Christopher Schuchardt 
+  Type: Standard
+  Status: Draft
+  Created: 2025-08-30
+
+ +==Abstract== + +One may want to charge a fee for calling one of their contract methods. Allowing non-native contracts to have fixed or dynamic pricing for methods within their contract then sendings the funds to the contract itself. + +==Motivation== + +Now contract owners, such as a exchange may want a way to get a little gas for sending funds to external wallets. This would allow the contract to have one or several methods to have a fixed price, taking a bit of GAS from transactioner for calling the a method (Much like an opcode price works). For example, a LP token maybe want you to pay a gas for withdrawing funds. Reason most LP/Fund contracts do this to pay for the LP contract pool's transaction fees for adjusting prices and other things. + +==Specification== + +The technical specification should describe the syntax and semantics of any new feature. The specification should be detailed enough to allow competing, interoperable implementations for any of the current NEO platforms. + +==Rationale== + +The rationale fleshes out the specification by describing what motivated the design and why particular design decisions were made. It should describe alternate designs that were considered and related work, e.g. how the feature is supported in other languages. + +The rationale may also provide evidence of consensus within the community, and should discuss important objections or concerns raised during discussion. + +==Backwards Compatibility== + +All NEPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The NEP must explain how the author proposes to deal with these incompatibilities. NEP submissions without a sufficient backwards compatibility treatise may be rejected outright. + +==Test Cases== + +Test cases for an implementation are mandatory for NEPs that are affecting consensus changes. Other NEPs can choose to include links to test cases if applicable. + +==Implementation== + +The implementations must be completed before any NEP is given status "Final", but it need not be completed before the NEP is accepted. It is better to finish the specification and rationale first and reach consensus on it before writing code. From 2386c52512ab5cc879743a9967f212a5750603d6 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Sat, 30 Aug 2025 10:39:57 -0400 Subject: [PATCH 02/12] fixed up text --- nep-33.mediawiki | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/nep-33.mediawiki b/nep-33.mediawiki index f7a1bf3f..73c8d279 100644 --- a/nep-33.mediawiki +++ b/nep-33.mediawiki @@ -17,22 +17,10 @@ Now contract owners, such as a exchange may want a way to get a little gas for s ==Specification== -The technical specification should describe the syntax and semantics of any new feature. The specification should be detailed enough to allow competing, interoperable implementations for any of the current NEO platforms. - ==Rationale== -The rationale fleshes out the specification by describing what motivated the design and why particular design decisions were made. It should describe alternate designs that were considered and related work, e.g. how the feature is supported in other languages. - -The rationale may also provide evidence of consensus within the community, and should discuss important objections or concerns raised during discussion. - ==Backwards Compatibility== -All NEPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The NEP must explain how the author proposes to deal with these incompatibilities. NEP submissions without a sufficient backwards compatibility treatise may be rejected outright. - ==Test Cases== -Test cases for an implementation are mandatory for NEPs that are affecting consensus changes. Other NEPs can choose to include links to test cases if applicable. - ==Implementation== - -The implementations must be completed before any NEP is given status "Final", but it need not be completed before the NEP is accepted. It is better to finish the specification and rationale first and reach consensus on it before writing code. From 71d08440c3ef1351df9e95c3244e4f0c59acbaae Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Sun, 31 Aug 2025 12:19:46 -0400 Subject: [PATCH 03/12] Added Specification --- nep-33.mediawiki | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/nep-33.mediawiki b/nep-33.mediawiki index 73c8d279..d90cd9a5 100644 --- a/nep-33.mediawiki +++ b/nep-33.mediawiki @@ -9,18 +9,50 @@ ==Abstract== -One may want to charge a fee for calling one of their contract methods. Allowing non-native contracts to have fixed or dynamic pricing for methods within their contract then sendings the funds to the contract itself. +One may want to charge a fee for calling one of their contract methods. Allowing non-native contracts to have fixed or dynamic +pricing for methods within their contract then sendings the funds to the contract itself. ==Motivation== -Now contract owners, such as a exchange may want a way to get a little gas for sending funds to external wallets. This would allow the contract to have one or several methods to have a fixed price, taking a bit of GAS from transactioner for calling the a method (Much like an opcode price works). For example, a LP token maybe want you to pay a gas for withdrawing funds. Reason most LP/Fund contracts do this to pay for the LP contract pool's transaction fees for adjusting prices and other things. +Now contract owners, such as a exchange may want a way to get a little gas for sending funds to external wallets. This would allow the contract to have +one or several methods to have a fixed price, taking a bit of GAS from transactioner for calling the a method (Much like an opcode price works). +For example, a LP token maybe want you to pay a gas for withdrawing funds. Reason most LP/Fund contracts do this to pay for the LP contract pool's +transaction fees for adjusting prices and other things. ==Specification== -==Rationale== - -==Backwards Compatibility== +Contract will have a JSON string in their storage at prefix 0xfe. At this storage location the JSON string will +contain all the meta data for each method name, amount and/or receiver address. + +When syscall System.Contract.Call calls a method within the contract it checks contract storage for 0xfe prefix. If it exists +then will read the JSON string to see if the method exists. If the method does exist than will follow the following. + +1. Method + - Method must exist within the contract's manifest. + - If method doesn't exist within the manifest than entry is ignored. + - Method can't start with underscores _. This limits malicious activity. For example if you were to require a fee for _initialize. + - Methods verify and onNEP17Payment will be ignored. +1. Amount + - If the amount doesn't exist. Then the small unit of GAS is used (_Datoshi_). _For example: GAS's small unit in Datoshi 0.00000001_ + - If the amount does exist. Than that unit amount will than be deducted from. + - Amount can't be negative. If is negative than this entry is ignored. +1. Receiver Address + - If receiver doesn't exist. Than the contract itself must have the onNEP17Payment method and must support NEP17 and accept GAS. + - If reciever does exist. Than the reciever address will be used. The reciever address can be a NEP17 contract. However the contract must accept GAS. + - If an error occurs for any reason the gas is then burned and added as an VM fee as used GAS. + - If 0x0000000000000000000000000000000000000000 is used the amount is burnned no matter what. + +==JSON== +
+[
+  {
+    "name": "MyMethod"                                        // method name
+    "amount": 1                                               // in Datoshi
+    "receive": "0x0000000000000000000000000000000000000000"   // Receiver address in ScriptHash format
+  }
+]
+
-==Test Cases== +==Storage== ==Implementation== From 9273b195908899566f81542eeb4ff14e16561493 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Sun, 31 Aug 2025 12:25:17 -0400 Subject: [PATCH 04/12] typoes --- nep-33.mediawiki | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nep-33.mediawiki b/nep-33.mediawiki index d90cd9a5..1ab6a381 100644 --- a/nep-33.mediawiki +++ b/nep-33.mediawiki @@ -34,10 +34,10 @@ then will read the JSON string to see if the method exists. If the - Methods verify and onNEP17Payment will be ignored. 1. Amount - If the amount doesn't exist. Then the small unit of GAS is used (_Datoshi_). _For example: GAS's small unit in Datoshi 0.00000001_ - - If the amount does exist. Than that unit amount will than be deducted from. + - If the amount does exist. Than that unit amount will than be deducted. - Amount can't be negative. If is negative than this entry is ignored. 1. Receiver Address - - If receiver doesn't exist. Than the contract itself must have the onNEP17Payment method and must support NEP17 and accept GAS. + - If receiver doesn't exist. Than the contract itself must have the onNEP17Payment method, must support NEP17 and accept GAS. - If reciever does exist. Than the reciever address will be used. The reciever address can be a NEP17 contract. However the contract must accept GAS. - If an error occurs for any reason the gas is then burned and added as an VM fee as used GAS. - If 0x0000000000000000000000000000000000000000 is used the amount is burnned no matter what. From abec27c0a31a4c01da2104a13b830767274c2ac8 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Sun, 31 Aug 2025 12:26:33 -0400 Subject: [PATCH 05/12] more typoes --- nep-33.mediawiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nep-33.mediawiki b/nep-33.mediawiki index 1ab6a381..fc977964 100644 --- a/nep-33.mediawiki +++ b/nep-33.mediawiki @@ -33,7 +33,7 @@ then will read the JSON string to see if the method exists. If the - Method can't start with underscores _. This limits malicious activity. For example if you were to require a fee for _initialize. - Methods verify and onNEP17Payment will be ignored. 1. Amount - - If the amount doesn't exist. Then the small unit of GAS is used (_Datoshi_). _For example: GAS's small unit in Datoshi 0.00000001_ + - If the amount doesn't exist. Then the smallest unit of GAS is used (_Datoshi_). _For example: GAS's small unit in Datoshi 0.00000001_ - If the amount does exist. Than that unit amount will than be deducted. - Amount can't be negative. If is negative than this entry is ignored. 1. Receiver Address From 7be9feb4f0a01a8ffff60288b1340e815adb23e9 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Sun, 31 Aug 2025 12:28:03 -0400 Subject: [PATCH 06/12] more typoes --- nep-33.mediawiki | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nep-33.mediawiki b/nep-33.mediawiki index fc977964..e4617550 100644 --- a/nep-33.mediawiki +++ b/nep-33.mediawiki @@ -37,9 +37,9 @@ then will read the JSON string to see if the method exists. If the - If the amount does exist. Than that unit amount will than be deducted. - Amount can't be negative. If is negative than this entry is ignored. 1. Receiver Address - - If receiver doesn't exist. Than the contract itself must have the onNEP17Payment method, must support NEP17 and accept GAS. - - If reciever does exist. Than the reciever address will be used. The reciever address can be a NEP17 contract. However the contract must accept GAS. - - If an error occurs for any reason the gas is then burned and added as an VM fee as used GAS. + - If receiver doesn't exist. Then the contract itself must have the onNEP17Payment method, must support NEP17 and accept GAS. + - If reciever does exist. Then the reciever address will be used. The reciever address can be a NEP17 contract. However that contract must accept GAS. + - If an error occurs for any reason the gas is than burned. - If 0x0000000000000000000000000000000000000000 is used the amount is burnned no matter what. ==JSON== From c6e87adc88d0417b611ad3201ddc2b7fced8385a Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Wed, 17 Sep 2025 12:00:28 -0400 Subject: [PATCH 07/12] Updated spec more --- nep-33.mediawiki | 60 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/nep-33.mediawiki b/nep-33.mediawiki index e4617550..702f5957 100644 --- a/nep-33.mediawiki +++ b/nep-33.mediawiki @@ -1,6 +1,6 @@
   NEP: 33
-  Title: Fixed/Dynamic Non-native Contract Method Pricing
+  Title: Fixed/Dynamic Gas Contract Method Pricing
   Author: Christopher Schuchardt 
   Type: Standard
   Status: Draft
@@ -16,7 +16,7 @@ pricing for methods within their contract then sendings the funds to the contrac
 
 Now contract owners, such as a exchange may want a way to get a little gas for sending funds to external wallets. This would allow the contract to have
 one or several methods to have a fixed price, taking a bit of GAS from transactioner for calling the a method (Much like an opcode price works).
-For example, a LP token maybe want you to pay a gas for withdrawing funds. Reason most LP/Fund contracts do this to pay for the LP contract pool's 
+For example, a LP token maybe want you to pay gas for withdrawing funds. Reason most LP/Fund contracts do this to pay for the LP contract pool's 
 transaction fees for adjusting prices and other things.
 
 ==Specification==
@@ -30,19 +30,21 @@ then will read the JSON string to see if the method exists. If the
 1. Method
    - Method must exist within the contract's manifest.
    - If method doesn't exist within the manifest than entry is ignored.
-   - Method can't start with underscores _. This limits malicious activity. For example if you were to require a fee for _initialize.
-   - Methods verify and onNEP17Payment will be ignored.
+   - Method can not start with a underscore _. This limits malicious activity. For example if you were to require a fee for _initialize.
+   - Methods verify, onNEP11Payment and onNEP17Payment will be ignored.
 1. Amount
    - If the amount doesn't exist. Then the smallest unit of GAS is used (_Datoshi_). _For example: GAS's small unit in Datoshi 0.00000001_
    - If the amount does exist. Than that unit amount will than be deducted.
-   - Amount can't be negative. If is negative than this entry is ignored.
+   - Amount can not be negative. If is negative than this entry is ignored.
 1. Receiver Address
-   - If receiver doesn't exist. Then the contract itself must have the onNEP17Payment method, must support NEP17 and accept GAS.
-   - If reciever does exist. Then the reciever address will be used. The reciever address can be a NEP17 contract. However that contract must accept GAS.
-   - If an error occurs for any reason the gas is than burned.
+   - If receiver doesn't exist. Then the contract itself **SHOULD** have the onNEP17Payment method and **SHOULD** support NEP17 and accept GAS.
+   - If reciever does exist. Then the reciever address will be used. The reciever address can be a NEP17 contract. However that contract **SHOULD** accept GAS.
    - If 0x0000000000000000000000000000000000000000 is used the amount is burnned no matter what.
 
-==JSON==
+===JSON Storage===
+
+The JSON _below_ is an example of the storage value at 0xfe.
+
 
 [
   {
@@ -53,6 +55,44 @@ then will read the JSON string to see if the method exists. If the
 ]
 
-==Storage== +===Contract Methods=== + +Other contract creators may want to know the price of a contract's methods. To do so there is a method required to get price of any method +within the contract. This method is called GetMethodPrice. This method returns either a JSON array or object depending upon +how you call the method. The method has one argument (_parameter_). This argument is of type String. The argument takes in the name of the +method you want the price for. This method name **MUST** exist in the contract's manifest (_if it does not than the method does not exist within the contract_). +Passing in null tells the method to return a JSON Array of all the contract's methods that has fees associated with them. Otherwise +passing in a method name will return **ONLY** that entry. + +===JSON Method Entry=== + +====JSON Array==== + +Calling GetMethodPrice with null. +
+[
+  {
+    "name": "MyMethod1"                                       // method name
+    "amount": 1                                               // in Datoshi
+    "receive": "0x0000000000000000000000000000000000000000"   // Receiver address in ScriptHash format
+  },
+  {
+    "name": "MyMethod2"                                       // method name
+    "amount": 100000000                                       // in Datoshi
+    "receive": "0x0000000000000000000000000000000000000000"   // Receiver address in ScriptHash format
+  }
+]
+
+ +====JSON Object==== + +Calling GetMethodPrice with MyMethod2. +
+{
+  "name": "MyMethod2"                                       // method name
+  "amount": 100000000                                       // in Datoshi
+  "receive": "0x0000000000000000000000000000000000000000"   // Receiver address in ScriptHash format
+}
+
==Implementation== From 50c2f31e88734ac2e0a4b407947f929ceee78d6b Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Wed, 17 Sep 2025 12:03:38 -0400 Subject: [PATCH 08/12] fixed formating --- nep-33.mediawiki | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/nep-33.mediawiki b/nep-33.mediawiki index 702f5957..66ac0cd1 100644 --- a/nep-33.mediawiki +++ b/nep-33.mediawiki @@ -28,18 +28,18 @@ When syscall System.Contract.Call calls a method within the contrac then will read the JSON string to see if the method exists. If the method does exist than will follow the following. 1. Method - - Method must exist within the contract's manifest. - - If method doesn't exist within the manifest than entry is ignored. - - Method can not start with a underscore _. This limits malicious activity. For example if you were to require a fee for _initialize. - - Methods verify, onNEP11Payment and onNEP17Payment will be ignored. + - Method must exist within the contract's manifest. + - If method doesn't exist within the manifest than entry is ignored. + - Method can not start with a underscore _. This limits malicious activity. For example if you were to require a fee for _initialize. + - Methods verify, onNEP11Payment and onNEP17Payment will be ignored. 1. Amount - - If the amount doesn't exist. Then the smallest unit of GAS is used (_Datoshi_). _For example: GAS's small unit in Datoshi 0.00000001_ - - If the amount does exist. Than that unit amount will than be deducted. - - Amount can not be negative. If is negative than this entry is ignored. + - If the amount doesn't exist. Then the smallest unit of GAS is used (_Datoshi_). _For example: GAS's small unit in Datoshi 0.00000001_ + - If the amount does exist. Than that unit amount will than be deducted. + - Amount can not be negative. If is negative than this entry is ignored. 1. Receiver Address - - If receiver doesn't exist. Then the contract itself **SHOULD** have the onNEP17Payment method and **SHOULD** support NEP17 and accept GAS. - - If reciever does exist. Then the reciever address will be used. The reciever address can be a NEP17 contract. However that contract **SHOULD** accept GAS. - - If 0x0000000000000000000000000000000000000000 is used the amount is burnned no matter what. + - If receiver doesn't exist. Then the contract itself **SHOULD** have the onNEP17Payment method and **SHOULD** support NEP17 and accept GAS. + - If reciever does exist. Then the reciever address will be used. The reciever address can be a NEP17 contract. However that contract **SHOULD** accept GAS. + - If 0x0000000000000000000000000000000000000000 is used the amount is burnned no matter what. ===JSON Storage=== From 4ecd34e86a58bf5f49f992cb66fc5e2abbd1f457 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Wed, 17 Sep 2025 12:07:58 -0400 Subject: [PATCH 09/12] formatting --- nep-33.mediawiki | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/nep-33.mediawiki b/nep-33.mediawiki index 66ac0cd1..39f4a9a6 100644 --- a/nep-33.mediawiki +++ b/nep-33.mediawiki @@ -28,18 +28,18 @@ When syscall System.Contract.Call calls a method within the contrac then will read the JSON string to see if the method exists. If the method does exist than will follow the following. 1. Method - - Method must exist within the contract's manifest. - - If method doesn't exist within the manifest than entry is ignored. - - Method can not start with a underscore _. This limits malicious activity. For example if you were to require a fee for _initialize. - - Methods verify, onNEP11Payment and onNEP17Payment will be ignored. -1. Amount - - If the amount doesn't exist. Then the smallest unit of GAS is used (_Datoshi_). _For example: GAS's small unit in Datoshi 0.00000001_ - - If the amount does exist. Than that unit amount will than be deducted. - - Amount can not be negative. If is negative than this entry is ignored. -1. Receiver Address - - If receiver doesn't exist. Then the contract itself **SHOULD** have the onNEP17Payment method and **SHOULD** support NEP17 and accept GAS. - - If reciever does exist. Then the reciever address will be used. The reciever address can be a NEP17 contract. However that contract **SHOULD** accept GAS. - - If 0x0000000000000000000000000000000000000000 is used the amount is burnned no matter what. + * Method must exist within the contract's manifest. + * If method doesn't exist within the manifest than entry is ignored. + * Method can not start with a underscore _. This limits malicious activity. For example if you were to require a fee for _initialize. + * Methods verify, onNEP11Payment and onNEP17Payment will be ignored. +2. Amount + * If the amount doesn't exist. Then the smallest unit of GAS is used (_Datoshi_). _For example: GAS's small unit in Datoshi 0.00000001_ + * If the amount does exist. Than that unit amount will than be deducted. + * Amount can not be negative. If is negative than this entry is ignored. +3. Receiver Address + * If receiver doesn't exist. Then the contract itself **SHOULD** have the onNEP17Payment method and **SHOULD** support NEP17 and accept GAS. + * If reciever does exist. Then the reciever address will be used. The reciever address can be a NEP17 contract. However that contract **SHOULD** accept GAS. + * If 0x0000000000000000000000000000000000000000 is used the amount is burnned no matter what. ===JSON Storage=== From 1e8f0263ba571fa05b9741e93d04e57856c4eee0 Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Wed, 17 Sep 2025 12:11:24 -0400 Subject: [PATCH 10/12] formatting --- nep-33.mediawiki | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/nep-33.mediawiki b/nep-33.mediawiki index 39f4a9a6..3069ab56 100644 --- a/nep-33.mediawiki +++ b/nep-33.mediawiki @@ -27,19 +27,19 @@ contain all the meta data for each method name, amount and/or receiver address. When syscall System.Contract.Call calls a method within the contract it checks contract storage for 0xfe prefix. If it exists then will read the JSON string to see if the method exists. If the method does exist than will follow the following. -1. Method - * Method must exist within the contract's manifest. - * If method doesn't exist within the manifest than entry is ignored. - * Method can not start with a underscore _. This limits malicious activity. For example if you were to require a fee for _initialize. - * Methods verify, onNEP11Payment and onNEP17Payment will be ignored. -2. Amount - * If the amount doesn't exist. Then the smallest unit of GAS is used (_Datoshi_). _For example: GAS's small unit in Datoshi 0.00000001_ - * If the amount does exist. Than that unit amount will than be deducted. - * Amount can not be negative. If is negative than this entry is ignored. -3. Receiver Address - * If receiver doesn't exist. Then the contract itself **SHOULD** have the onNEP17Payment method and **SHOULD** support NEP17 and accept GAS. - * If reciever does exist. Then the reciever address will be used. The reciever address can be a NEP17 contract. However that contract **SHOULD** accept GAS. - * If 0x0000000000000000000000000000000000000000 is used the amount is burnned no matter what. +* Method +** Method must exist within the contract's manifest. +** If method doesn't exist within the manifest than entry is ignored. +** Method can not start with a underscore _. This limits malicious activity. For example if you were to require a fee for _initialize. +** Methods verify, onNEP11Payment and onNEP17Payment will be ignored. +* Amount +** If the amount doesn't exist. Then the smallest unit of GAS is used (_Datoshi_). _For example: GAS's small unit in Datoshi 0.00000001_ +** If the amount does exist. Than that unit amount will than be deducted. +** Amount can not be negative. If is negative than this entry is ignored. +* Receiver Address +** If receiver doesn't exist. Then the contract itself **SHOULD** have the onNEP17Payment method and **SHOULD** support NEP17 and accept GAS. +** If reciever does exist. Then the reciever address will be used. The reciever address can be a NEP17 contract. However that contract **SHOULD** accept GAS. +** If 0x0000000000000000000000000000000000000000 is used the amount is burnned no matter what. ===JSON Storage=== From e08045972032fc091cc603d494764514829ff7bf Mon Sep 17 00:00:00 2001 From: "Christopher R. Schuchardt" Date: Wed, 17 Sep 2025 12:19:35 -0400 Subject: [PATCH 11/12] removed some formatting --- nep-33.mediawiki | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/nep-33.mediawiki b/nep-33.mediawiki index 3069ab56..69b8b89b 100644 --- a/nep-33.mediawiki +++ b/nep-33.mediawiki @@ -33,17 +33,17 @@ then will read the JSON string to see if the method exists. If the ** Method can not start with a underscore _. This limits malicious activity. For example if you were to require a fee for _initialize. ** Methods verify, onNEP11Payment and onNEP17Payment will be ignored. * Amount -** If the amount doesn't exist. Then the smallest unit of GAS is used (_Datoshi_). _For example: GAS's small unit in Datoshi 0.00000001_ +** If the amount doesn't exist. Then the smallest unit of GAS is used (Datoshi). For example: GAS's small unit in Datoshi 0.00000001 ** If the amount does exist. Than that unit amount will than be deducted. ** Amount can not be negative. If is negative than this entry is ignored. * Receiver Address -** If receiver doesn't exist. Then the contract itself **SHOULD** have the onNEP17Payment method and **SHOULD** support NEP17 and accept GAS. -** If reciever does exist. Then the reciever address will be used. The reciever address can be a NEP17 contract. However that contract **SHOULD** accept GAS. +** If receiver doesn't exist. Then the contract itself SHOULD have the onNEP17Payment method and SHOULD support NEP17 and accept GAS. +** If reciever does exist. Then the reciever address will be used. The reciever address can be a NEP17 contract. However that contract SHOULD accept GAS. ** If 0x0000000000000000000000000000000000000000 is used the amount is burnned no matter what. ===JSON Storage=== -The JSON _below_ is an example of the storage value at 0xfe. +The JSON below is an example of the storage value at 0xfe.
 [
@@ -59,10 +59,10 @@ The JSON _below_ is an example of the storage value at 0xferequired to get price of any method
 within the contract. This method is called GetMethodPrice. This method returns either a JSON array or object depending upon
-how you call the method. The method has one argument (_parameter_). This argument is of type String. The argument takes in the name of the
-method you want the price for. This method name **MUST** exist in the contract's manifest (_if it does not than the method does not exist within the contract_).
+how you call the method. The method has one argument (parameter). This argument is of type String. The argument takes in the name of the
+method you want the price for. This method name MUST exist in the contract's manifest (if it does not than the method does not exist within the contract).
 Passing in null tells the method to return a JSON Array of all the contract's methods that has fees associated with them. Otherwise
-passing in a method name will return **ONLY** that entry. 
+passing in a method name will return ONLY that entry. 
 
 ===JSON Method Entry===
 

From e5765e3a5de46b438726a47cab17cc9c9be869f1 Mon Sep 17 00:00:00 2001
From: "Christopher R. Schuchardt" 
Date: Mon, 27 Oct 2025 13:21:22 -0400
Subject: [PATCH 12/12] Add contract

---
 nep-33.md        | 186 +++++++++++++++++++++++++++++++++++++++++++++++
 nep-33.mediawiki |  98 -------------------------
 2 files changed, 186 insertions(+), 98 deletions(-)
 create mode 100644 nep-33.md
 delete mode 100644 nep-33.mediawiki

diff --git a/nep-33.md b/nep-33.md
new file mode 100644
index 00000000..6710eada
--- /dev/null
+++ b/nep-33.md
@@ -0,0 +1,186 @@
+# **NEP: Custom Contract Fees Mechanism**
+
+**NEP**: TBD
+**Title:** Custom Contract Fees with ABI Declaration and Runtime Enforcement
+**Author:** Chris Schuchardt 
+**Type:** Standard
+**Status:** Draft
+**Created:** 2025-10-27
+
+---
+
+## **Abstract**
+
+This NEP introduces a standardized mechanism for smart contracts to declare **custom execution fees** in addition to standard network gas. These fees are paid by the caller to the contract owner (or designated beneficiary) upon successful invocation. The fee structure is declared in the contract's ABI using a new `"fee"` metadata field, supported by updated DevPack tooling and enforced by the Neo VM at runtime.
+
+---
+
+## **Motivation**
+
+Many dApps require **micro-transactions**, **subscription models**, or **usage-based billing** directly within smart contracts. While GAS covers network costs, there is no native way to charge **contract-level fees** in NEO or NEP-17 tokens. This NEP enables:
+
+- Pay-per-use contracts
+- Premium feature access
+- Decentralized SaaS models
+- Fair compensation for contract maintainers
+
+---
+
+## **Specification**
+
+### 1. **ABI Extension: Fee Declaration**
+
+A new optional `"fee"` object is added to method definitions in the ABI.
+
+```json
+{
+  "methods": [
+    {
+      "name": "queryData",
+      "parameters": [...],
+      "returnType": "String",
+      "offset": 123,
+      "safe": true,
+      "fee": {
+        "asset": "NEO" | "GAS" | "0x..." (NEP-17 hash),
+        "amount": 100000000,  // 8 decimals (1.0 fixed-point)
+        "beneficiary": "NM7g6DAeN3Nx8iK53GqY7fpeyqX5bUq5fJ",  // optional, defaults to contract owner
+        "mode": "fixed" | "dynamic",  // fixed = static, dynamic = computed at runtime
+        "dynamicScriptHash": "0x..."   // required if mode=dynamic
+      }
+    }
+  ]
+}
+```
+
+#### Field Details:
+
+| Field | Type | Required | Description |
+|------|------|----------|-----------|
+| `asset` | string | Yes | `"NEO"`, `"GAS"`, or NEP-17 contract hash |
+| `amount` | integer | Yes (if `mode=fixed`) | Amount in smallest unit (8 decimals) |
+| `beneficiary` | string | No | Address to receive fee. Defaults to contract owner |
+| `mode` | string | Yes | `"fixed"` or `"dynamic"` |
+| `dynamicScriptHash` | string | Yes (if `mode=dynamic`) | Contract that computes fee via `calculateFee(...)` |
+
+> **Note**: Dynamic fees allow complex logic (e.g., tiered pricing, time-based rates).
+
+---
+
+### 2. **Dynamic Fee Computation Interface**
+
+Contracts declaring `mode: "dynamic"` must reference a fee calculator contract implementing:
+
+```csharp
+public interface IFeeCalculator
+{
+    BigInteger CalculateFee(ByteString method, object[] args);
+}
+```
+
+- Called by VM before execution
+- Must be **Safe** (no state changes)
+- Returns fee in smallest unit
+
+---
+
+### 3. **Decorators**
+
+#### C# Attributes
+
+```csharp
+[Fee(Asset = "GAS", Amount = 50000000, Mode = FeeMode.Fixed)] // 0.5 GAS
+[Fee(Asset = "0xd2a4cff31913016155e38e474a2c06d08be276cf", Amount = 100, Mode = FeeMode.Fixed)]
+public static string QueryData(string key) { ... }
+
+[Fee(
+    Asset = "GAS",
+    Mode = FeeMode.Dynamic,
+    Calculator = "0xb2a4cff31913016155e38e474a2c06d08be296cf"
+)]
+public static UInt160 GetReport(uint userId) { ... }
+```
+
+#### DevPack Export Logic
+
+- `[Fee]` attribute is parsed during ABI generation
+- Populates `fee` field in method ABI
+- Validates:
+  - Asset is valid (NEO, GAS, or deployed NEP-17)
+  - `Calculator` implements `IFeeCalculator` if `mode=dynamic`
+- Emits warning if fee > 10 GAS (configurable)
+
+---
+
+### 4. **Runtime Enforcement (Neo VM)**
+
+#### Invocation Flow
+
+```text
+1. Caller invokes contract.method(args)
+2. VM loads ABI
+3. If method has .fee:
+   a. If fixed → deduct exact amount from caller
+   b. If dynamic → invoke calculator.CalculateFee(method, args)
+        → deduct returned amount
+4. Transfer fee to beneficiary (or contract owner)
+5. Proceed with execution
+6. On failure → refund fee (but not network GAS)
+```
+
+#### Safety Guarantees
+
+- **Atomicity**: Fee deduction and transfer are atomic with execution
+- **Reentrancy-safe**: Uses `ExecutionEngine` snapshot
+- **No double-spend**: Fee checked before execution
+- **Refund on revert**: Fee returned if contract throws
+
+#### Syscall Additions
+
+```csharp
+// Pseudo-syscalls (internal)
+bool ChargeFee(UInt160 asset, BigInteger amount, UInt160 beneficiary);
+BigInteger QueryDynamicFee(UInt160 calculator, string method, object[] args);
+```
+
+---
+
+### 5. **Contract Owner & Beneficiary**
+
+- Contract owner = deployer address (stored in manifest)
+- `beneficiary` overrides owner per method
+- If omitted → owner
+
+---
+
+## **Security Considerations**
+
+- **DoS via high dynamic fees**: Mitigated by gas cap on calculator
+- **Calculator griefing**: Limit execution to 100k gas
+- **Fee spoofing**: ABI is signed in manifest
+- **Replay attacks**: Nonce not needed (fee per call)
+
+---
+
+## **Backwards Compatibility**
+
+- Existing contracts without `[Fee]` → no fee charged
+- Old DevPack versions → ignore attribute
+- VM ignores unknown ABI fields
+
+---
+
+## **Appendix**
+
+### Example Contract
+
+```csharp
+public class MyContract : SmartContract
+{
+    [Fee(Asset = "GAS", Amount = 100000000, Beneficiary = "NM7...")] // 1 GAS
+    public static string GetPrice(string symbol) { ... }
+
+    [Fee(Asset = "0xabc...", Mode = FeeMode.Dynamic, Calculator = "0xb2a4cff31913016155e38e474a2c06d08be296cf")]
+    public static UInt160 GetReport(uint tier) { ... }
+}
+```
\ No newline at end of file
diff --git a/nep-33.mediawiki b/nep-33.mediawiki
deleted file mode 100644
index 69b8b89b..00000000
--- a/nep-33.mediawiki
+++ /dev/null
@@ -1,98 +0,0 @@
-
-  NEP: 33
-  Title: Fixed/Dynamic Gas Contract Method Pricing
-  Author: Christopher Schuchardt 
-  Type: Standard
-  Status: Draft
-  Created: 2025-08-30
-
- -==Abstract== - -One may want to charge a fee for calling one of their contract methods. Allowing non-native contracts to have fixed or dynamic -pricing for methods within their contract then sendings the funds to the contract itself. - -==Motivation== - -Now contract owners, such as a exchange may want a way to get a little gas for sending funds to external wallets. This would allow the contract to have -one or several methods to have a fixed price, taking a bit of GAS from transactioner for calling the a method (Much like an opcode price works). -For example, a LP token maybe want you to pay gas for withdrawing funds. Reason most LP/Fund contracts do this to pay for the LP contract pool's -transaction fees for adjusting prices and other things. - -==Specification== - -Contract will have a JSON string in their storage at prefix 0xfe. At this storage location the JSON string will -contain all the meta data for each method name, amount and/or receiver address. - -When syscall System.Contract.Call calls a method within the contract it checks contract storage for 0xfe prefix. If it exists -then will read the JSON string to see if the method exists. If the method does exist than will follow the following. - -* Method -** Method must exist within the contract's manifest. -** If method doesn't exist within the manifest than entry is ignored. -** Method can not start with a underscore _. This limits malicious activity. For example if you were to require a fee for _initialize. -** Methods verify, onNEP11Payment and onNEP17Payment will be ignored. -* Amount -** If the amount doesn't exist. Then the smallest unit of GAS is used (Datoshi). For example: GAS's small unit in Datoshi 0.00000001 -** If the amount does exist. Than that unit amount will than be deducted. -** Amount can not be negative. If is negative than this entry is ignored. -* Receiver Address -** If receiver doesn't exist. Then the contract itself SHOULD have the onNEP17Payment method and SHOULD support NEP17 and accept GAS. -** If reciever does exist. Then the reciever address will be used. The reciever address can be a NEP17 contract. However that contract SHOULD accept GAS. -** If 0x0000000000000000000000000000000000000000 is used the amount is burnned no matter what. - -===JSON Storage=== - -The JSON below is an example of the storage value at 0xfe. - -
-[
-  {
-    "name": "MyMethod"                                        // method name
-    "amount": 1                                               // in Datoshi
-    "receive": "0x0000000000000000000000000000000000000000"   // Receiver address in ScriptHash format
-  }
-]
-
- -===Contract Methods=== - -Other contract creators may want to know the price of a contract's methods. To do so there is a method required to get price of any method -within the contract. This method is called GetMethodPrice. This method returns either a JSON array or object depending upon -how you call the method. The method has one argument (parameter). This argument is of type String. The argument takes in the name of the -method you want the price for. This method name MUST exist in the contract's manifest (if it does not than the method does not exist within the contract). -Passing in null tells the method to return a JSON Array of all the contract's methods that has fees associated with them. Otherwise -passing in a method name will return ONLY that entry. - -===JSON Method Entry=== - -====JSON Array==== - -Calling GetMethodPrice with null. -
-[
-  {
-    "name": "MyMethod1"                                       // method name
-    "amount": 1                                               // in Datoshi
-    "receive": "0x0000000000000000000000000000000000000000"   // Receiver address in ScriptHash format
-  },
-  {
-    "name": "MyMethod2"                                       // method name
-    "amount": 100000000                                       // in Datoshi
-    "receive": "0x0000000000000000000000000000000000000000"   // Receiver address in ScriptHash format
-  }
-]
-
- -====JSON Object==== - -Calling GetMethodPrice with MyMethod2. -
-{
-  "name": "MyMethod2"                                       // method name
-  "amount": 100000000                                       // in Datoshi
-  "receive": "0x0000000000000000000000000000000000000000"   // Receiver address in ScriptHash format
-}
-
- -==Implementation==