@agenticprimitives/contracts 1.0.0-alpha.14 → 1.0.0-alpha.15
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/deployments-base-sepolia.json +33 -33
- package/dist/abi/AgentAccount.json +19 -0
- package/dist/abi/CustodyPolicy.json +11 -0
- package/dist/abi/DelegationManager.json +11 -0
- package/dist/deployments/base-sepolia.js +33 -33
- package/package.json +1 -1
- package/src/AgentAccount.sol +36 -11
- package/src/agency/DelegationManager.sol +9 -0
- package/src/custody/CustodyPolicy.sol +28 -0
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
{
|
|
2
|
-
"agentAccountFactory": "
|
|
3
|
-
"agentAccountImplementation": "
|
|
4
|
-
"agentNameRegistry": "
|
|
5
|
-
"agentNameResolver": "
|
|
6
|
-
"agentNameUniversalResolver": "
|
|
7
|
-
"agentProfileResolver": "
|
|
8
|
-
"agentRegistryBase": "
|
|
9
|
-
"agentRelationship": "
|
|
10
|
-
"agreementRegistry": "
|
|
11
|
-
"allowedMethodsEnforcer": "
|
|
12
|
-
"allowedTargetsEnforcer": "
|
|
13
|
-
"approvedHashRegistry": "
|
|
14
|
-
"attestationRegistry": "
|
|
15
|
-
"callDataHashEnforcer": "
|
|
2
|
+
"agentAccountFactory": "0x1D3BF769B3991faA7847872cE05FA22beE2f07e5",
|
|
3
|
+
"agentAccountImplementation": "0xd96eA06208140763256899727B22A1DF7A09387d",
|
|
4
|
+
"agentNameRegistry": "0x4e5a6369C91B9093ad17bF0eAc8d344F1b41EdE0",
|
|
5
|
+
"agentNameResolver": "0x776a5bADb7DBE238048542E0c91f7a86933C8986",
|
|
6
|
+
"agentNameUniversalResolver": "0x8539fad5B148512775cD3712bb55F2fd5f1C85cB",
|
|
7
|
+
"agentProfileResolver": "0x82dBC51a77A92A51b177FFE97181cD40aa1c24ab",
|
|
8
|
+
"agentRegistryBase": "0x54f76a5faab995f194cc61c50eb5e272c8d85cf4",
|
|
9
|
+
"agentRelationship": "0x763c164d8275F76A806721B423aaB5ad6cb4fEA1",
|
|
10
|
+
"agreementRegistry": "0xaD300f03EdDf8F015D5583feC8D5F2D1BBC6f854",
|
|
11
|
+
"allowedMethodsEnforcer": "0xa149A31F32789cDBfd5f0eed1CF92E7BCb91b549",
|
|
12
|
+
"allowedTargetsEnforcer": "0xfb78589C8a9f464BD1e9A340BC809CEe7949b17c",
|
|
13
|
+
"approvedHashRegistry": "0x7152282bB71692E7f73ff31DdB43f4a50818845b",
|
|
14
|
+
"attestationRegistry": "0x9f9bCAF7b238b87c6d7897767c473b6f8C55C0a6",
|
|
15
|
+
"callDataHashEnforcer": "0x0fCE11eC016bd4Bc3a60951FbE4e9Fe7087955b1",
|
|
16
16
|
"chainId": 84532,
|
|
17
|
-
"custodyPolicy": "
|
|
18
|
-
"delegationManager": "
|
|
17
|
+
"custodyPolicy": "0xF408e54dA113Fe6b785615750c6Bab6F9008A9d9",
|
|
18
|
+
"delegationManager": "0xaCdB7001dB8305c23703f6DB0EfB8B9BC1d75D62",
|
|
19
19
|
"deployer": "0x31ed17fb99e82E02085Ab4B3cbdaB05489098b44",
|
|
20
|
-
"entryPoint": "
|
|
21
|
-
"geoFeatureRegistry": "
|
|
22
|
-
"mockUsdc": "
|
|
23
|
-
"ontologyTermRegistry": "
|
|
24
|
-
"paymentEnforcer": "
|
|
25
|
-
"paymentEscrow": "
|
|
26
|
-
"paymentReceiptRegistry": "
|
|
27
|
-
"permissionlessSubregistry": "
|
|
28
|
-
"permissionlessSubregistryDemoAgent": "
|
|
29
|
-
"quorumEnforcer": "
|
|
30
|
-
"relationshipTypeRegistry": "
|
|
31
|
-
"shapeRegistry": "
|
|
32
|
-
"skillDefinitionRegistry": "
|
|
33
|
-
"smartAgentPaymaster": "
|
|
34
|
-
"timestampEnforcer": "
|
|
35
|
-
"universalSignatureValidator": "
|
|
36
|
-
"valueEnforcer": "
|
|
20
|
+
"entryPoint": "0x9Eb5F84E8323aACD4AD61b4b980aDB23B15c2903",
|
|
21
|
+
"geoFeatureRegistry": "0x74AEb56420a8630c611c36b3053780d90191f94d",
|
|
22
|
+
"mockUsdc": "0x07190E7586a468E808D6bd58dC3CEdEC0b25B8E3",
|
|
23
|
+
"ontologyTermRegistry": "0x69670d24AcE8C47a294231a1fdC4207642669a25",
|
|
24
|
+
"paymentEnforcer": "0xF08946A2c4eed77f366AfDc39AE150F2262E5d29",
|
|
25
|
+
"paymentEscrow": "0x30f0fd53dc1245062962b498be3a7e9641cc7c6c",
|
|
26
|
+
"paymentReceiptRegistry": "0x36f0cd94A286b011b8C6106E69E14567fb7A498e",
|
|
27
|
+
"permissionlessSubregistry": "0xE37E1da21Bcce70b515d2f44d84D757c358b96B6",
|
|
28
|
+
"permissionlessSubregistryDemoAgent": "0xeE2Db355624BdB4a7551604229EbE72683f8fD85",
|
|
29
|
+
"quorumEnforcer": "0xd030196bD6Ba6a654e6E8f7e3609aAd470122422",
|
|
30
|
+
"relationshipTypeRegistry": "0x8D5f597f2dE53CC8Ed1a948abF972F8C85301a17",
|
|
31
|
+
"shapeRegistry": "0x41273A14e77d7dA256c05276B95D8F2054C54930",
|
|
32
|
+
"skillDefinitionRegistry": "0x9bcd76e8E7e5E57863dca0631F44707974cBa58B",
|
|
33
|
+
"smartAgentPaymaster": "0xb48Ccecb9118D579204868850f62feb4653c1469",
|
|
34
|
+
"timestampEnforcer": "0x5ce38463DcE9c910F09a14e6D0650258e72eEcCA",
|
|
35
|
+
"universalSignatureValidator": "0x4BC3Ea8920c87638e0612007eb9de0d70ee4Ba8f",
|
|
36
|
+
"valueEnforcer": "0x45170540Ba4d90C7c27103FBF4B136c5EdA545BB"
|
|
37
37
|
}
|
|
@@ -590,6 +590,25 @@
|
|
|
590
590
|
],
|
|
591
591
|
"stateMutability": "view"
|
|
592
592
|
},
|
|
593
|
+
{
|
|
594
|
+
"type": "function",
|
|
595
|
+
"name": "isExternalCustodianOrPasskey",
|
|
596
|
+
"inputs": [
|
|
597
|
+
{
|
|
598
|
+
"name": "account",
|
|
599
|
+
"type": "address",
|
|
600
|
+
"internalType": "address"
|
|
601
|
+
}
|
|
602
|
+
],
|
|
603
|
+
"outputs": [
|
|
604
|
+
{
|
|
605
|
+
"name": "",
|
|
606
|
+
"type": "bool",
|
|
607
|
+
"internalType": "bool"
|
|
608
|
+
}
|
|
609
|
+
],
|
|
610
|
+
"stateMutability": "view"
|
|
611
|
+
},
|
|
593
612
|
{
|
|
594
613
|
"type": "function",
|
|
595
614
|
"name": "isModuleInstalled",
|
|
@@ -854,6 +854,17 @@
|
|
|
854
854
|
"name": "EmptyOwnerSet",
|
|
855
855
|
"inputs": []
|
|
856
856
|
},
|
|
857
|
+
{
|
|
858
|
+
"type": "error",
|
|
859
|
+
"name": "InfeasibleOrgConfig",
|
|
860
|
+
"inputs": [
|
|
861
|
+
{
|
|
862
|
+
"name": "nSigners",
|
|
863
|
+
"type": "uint8",
|
|
864
|
+
"internalType": "uint8"
|
|
865
|
+
}
|
|
866
|
+
]
|
|
867
|
+
},
|
|
857
868
|
{
|
|
858
869
|
"type": "error",
|
|
859
870
|
"name": "InvalidCustodyAction",
|
|
@@ -622,6 +622,17 @@
|
|
|
622
622
|
"name": "LegacyRevocationDisabled",
|
|
623
623
|
"inputs": []
|
|
624
624
|
},
|
|
625
|
+
{
|
|
626
|
+
"type": "error",
|
|
627
|
+
"name": "NoCodeEnforcer",
|
|
628
|
+
"inputs": [
|
|
629
|
+
{
|
|
630
|
+
"name": "enforcer",
|
|
631
|
+
"type": "address",
|
|
632
|
+
"internalType": "address"
|
|
633
|
+
}
|
|
634
|
+
]
|
|
635
|
+
},
|
|
625
636
|
{
|
|
626
637
|
"type": "error",
|
|
627
638
|
"name": "NotDelegatorOrDelegate",
|
|
@@ -1,40 +1,40 @@
|
|
|
1
1
|
// Generated by scripts/build-deployments.mjs — do not edit by hand.
|
|
2
2
|
// Source: deployments-base-sepolia.json
|
|
3
3
|
export const CONTRACTS = {
|
|
4
|
-
"agentAccountFactory": "
|
|
5
|
-
"agentAccountImplementation": "
|
|
6
|
-
"agentNameRegistry": "
|
|
7
|
-
"agentNameResolver": "
|
|
8
|
-
"agentNameUniversalResolver": "
|
|
9
|
-
"agentProfileResolver": "
|
|
10
|
-
"agentRegistryBase": "
|
|
11
|
-
"agentRelationship": "
|
|
12
|
-
"agreementRegistry": "
|
|
13
|
-
"allowedMethodsEnforcer": "
|
|
14
|
-
"allowedTargetsEnforcer": "
|
|
15
|
-
"approvedHashRegistry": "
|
|
16
|
-
"attestationRegistry": "
|
|
17
|
-
"callDataHashEnforcer": "
|
|
4
|
+
"agentAccountFactory": "0x1D3BF769B3991faA7847872cE05FA22beE2f07e5",
|
|
5
|
+
"agentAccountImplementation": "0xd96eA06208140763256899727B22A1DF7A09387d",
|
|
6
|
+
"agentNameRegistry": "0x4e5a6369C91B9093ad17bF0eAc8d344F1b41EdE0",
|
|
7
|
+
"agentNameResolver": "0x776a5bADb7DBE238048542E0c91f7a86933C8986",
|
|
8
|
+
"agentNameUniversalResolver": "0x8539fad5B148512775cD3712bb55F2fd5f1C85cB",
|
|
9
|
+
"agentProfileResolver": "0x82dBC51a77A92A51b177FFE97181cD40aa1c24ab",
|
|
10
|
+
"agentRegistryBase": "0x54f76a5faab995f194cc61c50eb5e272c8d85cf4",
|
|
11
|
+
"agentRelationship": "0x763c164d8275F76A806721B423aaB5ad6cb4fEA1",
|
|
12
|
+
"agreementRegistry": "0xaD300f03EdDf8F015D5583feC8D5F2D1BBC6f854",
|
|
13
|
+
"allowedMethodsEnforcer": "0xa149A31F32789cDBfd5f0eed1CF92E7BCb91b549",
|
|
14
|
+
"allowedTargetsEnforcer": "0xfb78589C8a9f464BD1e9A340BC809CEe7949b17c",
|
|
15
|
+
"approvedHashRegistry": "0x7152282bB71692E7f73ff31DdB43f4a50818845b",
|
|
16
|
+
"attestationRegistry": "0x9f9bCAF7b238b87c6d7897767c473b6f8C55C0a6",
|
|
17
|
+
"callDataHashEnforcer": "0x0fCE11eC016bd4Bc3a60951FbE4e9Fe7087955b1",
|
|
18
18
|
"chainId": 84532,
|
|
19
|
-
"custodyPolicy": "
|
|
20
|
-
"delegationManager": "
|
|
19
|
+
"custodyPolicy": "0xF408e54dA113Fe6b785615750c6Bab6F9008A9d9",
|
|
20
|
+
"delegationManager": "0xaCdB7001dB8305c23703f6DB0EfB8B9BC1d75D62",
|
|
21
21
|
"deployer": "0x31ed17fb99e82E02085Ab4B3cbdaB05489098b44",
|
|
22
|
-
"entryPoint": "
|
|
23
|
-
"geoFeatureRegistry": "
|
|
24
|
-
"mockUsdc": "
|
|
25
|
-
"ontologyTermRegistry": "
|
|
26
|
-
"paymentEnforcer": "
|
|
27
|
-
"paymentEscrow": "
|
|
28
|
-
"paymentReceiptRegistry": "
|
|
29
|
-
"permissionlessSubregistry": "
|
|
30
|
-
"permissionlessSubregistryDemoAgent": "
|
|
31
|
-
"quorumEnforcer": "
|
|
32
|
-
"relationshipTypeRegistry": "
|
|
33
|
-
"shapeRegistry": "
|
|
34
|
-
"skillDefinitionRegistry": "
|
|
35
|
-
"smartAgentPaymaster": "
|
|
36
|
-
"timestampEnforcer": "
|
|
37
|
-
"universalSignatureValidator": "
|
|
38
|
-
"valueEnforcer": "
|
|
22
|
+
"entryPoint": "0x9Eb5F84E8323aACD4AD61b4b980aDB23B15c2903",
|
|
23
|
+
"geoFeatureRegistry": "0x74AEb56420a8630c611c36b3053780d90191f94d",
|
|
24
|
+
"mockUsdc": "0x07190E7586a468E808D6bd58dC3CEdEC0b25B8E3",
|
|
25
|
+
"ontologyTermRegistry": "0x69670d24AcE8C47a294231a1fdC4207642669a25",
|
|
26
|
+
"paymentEnforcer": "0xF08946A2c4eed77f366AfDc39AE150F2262E5d29",
|
|
27
|
+
"paymentEscrow": "0x30f0fd53dc1245062962b498be3a7e9641cc7c6c",
|
|
28
|
+
"paymentReceiptRegistry": "0x36f0cd94A286b011b8C6106E69E14567fb7A498e",
|
|
29
|
+
"permissionlessSubregistry": "0xE37E1da21Bcce70b515d2f44d84D757c358b96B6",
|
|
30
|
+
"permissionlessSubregistryDemoAgent": "0xeE2Db355624BdB4a7551604229EbE72683f8fD85",
|
|
31
|
+
"quorumEnforcer": "0xd030196bD6Ba6a654e6E8f7e3609aAd470122422",
|
|
32
|
+
"relationshipTypeRegistry": "0x8D5f597f2dE53CC8Ed1a948abF972F8C85301a17",
|
|
33
|
+
"shapeRegistry": "0x41273A14e77d7dA256c05276B95D8F2054C54930",
|
|
34
|
+
"skillDefinitionRegistry": "0x9bcd76e8E7e5E57863dca0631F44707974cBa58B",
|
|
35
|
+
"smartAgentPaymaster": "0xb48Ccecb9118D579204868850f62feb4653c1469",
|
|
36
|
+
"timestampEnforcer": "0x5ce38463DcE9c910F09a14e6D0650258e72eEcCA",
|
|
37
|
+
"universalSignatureValidator": "0x4BC3Ea8920c87638e0612007eb9de0d70ee4Ba8f",
|
|
38
|
+
"valueEnforcer": "0x45170540Ba4d90C7c27103FBF4B136c5EdA545BB"
|
|
39
39
|
};
|
|
40
40
|
export default CONTRACTS;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agenticprimitives/contracts",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.15",
|
|
4
4
|
"description": "Solidity contracts + ABIs + flattened sources + per-network deployment addresses for the agenticprimitives stack.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
package/src/AgentAccount.sol
CHANGED
|
@@ -1028,18 +1028,30 @@ contract AgentAccount is
|
|
|
1028
1028
|
revert NotInstalledExecutor(msg.sender);
|
|
1029
1029
|
}
|
|
1030
1030
|
emit ModuleExecuted(msg.sender, target, value);
|
|
1031
|
-
// CA-1 + Wave-1/ACCT-C1 (spec 300): a module action is a
|
|
1032
|
-
// context (
|
|
1033
|
-
//
|
|
1034
|
-
//
|
|
1035
|
-
//
|
|
1036
|
-
//
|
|
1037
|
-
//
|
|
1038
|
-
|
|
1039
|
-
|
|
1031
|
+
// CA-1 + Wave-1/ACCT-C1 (spec 300): a module action from the CANONICAL CustodyPolicy is a
|
|
1032
|
+
// custody-governed context (gated upstream by its own T5 quorum + timelock). `_custodyGovCtx`
|
|
1033
|
+
// authorizes every governance mutator this call dispatches (the ONLY way past
|
|
1034
|
+
// `_requireCustodyGoverned` on a `mode>0` account); `_upgradeAuthorizedCtx` additionally exempts a
|
|
1035
|
+
// dispatched upgrade from the per-account timelock (no double delay). Both transient.
|
|
1036
|
+
//
|
|
1037
|
+
// NEW-EXEC-MODULE-1 (2026-07-05 audit, internal + external): grant that context ONLY to the
|
|
1038
|
+
// canonical CustodyPolicy the factory installs — NOT to any installed executor. Otherwise a second
|
|
1039
|
+
// (utility / automation) executor a quorum installs believing it unprivileged would silently inherit
|
|
1040
|
+
// full authority to mutate custody / DM / upgrade / modules, bypassing quorum+timelock. Matching the
|
|
1041
|
+
// specific policy address is the same check `_hasCustodyGovernance` uses. A non-canonical executor
|
|
1042
|
+
// runs WITHOUT the context, so `_requireCustodyGoverned` rejects any governance mutator it dispatches
|
|
1043
|
+
// (defense-in-depth: not reachable at v1 since the factory installs only CustodyPolicy).
|
|
1044
|
+
address f = _factory;
|
|
1045
|
+
bool custodyGoverned = f != address(0) && msg.sender == IAgentAccountFactoryView(f).custodyPolicy();
|
|
1046
|
+
if (custodyGoverned) {
|
|
1047
|
+
_custodyGovCtx = true;
|
|
1048
|
+
_upgradeAuthorizedCtx = true;
|
|
1049
|
+
}
|
|
1040
1050
|
(bool ok, bytes memory ret) = target.call{value: value}(data);
|
|
1041
|
-
|
|
1042
|
-
|
|
1051
|
+
if (custodyGoverned) {
|
|
1052
|
+
_upgradeAuthorizedCtx = false;
|
|
1053
|
+
_custodyGovCtx = false;
|
|
1054
|
+
}
|
|
1043
1055
|
if (!ok) {
|
|
1044
1056
|
assembly { revert(add(ret, 32), mload(ret)) }
|
|
1045
1057
|
}
|
|
@@ -1465,6 +1477,19 @@ contract AgentAccount is
|
|
|
1465
1477
|
return _passkeyStorage().piaToCredentialId[account] != bytes32(0);
|
|
1466
1478
|
}
|
|
1467
1479
|
|
|
1480
|
+
/// @notice EXT-ISCUSTODIAN-1 (2026-07-05 audit): like `isCustodian` but EXCLUDES the account itself.
|
|
1481
|
+
/// @dev `isCustodian(address(this)) == true` is deliberate (account-actor compatibility), but that
|
|
1482
|
+
/// footgun can recreate the ACCT-C2 quorum-collapse class in a FUTURE consumer that reuses
|
|
1483
|
+
/// `isCustodian` as if it meant "external signer." Any quorum / authority consumer that must count
|
|
1484
|
+
/// only real external custodians / passkeys (never the account) should use THIS view. ACCT-C2 fixed
|
|
1485
|
+
/// the current CustodyPolicy quorum at the counting site (reject signer==account); this gives new
|
|
1486
|
+
/// consumers the safe primitive so the class can't reappear off-path.
|
|
1487
|
+
function isExternalCustodianOrPasskey(address account) external view returns (bool) {
|
|
1488
|
+
if (account == address(this)) return false;
|
|
1489
|
+
if (_externalCustodians[account]) return true;
|
|
1490
|
+
return _passkeyStorage().piaToCredentialId[account] != bytes32(0);
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1468
1493
|
/// @inheritdoc IAgentAccount
|
|
1469
1494
|
/// @dev Counts EOA/contract custodians PLUS registered passkeys —
|
|
1470
1495
|
/// each passkey contributes one PIA-custodian. Matches the
|
|
@@ -109,6 +109,10 @@ contract DelegationManager is IDelegationManager, ReentrancyGuard {
|
|
|
109
109
|
// Phase A.5 errors.
|
|
110
110
|
error NotDelegatorOrDelegate();
|
|
111
111
|
error HashMismatch();
|
|
112
|
+
/// @dev EXT-NOCODE-HOOK-1: a caveat enforcer with no code — a call to it would
|
|
113
|
+
/// succeed vacuously (fail-OPEN), so the live before/after hook loops reject it,
|
|
114
|
+
/// matching the view path (`verifyAuthorizationForCall` → "no-code-enforcer").
|
|
115
|
+
error NoCodeEnforcer(address enforcer);
|
|
112
116
|
/// @dev Thrown by the deprecated permissionless `revokeDelegation(bytes32)`
|
|
113
117
|
/// path. Migrate callers to `revokeDelegationByOwner(Delegation)`.
|
|
114
118
|
error LegacyRevocationDisabled();
|
|
@@ -454,6 +458,9 @@ contract DelegationManager is IDelegationManager, ReentrancyGuard {
|
|
|
454
458
|
) internal {
|
|
455
459
|
bytes32 dHash = hashDelegation(d);
|
|
456
460
|
for (uint256 j = 0; j < d.caveats.length; j++) {
|
|
461
|
+
// EXT-NOCODE-HOOK-1: fail closed on a no-code enforcer (a call would succeed vacuously),
|
|
462
|
+
// symmetric with the view path's "no-code-enforcer" reject — never rely on call semantics.
|
|
463
|
+
if (d.caveats[j].enforcer.code.length == 0) revert NoCodeEnforcer(d.caveats[j].enforcer);
|
|
457
464
|
ICaveatEnforcer(d.caveats[j].enforcer).beforeHook(
|
|
458
465
|
d.caveats[j].terms,
|
|
459
466
|
d.caveats[j].args,
|
|
@@ -475,6 +482,8 @@ contract DelegationManager is IDelegationManager, ReentrancyGuard {
|
|
|
475
482
|
) internal {
|
|
476
483
|
bytes32 dHash = hashDelegation(d);
|
|
477
484
|
for (uint256 j = 0; j < d.caveats.length; j++) {
|
|
485
|
+
// EXT-NOCODE-HOOK-1: fail closed on a no-code enforcer, symmetric with the before-hook + view path.
|
|
486
|
+
if (d.caveats[j].enforcer.code.length == 0) revert NoCodeEnforcer(d.caveats[j].enforcer);
|
|
478
487
|
ICaveatEnforcer(d.caveats[j].enforcer).afterHook(
|
|
479
488
|
d.caveats[j].terms,
|
|
480
489
|
d.caveats[j].args,
|
|
@@ -318,6 +318,10 @@ contract CustodyPolicy {
|
|
|
318
318
|
error TrusteeDoesNotExist(address guardian);
|
|
319
319
|
error InvalidMode(uint8 mode_);
|
|
320
320
|
error InvalidThresholdValue(uint8 thr);
|
|
321
|
+
/// @dev EXT-CHANGEMODE-1: a runtime ChangeMode into org mode (3) with an infeasible signer
|
|
322
|
+
/// count (< 2, so separation-of-duties can't split proposers from appliers). Mirrors the
|
|
323
|
+
/// factory's install-time InfeasibleOrgConfig.
|
|
324
|
+
error InfeasibleOrgConfig(uint8 nSigners);
|
|
321
325
|
/// @dev CP-1: a tier that must be configured (T4/T5 at install, T6 at read)
|
|
322
326
|
/// was left unset — refuse rather than fall to the 1-of-n read fallback.
|
|
323
327
|
error UnconfiguredTier(uint8 tier);
|
|
@@ -884,6 +888,23 @@ contract CustodyPolicy {
|
|
|
884
888
|
if (newMode > 3) revert InvalidMode(newMode);
|
|
885
889
|
uint8 oldMode = c.mode;
|
|
886
890
|
if (newMode == 0 && c.trusteeCount > 0) revert CannotDowngradeWithTrustees();
|
|
891
|
+
// EXT-CHANGEMODE-1 (2026-07-05 audit): a runtime ChangeMode must re-run the factory's install-time
|
|
892
|
+
// mode-3 feasibility so the account can't enter an un-executable / liveness-hostile org mode.
|
|
893
|
+
// Org SoD needs ≥2 signers (distinct proposers + appliers) and T4/T5 thresholds within the
|
|
894
|
+
// floor(N/2) cap (else 2×threshold > N → no disjoint quorum → the tier silently locks). Require the
|
|
895
|
+
// signer floor; cap the SoD-constrained thresholds (mirrors the factory, avoids a locked mode).
|
|
896
|
+
if (newMode == 3) {
|
|
897
|
+
uint256 n = IAgentAccount(account).custodianCount();
|
|
898
|
+
if (n < 2) revert InfeasibleOrgConfig(uint8(n));
|
|
899
|
+
uint8 sodCap = uint8(n / 2); // ≥ 1 since n ≥ 2
|
|
900
|
+
for (uint8 tier = 4; tier <= 5; tier++) {
|
|
901
|
+
uint8 v = c.approvalsRequiredByTier[tier];
|
|
902
|
+
if (v > sodCap) {
|
|
903
|
+
c.approvalsRequiredByTier[tier] = sodCap;
|
|
904
|
+
emit ThresholdChanged(account, tier, v, sodCap);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
}
|
|
887
908
|
c.mode = newMode;
|
|
888
909
|
emit ModeChanged(account, oldMode, newMode);
|
|
889
910
|
}
|
|
@@ -968,6 +989,13 @@ contract CustodyPolicy {
|
|
|
968
989
|
if (newCount == 0) revert InvalidThresholdValue(newCount);
|
|
969
990
|
uint256 n = IAgentAccount(account).custodianCount();
|
|
970
991
|
if (uint256(newCount) > n) revert InvalidThresholdValue(newCount);
|
|
992
|
+
// acct-mode3-sod-cap-runtime (2026-07-05 audit): the factory caps mode-3 (org) T4/T5 thresholds at
|
|
993
|
+
// floor(N/2) at install so a DISJOINT propose+apply quorum always exists (2×threshold ≤ N). A later
|
|
994
|
+
// ChangeApprovalsRequired must not raise them past that cap and silently lock the tier — reject an
|
|
995
|
+
// explicit over-cap request loud (the org can lower N-constraints or use a lower threshold).
|
|
996
|
+
if (c.mode == 3 && (tier == 4 || tier == 5) && uint256(newCount) > n / 2) {
|
|
997
|
+
revert InvalidThresholdValue(newCount);
|
|
998
|
+
}
|
|
971
999
|
uint8 oldValue = c.approvalsRequiredByTier[tier];
|
|
972
1000
|
if (oldValue == 0) oldValue = 1; // mirror _approvalsValue fallback
|
|
973
1001
|
c.approvalsRequiredByTier[tier] = newCount;
|