Skip to content

Commit a0646ef

Browse files
committed
merge main and fix conflict
2 parents 284b493 + c2d9365 commit a0646ef

File tree

14 files changed

+299
-51
lines changed

14 files changed

+299
-51
lines changed

content/contracts-cairo/3.x/guides/deploy-udc.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ under one important assumption: **the declared UDC class hash MUST be the same a
3333
Different compiler versions may produce different class hashes for the same contract, so you need to make
3434
sure you are using the same compiler version to build the UDC class (and the release profile).
3535

36-
The latest version of the UDC available in the `openzeppelin_presets` package was compiled with **Cairo v2.13.1** (release profile) and the resulting class hash is `0x01b2df6d8861670d4a8ca4670433b2418d78169c2947f46dc614e69f333745c8`.
36+
The latest UDC version available in the `openzeppelin_presets` package was compiled from library version `v2.0.0` using **Cairo v2.11.4** (release profile). The resulting class hash is `0x01b2df6d8861670d4a8ca4670433b2418d78169c2947f46dc614e69f333745c8`.
3737

3838
<Callout type='warn'>
3939
If you are using a different compiler version, you need to make sure the class hash is the same as the one above in order to keep the same address across all networks.
@@ -88,7 +88,7 @@ The bootstrapper contract is a simple contract that declares the UDC and allows
8888
You can find a reference implementation below:
8989

9090
<Callout>
91-
This reference implementation targets Cairo v2.13.1. If you are using a different version of Cairo, you may need to update the code to match your compiler version.
91+
This reference implementation targets Cairo v2.11.4. If you are using a different version of Cairo, you may need to update the code to match your compiler version.
9292
</Callout>
9393

9494
```rust

content/contracts-cairo/3.x/utils/constants.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
export const OPENZEPPELIN_INTERFACES_VERSION = "3.0.0";
2-
export const OPENZEPPELIN_UTILS_VERSION = "3.0.0";
1+
export const OPENZEPPELIN_INTERFACES_VERSION = "2.1.0";
2+
export const OPENZEPPELIN_UTILS_VERSION = "2.1.0";
33
export const UMBRELLA_VERSION = "3.0.0";
44
export const CLASS_HASH_SCARB_VERSION = "2.13.1";
55

@@ -15,7 +15,7 @@ export const CLASS_HASHES = {
1515
EthAccountUpgradeableClassHash:
1616
"0x000b5bcc16b8b0d86c24996e22206f6071bb8d7307837a02720f0ce2fa1b3d7c",
1717
UniversalDeployerClassHash:
18-
"0x01b2df6d8861670d4a8ca4670433b2418d78169c2947f46dc614e69f333745c8",
18+
"0x01724ff2f76fcddb8f4079d35783655e6d61935a281425721e98607cc480ef56",
1919
VestingWalletClassHash:
2020
"0x00540c7f907539e1a283318fb3da16f1bf9d9e60ad10c20d0557a0185043b08f",
2121
};
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
---
2+
title: Fee Abstraction
3+
---
4+
5+
[Source Code](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/fee-abstraction)
6+
7+
Fee abstraction enables users to pay for Stellar transactions with tokens (e.g., USDC) instead of native XLM. A relayer covers the XLM network fees and is compensated in the user's chosen token through an intermediary contract.
8+
9+
## Overview
10+
11+
The [fee-abstraction](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/fee-abstraction) package provides utilities for implementing fee forwarding contracts on Stellar. The system works by having a relayer submit transactions on behalf of users, then atomically collecting token payment from the user as compensation.
12+
13+
The flow involves an off-chain negotiation between the user and a relayer (quote request, fee agreement), but the actual execution happens through an intermediary contract called a **FeeForwarder**. This contract enforces that the user is charged at most `max_fee_amount`, the cap they signed. The relayer determines the actual `fee_amount` at submission time based on network conditions, but can never exceed the user's authorized maximum.
14+
15+
### Benefits
16+
17+
Fee abstraction provides different advantages depending on the account type. For Stellar classic accounts, users don't need to acquire XLM beyond the [minimum required account balance](https://developers.stellar.org/docs/learn/fundamentals/stellar-data-structures/accounts#base-reserves-and-subentries) just to transact. It’s a great UX improvement by allowing users to use dapps with stablecoins or other tokens they already hold.
18+
19+
Unlike classic accounts, smart accounts (contract-based accounts) don't need to maintain a minimum XLM balance, but they need a mechanism to compensate relayers who submit transactions on their behalf. The proposed fee abstraction model significantly improves smart accounts usability.
20+
21+
### How It Works
22+
23+
```mermaid
24+
sequenceDiagram
25+
actor User
26+
actor Relayer
27+
participant FeeForwarder
28+
participant Token
29+
participant Target Contract
30+
31+
User->>User: 1. Prepare call to Target.target_fn()
32+
User->>Relayer: 2. Request quote (fee token, expiration, target)
33+
Relayer-->>User: Quote: max_fee_amount
34+
35+
User->>User: 3. Sign authorization for FeeForwarder.forward()<br/>including subinvocations for:<br/> Token.approve() and Target.target_fn()
36+
User->>Relayer: 4. Hand over signed authorization entry
37+
38+
Relayer->>Relayer: 5. Verify params satisfy requirements<br/>(fee amount, token, fee recipient, ...)
39+
Relayer->>Relayer: 6. Sign as source_account
40+
Relayer->>FeeForwarder: 7. Execute forward():<br/>submit a tx and pay XLM fees
41+
42+
FeeForwarder->>FeeForwarder: 8. Validate authorizations
43+
FeeForwarder->>Token: 9. Approve max fee amount (optional)
44+
FeeForwarder->>Token: 10. Transfer fee amount to fee recipient
45+
FeeForwarder->>Target Contract: 11. Invoke target_fn(target_args)
46+
Target Contract-->>User: Result
47+
```
48+
49+
## Features
50+
51+
### Target Invocation (Forwarding) and Fee Collection
52+
53+
First and foremost, the user intention is to invoke a function on a target contract which might require an authorization from the user. In that case, the minimal authorization tree the user needs to sign is:
54+
55+
```
56+
FeeForwarder.forward()
57+
└─ Target.target_fn() ← nested sub-invocation (if needed)
58+
```
59+
60+
Equally important for the fee abstraction model is enabling the relayer to collect the compensation fee. This is done through the common 2-step fungible token flow of `approve` + `transfer_from`. Soroban authorization framework allows embedding the approval step in the same transaction, so the user might also need to sign `Token.approve()`.
61+
62+
To guarantee the atomicity of both, the target invocation and the fee collection, the [fee-abstraction](https://github.com/OpenZeppelin/stellar-contracts/tree/main/packages/fee-abstraction) package exposes the function `collect_fee_and_invoke()`, which, in most of the cases, will require signing an authorization tree that contains one entry with two sub-invocations:
63+
64+
```
65+
FeeForwarder.forward()
66+
└─ Token.approve()
67+
└─ Target.target_fn()
68+
```
69+
70+
The approval sub-invocation might be optional depending on the chosen approval strategy. The package supports two approval strategies:
71+
72+
1. **Lazy**
73+
74+
The user pre-approves a lump sum to the FeeForwarder contract in a separate transaction. Each forwarded call then draws from this existing allowance. This strategy is more resource efficient per call, but requires an initial approval transaction and trust in the FeeForwarder contract.
75+
76+
2. **Eager**
77+
78+
The approval is embedded directly in the authorization entry as a sub-invocation for every call, no preliminary transactions needed. This strategy is self-contained with no trust assumptions, but more expensive per call (~25%) due to the nested approval.
79+
80+
## Fee Token Allowlist
81+
82+
The package includes optional allowlist functionality to restrict which tokens can be used for fee payment:
83+
84+
```rust
85+
// Enable a token for fee payments
86+
set_allowed_fee_token(e, &usdc_token, true);
87+
88+
// Disable a token
89+
set_allowed_fee_token(e, &deprecated_token, false);
90+
91+
// Check if a token is allowed
92+
let is_allowed = is_allowed_fee_token(e, &token);
93+
```
94+
95+
The allowlist uses a swap-and-pop algorithm for efficient O(1) removal and automatically extends TTL for frequently-used tokens.
96+
97+
## Token Sweeping
98+
99+
For permissioned implementations where fees accumulate in the contract, the package provides a sweep function:
100+
101+
```rust
102+
// Transfer all accumulated tokens to a recipient
103+
let amount = sweep_token(e, &fee_token, &treasury);
104+
```
105+
106+
This is typically restricted to manager roles.
107+
108+
## Security Considerations
109+
110+
### Relayer Safety
111+
112+
Relayers **must** ensure the call to the target contract is safe before submitting a transaction. A malicious user could craft a call that harms the relayer or the FeeForwarder contract in a permissioned setup. In most cases, relayers should **simulate the transaction off-chain** before submission to verify:
113+
- The target contract call will succeed
114+
- The fee collection will complete
115+
- The overall transaction outcome is acceptable
116+
117+
This off-chain task is critical for relayer protection and should be part of any production relayer implementation.
118+
119+
## Implementation Examples
120+
121+
### Permissioned FeeForwarder
122+
123+
A role-based implementation where only authorized executors can relay transactions. Fees accumulate in the contract for later distribution.
124+
125+
**Characteristics:**
126+
- Uses `#[only_role]` macro for executor authorization
127+
- Collects fees to the contract (not directly to relayer)
128+
- Managers can sweep accumulated fees to designated recipients
129+
- Uses lazy approval strategy (requires pre-approval transaction)
130+
- Suitable for trusted relayer networks
131+
132+
```rust
133+
#[only_role(relayer, "executor")]
134+
pub fn forward(
135+
e: &Env,
136+
fee_token: Address,
137+
fee_amount: i128,
138+
max_fee_amount: i128,
139+
expiration_ledger: u32,
140+
target_contract: Address,
141+
target_fn: Symbol,
142+
target_args: Vec<Val>,
143+
user: Address,
144+
relayer: Address,
145+
) -> Val {
146+
collect_fee_then_invoke(
147+
e,
148+
&fee_token,
149+
fee_amount,
150+
max_fee_amount,
151+
expiration_ledger,
152+
&target_contract,
153+
&target_fn,
154+
&target_args,
155+
&user,
156+
&e.current_contract_address(), // fees go to contract
157+
FeeAbstractionApproval::Lazy,
158+
)
159+
}
160+
```
161+
162+
### Permissionless FeeForwarder
163+
164+
An open implementation where anyone can act as a relayer. Fees go directly to the relayer, creating a competitive market.
165+
166+
**Characteristics:**
167+
- No role restrictions
168+
- Fees transfer directly to the relayer
169+
- Uses eager approval strategy (approval embedded in each call)
170+
- Suitable for open relayer markets
171+
172+
```rust
173+
pub fn forward(
174+
e: &Env,
175+
fee_token: Address,
176+
fee_amount: i128,
177+
max_fee_amount: i128,
178+
expiration_ledger: u32,
179+
target_contract: Address,
180+
target_fn: Symbol,
181+
target_args: Vec<Val>,
182+
user: Address,
183+
relayer: Address,
184+
) -> Val {
185+
relayer.require_auth();
186+
187+
collect_fee_and_invoke(
188+
e,
189+
&fee_token,
190+
fee_amount,
191+
max_fee_amount,
192+
expiration_ledger,
193+
&target_contract,
194+
&target_fn,
195+
&target_args,
196+
&user,
197+
&relayer, // fees go directly to relayer
198+
FeeAbstractionApproval::Eager,
199+
)
200+
}
201+
```
202+
203+
## Integration with OpenZeppelin Relayer
204+
205+
The fee abstraction package is designed to work seamlessly with [OpenZeppelin Relayer](/relayer/1.3.x). The Relayer can:
206+
207+
1. **Submit sponsored transactions**: Pay XLM fees on behalf of users
208+
2. **Calculate optimal fees**: Determine the actual fee based on network conditions
209+
3. **Handle fee collection**: Coordinate with fee forwarder contracts to collect token payment
210+
211+
### Sponsored Transaction Flow
212+
213+
```mermaid
214+
sequenceDiagram
215+
participant User
216+
participant OZ Relayer
217+
participant Forwarder as FeeForwarder
218+
participant Target as Target Contract
219+
220+
User->>OZ Relayer: Request transaction sponsorship
221+
OZ Relayer->>OZ Relayer: Calculate fee quote
222+
OZ Relayer-->>User: Return fee estimate
223+
User->>User: Sign authorization
224+
User->>OZ Relayer: Submit signed authorization
225+
OZ Relayer->>Forwarder: Execute forward (pays XLM)
226+
Forwarder->>Target: Invoke user's action
227+
Forwarder->>OZ Relayer: Transfer token fee
228+
OZ Relayer-->>User: Transaction complete
229+
```
230+
231+
For detailed integration instructions, see the [Stellar Sponsored Transactions Guide](/relayer/1.3.x/guides/stellar-sponsored-transactions-guide).
232+
233+
## See Also
234+
235+
- [Smart Accounts](/stellar-contracts/accounts/smart-account)
236+
- [Access Control](/stellar-contracts/access/access-control)
237+
- [OpenZeppelin Relayer](/relayer/1.3.x)
238+
- [Stellar Sponsored Transactions Guide](/relayer/1.3.x/guides/stellar-sponsored-transactions-guide)

content/stellar-contracts/index.mdx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ for access control and contract management.
3232
* **[Upgradeable](/stellar-contracts/utils/upgradeable)**: Manage contract upgrades and data migrations seamlessly.
3333
* **[Cryptography](/stellar-contracts/utils/crypto/crypto)**: A set of cryptographic primitives and utilities for Soroban contracts.
3434

35+
## Fee Abstraction
36+
37+
* **[Fee Abstraction](/stellar-contracts/fee-abstraction)**: Enable users to pay transaction fees in tokens (e.g. USDC) while relayers cover XLM fees.
38+
3539
## Security and Audits
3640

3741
Our contracts are built with security as a top priority. You can find our audit reports [here](https://github.com/OpenZeppelin/stellar-contracts/tree/main/audits).
@@ -61,6 +65,7 @@ Similarly, utilities and other modules have their own error codes:
6165
* Role Transfer (internal common module for 2-step role transfer): `22XX`
6266
* Accounts: `3XXX`
6367
* Governance: `4XXX`
68+
* Fee Abstraction: `5XXX`
6469

6570
## Important Notes
6671
As a deliberate design choice, this library manages the TTL for temporary and persistent storage items.

public/android-chrome-192x192.png

-3.95 KB
Loading

public/android-chrome-512x512.png

-18.7 KB
Loading

public/apple-touch-icon.png

-3.3 KB
Loading

public/favicon-16x16.png

-152 Bytes
Loading

public/favicon-32x32.png

-305 Bytes
Loading

public/favicon.ico

13.9 KB
Binary file not shown.

0 commit comments

Comments
 (0)