1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561 |
84×
84×
54×
147×
146×
146×
146×
77×
77×
76×
29×
16×
15×
15×
13×
13×
4×
9×
9×
8×
17×
16×
16×
14×
14×
4×
10×
10×
71×
71×
58×
58×
16×
15×
14×
14×
4×
4×
83×
81×
81×
81×
81×
26×
26×
79×
79×
85×
84×
82×
80×
78×
76×
74×
17×
17×
15×
57×
57×
55×
70×
68×
64×
62×
15×
47×
47×
47×
47×
47×
47×
62×
62×
76×
76×
76×
76×
74×
74×
74×
81×
81×
81×
55×
26×
153×
152×
47×
47×
19×
16×
37×
30×
70×
69×
69×
69×
69×
68×
68×
68×
68×
68×
67×
67×
67×
67×
66×
66×
64×
62×
18×
17×
17×
9×
155×
153×
151×
149×
147×
145×
143×
143×
143×
143×
143×
143×
143×
143×
131×
| // SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.6;
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol";
import "./../interfaces/ISOLACE.sol";
import "./../interfaces/staking/IxsLocker.sol";
import "./../interfaces/bonds/IBondDepository.sol";
import "./../utils/ERC721EnhancedInitializable.sol";
import "./../utils/Cloneable.sol";
import "./../utils/GovernableInitializable.sol";
import "./../interfaces/bonds/IBondTellerErc20.sol";
/**
* @title BondTellerErc20
* @author solace.fi
* @notice A bond teller that accepts an ERC20 as payment.
*
* Bond tellers allow users to buy bonds. Payments are made in `principal` which is sent to the underwriting pool and used to back risk. Users will receive [**SOLACE**](./../SOLACE) but it must be bonded or staked. If bonded, the [**SOLACE**](./../SOLACE) will be vested linearly and redeemed over time. If staked, the [**SOLACE**](./../SOLACE) only be withdrawable after the lock expires but will give the user extra [**SOLACE**](./../SOLACE) rewards and voting rights.
*
* Bonds can be purchased via [`deposit()`](#deposit) or [`depositSigned()`](#depositsigned). Bonds are represented as ERC721s, can be viewed with [`bonds()`](#bonds), and redeemed with [`claimRewards()`](#claimrewards). If staked, an [`xsLocker`](./../staking/xsLocker) lock is created instead of a bond.
*/
contract BondTellerErc20 is IBondTellerErc20, ReentrancyGuard, ERC721EnhancedInitializable, Cloneable, GovernableInitializable {
/***************************************
GLOBAL VARIABLES
***************************************/
// prices
uint256 public capacity; // capacity remaining for all bonds
uint256 public nextPrice; // the price of the next bond before decay
uint256 public minimumPrice; // price floor measured in principal per 1 solace
uint128 public priceAdjNum; // factor that increases price after purchase
uint128 public priceAdjDenom; // factor that increases price after purchase
uint256 public halfLife; // factor for price decay
uint256 public lastPriceUpdate; // last timestamp price was updated
uint256 public maxPayout; // max payout in a single bond measured in principal
uint256 internal constant MAX_BPS = 10000; // 10k basis points (100%)
uint256 public protocolFeeBps; // portion of principal that is sent to the dao, the rest to the pool
bool public termsSet; // have terms been set
bool public capacityIsPayout; // capacity limit is for payout vs principal
bool public paused; // pauses deposits
// times
uint40 public startTime; // timestamp bonds start
uint40 public endTime; // timestamp bonds no longer offered
uint40 public globalVestingTerm; // duration in seconds (fixed-term)
// bonds
uint256 public numBonds; // total number of bonds that have been created
struct Bond {
uint256 payoutAmount; // amount of solace to be paid in total on the bond
uint256 payoutAlreadyClaimed; // amount of solace that has already been claimed on the bond
uint256 principalPaid; // amount of principal paid for this bond
uint40 vestingStart; // timestamp at which bond was minted
uint40 localVestingTerm; // vesting term for this bond
}
mapping (uint256 => Bond) public bonds; // mapping of bondID to Bond object
// addresses
address public solace; // solace native token
address public xsLocker; // xsLocker staking contract
address public principal; // token to accept as payment
bool public isPermittable; // true if principal supports EIP2612.
address public underwritingPool; // the underwriting pool to back risks
address public dao; // the dao
address public bondDepo; // the bond depository
/***************************************
INITIALIZER
***************************************/
/**
* @notice Creates a new `BondTellerERC20`. The new teller will be a minimal proxy to this instance.
* @param name_ The name of the bond token.
* @param governance_ The address of the teller's [governor](/docs/protocol/governance).
* @param principal_ The ERC20 token that users give.
* @param isPermittable_ True if `principal` supports `EIP2612`.
* @param salt_ Input for deterministic address calculation.
* @return teller The address of the new teller.
*/
function clone(
string memory name_,
address governance_,
address principal_,
bool isPermittable_,
bytes32 salt_
) external override returns (address teller) {
teller = _deployMinimalProxy(salt_);
IBondTellerErc20(teller).initialize(name_, governance_, solace, xsLocker, underwritingPool, dao, principal_, isPermittable_, bondDepo);
return teller;
}
/**
* @notice Initializes the teller.
* @param name_ The name of the bond token.
* @param governance_ The address of the [governor](/docs/protocol/governance).
* @param solace_ The [**SOLACE**](./../SOLACE) token.
* @param xsLocker_ The [**xsLocker**](./../staking/xsLocker) contract.
* @param pool_ The underwriting pool.
* @param dao_ The DAO.
* @param principal_ The ERC20 token that users deposit.
* @param isPermittable_ True if `principal` supports `EIP2612`.
* @param bondDepo_ The bond depository.
*/
function initialize(
string memory name_,
address governance_,
address solace_,
address xsLocker_,
address pool_,
address dao_,
address principal_,
bool isPermittable_,
address bondDepo_
) external override initializer {
__Governable_init(governance_);
string memory symbol = "SBT";
__ERC721Enhanced_init(name_, symbol);
_setAddresses(solace_, xsLocker_, pool_, dao_, principal_, isPermittable_, bondDepo_);
}
/***************************************
VIEW FUNCTIONS
***************************************/
// BOND PRICE
/**
* @notice Calculate the current price of a bond.
* Assumes 1 [**SOLACE**](./../SOLACE) payout.
* @return price_ The price of the bond measured in `principal`.
*/
function bondPrice() public view override returns (uint256 price_) {
// solhint-disable-next-line not-rely-on-time
uint256 timeSinceLast = block.timestamp - lastPriceUpdate;
price_ = exponentialDecay(nextPrice, timeSinceLast);
if (price_ < minimumPrice) {
price_ = minimumPrice;
}
}
/**
* @notice Calculate the amount of [**SOLACE**](./../SOLACE) out for an amount of `principal`.
* @param amountIn Amount of principal to deposit.
* @param stake True to stake, false to not stake.
* @return amountOut Amount of [**SOLACE**](./../SOLACE) out.
*/
function calculateAmountOut(uint256 amountIn, bool stake) external view override returns (uint256 amountOut) {
require(termsSet, "not initialized");
// exchange rate
uint256 bondPrice_ = bondPrice();
require(bondPrice_ > 0, "zero price");
amountOut = 1 ether * amountIn / bondPrice_; // 1 ether => 1 solace
// ensure there is remaining capacity for bond
if (capacityIsPayout) {
// capacity in payout terms
require(capacity >= amountOut, "bond at capacity");
} else {
// capacity in principal terms
require(capacity >= amountIn, "bond at capacity");
}
require(amountOut <= maxPayout, "bond too large");
return amountOut;
}
/**
* @notice Calculate the amount of `principal` in for an amount of [**SOLACE**](./../SOLACE) out.
* @param amountOut Amount of [**SOLACE**](./../SOLACE) out.
* @param stake True to stake, false to not stake.
* @return amountIn Amount of principal to deposit.
*/
function calculateAmountIn(uint256 amountOut, bool stake) external view override returns (uint256 amountIn) {
require(termsSet, "not initialized");
// exchange rate
uint256 bondPrice_ = bondPrice();
require(bondPrice_ > 0, "zero price");
amountIn = amountOut * bondPrice_ / 1 ether;
// ensure there is remaining capacity for bond
if (capacityIsPayout) {
// capacity in payout terms
require(capacity >= amountOut, "bond at capacity");
} else {
// capacity in principal terms
require(capacity >= amountIn, "bond at capacity");
}
require(amountOut <= maxPayout, "bond too large");
}
/***************************************
BONDER FUNCTIONS
***************************************/
/**
* @notice Create a bond by depositing `amount` of `principal`.
* Principal will be transferred from `msg.sender` using `allowance`.
* @param amount Amount of principal to deposit.
* @param minAmountOut The minimum [**SOLACE**](./../SOLACE) out.
* @param depositor The bond recipient, default msg.sender.
* @param stake True to stake, false to not stake.
* @return payout The amount of [**SOLACE**](./../SOLACE) in the bond.
* @return tokenID The ID of the newly created bond or lock.
*/
function deposit(
uint256 amount,
uint256 minAmountOut,
address depositor,
bool stake
) external override nonReentrant returns (uint256 payout, uint256 tokenID) {
// accounting
uint256 protocolFee;
(payout, tokenID, protocolFee) = _deposit(amount, minAmountOut, depositor, stake);
// route principal - put last as Checks-Effects-Interactions
if(protocolFee > 0) SafeERC20.safeTransferFrom(IERC20(principal), depositor, dao, protocolFee);
SafeERC20.safeTransferFrom(IERC20(principal), depositor, underwritingPool, amount - protocolFee);
}
/**
* @notice Create a bond by depositing `amount` of `principal`.
* Principal will be transferred from `depositor` using `permit`.
* Note that not all ERC20s have a permit function, in which case this function will revert.
* @param amount Amount of principal to deposit.
* @param minAmountOut The minimum [**SOLACE**](./../SOLACE) out.
* @param depositor The bond recipient, default msg.sender.
* @param stake True to stake, false to not stake.
* @param deadline Time the transaction must go through before.
* @param v secp256k1 signature
* @param r secp256k1 signature
* @param s secp256k1 signature
* @return payout The amount of [**SOLACE**](./../SOLACE) in the bond.
* @return tokenID The ID of the newly created bond or lock.
*/
function depositSigned(
uint256 amount,
uint256 minAmountOut,
address depositor,
bool stake,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external override nonReentrant returns (uint256 payout, uint256 tokenID) {
// permit
require(isPermittable, "principal does not support permit");
IERC20Permit(address(principal)).permit(depositor, address(this), amount, deadline, v, r, s);
// accounting
uint256 protocolFee;
(payout, tokenID, protocolFee) = _deposit(amount, minAmountOut, depositor, stake);
// route principal - put last as Checks-Effects-Interactions
if(protocolFee > 0) SafeERC20.safeTransferFrom(IERC20(principal), depositor, dao, protocolFee);
SafeERC20.safeTransferFrom(IERC20(principal), depositor, underwritingPool, amount - protocolFee);
}
/**
* @notice Claim payout for a bond that the user holds.
* User calling `claimPayout()`` must be either the owner or approved for the entered bondID.
* @param bondID The ID of the bond to redeem.
*/
function claimPayout(uint256 bondID) external override nonReentrant tokenMustExist(bondID) {
// checks
require(_isApprovedOrOwner(msg.sender, bondID), "!bonder");
// Payout as per vesting terms
Bond memory bond = bonds[bondID];
uint256 eligiblePayout = _calculateEligiblePayout(bondID);
bonds[bondID].payoutAlreadyClaimed += eligiblePayout;
// Burn bond if vesting completed
// solhint-disable-next-line not-rely-on-time
if (block.timestamp > bond.vestingStart + bond.localVestingTerm) {
_burn(bondID);
delete bonds[bondID];
}
emit RedeemBond(bondID, msg.sender, eligiblePayout);
// Place SafeERC20.safeTransfer last as per Checks-Effects-Interactions
SafeERC20.safeTransfer(IERC20(solace), msg.sender, eligiblePayout);
}
/***************************************
HELPER FUNCTIONS
***************************************/
/**
* @notice Create a bond by depositing `amount` of `principal`.
* @param amount Amount of principal to deposit.
* @param minAmountOut The minimum [**SOLACE**](./../SOLACE) out.
* @param depositor The bond recipient, default msg.sender.
* @param stake True to stake, false to not stake.
* @return payout The amount of [**SOLACE**](./../SOLACE) in the bond.
* @return tokenID The ID of the newly created bond or lock.
* @return protocolFee Amount of principal paid to dao
*/
function _deposit(
uint256 amount,
uint256 minAmountOut,
address depositor,
bool stake
) internal returns (uint256 payout, uint256 tokenID, uint256 protocolFee) {
require(depositor != address(0), "invalid address");
require(!paused, "cannot deposit while paused");
require(termsSet, "not initialized");
// solhint-disable-next-line not-rely-on-time
require(block.timestamp >= uint256(startTime), "bond not yet started");
// solhint-disable-next-line not-rely-on-time
require(block.timestamp <= uint256(endTime), "bond concluded");
payout = _calculateTotalPayout(amount);
// ensure there is remaining capacity for bond
if (capacityIsPayout) {
// capacity in payout terms
uint256 cap = capacity;
require(cap >= payout, "bond at capacity");
capacity = cap - payout;
} else {
// capacity in principal terms
uint256 cap = capacity;
require(cap >= amount, "bond at capacity");
capacity = cap - amount;
}
require(payout <= maxPayout, "bond too large");
require(minAmountOut <= payout, "slippage protection");
// route solace
IBondDepository(bondDepo).pullSolace(payout);
// optionally stake
if(stake) {
// solhint-disable-next-line not-rely-on-time
tokenID = IxsLocker(xsLocker).createLock(depositor, payout, block.timestamp+globalVestingTerm);
} else {
// record bond info
tokenID = ++numBonds;
// solhint-disable-next-line not-rely-on-time
uint40 vestingStart = toUint40(block.timestamp);
uint40 vestingTerm = globalVestingTerm;
bonds[tokenID] = Bond({
payoutAmount: payout,
payoutAlreadyClaimed: 0,
principalPaid: amount,
vestingStart: vestingStart,
localVestingTerm: vestingTerm
});
_mint(depositor, tokenID);
emit CreateBond(tokenID, amount, payout, vestingStart, vestingTerm);
}
protocolFee = amount * protocolFeeBps / MAX_BPS;
return (payout, tokenID, protocolFee);
}
/**
* @notice Calculate the payout in [**SOLACE**](./../SOLACE) and update the current price of a bond.
* @param depositAmount The amount of `principal` to deposit.
* @return amountOut The amount of [**SOLACE**](./../SOLACE) out.
*/
function _calculateTotalPayout(uint256 depositAmount) internal returns (uint256 amountOut) {
// calculate this price
// solhint-disable-next-line not-rely-on-time
uint256 timeSinceLast = block.timestamp - lastPriceUpdate;
uint256 price_ = exponentialDecay(nextPrice, timeSinceLast);
if(price_ < minimumPrice) price_ = minimumPrice;
require(price_ != 0, "invalid price");
// solhint-disable-next-line not-rely-on-time
lastPriceUpdate = block.timestamp;
// calculate amount out
amountOut = (1 ether * depositAmount) / price_; // 1 ether => 1 solace
// update next price
nextPrice = price_ + ( (amountOut * uint256(priceAdjNum)) / uint256(priceAdjDenom));
}
/**
* @notice Calculates current eligible payout on a bond, based on `bond.localVestingTerm` and `bonds[bondID].payoutAlreadyClaimed`.
* @param bondID The ID of the bond to calculate eligible payout on.
* @return eligiblePayout Amount of [**SOLACE**](./../SOLACE) that can be currently claimed for the bond.
*/
function _calculateEligiblePayout(uint256 bondID) internal view returns (uint256 eligiblePayout) {
Bond memory bond = bonds[bondID];
// Sanity check
assert(bond.payoutAlreadyClaimed <= bond.payoutAmount);
// Calculation if still vesting
// solhint-disable-next-line not-rely-on-time
if (block.timestamp <= bond.vestingStart + bond.localVestingTerm) {
// solhint-disable-next-line not-rely-on-time
eligiblePayout = ( ( bond.payoutAmount * ( block.timestamp - bond.vestingStart ) ) / bond.localVestingTerm ) - bond.payoutAlreadyClaimed;
} else {
// Calculation if vesting completed
eligiblePayout = bond.payoutAmount - bond.payoutAlreadyClaimed;
}
}
/**
* @notice Calculates exponential decay.
* @dev Linear approximation, trades precision for speed.
* @param initValue The initial value.
* @param time The time elapsed.
* @return endValue The value at the end.
*/
function exponentialDecay(uint256 initValue, uint256 time) internal view returns (uint256 endValue) {
endValue = initValue >> (time / halfLife);
endValue -= endValue * (time % halfLife) / halfLife / 2;
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toUint40(uint256 value) internal pure returns (uint40) {
Erequire(value < 2**40, "SafeCast: value doesn\'t fit in 40 bits");
return uint40(value);
}
/***************************************
GOVERNANCE FUNCTIONS
***************************************/
/**
* @notice Pauses deposits.
* Can only be called by the current [**governor**](/docs/protocol/governance).
*/
function pause() external override onlyGovernance {
paused = true;
emit Paused();
}
/**
* @notice Unpauses deposits.
* Can only be called by the current [**governor**](/docs/protocol/governance).
*/
function unpause() external override onlyGovernance {
paused = false;
emit Unpaused();
}
struct Terms {
uint256 startPrice; // The starting price, measured in `principal` for one [**SOLACE**](./../SOLACE).
uint256 minimumPrice; // The minimum price of a bond, measured in `principal` for one [**SOLACE**](./../SOLACE).
uint256 maxPayout; // The maximum [**SOLACE**](./../SOLACE) that can be sold in a single bond.
uint128 priceAdjNum; // Used to calculate price increase after bond purchase.
uint128 priceAdjDenom; // Used to calculate price increase after bond purchase.
uint256 capacity; // The amount still sellable.
bool capacityIsPayout; // True if `capacity_` is measured in [**SOLACE**](./../SOLACE), false if measured in `principal`.
uint40 startTime; // The time that purchases start.
uint40 endTime; // The time that purchases end.
uint40 globalVestingTerm; // The duration that users must wait to redeem bonds.
uint40 halfLife; // Used to calculate price decay.
}
/**
* @notice Sets the bond terms.
* Can only be called by the current [**governor**](/docs/protocol/governance).
* @param terms The terms of the bond.
*/
function setTerms(Terms calldata terms) external onlyGovernance {
require(terms.startPrice > 0, "invalid price");
nextPrice = terms.startPrice;
minimumPrice = terms.minimumPrice;
maxPayout = terms.maxPayout;
require(terms.priceAdjDenom != 0, "1/0");
priceAdjNum = terms.priceAdjNum;
priceAdjDenom = terms.priceAdjDenom;
capacity = terms.capacity;
capacityIsPayout = terms.capacityIsPayout;
require(terms.startTime <= terms.endTime, "invalid dates");
startTime = terms.startTime;
endTime = terms.endTime;
globalVestingTerm = terms.globalVestingTerm;
require(terms.halfLife > 0, "invalid halflife");
halfLife = terms.halfLife;
termsSet = true;
// solhint-disable-next-line not-rely-on-time
lastPriceUpdate = block.timestamp;
emit TermsSet();
}
/**
* @notice Sets the bond fees.
* @param protocolFee The fraction of `principal` that will be sent to the dao measured in BPS.
*/
function setFees(uint256 protocolFee) external onlyGovernance {
require(protocolFee <= MAX_BPS, "invalid protocol fee");
protocolFeeBps = protocolFee;
emit FeesSet();
}
/**
* @notice Sets the addresses to call out.
* Can only be called by the current [**governor**](/docs/protocol/governance).
* @param solace_ The [**SOLACE**](./../SOLACE) token.
* @param xsLocker_ The [**xsLocker**](./../staking/xsLocker) contract.
* @param pool_ The underwriting pool.
* @param dao_ The DAO.
* @param principal_ The ERC20 token that users deposit.
* @param isPermittable_ True if `principal` supports `EIP2612`.
* @param bondDepo_ The bond depository.
*/
function setAddresses(
address solace_,
address xsLocker_,
address pool_,
address dao_,
address principal_,
bool isPermittable_,
address bondDepo_
) external override onlyGovernance {
_setAddresses(solace_, xsLocker_, pool_, dao_, principal_, isPermittable_, bondDepo_);
}
/**
* @notice Sets the addresses to call out.
* Can only be called by the current [**governor**](/docs/protocol/governance).
* @param solace_ The [**SOLACE**](./../SOLACE) token.
* @param xsLocker_ The [**xsLocker**](./../staking/xsLocker) contract.
* @param pool_ The underwriting pool.
* @param dao_ The DAO.
* @param principal_ The ERC20 token that users deposit.
* @param isPermittable_ True if `principal` supports `EIP2612`.
* @param bondDepo_ The bond depository.
*/
function _setAddresses(
address solace_,
address xsLocker_,
address pool_,
address dao_,
address principal_,
bool isPermittable_,
address bondDepo_
) internal {
require(solace_ != address(0x0), "zero address solace");
require(xsLocker_ != address(0x0), "zero address xslocker");
require(pool_ != address(0x0), "zero address pool");
require(dao_ != address(0x0), "zero address dao");
require(principal_ != address(0x0), "zero address principal");
require(bondDepo_ != address(0x0), "zero address bond depo");
solace = solace_;
xsLocker = xsLocker_;
IERC20(solace).approve(xsLocker_, type(uint256).max);
underwritingPool = pool_;
dao = dao_;
principal = principal_;
isPermittable = isPermittable_;
bondDepo = bondDepo_;
emit AddressesSet();
}
}
|