@bloxchain/contracts 1.0.0-alpha → 1.0.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -18
- package/abi/{ControlBlox.abi.json → AccountBlox.abi.json} +699 -2974
- package/abi/BareBlox.abi.json +127 -90
- package/abi/BaseStateMachine.abi.json +127 -90
- package/abi/EngineBlox.abi.json +11 -31
- package/abi/GuardController.abi.json +217 -895
- package/abi/GuardControllerDefinitions.abi.json +380 -0
- package/abi/IDefinition.abi.json +19 -0
- package/abi/RoleBlox.abi.json +818 -2404
- package/abi/RuntimeRBAC.abi.json +122 -328
- package/abi/RuntimeRBACDefinitions.abi.json +243 -0
- package/abi/SecureBlox.abi.json +620 -1952
- package/abi/SecureOwnable.abi.json +469 -1801
- package/abi/SecureOwnableDefinitions.abi.json +57 -0
- package/abi/SimpleRWA20.abi.json +486 -1999
- package/abi/SimpleRWA20Definitions.abi.json +19 -0
- package/abi/SimpleVault.abi.json +884 -2685
- package/abi/SimpleVaultDefinitions.abi.json +19 -0
- package/components/README.md +8 -0
- package/core/access/RuntimeRBAC.sol +184 -0
- package/core/access/interface/IRuntimeRBAC.sol +55 -0
- package/{contracts/core → core}/access/lib/definitions/RuntimeRBACDefinitions.sol +121 -1
- package/{contracts/core → core}/base/BaseStateMachine.sol +187 -54
- package/{contracts/core → core}/base/interface/IBaseStateMachine.sol +7 -0
- package/{contracts/core → core}/execution/GuardController.sol +89 -155
- package/{contracts/core → core}/execution/interface/IGuardController.sol +52 -12
- package/{contracts/core → core}/execution/lib/definitions/GuardControllerDefinitions.sol +91 -2
- package/{contracts/core → core}/lib/EngineBlox.sol +167 -64
- package/{contracts → core/lib}/interfaces/IDefinition.sol +15 -6
- package/{contracts → core/lib}/interfaces/IEventForwarder.sol +1 -1
- package/{contracts → core/lib}/utils/SharedValidation.sol +490 -486
- package/core/pattern/Account.sol +75 -0
- package/core/research/BloxchainWallet.sol +133 -0
- package/core/research/FactoryBlox/FactoryBlox.sol +344 -0
- package/core/research/FactoryBlox/FactoryBloxDefinitions.sol +144 -0
- package/core/research/erc1155-blox/ERC1155Blox.sol +170 -0
- package/core/research/erc1155-blox/lib/definitions/ERC1155BloxDefinitions.sol +203 -0
- package/core/research/erc20-blox/ERC20Blox.sol +135 -0
- package/core/research/erc20-blox/lib/definitions/ERC20BloxDefinitions.sol +185 -0
- package/core/research/erc721-blox/ERC721Blox.sol +131 -0
- package/core/research/erc721-blox/lib/definitions/ERC721BloxDefinitions.sol +172 -0
- package/core/research/lending-blox/.gitkeep +1 -0
- package/core/research/p2p-blox/P2PBlox.sol +266 -0
- package/core/research/p2p-blox/README.md +85 -0
- package/core/research/p2p-blox/lib/definitions/P2PBloxDefinitions.sol +19 -0
- package/{contracts/core → core}/security/SecureOwnable.sol +390 -419
- package/{contracts/core → core}/security/interface/ISecureOwnable.sol +27 -40
- package/{contracts/core → core}/security/lib/definitions/SecureOwnableDefinitions.sol +786 -757
- package/package.json +49 -47
- package/standards/README.md +12 -0
- package/standards/behavior/ICopyable.sol +36 -0
- package/standards/hooks/IOnActionHook.sol +21 -0
- package/contracts/core/access/RuntimeRBAC.sol +0 -344
- package/contracts/core/access/interface/IRuntimeRBAC.sol +0 -108
- package/contracts/interfaces/IOnActionHook.sol +0 -79
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
2
|
+
pragma solidity 0.8.33;
|
|
3
|
+
|
|
4
|
+
// OpenZeppelin
|
|
5
|
+
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
|
|
6
|
+
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
7
|
+
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
8
|
+
|
|
9
|
+
// Core
|
|
10
|
+
import "../../security/SecureOwnable.sol";
|
|
11
|
+
import "../../access/RuntimeRBAC.sol";
|
|
12
|
+
import "../../execution/GuardController.sol";
|
|
13
|
+
import "../../base/BaseStateMachine.sol";
|
|
14
|
+
import "../../lib/utils/SharedValidation.sol";
|
|
15
|
+
import "./lib/definitions/P2PBloxDefinitions.sol";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @title P2PBlox
|
|
19
|
+
* @dev Peer-to-peer token swap blox: two parties agree on a direct transfer where
|
|
20
|
+
* the first entity sends token A and the second entity sends token B.
|
|
21
|
+
* Each side submits a request (offer / counter-offer); a dedicated P2P_APPROVER_ROLE
|
|
22
|
+
* then approves or rejects the swap before execution.
|
|
23
|
+
*
|
|
24
|
+
* Flow:
|
|
25
|
+
* 1. Party A calls requestOffer(tokenA, amountA, tokenB, amountB) — "I send token A, I want token B".
|
|
26
|
+
* 2. Party B calls requestCounterOffer(offerId, tokenB, amountB, tokenA, amountA) — must match the offer.
|
|
27
|
+
* 3. A wallet with P2P_APPROVER_ROLE calls approveSwap(offerId) to execute both transfers, or
|
|
28
|
+
* rejectSwap(offerId) to reject the swap.
|
|
29
|
+
*
|
|
30
|
+
* Both parties must have approved this contract for their respective tokens before approveSwap.
|
|
31
|
+
* Uses SafeERC20 and CEI pattern; protected by ReentrancyGuard where state changes and external calls occur.
|
|
32
|
+
*
|
|
33
|
+
* @custom:security-contact security@particlecrypto.com
|
|
34
|
+
*/
|
|
35
|
+
contract P2PBlox is GuardController, RuntimeRBAC, SecureOwnable {
|
|
36
|
+
using SafeERC20 for IERC20;
|
|
37
|
+
|
|
38
|
+
/// @dev Swap lifecycle status
|
|
39
|
+
enum SwapStatus {
|
|
40
|
+
PENDING, // Offer created, no counter yet
|
|
41
|
+
MATCHED, // Counter-offer submitted, awaiting approver
|
|
42
|
+
EXECUTING, // Transfers in progress (internal; prevents reentrancy)
|
|
43
|
+
EXECUTED, // Swap approved and transfers completed
|
|
44
|
+
REJECTED, // Swap rejected by approver
|
|
45
|
+
CANCELLED // Offer cancelled (reserved for future use)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// @dev Single offer/counter pair: party A gives (tokenA, amountA), party B gives (tokenB, amountB)
|
|
49
|
+
struct P2PSwap {
|
|
50
|
+
address partyA;
|
|
51
|
+
address tokenA;
|
|
52
|
+
uint256 amountA;
|
|
53
|
+
address tokenB;
|
|
54
|
+
uint256 amountB;
|
|
55
|
+
address partyB; // Set when counter-offer is submitted
|
|
56
|
+
SwapStatus status;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/// @dev Next offer ID (1-based)
|
|
60
|
+
uint256 private _nextOfferId;
|
|
61
|
+
|
|
62
|
+
/// @dev offerId => P2PSwap
|
|
63
|
+
mapping(uint256 => P2PSwap) private _swaps;
|
|
64
|
+
|
|
65
|
+
/// @dev Emitted when party A creates an offer
|
|
66
|
+
event OfferCreated(
|
|
67
|
+
uint256 indexed offerId,
|
|
68
|
+
address indexed partyA,
|
|
69
|
+
address tokenA,
|
|
70
|
+
uint256 amountA,
|
|
71
|
+
address tokenB,
|
|
72
|
+
uint256 amountB
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
/// @dev Emitted when party B submits a counter-offer matching the offer
|
|
76
|
+
event CounterOfferCreated(
|
|
77
|
+
uint256 indexed offerId,
|
|
78
|
+
address indexed partyB
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
/// @dev Emitted when the approver executes the swap
|
|
82
|
+
event SwapApproved(uint256 indexed offerId);
|
|
83
|
+
|
|
84
|
+
/// @dev Emitted when the approver rejects the swap
|
|
85
|
+
event SwapRejected(uint256 indexed offerId);
|
|
86
|
+
|
|
87
|
+
error InvalidOffer(uint256 offerId);
|
|
88
|
+
error OfferNotPending(uint256 offerId);
|
|
89
|
+
error OfferNotMatched(uint256 offerId);
|
|
90
|
+
error CounterOfferMismatch();
|
|
91
|
+
error InvalidParties();
|
|
92
|
+
error OnlyApprover();
|
|
93
|
+
error ZeroAmount();
|
|
94
|
+
|
|
95
|
+
/// @custom:oz-upgrades-unsafe-allow constructor
|
|
96
|
+
constructor() {
|
|
97
|
+
_disableInitializers();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @notice Initializes P2PBlox and creates the P2P_APPROVER_ROLE.
|
|
102
|
+
* Assign approver wallets via RuntimeRBAC roleConfigBatch (ADD_WALLET for P2P_APPROVER_ROLE) after deployment.
|
|
103
|
+
* @param initialOwner Initial owner
|
|
104
|
+
* @param broadcaster Broadcaster address
|
|
105
|
+
* @param recovery Recovery address
|
|
106
|
+
* @param timeLockPeriodSec Time-lock period in seconds
|
|
107
|
+
* @param eventForwarder Event forwarder address
|
|
108
|
+
*/
|
|
109
|
+
function initialize(
|
|
110
|
+
address initialOwner,
|
|
111
|
+
address broadcaster,
|
|
112
|
+
address recovery,
|
|
113
|
+
uint256 timeLockPeriodSec,
|
|
114
|
+
address eventForwarder
|
|
115
|
+
) public virtual override(GuardController, RuntimeRBAC, SecureOwnable) initializer {
|
|
116
|
+
GuardController.initialize(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
|
|
117
|
+
RuntimeRBAC.initialize(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
|
|
118
|
+
SecureOwnable.initialize(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
|
|
119
|
+
|
|
120
|
+
// Create dedicated approver role (non-protected; assign wallets via RuntimeRBAC roleConfigBatch)
|
|
121
|
+
_createRole("P2P_APPROVER_ROLE", P2PBloxDefinitions.P2P_APPROVER_MAX_WALLETS, false);
|
|
122
|
+
|
|
123
|
+
_nextOfferId = 1;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @notice Party A creates an offer: "I send token A (amount A), I want token B (amount B)".
|
|
128
|
+
* @param tokenA Token address party A will send
|
|
129
|
+
* @param amountA Amount of token A
|
|
130
|
+
* @param tokenB Token address party A wants to receive
|
|
131
|
+
* @param amountB Amount of token B
|
|
132
|
+
* @return offerId The created offer ID
|
|
133
|
+
*/
|
|
134
|
+
function requestOffer(
|
|
135
|
+
address tokenA,
|
|
136
|
+
uint256 amountA,
|
|
137
|
+
address tokenB,
|
|
138
|
+
uint256 amountB
|
|
139
|
+
) external nonReentrant returns (uint256 offerId) {
|
|
140
|
+
SharedValidation.validateNotZeroAddress(tokenA);
|
|
141
|
+
SharedValidation.validateNotZeroAddress(tokenB);
|
|
142
|
+
if (amountA == 0 || amountB == 0) revert ZeroAmount();
|
|
143
|
+
|
|
144
|
+
offerId = _nextOfferId++;
|
|
145
|
+
unchecked {
|
|
146
|
+
_nextOfferId = _nextOfferId;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
_swaps[offerId] = P2PSwap({
|
|
150
|
+
partyA: msg.sender,
|
|
151
|
+
tokenA: tokenA,
|
|
152
|
+
amountA: amountA,
|
|
153
|
+
tokenB: tokenB,
|
|
154
|
+
amountB: amountB,
|
|
155
|
+
partyB: address(0),
|
|
156
|
+
status: SwapStatus.PENDING
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
emit OfferCreated(offerId, msg.sender, tokenA, amountA, tokenB, amountB);
|
|
160
|
+
return offerId;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @notice Party B submits a counter-offer that must match the existing offer (token/amounts).
|
|
165
|
+
* @param offerId The offer to match
|
|
166
|
+
* @param tokenB Token address party B will send (must equal offer.tokenB)
|
|
167
|
+
* @param amountB Amount party B will send (must equal offer.amountB)
|
|
168
|
+
* @param tokenA Token address party B wants to receive (must equal offer.tokenA)
|
|
169
|
+
* @param amountA Amount party B wants (must equal offer.amountA)
|
|
170
|
+
*/
|
|
171
|
+
function requestCounterOffer(
|
|
172
|
+
uint256 offerId,
|
|
173
|
+
address tokenB,
|
|
174
|
+
uint256 amountB,
|
|
175
|
+
address tokenA,
|
|
176
|
+
uint256 amountA
|
|
177
|
+
) external nonReentrant {
|
|
178
|
+
P2PSwap storage swap = _swaps[offerId];
|
|
179
|
+
if (swap.partyA == address(0)) revert InvalidOffer(offerId);
|
|
180
|
+
if (swap.status != SwapStatus.PENDING) revert OfferNotPending(offerId);
|
|
181
|
+
if (msg.sender == swap.partyA) revert InvalidParties();
|
|
182
|
+
|
|
183
|
+
if (
|
|
184
|
+
tokenA != swap.tokenA || amountA != swap.amountA ||
|
|
185
|
+
tokenB != swap.tokenB || amountB != swap.amountB
|
|
186
|
+
) revert CounterOfferMismatch();
|
|
187
|
+
|
|
188
|
+
swap.partyB = msg.sender;
|
|
189
|
+
swap.status = SwapStatus.MATCHED;
|
|
190
|
+
|
|
191
|
+
emit CounterOfferCreated(offerId, msg.sender);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* @notice Executes the swap: transfers token A from party A to party B and token B from party B to party A.
|
|
196
|
+
* Callable only by P2P_APPROVER_ROLE. Both parties must have approved this contract for their tokens.
|
|
197
|
+
* @param offerId The matched offer to execute
|
|
198
|
+
*/
|
|
199
|
+
function approveSwap(uint256 offerId) external nonReentrant {
|
|
200
|
+
if (!hasRole(P2PBloxDefinitions.P2P_APPROVER_ROLE, msg.sender)) revert OnlyApprover();
|
|
201
|
+
|
|
202
|
+
P2PSwap storage swap = _swaps[offerId];
|
|
203
|
+
if (swap.partyA == address(0)) revert InvalidOffer(offerId);
|
|
204
|
+
if (swap.status != SwapStatus.MATCHED) revert OfferNotMatched(offerId);
|
|
205
|
+
|
|
206
|
+
// CEI: capture and update state before external calls
|
|
207
|
+
address partyA = swap.partyA;
|
|
208
|
+
address partyB = swap.partyB;
|
|
209
|
+
address tokenA = swap.tokenA;
|
|
210
|
+
address tokenB = swap.tokenB;
|
|
211
|
+
uint256 amountA = swap.amountA;
|
|
212
|
+
uint256 amountB = swap.amountB;
|
|
213
|
+
|
|
214
|
+
swap.status = SwapStatus.EXECUTING;
|
|
215
|
+
|
|
216
|
+
IERC20(tokenA).safeTransferFrom(partyA, partyB, amountA);
|
|
217
|
+
IERC20(tokenB).safeTransferFrom(partyB, partyA, amountB);
|
|
218
|
+
|
|
219
|
+
swap.status = SwapStatus.EXECUTED;
|
|
220
|
+
emit SwapApproved(offerId);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* @notice Rejects a matched swap. Callable only by P2P_APPROVER_ROLE.
|
|
225
|
+
* @param offerId The matched offer to reject
|
|
226
|
+
*/
|
|
227
|
+
function rejectSwap(uint256 offerId) external {
|
|
228
|
+
if (!hasRole(P2PBloxDefinitions.P2P_APPROVER_ROLE, msg.sender)) revert OnlyApprover();
|
|
229
|
+
|
|
230
|
+
P2PSwap storage swap = _swaps[offerId];
|
|
231
|
+
if (swap.partyA == address(0)) revert InvalidOffer(offerId);
|
|
232
|
+
if (swap.status != SwapStatus.MATCHED) revert OfferNotMatched(offerId);
|
|
233
|
+
|
|
234
|
+
swap.status = SwapStatus.REJECTED;
|
|
235
|
+
emit SwapRejected(offerId);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* @notice Returns the full swap record for an offer ID.
|
|
240
|
+
* @param offerId The offer ID
|
|
241
|
+
*/
|
|
242
|
+
function getSwap(uint256 offerId) external view returns (P2PSwap memory) {
|
|
243
|
+
return _swaps[offerId];
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* @notice Returns the next offer ID (for frontends).
|
|
248
|
+
*/
|
|
249
|
+
function getNextOfferId() external view returns (uint256) {
|
|
250
|
+
return _nextOfferId;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function supportsInterface(bytes4 interfaceId)
|
|
254
|
+
public
|
|
255
|
+
view
|
|
256
|
+
virtual
|
|
257
|
+
override(GuardController, RuntimeRBAC, SecureOwnable)
|
|
258
|
+
returns (bool)
|
|
259
|
+
{
|
|
260
|
+
return GuardController.supportsInterface(interfaceId)
|
|
261
|
+
|| RuntimeRBAC.supportsInterface(interfaceId)
|
|
262
|
+
|| SecureOwnable.supportsInterface(interfaceId);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
uint256[50] private __gap;
|
|
266
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# P2P Blox — Peer-to-Peer Token Swap
|
|
2
|
+
|
|
3
|
+
P2PBlox is a research blox that lets two parties agree on a **direct token swap**: the first entity sends **token A** and the second entity sends **token B**. Each side submits a request (offer and counter-offer); a **dedicated approver role** then approves or rejects the swap before execution.
|
|
4
|
+
|
|
5
|
+
## Design
|
|
6
|
+
|
|
7
|
+
- **Dual request**: Party A creates an offer; Party B submits a counter-offer that must match (same tokens and amounts).
|
|
8
|
+
- **Approver role**: Only wallets with `P2P_APPROVER_ROLE` can call `approveSwap` or `rejectSwap`.
|
|
9
|
+
- **Execution**: On approval, the contract performs `transferFrom` for both legs (A→B for token A, B→A for token B). Both parties must have approved the P2PBlox contract for their respective tokens before approval.
|
|
10
|
+
- **Stack**: P2PBlox extends GuardController, RuntimeRBAC, and SecureOwnable (same pattern as FactoryBlox). Swap lifecycle (offer / counter / approve / reject) is implemented in the blox; approver membership is managed via RuntimeRBAC.
|
|
11
|
+
|
|
12
|
+
## Flow
|
|
13
|
+
|
|
14
|
+
1. **Party A** — `requestOffer(tokenA, amountA, tokenB, amountB)`
|
|
15
|
+
“I send `tokenA` amount `amountA`, I want `tokenB` amount `amountB`.”
|
|
16
|
+
|
|
17
|
+
2. **Party B** — `requestCounterOffer(offerId, tokenB, amountB, tokenA, amountA)`
|
|
18
|
+
Must match the offer: same `tokenA`/`amountA` and `tokenB`/`amountB`. Party B cannot be the same as Party A.
|
|
19
|
+
|
|
20
|
+
3. **Approver** — either:
|
|
21
|
+
- `approveSwap(offerId)`
|
|
22
|
+
Executes: `tokenA.transferFrom(partyA, partyB, amountA)` and `tokenB.transferFrom(partyB, partyA, amountB)`.
|
|
23
|
+
- `rejectSwap(offerId)`
|
|
24
|
+
Marks the swap as rejected (no transfers).
|
|
25
|
+
|
|
26
|
+
## State and Status
|
|
27
|
+
|
|
28
|
+
- **PENDING** — Offer created, no counter-offer yet.
|
|
29
|
+
- **MATCHED** — Counter-offer submitted; waiting for approver.
|
|
30
|
+
- **EXECUTING** — Internal state during transfers (reentrancy guard).
|
|
31
|
+
- **EXECUTED** — Swap completed.
|
|
32
|
+
- **REJECTED** — Approver rejected the swap.
|
|
33
|
+
- **CANCELLED** — Reserved for future use.
|
|
34
|
+
|
|
35
|
+
## Role: P2P_APPROVER_ROLE
|
|
36
|
+
|
|
37
|
+
- Created at initialization (non-protected).
|
|
38
|
+
- Max wallets defined in `P2PBloxDefinitions.P2P_APPROVER_MAX_WALLETS` (e.g. 5).
|
|
39
|
+
- No wallets are assigned in `initialize`; assign approvers after deployment via RuntimeRBAC `roleConfigBatch` (e.g. `ADD_WALLET` for `P2P_APPROVER_ROLE`).
|
|
40
|
+
|
|
41
|
+
## Security
|
|
42
|
+
|
|
43
|
+
- **CEI**: State (including status) is updated before external `transferFrom` calls.
|
|
44
|
+
- **Reentrancy**: `requestOffer`, `requestCounterOffer`, and `approveSwap` use `nonReentrant`.
|
|
45
|
+
- **Inputs**: Zero address and zero amount checks; counter-offer must match offer; party B ≠ party A.
|
|
46
|
+
- **Transfers**: SafeERC20 for both tokens.
|
|
47
|
+
|
|
48
|
+
## API Summary
|
|
49
|
+
|
|
50
|
+
| Function | Caller | Description |
|
|
51
|
+
|----------|--------|-------------|
|
|
52
|
+
| `requestOffer(tokenA, amountA, tokenB, amountB)` | Any | Create offer; returns `offerId`. |
|
|
53
|
+
| `requestCounterOffer(offerId, tokenB, amountB, tokenA, amountA)` | Any (≠ party A) | Match offer; must match tokens/amounts. |
|
|
54
|
+
| `approveSwap(offerId)` | P2P_APPROVER_ROLE | Execute both transfers. |
|
|
55
|
+
| `rejectSwap(offerId)` | P2P_APPROVER_ROLE | Reject swap (no transfers). |
|
|
56
|
+
| `getSwap(offerId)` | Any | Return full swap record. |
|
|
57
|
+
| `getNextOfferId()` | Any | Next offer ID (for UIs). |
|
|
58
|
+
|
|
59
|
+
## Events
|
|
60
|
+
|
|
61
|
+
- `OfferCreated(offerId, partyA, tokenA, amountA, tokenB, amountB)`
|
|
62
|
+
- `CounterOfferCreated(offerId, partyB)`
|
|
63
|
+
- `SwapApproved(offerId)`
|
|
64
|
+
- `SwapRejected(offerId)`
|
|
65
|
+
|
|
66
|
+
## Files
|
|
67
|
+
|
|
68
|
+
- `P2PBlox.sol` — Main contract (swap logic, role checks, SafeERC20, CEI).
|
|
69
|
+
- `lib/definitions/P2PBloxDefinitions.sol` — Constants: `P2P_APPROVER_ROLE`, `P2P_APPROVER_MAX_WALLETS`, `P2P_SWAP_OPERATION`.
|
|
70
|
+
|
|
71
|
+
## Deployment and Setup
|
|
72
|
+
|
|
73
|
+
1. Deploy P2PBlox (e.g. via proxy/clone pattern used elsewhere in the protocol).
|
|
74
|
+
2. Call `initialize(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder)`.
|
|
75
|
+
3. Assign at least one wallet to `P2P_APPROVER_ROLE` using RuntimeRBAC `roleConfigBatch` (CREATE_ROLE already done in `initialize`; use ADD_WALLET for the approver role hash from `P2PBloxDefinitions.P2P_APPROVER_ROLE`).
|
|
76
|
+
|
|
77
|
+
## Custom Errors
|
|
78
|
+
|
|
79
|
+
- `InvalidOffer(offerId)` — Offer does not exist.
|
|
80
|
+
- `OfferNotPending(offerId)` — Offer not in PENDING (e.g. already matched).
|
|
81
|
+
- `OfferNotMatched(offerId)` — Offer not in MATCHED (cannot approve/reject).
|
|
82
|
+
- `CounterOfferMismatch()` — Counter-offer tokens/amounts do not match offer.
|
|
83
|
+
- `InvalidParties()` — Counter-offer from same address as party A.
|
|
84
|
+
- `OnlyApprover()` — Caller does not have `P2P_APPROVER_ROLE`.
|
|
85
|
+
- `ZeroAmount()` — Either amount in an offer is zero.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
2
|
+
pragma solidity 0.8.33;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @title P2PBloxDefinitions
|
|
6
|
+
* @dev Constants and definitions for the P2P (peer-to-peer) swap blox.
|
|
7
|
+
* Defines the dedicated approver role and operation types used by P2PBlox.
|
|
8
|
+
* @custom:security-contact security@particlecrypto.com
|
|
9
|
+
*/
|
|
10
|
+
library P2PBloxDefinitions {
|
|
11
|
+
/// @dev Role hash for the dedicated swap approver (can approve or reject matched swaps)
|
|
12
|
+
bytes32 public constant P2P_APPROVER_ROLE = keccak256(bytes("P2P_APPROVER_ROLE"));
|
|
13
|
+
|
|
14
|
+
/// @dev Maximum number of wallets that can hold the P2P_APPROVER_ROLE
|
|
15
|
+
uint256 public constant P2P_APPROVER_MAX_WALLETS = 5;
|
|
16
|
+
|
|
17
|
+
/// @dev Operation type for P2P swap lifecycle (offer, counter, approve, reject)
|
|
18
|
+
bytes32 public constant P2P_SWAP_OPERATION = keccak256("P2P_SWAP_OPERATION");
|
|
19
|
+
}
|