@bananapus/suckers-v6 0.0.49 → 0.0.50
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/CHANGELOG.md +35 -0
- package/package.json +2 -2
- package/src/JBSucker.sol +60 -19
- package/src/interfaces/IJBSucker.sol +8 -1
- package/src/structs/JBLeaf.sol +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,41 @@ This file describes the verified change from `nana-suckers-v5` to the current `n
|
|
|
15
15
|
- `JBCeloSucker`
|
|
16
16
|
- the deployers, structs, and interfaces under `src/`
|
|
17
17
|
|
|
18
|
+
## Unreleased — `JBLeaf.metadata` attribution field
|
|
19
|
+
|
|
20
|
+
The merkle leaf now carries a fifth field: a `bytes32 metadata` payload that travels inside the leaf hash but is
|
|
21
|
+
opaque to the sucker protocol itself.
|
|
22
|
+
|
|
23
|
+
**What changed**
|
|
24
|
+
|
|
25
|
+
- `JBLeaf` struct: new trailing `bytes32 metadata` field.
|
|
26
|
+
- `IJBSucker.prepare`: new trailing `bytes32 metadata` parameter. The metadata is included in the leaf hash, so it's
|
|
27
|
+
covered by the merkle root — receivers can trust it once the claim's merkle proof verifies.
|
|
28
|
+
- `_buildTreeHash` hashes 128 bytes (was 96): `keccak256(projectTokenCount || terminalTokenAmount || beneficiary || metadata)`.
|
|
29
|
+
- `_insertIntoTree`, `_validate`, `_validateBranchRoot`, `_validateForEmergencyExit` all thread `metadata` through.
|
|
30
|
+
- Events `InsertToOutboxTree` and `Claimed` carry the field so off-chain indexers can read it directly without
|
|
31
|
+
cracking the leaf.
|
|
32
|
+
- SVM leaf encoding widens from 96 bytes to 128 bytes; the new 32-byte suffix is the `metadata` field. The
|
|
33
|
+
`_svmBuildTreeHash` interop test mirrors the layout exactly so EVM↔SVM hash equality is preserved.
|
|
34
|
+
|
|
35
|
+
**Intended use**
|
|
36
|
+
|
|
37
|
+
The original motivator is the cross-chain referral split hook (`nana-referral-split-hook-v6`): when a referrer
|
|
38
|
+
on chain Y earns credit for fee-paying activity on chain X, the hook on X uses the fee project's sucker to
|
|
39
|
+
bridge the entitled fee-project tokens. The leaf's `metadata` carries `(originChainId, referralProjectId)` so the
|
|
40
|
+
sibling hook on chain Y can atomically claim, re-pay the fee project locally, and push to the local distributor
|
|
41
|
+
for the right referrer — all under the merkle proof's authentication, no off-chain coordination needed.
|
|
42
|
+
|
|
43
|
+
The field is generic: any future leaf consumer (NFT split hooks, buyback hooks, etc.) can use it for its own
|
|
44
|
+
attribution scheme without further sucker changes. Pass `bytes32(0)` for ordinary bridges that don't need it.
|
|
45
|
+
|
|
46
|
+
**Risk surface**
|
|
47
|
+
|
|
48
|
+
Zero new trust paths. The bridge protocol stays leaf-in-leaf-out; we just put one more 32-byte field under the
|
|
49
|
+
same root. Existing claim, emergency-exit, and root-relay flows behave identically when `metadata == bytes32(0)`.
|
|
50
|
+
The leaf-hash domain changes (96 → 128 bytes), but since nothing is deployed yet there's no on-chain
|
|
51
|
+
compatibility concern.
|
|
52
|
+
|
|
18
53
|
## 0.0.46 — Bump nana-core-v6 to 0.0.53
|
|
19
54
|
|
|
20
55
|
`@bananapus/core-v6@0.0.53` ([nana-core-v6 PR #145](https://github.com/Bananapus/nana-core-v6/pull/145)) drops the `via_ir` requirement on `JBCashOutHookSpecsLib`, which lets this package consume the cross-project cashout work (`payAfterCashOutTokensOf` / `addToBalanceAfterCashOutTokensOf`) without needing `via_ir = true` in its own foundry profile.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bananapus/suckers-v6",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.50",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@arbitrum/nitro-contracts": "3.2.0",
|
|
33
|
-
"@bananapus/core-v6": "^0.0.
|
|
33
|
+
"@bananapus/core-v6": "^0.0.59",
|
|
34
34
|
"@bananapus/permission-ids-v6": "^0.0.26",
|
|
35
35
|
"@chainlink/contracts-ccip": "1.6.4",
|
|
36
36
|
"@chainlink/local": "0.2.7",
|
package/src/JBSucker.sol
CHANGED
|
@@ -294,12 +294,15 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
294
294
|
/// project tokens for the beneficiary, and deposits the terminal tokens into the project's local balance.
|
|
295
295
|
/// @param claimData The terminal token, merkle tree leaf, and proof for the claim.
|
|
296
296
|
function claim(JBClaim calldata claimData) public virtual override {
|
|
297
|
-
// Attempt to validate the proof against the inbox tree for the terminal token.
|
|
297
|
+
// Attempt to validate the proof against the inbox tree for the terminal token. The leaf hash includes
|
|
298
|
+
// `claimData.leaf.metadata` so the proof is only valid for the exact (amount, beneficiary, metadata) tuple the
|
|
299
|
+
// origin committed to.
|
|
298
300
|
_validate({
|
|
299
301
|
projectTokenCount: claimData.leaf.projectTokenCount,
|
|
300
302
|
terminalToken: claimData.token,
|
|
301
303
|
terminalTokenAmount: claimData.leaf.terminalTokenAmount,
|
|
302
304
|
beneficiary: claimData.leaf.beneficiary,
|
|
305
|
+
metadata: claimData.leaf.metadata,
|
|
303
306
|
index: claimData.leaf.index,
|
|
304
307
|
leaves: claimData.proof
|
|
305
308
|
});
|
|
@@ -310,6 +313,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
310
313
|
projectTokenCount: claimData.leaf.projectTokenCount,
|
|
311
314
|
terminalTokenAmount: claimData.leaf.terminalTokenAmount,
|
|
312
315
|
index: claimData.leaf.index,
|
|
316
|
+
metadata: claimData.leaf.metadata,
|
|
313
317
|
caller: _msgSender()
|
|
314
318
|
});
|
|
315
319
|
|
|
@@ -330,7 +334,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
330
334
|
uint256 _projectId = projectId();
|
|
331
335
|
|
|
332
336
|
_requirePermissionFrom({
|
|
333
|
-
account:
|
|
337
|
+
account: _ownerOf(_projectId), projectId: _projectId, permissionId: JBPermissionIds.SUCKER_SAFETY
|
|
334
338
|
});
|
|
335
339
|
|
|
336
340
|
// Enable the emergency hatch for each token.
|
|
@@ -352,12 +356,14 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
352
356
|
/// @param claimData The terminal token, merkle tree leaf, and proof for the claim.
|
|
353
357
|
function exitThroughEmergencyHatch(JBClaim calldata claimData) external override {
|
|
354
358
|
// Does all the needed validation to ensure that the claim is valid *and* that claiming through the emergency
|
|
355
|
-
// hatch is allowed.
|
|
359
|
+
// hatch is allowed. The leaf hash covers `metadata` so a remote-attribution leaf is only exitable if the
|
|
360
|
+
// emergency exiter knows the exact `metadata` value the origin committed to.
|
|
356
361
|
_validateForEmergencyExit({
|
|
357
362
|
projectTokenCount: claimData.leaf.projectTokenCount,
|
|
358
363
|
terminalToken: claimData.token,
|
|
359
364
|
terminalTokenAmount: claimData.leaf.terminalTokenAmount,
|
|
360
365
|
beneficiary: claimData.leaf.beneficiary,
|
|
366
|
+
metadata: claimData.leaf.metadata,
|
|
361
367
|
index: claimData.leaf.index,
|
|
362
368
|
leaves: claimData.proof
|
|
363
369
|
});
|
|
@@ -532,7 +538,8 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
532
538
|
uint256 projectTokenCount,
|
|
533
539
|
bytes32 beneficiary,
|
|
534
540
|
uint256 minTokensReclaimed,
|
|
535
|
-
address token
|
|
541
|
+
address token,
|
|
542
|
+
bytes32 metadata
|
|
536
543
|
)
|
|
537
544
|
external
|
|
538
545
|
override
|
|
@@ -564,12 +571,15 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
564
571
|
projectToken: projectToken, count: projectTokenCount, token: token, minTokensReclaimed: minTokensReclaimed
|
|
565
572
|
});
|
|
566
573
|
|
|
567
|
-
// Insert the item into the outbox tree for the terminal `token`.
|
|
574
|
+
// Insert the item into the outbox tree for the terminal `token`. The `metadata` field travels inside the leaf
|
|
575
|
+
// hash so receivers can read attribution context from a proven claim — the sucker protocol itself never
|
|
576
|
+
// inspects it.
|
|
568
577
|
_insertIntoTree({
|
|
569
578
|
projectTokenCount: projectTokenCount,
|
|
570
579
|
token: token,
|
|
571
580
|
terminalTokenAmount: terminalTokenAmount,
|
|
572
|
-
beneficiary: beneficiary
|
|
581
|
+
beneficiary: beneficiary,
|
|
582
|
+
metadata: metadata
|
|
573
583
|
});
|
|
574
584
|
}
|
|
575
585
|
|
|
@@ -586,9 +596,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
586
596
|
|
|
587
597
|
// The caller must be the project owner or have the `SET_SUCKER_DEPRECATION` permission from them.
|
|
588
598
|
_requirePermissionFrom({
|
|
589
|
-
account:
|
|
590
|
-
projectId: _projectId,
|
|
591
|
-
permissionId: JBPermissionIds.SET_SUCKER_DEPRECATION
|
|
599
|
+
account: _ownerOf(_projectId), projectId: _projectId, permissionId: JBPermissionIds.SET_SUCKER_DEPRECATION
|
|
592
600
|
});
|
|
593
601
|
|
|
594
602
|
// This is the earliest time for when the sucker can be considered deprecated.
|
|
@@ -996,16 +1004,20 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
996
1004
|
uint256 projectTokenCount,
|
|
997
1005
|
address token,
|
|
998
1006
|
uint256 terminalTokenAmount,
|
|
999
|
-
bytes32 beneficiary
|
|
1007
|
+
bytes32 beneficiary,
|
|
1008
|
+
bytes32 metadata
|
|
1000
1009
|
)
|
|
1001
1010
|
internal
|
|
1002
1011
|
{
|
|
1003
1012
|
// Guard against amounts that would overflow uint128 on SVM (INTEROP-5).
|
|
1004
1013
|
if (terminalTokenAmount > type(uint128).max) revert JBSucker_AmountExceedsUint128(terminalTokenAmount);
|
|
1005
1014
|
if (projectTokenCount > type(uint128).max) revert JBSucker_AmountExceedsUint128(projectTokenCount);
|
|
1006
|
-
// Build a hash based on the token amounts and the
|
|
1015
|
+
// Build a hash based on the token amounts, the beneficiary, and the attribution metadata.
|
|
1007
1016
|
bytes32 hashed = _buildTreeHash({
|
|
1008
|
-
projectTokenCount: projectTokenCount,
|
|
1017
|
+
projectTokenCount: projectTokenCount,
|
|
1018
|
+
terminalTokenAmount: terminalTokenAmount,
|
|
1019
|
+
beneficiary: beneficiary,
|
|
1020
|
+
metadata: metadata
|
|
1009
1021
|
});
|
|
1010
1022
|
|
|
1011
1023
|
// Get the outbox in storage.
|
|
@@ -1023,6 +1035,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1023
1035
|
root: _computeOutboxRoot(outbox.tree),
|
|
1024
1036
|
projectTokenCount: projectTokenCount,
|
|
1025
1037
|
terminalTokenAmount: terminalTokenAmount,
|
|
1038
|
+
metadata: metadata,
|
|
1026
1039
|
caller: _msgSender()
|
|
1027
1040
|
});
|
|
1028
1041
|
}
|
|
@@ -1064,7 +1077,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1064
1077
|
|
|
1065
1078
|
// The registry can map during authorized deployment. Otherwise, require the project's mapping permission.
|
|
1066
1079
|
_requirePermissionAllowingOverrideFrom({
|
|
1067
|
-
account:
|
|
1080
|
+
account: _ownerOf(_projectId),
|
|
1068
1081
|
projectId: _projectId,
|
|
1069
1082
|
permissionId: JBPermissionIds.MAP_SUCKER_TOKEN,
|
|
1070
1083
|
alsoGrantAccessIf: _msgSender() == address(REGISTRY)
|
|
@@ -1170,7 +1183,9 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1170
1183
|
// Record the balance before the cash out for the sanity check.
|
|
1171
1184
|
uint256 balanceBefore = _balanceOf({token: token, addr: address(this)});
|
|
1172
1185
|
|
|
1173
|
-
// Cash out the project tokens for terminal tokens.
|
|
1186
|
+
// Cash out the project tokens for terminal tokens. Suckers are a transparent value-mover (the bridge
|
|
1187
|
+
// accounting is the entirety of their function) — they're not a fee-paying entry point for any referrer,
|
|
1188
|
+
// so `referralProjectId: 0` is correct.
|
|
1174
1189
|
reclaimedAmount = terminal.cashOutTokensOf({
|
|
1175
1190
|
holder: address(this),
|
|
1176
1191
|
projectId: cachedProjectId,
|
|
@@ -1178,7 +1193,8 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1178
1193
|
tokenToReclaim: token,
|
|
1179
1194
|
minTokensReclaimed: minTokensReclaimed,
|
|
1180
1195
|
beneficiary: payable(address(this)),
|
|
1181
|
-
metadata: bytes("")
|
|
1196
|
+
metadata: bytes(""),
|
|
1197
|
+
referralProjectId: 0
|
|
1182
1198
|
});
|
|
1183
1199
|
|
|
1184
1200
|
// Sanity check to make sure we received the expected amount.
|
|
@@ -1289,6 +1305,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1289
1305
|
address terminalToken,
|
|
1290
1306
|
uint256 terminalTokenAmount,
|
|
1291
1307
|
bytes32 beneficiary,
|
|
1308
|
+
bytes32 metadata,
|
|
1292
1309
|
uint256 index,
|
|
1293
1310
|
bytes32[_TREE_DEPTH] calldata leaves
|
|
1294
1311
|
)
|
|
@@ -1312,6 +1329,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1312
1329
|
projectTokenCount: projectTokenCount,
|
|
1313
1330
|
terminalTokenAmount: terminalTokenAmount,
|
|
1314
1331
|
beneficiary: beneficiary,
|
|
1332
|
+
metadata: metadata,
|
|
1315
1333
|
index: index,
|
|
1316
1334
|
leaves: leaves
|
|
1317
1335
|
});
|
|
@@ -1331,6 +1349,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1331
1349
|
uint256 projectTokenCount,
|
|
1332
1350
|
uint256 terminalTokenAmount,
|
|
1333
1351
|
bytes32 beneficiary,
|
|
1352
|
+
bytes32 metadata,
|
|
1334
1353
|
uint256 index,
|
|
1335
1354
|
bytes32[_TREE_DEPTH] calldata leaves
|
|
1336
1355
|
)
|
|
@@ -1341,7 +1360,10 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1341
1360
|
// Delegates to JBSuckerLib (via DELEGATECALL) to keep MerkleLib.branchRoot bytecode out of each sucker.
|
|
1342
1361
|
bytes32 root = JBSuckerLib.computeBranchRoot({
|
|
1343
1362
|
item: _buildTreeHash({
|
|
1344
|
-
projectTokenCount: projectTokenCount,
|
|
1363
|
+
projectTokenCount: projectTokenCount,
|
|
1364
|
+
terminalTokenAmount: terminalTokenAmount,
|
|
1365
|
+
beneficiary: beneficiary,
|
|
1366
|
+
metadata: metadata
|
|
1345
1367
|
}),
|
|
1346
1368
|
branch: leaves,
|
|
1347
1369
|
index: index
|
|
@@ -1385,6 +1407,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1385
1407
|
address terminalToken,
|
|
1386
1408
|
uint256 terminalTokenAmount,
|
|
1387
1409
|
bytes32 beneficiary,
|
|
1410
|
+
bytes32 metadata,
|
|
1388
1411
|
uint256 index,
|
|
1389
1412
|
bytes32[_TREE_DEPTH] calldata leaves
|
|
1390
1413
|
)
|
|
@@ -1435,6 +1458,7 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1435
1458
|
projectTokenCount: projectTokenCount,
|
|
1436
1459
|
terminalTokenAmount: terminalTokenAmount,
|
|
1437
1460
|
beneficiary: beneficiary,
|
|
1461
|
+
metadata: metadata,
|
|
1438
1462
|
index: index,
|
|
1439
1463
|
leaves: leaves
|
|
1440
1464
|
});
|
|
@@ -1482,24 +1506,27 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1482
1506
|
/// @param projectTokenCount The number of project tokens to cash out.
|
|
1483
1507
|
/// @param terminalTokenAmount The amount of terminal tokens to reclaim from the cash out.
|
|
1484
1508
|
/// @param beneficiary The beneficiary which will receive the project tokens (bytes32 for cross-VM compatibility).
|
|
1509
|
+
/// @param metadata Opaque caller-defined attribution payload travelling inside the leaf hash.
|
|
1485
1510
|
/// @return hash The keccak256 hash of the leaf data.
|
|
1486
1511
|
function _buildTreeHash(
|
|
1487
1512
|
uint256 projectTokenCount,
|
|
1488
1513
|
uint256 terminalTokenAmount,
|
|
1489
|
-
bytes32 beneficiary
|
|
1514
|
+
bytes32 beneficiary,
|
|
1515
|
+
bytes32 metadata
|
|
1490
1516
|
)
|
|
1491
1517
|
internal
|
|
1492
1518
|
pure
|
|
1493
1519
|
returns (bytes32 hash)
|
|
1494
1520
|
{
|
|
1495
|
-
// All
|
|
1521
|
+
// All four arguments are 32 bytes — hash from free memory to avoid abi.encode allocation overhead.
|
|
1496
1522
|
// forge-lint: disable-next-line(asm-keccak256)
|
|
1497
1523
|
assembly {
|
|
1498
1524
|
let ptr := mload(0x40)
|
|
1499
1525
|
mstore(ptr, projectTokenCount)
|
|
1500
1526
|
mstore(add(ptr, 0x20), terminalTokenAmount)
|
|
1501
1527
|
mstore(add(ptr, 0x40), beneficiary)
|
|
1502
|
-
|
|
1528
|
+
mstore(add(ptr, 0x60), metadata)
|
|
1529
|
+
hash := keccak256(ptr, 0x80)
|
|
1503
1530
|
}
|
|
1504
1531
|
}
|
|
1505
1532
|
|
|
@@ -1543,6 +1570,20 @@ abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC
|
|
|
1543
1570
|
return ERC2771Context._msgSender();
|
|
1544
1571
|
}
|
|
1545
1572
|
|
|
1573
|
+
/// @notice Resolve the current owner of the project this sucker belongs to.
|
|
1574
|
+
/// @dev `PROJECTS.ownerOf(...)` is the source of truth for "project owner" permission checks; we hit it from
|
|
1575
|
+
/// every permission-gated entrypoint (`enableEmergencyHatchFor`, `setDeprecation`, `_mapToken`). Routing all
|
|
1576
|
+
/// three through this internal helper emits the abi-encode + STATICCALL + return-decode sequence once in the
|
|
1577
|
+
/// child contract's bytecode instead of inlining it at each call site, which is what keeps `JBSwapCCIPSucker`
|
|
1578
|
+
/// under the EIP-170 limit after the leaf-`metadata` thread-through landed.
|
|
1579
|
+
/// @param forProjectId The project ID to look up — always the sucker's own `projectId()`, but accepted as a
|
|
1580
|
+
/// parameter so callers can pass the cached local they already computed (avoiding a redundant `projectId()`
|
|
1581
|
+
/// call against the read-only registry).
|
|
1582
|
+
/// @return owner The address currently registered as the project's ERC-721 holder.
|
|
1583
|
+
function _ownerOf(uint256 forProjectId) internal view returns (address owner) {
|
|
1584
|
+
return PROJECTS.ownerOf(forProjectId);
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1546
1587
|
/// @notice Retain a failed `toRemoteFee` payment for later caller refund.
|
|
1547
1588
|
/// @param account The account that can reclaim the retained fee.
|
|
1548
1589
|
/// @param amount The retained fee amount.
|
|
@@ -30,6 +30,7 @@ interface IJBSucker is IERC165 {
|
|
|
30
30
|
uint256 projectTokenCount,
|
|
31
31
|
uint256 terminalTokenAmount,
|
|
32
32
|
uint256 index,
|
|
33
|
+
bytes32 metadata,
|
|
33
34
|
address caller
|
|
34
35
|
);
|
|
35
36
|
|
|
@@ -50,6 +51,7 @@ interface IJBSucker is IERC165 {
|
|
|
50
51
|
bytes32 root,
|
|
51
52
|
uint256 projectTokenCount,
|
|
52
53
|
uint256 terminalTokenAmount,
|
|
54
|
+
bytes32 metadata,
|
|
53
55
|
address caller
|
|
54
56
|
);
|
|
55
57
|
|
|
@@ -191,11 +193,16 @@ interface IJBSucker is IERC165 {
|
|
|
191
193
|
/// @param beneficiary The beneficiary on the remote chain (bytes32 for cross-VM compatibility).
|
|
192
194
|
/// @param minTokensReclaimed The minimum terminal tokens to receive from the cash out.
|
|
193
195
|
/// @param token The terminal token to cash out into.
|
|
196
|
+
/// @param metadata Opaque caller-defined attribution payload included in the leaf hash. The sucker protocol does
|
|
197
|
+
/// not inspect this value — it's covered by the merkle root, so the destination contract that consumes the claim
|
|
198
|
+
/// can
|
|
199
|
+
/// trust it once the proof verifies. Pass `bytes32(0)` for an ordinary bridge with no attribution context.
|
|
194
200
|
function prepare(
|
|
195
201
|
uint256 projectTokenCount,
|
|
196
202
|
bytes32 beneficiary,
|
|
197
203
|
uint256 minTokensReclaimed,
|
|
198
|
-
address token
|
|
204
|
+
address token,
|
|
205
|
+
bytes32 metadata
|
|
199
206
|
)
|
|
200
207
|
external;
|
|
201
208
|
|
package/src/structs/JBLeaf.sol
CHANGED
|
@@ -6,9 +6,15 @@ pragma solidity ^0.8.0;
|
|
|
6
6
|
/// @custom:member beneficiary The beneficiary of the leaf.
|
|
7
7
|
/// @custom:member projectTokenCount The number of project tokens to claim.
|
|
8
8
|
/// @custom:member terminalTokenAmount The amount of terminal tokens to claim.
|
|
9
|
+
/// @custom:member metadata Opaque, caller-defined attribution payload that travels with the leaf inside the merkle
|
|
10
|
+
/// root. Use cases include cross-chain referral split hooks tagging a leaf with `(originChainId, referralProjectId)`
|
|
11
|
+
/// so the destination contract can settle the bridged value atomically. The sucker protocol itself never inspects
|
|
12
|
+
/// this field — it's covered by the leaf hash, so receivers can trust the value once the merkle proof verifies.
|
|
13
|
+
/// Pass `bytes32(0)` when no attribution context is needed.
|
|
9
14
|
struct JBLeaf {
|
|
10
15
|
uint256 index;
|
|
11
16
|
bytes32 beneficiary;
|
|
12
17
|
uint256 projectTokenCount;
|
|
13
18
|
uint256 terminalTokenAmount;
|
|
19
|
+
bytes32 metadata;
|
|
14
20
|
}
|