or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

access-control.mdcrosschain.mdfinance.mdgovernance.mdindex.mdmetatx.mdproxy.mdsecurity.mdtokens.mdutilities.md

finance.mddocs/

0

# Financial Primitives

1

2

OpenZeppelin Contracts provides essential financial mechanisms for handling cryptocurrency payments, distributions, and vesting schedules in smart contracts.

3

4

## Core Imports

5

6

Import financial contracts using Solidity import statements:

7

8

```solidity

9

import "@openzeppelin/contracts/finance/PaymentSplitter.sol";

10

import "@openzeppelin/contracts/finance/VestingWallet.sol";

11

```

12

13

## Capabilities

14

15

### Payment Splitter

16

17

Splits incoming payments among multiple payees according to their share allocation. Supports both Ether and ERC20 token distributions.

18

19

```solidity { .api }

20

contract PaymentSplitter is Context {

21

constructor(address[] memory payees, uint256[] memory shares_);

22

23

function totalShares() public view returns (uint256);

24

function totalReleased() public view returns (uint256);

25

function totalReleased(IERC20 token) public view returns (uint256);

26

function shares(address account) public view returns (uint256);

27

function released(address account) public view returns (uint256);

28

function released(IERC20 token, address account) public view returns (uint256);

29

function payee(uint256 index) public view returns (address);

30

function releasable(address account) public view returns (uint256);

31

function releasable(IERC20 token, address account) public view returns (uint256);

32

function release(address payable account) public virtual;

33

function release(IERC20 token, address account) public virtual;

34

}

35

```

36

37

#### Events

38

39

```solidity { .api }

40

event PayeeAdded(address account, uint256 shares);

41

event PaymentReleased(address to, uint256 amount);

42

event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount);

43

event PaymentReceived(address from, uint256 amount);

44

```

45

46

#### Usage Example

47

48

```solidity

49

pragma solidity ^0.8.0;

50

51

import "@openzeppelin/contracts/finance/PaymentSplitter.sol";

52

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

53

54

contract RevenueSharing is PaymentSplitter {

55

constructor(

56

address[] memory founders,

57

uint256[] memory founderShares

58

) PaymentSplitter(founders, founderShares) {}

59

60

// Contract automatically receives and splits payments

61

receive() external payable {

62

// PaymentSplitter handles the payment tracking

63

}

64

65

function releaseAll() external {

66

for (uint256 i = 0; i < payee(i) != address(0); i++) {

67

address payeeAddress = payee(i);

68

if (releasable(payeeAddress) > 0) {

69

release(payable(payeeAddress));

70

}

71

}

72

}

73

74

function releaseToken(IERC20 token) external {

75

for (uint256 i = 0; i < payee(i) != address(0); i++) {

76

address payeeAddress = payee(i);

77

if (releasable(token, payeeAddress) > 0) {

78

release(token, payeeAddress);

79

}

80

}

81

}

82

}

83

```

84

85

### Vesting Wallet

86

87

Implements token vesting functionality with linear release schedules over a specified duration.

88

89

```solidity { .api }

90

contract VestingWallet is Context {

91

constructor(address beneficiaryAddress, uint64 startTimestamp, uint64 durationSeconds);

92

93

function beneficiary() public view virtual returns (address);

94

function start() public view virtual returns (uint256);

95

function duration() public view virtual returns (uint256);

96

function released() public view virtual returns (uint256);

97

function released(address token) public view virtual returns (uint256);

98

function releasable() public view virtual returns (uint256);

99

function releasable(address token) public view virtual returns (uint256);

100

function release() public virtual;

101

function release(address token) public virtual;

102

function vestedAmount(uint64 timestamp) public view virtual returns (uint256);

103

function vestedAmount(address token, uint64 timestamp) public view virtual returns (uint256);

104

}

105

```

106

107

#### Events

108

109

```solidity { .api }

110

event EtherReleased(uint256 amount);

111

event ERC20Released(address indexed token, uint256 amount);

112

```

113

114

#### Usage Example

115

116

```solidity

117

pragma solidity ^0.8.0;

118

119

import "@openzeppelin/contracts/finance/VestingWallet.sol";

120

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

121

122

contract TeamVesting {

123

mapping(address => VestingWallet) public vestingWallets;

124

IERC20 public token;

125

126

constructor(IERC20 _token) {

127

token = _token;

128

}

129

130

function createVestingSchedule(

131

address beneficiary,

132

uint256 amount,

133

uint64 startTime,

134

uint64 duration

135

) external {

136

// Create vesting wallet for team member

137

VestingWallet vestingWallet = new VestingWallet(

138

beneficiary,

139

startTime,

140

duration

141

);

142

143

vestingWallets[beneficiary] = vestingWallet;

144

145

// Transfer tokens to vesting wallet

146

token.transfer(address(vestingWallet), amount);

147

}

148

149

function releaseVestedTokens(address beneficiary) external {

150

VestingWallet wallet = vestingWallets[beneficiary];

151

require(address(wallet) != address(0), "No vesting schedule");

152

153

wallet.release(address(token));

154

}

155

156

function getVestingInfo(address beneficiary)

157

external

158

view

159

returns (

160

uint256 totalAmount,

161

uint256 releasedAmount,

162

uint256 releasableAmount,

163

uint256 startTime,

164

uint256 duration

165

)

166

{

167

VestingWallet wallet = vestingWallets[beneficiary];

168

require(address(wallet) != address(0), "No vesting schedule");

169

170

totalAmount = token.balanceOf(address(wallet)) + wallet.released(address(token));

171

releasedAmount = wallet.released(address(token));

172

releasableAmount = wallet.releasable(address(token));

173

startTime = wallet.start();

174

duration = wallet.duration();

175

}

176

}

177

```

178

179

## Advanced Financial Patterns

180

181

### Escrow Pattern with Payment Splitter

182

183

```solidity

184

pragma solidity ^0.8.0;

185

186

import "@openzeppelin/contracts/finance/PaymentSplitter.sol";

187

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

188

189

contract EscrowPaymentSplitter is PaymentSplitter, ReentrancyGuard {

190

enum EscrowState { Pending, Released, Disputed }

191

192

struct Escrow {

193

uint256 amount;

194

EscrowState state;

195

uint256 releaseTime;

196

}

197

198

mapping(bytes32 => Escrow) public escrows;

199

address public arbiter;

200

201

constructor(

202

address[] memory payees,

203

uint256[] memory shares_,

204

address _arbiter

205

) PaymentSplitter(payees, shares_) {

206

arbiter = _arbiter;

207

}

208

209

function createEscrow(bytes32 escrowId, uint256 releaseTime) external payable {

210

require(escrows[escrowId].amount == 0, "Escrow already exists");

211

require(msg.value > 0, "Must send funds");

212

213

escrows[escrowId] = Escrow({

214

amount: msg.value,

215

state: EscrowState.Pending,

216

releaseTime: releaseTime

217

});

218

}

219

220

function releaseEscrow(bytes32 escrowId) external nonReentrant {

221

Escrow storage escrow = escrows[escrowId];

222

require(escrow.state == EscrowState.Pending, "Escrow not pending");

223

require(block.timestamp >= escrow.releaseTime, "Release time not reached");

224

225

escrow.state = EscrowState.Released;

226

227

// Funds automatically split according to PaymentSplitter shares

228

(bool success, ) = address(this).call{value: escrow.amount}("");

229

require(success, "Failed to release funds");

230

}

231

}

232

```

233

234

### Milestone-Based Vesting

235

236

```solidity

237

pragma solidity ^0.8.0;

238

239

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

240

import "@openzeppelin/contracts/access/Ownable.sol";

241

242

contract MilestoneVesting is Ownable {

243

struct Milestone {

244

uint256 amount;

245

bool completed;

246

bool released;

247

}

248

249

struct VestingSchedule {

250

address beneficiary;

251

uint256 totalAmount;

252

uint256 releasedAmount;

253

Milestone[] milestones;

254

}

255

256

mapping(address => VestingSchedule) public vestingSchedules;

257

IERC20 public token;

258

259

event MilestoneCompleted(address indexed beneficiary, uint256 milestoneIndex);

260

event TokensReleased(address indexed beneficiary, uint256 amount);

261

262

constructor(IERC20 _token) {

263

token = _token;

264

}

265

266

function createVestingSchedule(

267

address beneficiary,

268

uint256[] memory milestoneAmounts

269

) external onlyOwner {

270

require(vestingSchedules[beneficiary].beneficiary == address(0), "Schedule exists");

271

272

VestingSchedule storage schedule = vestingSchedules[beneficiary];

273

schedule.beneficiary = beneficiary;

274

275

uint256 totalAmount = 0;

276

for (uint256 i = 0; i < milestoneAmounts.length; i++) {

277

schedule.milestones.push(Milestone({

278

amount: milestoneAmounts[i],

279

completed: false,

280

released: false

281

}));

282

totalAmount += milestoneAmounts[i];

283

}

284

285

schedule.totalAmount = totalAmount;

286

token.transferFrom(msg.sender, address(this), totalAmount);

287

}

288

289

function completeMilestone(address beneficiary, uint256 milestoneIndex) external onlyOwner {

290

VestingSchedule storage schedule = vestingSchedules[beneficiary];

291

require(schedule.beneficiary != address(0), "No vesting schedule");

292

require(milestoneIndex < schedule.milestones.length, "Invalid milestone");

293

require(!schedule.milestones[milestoneIndex].completed, "Already completed");

294

295

schedule.milestones[milestoneIndex].completed = true;

296

emit MilestoneCompleted(beneficiary, milestoneIndex);

297

}

298

299

function releaseMilestone(uint256 milestoneIndex) external {

300

VestingSchedule storage schedule = vestingSchedules[msg.sender];

301

require(schedule.beneficiary == msg.sender, "Not beneficiary");

302

require(milestoneIndex < schedule.milestones.length, "Invalid milestone");

303

304

Milestone storage milestone = schedule.milestones[milestoneIndex];

305

require(milestone.completed, "Milestone not completed");

306

require(!milestone.released, "Already released");

307

308

milestone.released = true;

309

schedule.releasedAmount += milestone.amount;

310

311

token.transfer(msg.sender, milestone.amount);

312

emit TokensReleased(msg.sender, milestone.amount);

313

}

314

}

315

```

316

317

## Financial Best Practices

318

319

1. **Payment Splitting**: Use PaymentSplitter for automatic revenue distribution

320

2. **Vesting Security**: Implement proper access controls for vesting schedule modifications

321

3. **Reentrancy Protection**: Use ReentrancyGuard for contracts handling payments

322

4. **Token Support**: Design contracts to support both Ether and ERC20 tokens

323

5. **Release Mechanisms**: Implement both automatic and manual release options

324

6. **Audit Considerations**: Financial contracts require thorough security audits

325

326

## Integration Patterns

327

328

### With Governance

329

330

```solidity

331

// Combine vesting with governance tokens

332

contract GovernanceVesting is VestingWallet {

333

constructor(

334

address beneficiary,

335

uint64 startTimestamp,

336

uint64 durationSeconds

337

) VestingWallet(beneficiary, startTimestamp, durationSeconds) {}

338

339

// Override to delegate voting power while tokens are vesting

340

function _afterTokenTransfer(address token, uint256 amount) internal {

341

// Delegate voting power to beneficiary even while tokens are locked

342

IVotes(token).delegate(beneficiary());

343

}

344

}

345

```

346

347

### With Access Control

348

349

```solidity

350

// Multi-signature controlled payment splitter

351

contract MultiSigPaymentSplitter is PaymentSplitter, AccessControl {

352

bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");

353

354

modifier onlyAdmin() {

355

require(hasRole(ADMIN_ROLE, msg.sender), "Not admin");

356

_;

357

}

358

359

function emergencyWithdraw(address token, uint256 amount) external onlyAdmin {

360

// Emergency withdrawal mechanism

361

}

362

}

363

```

364

365

## Error Handling

366

367

Financial contracts may revert with various errors:

368

369

- **PaymentSplitter**: `PaymentSplitter__InvalidShares()`, `PaymentSplitter__NoPayeesProvided()`

370

- **VestingWallet**: No specific custom errors, uses standard transfer/balance checks

371

- **General**: Standard ERC20 transfer errors, access control violations, reentrancy protection