Skip to content

Commit fd040a0

Browse files
Copilotkatspaugh
andcommitted
Add user guide for custom mastercopy support
Co-authored-by: katspaugh <381895+katspaugh@users.noreply.github.com>
1 parent ad103e6 commit fd040a0

File tree

1 file changed

+136
-0
lines changed

1 file changed

+136
-0
lines changed

docs/CUSTOM_MASTERCOPY_GUIDE.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Using Safe SDK with Custom Mastercopy Deployments
2+
3+
## Overview
4+
5+
The Safe protocol-kit now supports Safe contracts that use custom-deployed mastercopies (also called singletons), as long as the mastercopy bytecode exactly matches an official Safe version. This enables using the SDK on custom networks, testnets, or with independently deployed Safe contracts.
6+
7+
## How It Works
8+
9+
When you initialize a Safe instance, the SDK will:
10+
11+
1. **First attempt**: Call the `VERSION()` method on the Safe contract to determine its version
12+
2. **Fallback mechanism**: If the VERSION() call fails:
13+
- Read the mastercopy address from storage slot 0 of the Safe proxy
14+
- Fetch the bytecode of the mastercopy contract
15+
- Compare the bytecode hash against all known Safe versions (1.0.0, 1.1.1, 1.2.0, 1.3.0, 1.4.1)
16+
- If a match is found, use that version to initialize the SDK
17+
- If no match is found, fall back to the default version (1.3.0)
18+
19+
## Usage Example
20+
21+
No changes are required to your existing code! The new functionality works transparently:
22+
23+
```typescript
24+
import Safe from '@safe-global/protocol-kit'
25+
26+
// Initialize with a Safe that uses a custom-deployed mastercopy
27+
const safe = await Safe.init({
28+
provider: 'https://your-rpc-url',
29+
signer: privateKey,
30+
safeAddress: '0xYourSafeAddress'
31+
})
32+
33+
// The SDK will automatically:
34+
// 1. Try to call VERSION()
35+
// 2. If that fails, read the mastercopy address
36+
// 3. Match the mastercopy bytecode against known versions
37+
// 4. Initialize with the detected version
38+
39+
console.log(safe.getContractVersion()) // e.g., "1.3.0"
40+
```
41+
42+
## Requirements
43+
44+
For the mastercopy matching to work, the following conditions must be met:
45+
46+
1. **Exact bytecode match**: The mastercopy bytecode must be byte-for-byte identical to an official Safe deployment
47+
2. **Contract must be deployed**: Both the Safe proxy and the mastercopy must be deployed on the network
48+
3. **Supported version**: The mastercopy must match one of the supported Safe versions (1.0.0, 1.1.1, 1.2.0, 1.3.0, or 1.4.1)
49+
50+
## Benefits
51+
52+
- **Custom network support**: Deploy Safes on your own test network using official Safe bytecode
53+
- **Independent deployments**: Use Safes where the mastercopy was deployed separately
54+
- **Automatic version detection**: No need to manually specify the version
55+
- **Backward compatible**: Existing code works without modifications
56+
57+
## What Gets Detected
58+
59+
The mastercopy matching detects:
60+
- **Safe version**: Which Safe contract version (1.0.0, 1.1.1, 1.2.0, 1.3.0, or 1.4.1)
61+
- **Singleton type**: Whether it's an L1 singleton or L2 singleton
62+
- **Mastercopy address**: The address of the matched mastercopy
63+
64+
## Limitations
65+
66+
- Only works with official Safe bytecode (no modified versions)
67+
- The mastercopy must be deployed and accessible on the network
68+
- Performance: The first initialization with a custom mastercopy will require additional RPC calls to fetch and compare bytecode
69+
70+
## Technical Details
71+
72+
For more technical information about the implementation, see [MASTERCOPY_MATCHING.md](../MASTERCOPY_MATCHING.md).
73+
74+
## Troubleshooting
75+
76+
### My Safe initialization fails with "Invalid ... contract address"
77+
78+
This error means the SDK couldn't find a deployment for the contract type on your network. Make sure:
79+
- The Safe proxy is deployed at the specified address
80+
- The mastercopy referenced by the proxy is also deployed
81+
- You're using the correct RPC endpoint for your network
82+
83+
### The detected version is incorrect
84+
85+
If the SDK detects the wrong version, it likely means:
86+
- The mastercopy bytecode has been modified (not an exact match)
87+
- There's an issue with the RPC provider returning incorrect bytecode
88+
89+
You can always manually specify the version using `contractNetworks`:
90+
91+
```typescript
92+
const safe = await Safe.init({
93+
provider: 'https://your-rpc-url',
94+
signer: privateKey,
95+
safeAddress: '0xYourSafeAddress',
96+
contractNetworks: {
97+
[chainId]: {
98+
safeSingletonAddress: '0xYourMastercopyAddress',
99+
safeSingletonAbi: [...] // optional
100+
}
101+
}
102+
})
103+
```
104+
105+
## Example: Using Safe SDK on a Custom Testnet
106+
107+
```typescript
108+
import Safe from '@safe-global/protocol-kit'
109+
110+
// Scenario: You've deployed a Safe on a custom testnet using official v1.3.0 bytecode
111+
// The Safe proxy address is 0x123...
112+
// The mastercopy was deployed at 0xabc...
113+
114+
const safe = await Safe.init({
115+
provider: 'https://custom-testnet-rpc.example.com',
116+
signer: '0xYourPrivateKey',
117+
safeAddress: '0x123...'
118+
})
119+
120+
// The SDK will:
121+
// 1. Call VERSION() on 0x123... (delegates to mastercopy at 0xabc...)
122+
// 2. If that works, use the returned version
123+
// 3. If that fails:
124+
// - Read mastercopy address from storage (gets 0xabc...)
125+
// - Fetch bytecode from 0xabc...
126+
// - Compare with known Safe versions
127+
// - Find it matches v1.3.0
128+
// - Initialize using v1.3.0 ABI
129+
130+
console.log(safe.getContractVersion()) // "1.3.0"
131+
132+
// Now you can use all Safe SDK features normally
133+
const owners = await safe.getOwners()
134+
const threshold = await safe.getThreshold()
135+
// ... etc
136+
```

0 commit comments

Comments
 (0)