or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

access-control.mdaccount-abstraction.mdfinance.mdgovernance.mdindex.mdmeta-transactions.mdproxy-upgradeability.mdsecurity-utilities.mdtoken-standards.md

proxy-upgradeability.mddocs/

0

# Proxy and Upgradeability

1

2

Core upgradeability infrastructure including the Initializable base contract and UUPS upgrade pattern for implementing upgradeable smart contracts with secure state preservation.

3

4

## Capabilities

5

6

### Initializable Base Contract

7

8

Foundational contract that controls the initialization of upgradeable contracts.

9

10

```solidity { .api }

11

/**

12

* @dev Helper to support initializer functions

13

*/

14

abstract contract Initializable {

15

/**

16

* @dev Triggered when the contract has been initialized or reinitialized

17

*/

18

event Initialized(uint64 version);

19

20

/**

21

* @dev Modifier used by the initializer function of a contract

22

*/

23

modifier initializer();

24

25

/**

26

* @dev Modifier used by reinitializer functions

27

*/

28

modifier reinitializer(uint64 version);

29

30

/**

31

* @dev Modifier that restricts functions to the initializing phase

32

*/

33

modifier onlyInitializing();

34

35

/**

36

* @dev Locks the contract, preventing any future reinitialization

37

*/

38

function _disableInitializers() internal;

39

40

/**

41

* @dev Returns the highest version that has been initialized

42

*/

43

function _getInitializedVersion() internal view returns (uint64);

44

45

/**

46

* @dev Returns true if the contract is currently initializing

47

*/

48

function _isInitializing() internal view returns (bool);

49

}

50

```

51

52

### UUPS Upgradeable Pattern

53

54

Universal Upgradeable Proxy Standard implementation that allows contracts to upgrade themselves.

55

56

```solidity { .api }

57

/**

58

* @dev Implementation of the UUPS (Universal Upgradeable Proxy Standard) pattern

59

*/

60

abstract contract UUPSUpgradeable {

61

function __UUPSUpgradeable_init() internal onlyInitializing;

62

63

/**

64

* @dev The version of the upgrade interface of the contract

65

*/

66

string constant UPGRADE_INTERFACE_VERSION = "5.0.0";

67

68

/**

69

* @dev Storage slot with the address of the current implementation

70

*/

71

bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

72

73

/**

74

* @dev Upgrade the implementation of the proxy to newImplementation and call a function on the new implementation

75

*/

76

function upgradeToAndCall(address newImplementation, bytes memory data) external payable;

77

78

/**

79

* @dev Returns the current implementation address

80

*/

81

function proxiableUUID() external view returns (bytes32);

82

83

/**

84

* @dev Function that should revert when msg.sender is not authorized to upgrade the contract

85

*/

86

function _authorizeUpgrade(address newImplementation) internal virtual;

87

88

/**

89

* @dev Returns the current implementation address

90

*/

91

function _getImplementation() internal view returns (address);

92

93

/**

94

* @dev Stores a new address in the EIP-1967 implementation slot

95

*/

96

function _setImplementation(address newImplementation) private;

97

}

98

99

// Events

100

event Upgraded(address indexed implementation);

101

```

102

103

## Upgrade Patterns

104

105

### Storage Layout Considerations

106

107

All upgradeable contracts use ERC-7201 namespaced storage to prevent storage collisions:

108

109

```solidity

110

// Example storage pattern used in upgradeable contracts

111

struct ContractNameStorage {

112

// Contract state variables

113

mapping(address => uint256) _balances;

114

uint256 _totalSupply;

115

string _name;

116

string _symbol;

117

}

118

119

// Storage location calculation

120

bytes32 private constant ContractNameStorageLocation =

121

keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ContractName")) - 1)) & ~bytes32(uint256(0xff));

122

123

function _getContractNameStorage() private pure returns (ContractNameStorage storage $) {

124

assembly {

125

$.slot := ContractNameStorageLocation

126

}

127

}

128

```

129

130

### Initialization Pattern

131

132

Standard initialization pattern for upgradeable contracts:

133

134

```solidity

135

// Internal initialization functions

136

function __ContractName_init(/* parameters */) internal onlyInitializing {

137

__ContractName_init_unchained(/* parameters */);

138

}

139

140

function __ContractName_init_unchained(/* parameters */) internal onlyInitializing {

141

ContractNameStorage storage $ = _getContractNameStorage();

142

// Initialize contract state

143

}

144

```

145

146

## Usage Examples

147

148

### Basic Upgradeable Contract

149

150

```solidity

151

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

152

import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

153

import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

154

155

contract MyUpgradeableContract is Initializable, UUPSUpgradeable, OwnableUpgradeable {

156

uint256 public value;

157

158

/// @custom:oz-upgrades-unsafe-allow constructor

159

constructor() {

160

_disableInitializers();

161

}

162

163

function initialize(address initialOwner, uint256 initialValue) initializer public {

164

__Ownable_init(initialOwner);

165

__UUPSUpgradeable_init();

166

value = initialValue;

167

}

168

169

function setValue(uint256 newValue) public onlyOwner {

170

value = newValue;

171

}

172

173

function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}

174

}

175

```

176

177

### Upgradeable Token Contract

178

179

```solidity

180

import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";

181

import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

182

import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

183

184

contract UpgradeableToken is ERC20Upgradeable, UUPSUpgradeable, OwnableUpgradeable {

185

/// @custom:oz-upgrades-unsafe-allow constructor

186

constructor() {

187

_disableInitializers();

188

}

189

190

function initialize(

191

string memory name,

192

string memory symbol,

193

address initialOwner,

194

uint256 initialSupply

195

) initializer public {

196

__ERC20_init(name, symbol);

197

__Ownable_init(initialOwner);

198

__UUPSUpgradeable_init();

199

200

if (initialSupply > 0) {

201

_mint(initialOwner, initialSupply);

202

}

203

}

204

205

function mint(address to, uint256 amount) public onlyOwner {

206

_mint(to, amount);

207

}

208

209

function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}

210

}

211

```

212

213

### Contract V2 with New Features

214

215

```solidity

216

// Upgraded version of the contract with new functionality

217

contract MyUpgradeableContractV2 is MyUpgradeableContract {

218

uint256 public newFeature;

219

220

function initializeV2(uint256 newFeatureValue) reinitializer(2) public {

221

newFeature = newFeatureValue;

222

}

223

224

function setNewFeature(uint256 newValue) public onlyOwner {

225

newFeature = newValue;

226

}

227

228

function getVersion() public pure returns (string memory) {

229

return "2.0.0";

230

}

231

}

232

```

233

234

### Deployment Script Pattern

235

236

```solidity

237

// Example deployment script for upgradeable contracts

238

import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

239

240

contract DeployUpgradeable {

241

function deploy() external returns (address) {

242

// Deploy implementation

243

MyUpgradeableContract implementation = new MyUpgradeableContract();

244

245

// Encode initializer call

246

bytes memory data = abi.encodeCall(

247

MyUpgradeableContract.initialize,

248

(msg.sender, 100) // initialOwner, initialValue

249

);

250

251

// Deploy proxy

252

ERC1967Proxy proxy = new ERC1967Proxy(address(implementation), data);

253

254

return address(proxy);

255

}

256

}

257

```

258

259

## Best Practices

260

261

### Constructor Disabling

262

263

Always disable initializers in the constructor to prevent initialization of the implementation contract:

264

265

```solidity

266

/// @custom:oz-upgrades-unsafe-allow constructor

267

constructor() {

268

_disableInitializers();

269

}

270

```

271

272

### Storage Gap Pattern

273

274

For contracts that may be inherited, consider adding storage gaps:

275

276

```solidity

277

// Reserve storage slots for future variables

278

uint256[50] private __gap;

279

```

280

281

### Upgrade Authorization

282

283

Always implement proper authorization for upgrades:

284

285

```solidity

286

function _authorizeUpgrade(address newImplementation) internal override onlyOwner {

287

// Additional upgrade validation logic can be added here

288

}

289

```