Skip to content

Commit 7e3abf1

Browse files
authored
Academy: Proxy Patterns (ava-labs#2714)
1 parent a88bdd8 commit 7e3abf1

File tree

8 files changed

+210
-43
lines changed

8 files changed

+210
-43
lines changed
Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,71 @@
11
"use client";
2-
import React, { useEffect, type JSX } from "react";
2+
import React, { useEffect, useRef, type JSX } from "react";
33
import mermaid from "mermaid";
44

5-
mermaid.initialize({
6-
startOnLoad: true,
7-
});
8-
95
type MermaidProps = {
106
readonly chart: string;
117
};
128

139
const Mermaid = ({ chart }: MermaidProps): JSX.Element => {
14-
useEffect(() => mermaid.contentLoaded(), []);
10+
const containerRef = useRef<HTMLDivElement>(null);
11+
12+
useEffect(() => {
13+
let destroyed = false;
14+
15+
const renderDiagram = async (): Promise<void> => {
16+
if (!containerRef.current) return;
17+
18+
const isDarkMode =
19+
document.documentElement.classList.contains("dark") ||
20+
document.documentElement.getAttribute("data-theme") === "dark";
21+
22+
// Configure theme each time before rendering
23+
mermaid.initialize({ startOnLoad: false, theme: isDarkMode ? "dark" : "default" });
24+
25+
// Render to SVG string and inject; avoid SSR/client mismatches
26+
try {
27+
// Unique ID only used internally by mermaid render
28+
const renderId = `mmd-${Date.now()}-${Math.random().toString(36).slice(2)}`;
29+
const { svg } = await mermaid.render(renderId, chart);
30+
if (!destroyed && containerRef.current) {
31+
containerRef.current.innerHTML = svg;
32+
}
33+
} catch (_err) {
34+
// Fallback: show raw text if render fails
35+
if (!destroyed && containerRef.current) {
36+
containerRef.current.textContent = chart;
37+
}
38+
}
39+
};
40+
41+
// Initial render on mount
42+
void renderDiagram();
43+
44+
// Watch theme toggles on <html>
45+
const observer = new MutationObserver(() => {
46+
void renderDiagram();
47+
});
48+
observer.observe(document.documentElement, {
49+
attributes: true,
50+
attributeFilter: ["class", "data-theme"],
51+
});
52+
53+
// Watch OS theme changes
54+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
55+
const handleMediaChange = (): void => {
56+
void renderDiagram();
57+
};
58+
mediaQuery.addEventListener("change", handleMediaChange);
59+
60+
return () => {
61+
destroyed = true;
62+
observer.disconnect();
63+
mediaQuery.removeEventListener("change", handleMediaChange);
64+
};
65+
}, [chart]);
1566

16-
return <div className="mermaid">{chart}</div>;
67+
// Render an empty container on server; client fills it post-mount
68+
return <div ref={containerRef} />;
1769
};
1870

1971
export default Mermaid;

components/quizzes/quizData.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,18 +1139,18 @@
11391139
"chapter": "Proof of Authority"
11401140
},
11411141
"413": {
1142-
"question": "In the PoA validator management architecture, why is the PoAManager a separate contract that uses composition rather than inheritance?",
1142+
"question": "What is the key architectural relationship between ACP99Manager and ValidatorManager in the validator management system?",
11431143
"options": [
1144-
"To reduce gas costs during deployment",
1145-
"To make the code more complex and secure",
1146-
"To separate access control from core validator logic, allowing the same ValidatorManager to work with different consensus models",
1147-
"To comply with Solidity language limitations"
1144+
"ValidatorManager uses composition to access ACP99Manager functions",
1145+
"ValidatorManager extends ACP99Manager through inheritance, implementing the abstract base contract",
1146+
"ACP99Manager delegates all operations to ValidatorManager",
1147+
"Both contracts are independent and don't interact with each other"
11481148
],
11491149
"correctAnswers": [
1150-
2
1150+
1
11511151
],
1152-
"hint": "Think about how this design pattern enables flexibility in access control models.",
1153-
"explanation": "The PoAManager uses composition (has a ValidatorManager) rather than inheritance to create a clean separation between access control and core validator logic. This allows the same ValidatorManager implementation to be controlled by different access models (PoA with single owner, PoS with staking-based control, or custom governance) simply by changing the controlling contract, without modifying the core validator management functionality.",
1152+
"hint": "Think about the inheritance pattern shown in the class diagram.",
1153+
"explanation": "ValidatorManager extends ACP99Manager through inheritance, implementing the abstract base contract. This design pattern allows ValidatorManager to inherit the standardized validator management functions defined in ACP99Manager while adding its own concrete implementation of the initiate functions. The inheritance relationship ensures that all validator managers follow the same interface defined by ACP99Manager, providing consistency across different implementations.",
11541154
"chapter": "Validator Manager Contract"
11551155
}
11561156
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
---
2+
title: Proxy Patterns
3+
description: Understanding proxy patterns in blockchain - their importance, advantages, and risks.
4+
updated: 2025-07-08
5+
authors: [nicolasarnedo]
6+
icon: BookOpen
7+
---
8+
9+
In blockchain development smart contracts are **immutable by design**, this can be beneficial for users to trust the software they are using will not be altered, but can represent challenges for developers to improve existing software or fix bugs.
10+
11+
Proxy patterns offer a solution to this dilemma by separating contract logic from data storage, enabling upgrades while maintaining the same contract address.
12+
13+
## How Proxy Patterns Work
14+
15+
At its core, a proxy pattern involves two main components:
16+
17+
1. **Proxy Contract**: Holds the permanent address and stores all contract state
18+
2. **Implementation Contract**: Contains the actual business logic that can be upgraded
19+
20+
When users interact with the proxy contract, it delegates all calls to the implementation contract using the `delegatecall` opcode, executing the implementation's code in the proxy's storage context.
21+
22+
When the owner/admin of the proxy wants to upgrade
23+
24+
![Proxy Pattern Basic Flow](/common-images/permissioned-l1s/ProxyBasic.png)
25+
26+
## Common Proxy Patterns
27+
28+
### 1. Transparent Proxy Pattern
29+
30+
The Transparent Proxy pattern, developed by OpenZeppelin, ensures clear separation between admin and user interactions:
31+
32+
- Admin calls are handled by the proxy itself
33+
- User calls are delegated to the implementation
34+
- Prevents function selector clashes between proxy and implementation
35+
36+
### 2. UUPS (Universal Upgradeable Proxy Standard)
37+
38+
UUPS moves the upgrade logic to the implementation contract itself:
39+
40+
- More gas-efficient than transparent proxies
41+
- Implementation controls its own upgradeability
42+
- Requires careful implementation to avoid locking upgrades
43+
44+
### 3. Beacon Proxy Pattern
45+
46+
Beacon proxies enable multiple proxy instances to share the same implementation:
47+
48+
- All proxies point to a single beacon contract
49+
- Beacon contract holds the implementation address
50+
- Upgrading the beacon updates all proxy instances simultaneously
51+
52+
## Advantages of Proxy Patterns
53+
54+
1. **Upgradability Without Address Changes** - Users and integrations can continue using the same contract address even after upgrades, preserving user interfaces, exchange listings, and historical transaction references.
55+
2. **Bug Fixes and Security Patches** - Critical vulnerabilities can be addressed without requiring users to migrate to a new contract, enabling rapid response to security incidents with minimal disruption.
56+
3. **Feature Addition and Optimization** - New functionality can be added over time including gas optimization improvements, new features based on user feedback, and integration with newer protocols.
57+
58+
## Risks and Considerations
59+
60+
1. **Centralization Risk** - Upgrade capabilities rest with a single admin or small group, creating a single point of failure and potential for malicious upgrades.
61+
2. **Function Selector Clashes** - Proxy and implementation functions can have matching selectors, causing unexpected behavior, security vulnerabilities, and upgrade failures.
62+
3. **Complexity** - Proxy patterns add complexity through additional gas costs for delegatecall operations and make contracts harder to audit and verify.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
title: Transparent Proxy
3+
description: In-depth guide of the Transparent Proxy smart contract structure flows
4+
updated: 2025-01-10
5+
authors: [nicolasarnedo]
6+
icon: BookOpen
7+
---
8+
9+
**Key Components:**
10+
- **TransparentUpgradeableProxy**: Main proxy with immutable admin address for gas efficiency
11+
- **ProxyAdmin**: Administrative contract managing upgrades through ownership transfer capability
12+
- **Implementation**: Contains actual business logic, upgradeable via ProxyAdmin
13+
14+
## Admin Flow
15+
16+
Admins can only call upgrade functions. All other calls are blocked to prevent selector clashes.
17+
18+
<Mermaid
19+
chart="
20+
sequenceDiagram
21+
participant Admin
22+
participant ProxyAdmin
23+
participant TransparentProxy
24+
participant NewImplementation
25+
26+
Admin->>ProxyAdmin: upgradeAndCall(proxy, newImpl, data)
27+
ProxyAdmin->>TransparentProxy: upgradeToAndCall(newImpl, data)
28+
TransparentProxy->>TransparentProxy: _dispatchUpgradeToAndCall()
29+
TransparentProxy->>NewImplementation: delegatecall(data) [if data provided]
30+
"
31+
/>
32+
33+
## User Flow
34+
35+
Regular users can call any function except upgrade functions, which are delegated to the implementation contract.
36+
37+
<Mermaid
38+
chart="
39+
sequenceDiagram
40+
participant User
41+
participant TransparentProxy
42+
participant Implementation
43+
44+
User->>TransparentProxy: call someFunction()
45+
Note over TransparentProxy: msg.sender != admin
46+
TransparentProxy->>Implementation: delegatecall(someFunction())
47+
Implementation-->>TransparentProxy: return result
48+
TransparentProxy-->>User: return result
49+
"
50+
/>
Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,36 @@ The contracts in the [`validator-manager`](https://github.com/ava-labs/icm-contr
1111

1212
For this section we have blurred out some of the contracts, as these are not relevant to the Proof of Authority hierarchy used to create a **permissioned L1**:
1313

14-
![Validator Manager Contract Class Diagram](/common-images/permissioned-l1s/ValidatorManagerContractBlurr.png)
14+
<div style={{ display: 'flex', justifyContent: 'center', margin: '2rem 0' }}>
15+
<Mermaid
16+
chart="
17+
classDiagram
18+
class ACP99Manager {
19+
+initializeValidatorSet()
20+
+completeValidatorRegistration()
21+
+completeValidatorRemoval()
22+
+completeValidatorWeightUpdate()
23+
-_initiateValidatorRegistration()
24+
-_initiateValidatorRemoval()
25+
-_initiateValidatorWeightUpdate()
26+
}
27+
<<Abstract>> ACP99Manager
28+
29+
class ValidatorManager {
30+
+initializeValidatorSet()
31+
+completeValidatorRegistration() onlyOwner
32+
+completeValidatorRemoval() onlyOwner
33+
+completeValidatorWeightUpdate() onlyOwner
34+
+initiateValidatorRegistration() onlyOwner
35+
+initiateValidatorRemoval() onlyOwner
36+
+initiateValidatorWeightUpdate() onlyOwner
37+
}
38+
39+
ACP99Manager <|-- ValidatorManager
40+
"
41+
/>
42+
</div>
43+
1544

1645
## Understanding Contract Hierarchy
1746

@@ -44,30 +73,4 @@ The `ValidatorManager` extends `ACP99Manager` and adds the complete validator li
4473
- Tracks validator states throughout their lifecycle
4574
- Handles Warp message construction and verification
4675

47-
### PoAManager (Access Control Layer)
48-
49-
The `PoAManager` is a separate contract that provides a Proof of Authority interface to the ValidatorManager:
50-
51-
**Architecture Pattern:**
52-
- Uses composition rather than inheritance (has a ValidatorManager, doesn't extend it)
53-
- Acts as an access control proxy to the ValidatorManager
54-
- Separates ownership concerns from core validator logic
55-
56-
**Key Functions:**
57-
- **Initiate functions** (onlyOwner): Only the PoA owner can start validator operations
58-
- **Complete functions** (no restrictions): Anyone can complete pending operations once initiated
59-
- **`transferValidatorManagerOwnership()`** (onlyOwner): Allows transferring control of the underlying ValidatorManager
60-
61-
This separation of concerns allows the same ValidatorManager to be used with different access control models (PoA, PoS, or custom governance) by simply changing the controlling contract.
62-
63-
### PoA Architecture Benefits
64-
65-
The two-contract design provides several advantages:
66-
67-
- **Separation of Concerns**: Core validator logic is separate from access control
68-
- **Upgradeability**: ValidatorManager can be upgraded without affecting PoAManager
69-
- **Flexibility**: Different access control models can be implemented without changing the core
70-
- **Security**: Owner permissions are clearly defined and isolated in PoAManager
71-
72-
7376
<Quiz quizId="413" />
279 KB
Loading
Binary file not shown.

toolbox/src/versions.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"avaplatform/avalanchego": "v1.13.3",
3-
"avaplatform/subnet-evm_avalanchego": "v0.7.7_v1.13.3",
3+
"avaplatform/subnet-evm_avalanchego": "v0.7.8_v1.13.4",
44
"avaplatform/icm-relayer": "v1.6.6"
55
}

0 commit comments

Comments
 (0)