Skip to content

Commit c37d8b9

Browse files
authored
Merge pull request #687 from bancorprotocol/reroute-vortex
Updated the vortex to perform BNT -> vBNT trades through v3
2 parents 3ec50f7 + a9490c8 commit c37d8b9

File tree

5 files changed

+120
-24
lines changed

5 files changed

+120
-24
lines changed

components/Contracts.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
LiquidityProtectionStats__factory,
2020
LiquidityProtectionStore__factory,
2121
LiquidityProtectionSystemStore__factory,
22+
MockVortexBurner__factory,
2223
NetworkSettings__factory,
2324
Owned__factory,
2425
StakingRewards__factory,
@@ -156,6 +157,7 @@ const getContracts = (signer?: Signer) => {
156157
LiquidityProtectionSystemStore__factory,
157158
signer
158159
),
160+
MockVortexBurner: deployOrAttach('MockVortexBurner', MockVortexBurner__factory, signer),
159161
NetworkSettings: deployOrAttach('NetworkSettings', NetworkSettings__factory, signer),
160162
Owned: deployOrAttach('Owned', Owned__factory, signer),
161163
StakingRewards: deployOrAttach('StakingRewards', StakingRewards__factory, signer),
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
2+
pragma solidity 0.6.12;
3+
4+
contract MockVortexBurner {
5+
uint256 _total = 1000;
6+
7+
function totalBurnedAmount() external view returns (uint256) {
8+
return _total;
9+
}
10+
}

contracts/helpers/TestBancorNetworkV3.sol

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,24 @@ contract TestBancorNetworkV3 is BancorNetwork {
4242
reserveToken.safeTransferFrom(msg.sender, _bancorVault, availableAmount);
4343
}
4444
}
45+
46+
function tradeBySourceAmount(
47+
IERC20 sourceToken,
48+
IERC20 targetToken,
49+
uint256 sourceAmount,
50+
uint256 minReturnAmount,
51+
uint256 deadline,
52+
address /* beneficiary */
53+
) external payable returns (uint256) {
54+
require(sourceToken != targetToken);
55+
require(sourceAmount > 0);
56+
require(minReturnAmount > 0);
57+
require(deadline >= block.timestamp);
58+
59+
// transfer the source tokens from the caller
60+
sourceToken.safeTransferFrom(msg.sender, address(this), sourceAmount);
61+
62+
// requires that the contract hold sufficient target tokens
63+
targetToken.transfer(msg.sender, sourceAmount * 2);
64+
}
4565
}

contracts/vortex/VortexBurner.sol

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,21 @@ import "../token/ReserveToken.sol";
2020
import "../INetworkSettings.sol";
2121
import "../IBancorNetwork.sol";
2222

23+
interface IVortexBurner {
24+
function totalBurnedAmount() external view returns (uint256);
25+
}
26+
27+
interface IBancorNetworkV3 {
28+
function tradeBySourceAmount(
29+
IERC20 sourceToken,
30+
IERC20 targetToken,
31+
uint256 sourceAmount,
32+
uint256 minReturnAmount,
33+
uint256 deadline,
34+
address beneficiary
35+
) external payable returns (uint256);
36+
}
37+
2338
/**
2439
* @dev This contract provides any user to trigger a network fee burning event
2540
*/
@@ -51,6 +66,9 @@ contract VortexBurner is Owned, Utils, ReentrancyGuard, ContractRegistryClient {
5166
// the address of the governance token security module
5267
ITokenGovernance private immutable _govTokenGovernance;
5368

69+
// the address of the v3 network
70+
IBancorNetworkV3 private immutable _networkV3;
71+
5472
// the percentage of the converted network tokens to be sent to the caller of the burning event (in units of PPM)
5573
uint32 private _burnReward;
5674

@@ -86,16 +104,24 @@ contract VortexBurner is Owned, Utils, ReentrancyGuard, ContractRegistryClient {
86104
constructor(
87105
IERC20 networkToken,
88106
ITokenGovernance govTokenGovernance,
89-
IContractRegistry registry
107+
IContractRegistry registry,
108+
IVortexBurner prevBurner,
109+
IBancorNetworkV3 networkV3
90110
)
91111
public
92112
ContractRegistryClient(registry)
93113
validAddress(address(networkToken))
94114
validAddress(address(govTokenGovernance))
115+
validAddress(address(networkV3))
95116
{
96117
_networkToken = networkToken;
97118
_govTokenGovernance = govTokenGovernance;
98119
_govToken = govTokenGovernance.token();
120+
_networkV3 = networkV3;
121+
122+
if (address(prevBurner) != address(0x0)) {
123+
_totalBurnedAmount = prevBurner.totalBurnedAmount();
124+
}
99125
}
100126

101127
/**
@@ -186,11 +212,11 @@ contract VortexBurner is Owned, Utils, ReentrancyGuard, ContractRegistryClient {
186212

187213
// in case there are network tokens to burn, convert them to the governance token
188214
if (sourceAmount > 0) {
189-
// approve the network to withdraw the network token amount
190-
_networkToken.ensureApprove(address(network), sourceAmount);
215+
// allow BancorNetwork v3 to transfer the BNT from this contract
216+
_networkToken.ensureApprove(address(_networkV3), sourceAmount);
191217

192-
// convert the entire network token amount to the governance token
193-
network.convertByPath(strategy.govPath, sourceAmount, 1, address(this), address(0), 0);
218+
// trade BNT to vBNT on BancorNetwork v3
219+
_networkV3.tradeBySourceAmount(_networkToken, _govToken, sourceAmount, 1, block.timestamp + 100, address(this));
194220
}
195221

196222
// get the governance token balance

test/vortex/VortexBurner.js

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const { expect } = require('chai');
22
const { BigNumber } = require('ethers');
3-
const { ethers } = require('hardhat');
3+
const { ethers, network } = require('hardhat');
44

55
const { NATIVE_TOKEN_ADDRESS, registry, ZERO_ADDRESS } = require('../helpers/Constants');
66
const Contracts = require('../../components/Contracts').default;
@@ -14,6 +14,8 @@ const STANDARD_CONVERTER_WEIGHTS = [500_000, 500_000];
1414
const TOTAL_SUPPLY = BigNumber.from(1_000_000_000).mul(TKN);
1515

1616
let contractRegistry;
17+
let prevBurner;
18+
let networkV3;
1719
let bancorNetwork;
1820
let networkToken;
1921
let govToken;
@@ -44,6 +46,9 @@ describe('VortexBurner', () => {
4446

4547
networkSettings = await Contracts.NetworkSettings.deploy(owner.address, BigNumber.from(0));
4648
await contractRegistry.registerAddress(registry.NETWORK_SETTINGS, networkSettings.address);
49+
50+
prevBurner = await Contracts.MockVortexBurner.deploy();
51+
networkV3 = await Contracts.TestBancorNetworkV3.deploy(contractRegistry.address);
4752
});
4853

4954
beforeEach(async () => {
@@ -65,6 +70,7 @@ describe('VortexBurner', () => {
6570

6671
govToken = await Contracts.DSToken.deploy('vBNT', 'vBNT', 18);
6772
await govToken.issue(owner.address, TOTAL_SUPPLY);
73+
await govToken.transfer(networkV3.address, TOTAL_SUPPLY.div(10));
6874

6975
govTokenGovernance = await Contracts.TestTokenGovernance.deploy(govToken.address);
7076
await govToken.transferOwnership(govTokenGovernance.address);
@@ -75,7 +81,9 @@ describe('VortexBurner', () => {
7581
vortex = await Contracts.VortexBurner.deploy(
7682
networkToken.address,
7783
govTokenGovernance.address,
78-
contractRegistry.address
84+
contractRegistry.address,
85+
ZERO_ADDRESS,
86+
networkV3.address
7987
);
8088

8189
await networkFeeWallet.transferOwnership(vortex.address);
@@ -93,21 +101,59 @@ describe('VortexBurner', () => {
93101
expect(await vortex.totalBurnedAmount()).to.equal(BigNumber.from(0));
94102
});
95103

104+
it('should allow initializing with a previous burner address', async () => {
105+
vortex = await Contracts.VortexBurner.deploy(
106+
networkToken.address,
107+
govTokenGovernance.address,
108+
contractRegistry.address,
109+
prevBurner.address,
110+
networkV3.address
111+
);
112+
113+
expect(await vortex.totalBurnedAmount()).to.equal(BigNumber.from(1000));
114+
});
115+
96116
it('should revert if initialized with an invalid network token address', async () => {
97117
await expect(
98-
Contracts.VortexBurner.deploy(ZERO_ADDRESS, govTokenGovernance.address, contractRegistry.address)
118+
Contracts.VortexBurner.deploy(
119+
ZERO_ADDRESS,
120+
govTokenGovernance.address,
121+
contractRegistry.address,
122+
prevBurner.address,
123+
networkV3.address)
99124
).to.be.revertedWith('ERR_INVALID_ADDRESS');
100125
});
101126

102127
it('should revert if initialized with an invalid governance token governance address', async () => {
103128
await expect(
104-
Contracts.VortexBurner.deploy(networkToken.address, ZERO_ADDRESS, contractRegistry.address)
129+
Contracts.VortexBurner.deploy(
130+
networkToken.address,
131+
ZERO_ADDRESS,
132+
contractRegistry.address,
133+
prevBurner.address,
134+
networkV3.address)
105135
).to.be.revertedWith('ERR_INVALID_ADDRESS');
106136
});
107137

108138
it('should revert if initialized with an invalid contract registry address', async () => {
109139
await expect(
110-
Contracts.VortexBurner.deploy(networkToken.address, govTokenGovernance.address, ZERO_ADDRESS)
140+
Contracts.VortexBurner.deploy(
141+
networkToken.address,
142+
govTokenGovernance.address,
143+
ZERO_ADDRESS,
144+
prevBurner.address,
145+
networkV3.address)
146+
).to.be.revertedWith('ERR_INVALID_ADDRESS');
147+
});
148+
149+
it('should revert if initialized with an invalid contract network v3 address', async () => {
150+
await expect(
151+
Contracts.VortexBurner.deploy(
152+
networkToken.address,
153+
govTokenGovernance.address,
154+
contractRegistry.address,
155+
prevBurner.address,
156+
ZERO_ADDRESS)
111157
).to.be.revertedWith('ERR_INVALID_ADDRESS');
112158
});
113159
});
@@ -139,7 +185,9 @@ describe('VortexBurner', () => {
139185
const newVortex = await Contracts.VortexBurner.deploy(
140186
networkToken.address,
141187
govTokenGovernance.address,
142-
contractRegistry.address
188+
contractRegistry.address,
189+
ZERO_ADDRESS,
190+
networkV3.address
143191
);
144192

145193
await vortex.transferNetworkFeeWalletOwnership(newVortex.address);
@@ -392,19 +440,9 @@ describe('VortexBurner', () => {
392440
netNetworkTokenConversionAmount.sub(burnRewardAmount);
393441
}
394442

395-
// take into account that if one of the source tokens is the governance token -
396-
// we won't be able to use rateByPath explicitly, since it wouldn't take into
397-
// account a previous conversion.
398-
totalBurnedAmount = totalBurnedAmount.add(
399-
await bancorNetwork.rateByPath(
400-
[
401-
networkToken.address,
402-
data.GOV.poolToken.address,
403-
govToken.address
404-
],
405-
netNetworkTokenConversionAmount
406-
)
407-
);
443+
// assuming the BNT -> vBNT trade on Bancor v3 returns double the source amount,
444+
// since a mock network is used
445+
totalBurnedAmount = totalBurnedAmount.add(netNetworkTokenConversionAmount.mul(2));
408446

409447
return {
410448
convertibleTokens,

0 commit comments

Comments
 (0)