You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/developers/developer-reference/sc-payments.md
+121-9Lines changed: 121 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,9 +10,12 @@ title: Smart contract payments
10
10
We want to offer an overview on how smart contracts process payments. This includes two complementary parts: receiving tokens and sending them.
11
11
12
12
:::important important
13
-
On MultiversX it is impossible to send both EGLD and any ESDT token at the same time.
13
+
On MultiversX it is possible to send one or more tokens with any transaction. This includes EGLD, and it is also possible (though impractical) to send several payments of the same token at once.
14
+
:::
15
+
14
16
15
-
For this reason you will see no syntax for transferring both, neither when sending, nor receiving.
17
+
:::note note
18
+
Historically, it used to be impossible to send EGLD and ESDT at the same time, this is why some of the legacy APIs have this restriction. This restriction no longer applies since the [Spica release](https://multiversx.com/release/release-spica-patch-4-v1-8-12).
16
19
:::
17
20
18
21
---
@@ -29,7 +32,7 @@ There are two ways in which a smart contract can receive payments:
29
32
30
33
### Receiving payments directly
31
34
32
-
Sending EGLD and ESDT tokens directly to accounts works the same way for EOAs (extrernally owned accounts) as for smart contracts: the tokens are transferred from one account to the other without firing up the VM.
35
+
Sending EGLD and ESDT tokens directly to accounts works the same way for EOAs (externally owned accounts) as for smart contracts: the tokens are transferred from one account to the other without firing up the VM.
33
36
34
37
However, not all smart contracts are allowed to receive tokens directly. There is a flag that controls this, called "payable". This flag is part of the [code metadata](/developers/data/code-metadata), and is specified in the transaction that deploys or upgrades the smart contract.
35
38
@@ -39,7 +42,7 @@ The rationale for this is as follows: the MultiversX blockchain doesn't offer an
39
42
40
43
### Receiving payments via endpoints
41
44
42
-
The most common way for contracts to accept payments is by having endpoints annotated with the `#[payable(...)]` annotation.
45
+
The most common way for contracts to accept payments is by having endpoints annotated with the `#[payable]` annotation (or `#[payable("*")]`).
43
46
44
47
:::important important
45
48
The "payable" flag in the code metadata only refers to direct transfers. Transferring tokens via contract endpoint calls is not affected by it in any way.
@@ -55,13 +58,13 @@ fn accept_egld(&self) {
55
58
}
56
59
```
57
60
58
-
When annotated like this, the contract will reject any ESDT payment. Calling this function without any payment will work.
61
+
When annotated like this, the contract will only accept a single EGLD payment.
59
62
60
-
To accept any kind of payment, do annotate the endpoints with `#[payable("*")]`:
63
+
To accept any kind of payment, annotate the endpoints with `#[payable]`:
It is also possible to hard-code a token identifier in the `payable`, e.g. `#[payable("MYTOKEN-123456")]`. It is rarely, if ever, used, tokens should normally be configured in storage, or at runtime.
72
75
:::
73
76
77
+
[comment]: #(mx-context-auto)
78
+
79
+
## Payment Types
80
+
81
+
The framework provides a unified approach to handling payments using the `Payment` type that treats EGLD and ESDT tokens uniformly. EGLD is represented as `EGLD-000000` token identifier, making all payment handling consistent.
82
+
83
+
**`Payment<A>`** - The primary payment type that combines:
84
+
-`token_identifier`: `TokenId<A>` - unified token identifier (EGLD serialized as "EGLD-000000")
85
+
-`token_nonce`: `u64` - token nonce for NFTs/SFTs, which is zero for all fungible tokens (incl. EGLD)
**`PaymentVec<A>`** - A managed vector of `Payment<A>` objects, representing multiple payments in a single transaction.
89
+
90
+
[comment]: #(mx-context-auto)
91
+
92
+
## Call Value Methods
93
+
74
94
Additional restrictions on the incoming tokens can be imposed in the body of the endpoint, by calling the call value API. Most of these functions retrieve data about the received payment, while also stopping execution if the payment is not of the expected type.
95
+
96
+
[comment]: #(mx-context-auto)
97
+
98
+
### `all()` - Complete Payment Collection
99
+
100
+
`self.call_value().all()` retrieves all payments sent with the transaction as a `PaymentVec<A>`. It handles all tokens uniformly, including EGLD (represented as "EGLD-000000"). Never stops execution.
101
+
102
+
```rust
103
+
#[payable("*")]
104
+
#[endpoint]
105
+
pubfnprocess_all_payments(&self) {
106
+
letpayments=self.call_value().all();
107
+
forpaymentinpayments.iter() {
108
+
lettoken_id=&payment.token_identifier;
109
+
letamount=payment.amount;
110
+
letnonce=&payment.token_nonce;
111
+
// Handle each payment uniformly
112
+
self.process_payment(token_id, nonce, amount);
113
+
}
114
+
}
115
+
```
116
+
117
+
[comment]: #(mx-context-auto)
118
+
119
+
### `single()` - Strict Single Payment
120
+
121
+
`self.call_value().single()` expects exactly one payment and returns it. Will halt execution if zero or multiple payments are received. Returns a `Payment<A>` object.
`self.call_value().single_optional()` accepts either zero or one payment. Returns `Option<Payment<A>>` for graceful handling. Will halt execution if multiple payments are received.
141
+
142
+
```rust
143
+
#[payable("*")]
144
+
#[endpoint]
145
+
pubfnexecute_with_optional_fee(&self) {
146
+
matchself.call_value().single_optional() {
147
+
Some(payment) => {
148
+
// Process the payment as fee
149
+
self.execute_premium_service(payment);
150
+
},
151
+
None=> {
152
+
// Handle no payment scenario
153
+
self.execute_basic_service();
154
+
}
155
+
}
156
+
}
157
+
```
158
+
159
+
[comment]: #(mx-context-auto)
160
+
161
+
### `array()` - Fixed-Size Payment Array
162
+
163
+
`self.call_value().array<N>()` expects exactly N payments and returns them as a fixed-size array. Will halt execution if the number of payments doesn't match exactly.
164
+
165
+
```rust
166
+
#[payable("*")]
167
+
#[endpoint]
168
+
pubfnswap(&self) {
169
+
// Expect exactly 2 payments for the swap
170
+
let [input_payment, fee_payment] =self.call_value().array();
The following methods are available for backwards compatibility but may be deprecated in future versions:
186
+
75
187
-`self.call_value().egld_value()` retrieves the EGLD value transferred, or zero. Never stops execution.
76
188
-`self.call_value().all_esdt_transfers()` retrieves all the ESDT transfers received, or an empty list. Never stops execution.
77
189
-`self.call_value().multi_esdt<N>()` is ideal when we know exactly how many ESDT transfers we expect. It returns an array of `EsdtTokenPayment`. It knows exactly how many transfers to expect based on the return type (it is polymorphic in the length of the array). Will fail execution if the number of ESDT transfers does not match.
78
190
-`self.call_value().single_esdt()` expects a single ESDT transfer, fails otherwise. Will return the received `EsdtTokenPayment`. It is a special case of `multi_esdt`, where `N` is 1.
79
191
-`self.call_value().single_fungible_esdt()` further restricts `single_esdt` to only fungible tokens, so those with their nonce zero. Returns the token identifier and amount, as pair.
80
192
-`self.call_value().egld_or_single_esdt()` retrieves an object of type `EgldOrEsdtTokenPayment`. Will halt execution in case of ESDT multi-transfer.
81
193
-`self.call_value().egld_or_single_fungible_esdt()` further restricts `egld_or_single_esdt` to fungible ESDT tokens. It will return a pair of `EgldOrEsdtTokenIdentifier` and an amount.
82
-
-`self.call_value().any_payment()` is the most general payment retriever. Never stops execution. Returns an object of type `EgldOrMultiEsdtPayment`.
194
+
-`self.call_value().any_payment()` is the most general payment retriever. Never stops execution. Returns an object of type `EgldOrMultiEsdtPayment`.*(Deprecated since 0.64.0 - use `all()` instead)*
83
195
84
196
---
85
197
86
198
[comment]: #(mx-context-auto)
87
199
88
200
## Sending payments
89
201
90
-
We have seen how contracts can accommodate receiving tokens. Sending them is, in principle, even more straightforward, as it only involves specializing the `Payment` generic of the transaction using specific methods, or better said, attaching a payload to a regular transaction. Read more about payments [here](../transactions/tx-payment.md).
202
+
We have seen how contracts can accommodate receiving tokens. Sending them is, in principle, even more straightforward, as it only involves specializing the `Payment` generic of the transaction using specific methods, essentially attaching a payload to a regular transaction. Read more about payments [here](../transactions/tx-payment.md).
0 commit comments