Skip to content

Commit f1b1071

Browse files
bweickrichardliang
authored andcommitted
SingularityNet migrator that burns unmigrated tokens, thus zeroing ou… (#113)
SingularityNet migrator that burns unmigrated tokens, thus zeroing out position.
1 parent 2741184 commit f1b1071

File tree

8 files changed

+441
-1
lines changed

8 files changed

+441
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
Copyright 2021 Set Labs Inc.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
16+
SPDX-License-Identifier: Apache License, Version 2.0
17+
*/
18+
19+
pragma solidity 0.6.10;
20+
21+
/**
22+
* @title AGIMigrationAdapter
23+
* @author Set Protocol
24+
*
25+
* "Migration" adapter that burns the AGI tokens currently in the Set in order to remove them
26+
* from the Set's positions. The AGI token was permanently paused after migration however it still
27+
* remains as a position in Sets that hold it. By calling the burn function we can zero out a
28+
* Set's position and remove it from tracking.
29+
*/
30+
contract AGIMigrationWrapAdapter {
31+
32+
/* ============ State Variables ============ */
33+
34+
address public immutable agiLegacyToken;
35+
address public immutable agixToken;
36+
37+
/* ============ Constructor ============ */
38+
39+
/**
40+
* Set state variables
41+
*
42+
* @param _agiLegacyToken Address of AGI Legacy token
43+
* @param _agixToken Address of AGIX token
44+
*/
45+
constructor(
46+
address _agiLegacyToken,
47+
address _agixToken
48+
)
49+
public
50+
{
51+
agiLegacyToken = _agiLegacyToken;
52+
agixToken = _agixToken;
53+
}
54+
55+
/* ============ External Getter Functions ============ */
56+
57+
/**
58+
* Generates the calldata to burn AGI. Requires underlying to be AGI address and wrapped
59+
* token to be AGIX address.
60+
*
61+
* @param _underlyingToken Address of the component to be wrapped
62+
* @param _wrappedToken Address of the wrapped component
63+
* @param _underlyingUnits Total quantity of underlying units to wrap
64+
*
65+
* @return address Target contract address
66+
* @return uint256 Total quantity of underlying units (if underlying is ETH)
67+
* @return bytes Wrap calldata
68+
*/
69+
function getWrapCallData(
70+
address _underlyingToken,
71+
address _wrappedToken,
72+
uint256 _underlyingUnits
73+
)
74+
external
75+
view
76+
returns (address, uint256, bytes memory)
77+
{
78+
require(_underlyingToken == agiLegacyToken, "Must be AGI token");
79+
require(_wrappedToken == agixToken, "Must be AGIX token");
80+
81+
// burn(uint256 value)
82+
bytes memory callData = abi.encodeWithSignature("burn(uint256)", _underlyingUnits);
83+
84+
return (agiLegacyToken, 0, callData);
85+
}
86+
87+
/**
88+
* This function will revert, since burn cannot be reversed.
89+
*/
90+
function getUnwrapCallData(
91+
address /* _underlyingToken */,
92+
address /* _wrappedToken */,
93+
uint256 /* _wrappedTokenUnits */
94+
)
95+
external
96+
pure
97+
returns (address, uint256, bytes memory)
98+
{
99+
revert("AGI burn cannot be reversed");
100+
}
101+
102+
/**
103+
* Returns the address to approve source tokens for wrapping.
104+
*
105+
* @return address Address of the contract to approve tokens to
106+
*/
107+
function getSpenderAddress(address /* _underlyingToken */, address /* _wrappedToken */) external view returns(address) {
108+
return agiLegacyToken;
109+
}
110+
}

external/abi/singularityNET/singularityNetToken.json

+5
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import "module-alias/register";
2+
import { BigNumber } from "@ethersproject/bignumber";
3+
4+
import { Address } from "@utils/types";
5+
import { Account } from "@utils/test/types";
6+
import { ADDRESS_ZERO, ZERO } from "@utils/constants";
7+
import {
8+
AGIMigrationWrapAdapter,
9+
SetToken,
10+
SingularityNetToken,
11+
StandardTokenMock,
12+
WrapModule
13+
} from "@utils/contracts";
14+
import DeployHelper from "@utils/deploys";
15+
import {
16+
ether,
17+
} from "@utils/index";
18+
import {
19+
addSnapshotBeforeRestoreAfterEach,
20+
getAccounts,
21+
getWaffleExpect,
22+
getSystemFixture,
23+
} from "@utils/test/index";
24+
import { SystemFixture } from "@utils/fixtures";
25+
26+
const expect = getWaffleExpect();
27+
28+
describe("AGIMigrationWrapModule", () => {
29+
let owner: Account;
30+
let deployer: DeployHelper;
31+
let setup: SystemFixture;
32+
33+
let wrapModule: WrapModule;
34+
let agiMigrationWrapAdapter: AGIMigrationWrapAdapter;
35+
let agiToken: SingularityNetToken;
36+
let agixToken: StandardTokenMock;
37+
38+
const agiMigrationWrapAdapterIntegrationName: string = "AGI_MIGRATION_WRAPPER";
39+
40+
before(async () => {
41+
[
42+
owner,
43+
] = await getAccounts();
44+
45+
// System setup
46+
deployer = new DeployHelper(owner.wallet);
47+
setup = getSystemFixture(owner.address);
48+
await setup.initialize();
49+
50+
// WrapModule setup
51+
wrapModule = await deployer.modules.deployWrapModule(setup.controller.address, setup.weth.address);
52+
await setup.controller.addModule(wrapModule.address);
53+
54+
// Deploy AGI and AGIX token
55+
agiToken = await deployer.external.deploySingularityNetToken();
56+
agixToken = await deployer.mocks.deployTokenMock(owner.address);
57+
58+
// AaveMigrationWrapAdapter setup
59+
agiMigrationWrapAdapter = await deployer.adapters.deployAGIMigrationWrapAdapter(
60+
agiToken.address,
61+
agixToken.address
62+
);
63+
64+
await setup.integrationRegistry.addIntegration(wrapModule.address, agiMigrationWrapAdapterIntegrationName, agiMigrationWrapAdapter.address);
65+
});
66+
67+
addSnapshotBeforeRestoreAfterEach();
68+
69+
context("when a SetToken has been deployed and issued", async () => {
70+
let setToken: SetToken;
71+
let setTokensIssued: BigNumber;
72+
73+
before(async () => {
74+
setToken = await setup.createSetToken(
75+
[agiToken.address],
76+
[BigNumber.from(10 ** 8)],
77+
[setup.issuanceModule.address, wrapModule.address]
78+
);
79+
80+
// Initialize modules
81+
await setup.issuanceModule.initialize(setToken.address, ADDRESS_ZERO);
82+
await wrapModule.initialize(setToken.address);
83+
84+
// Issue some Sets
85+
setTokensIssued = ether(10);
86+
const underlyingRequired = setTokensIssued.div(10 ** 10);
87+
await agiToken.approve(setup.issuanceModule.address, underlyingRequired);
88+
89+
await setup.issuanceModule.issue(setToken.address, setTokensIssued, owner.address);
90+
});
91+
92+
describe("#wrap", async () => {
93+
let subjectSetToken: Address;
94+
let subjectUnderlyingToken: Address;
95+
let subjectWrappedToken: Address;
96+
let subjectUnderlyingUnits: BigNumber;
97+
let subjectIntegrationName: string;
98+
let subjectCaller: Account;
99+
100+
beforeEach(async () => {
101+
subjectSetToken = setToken.address;
102+
subjectUnderlyingToken = agiToken.address;
103+
subjectWrappedToken = agixToken.address;
104+
subjectUnderlyingUnits = BigNumber.from(10 ** 8);
105+
subjectIntegrationName = agiMigrationWrapAdapterIntegrationName;
106+
subjectCaller = owner;
107+
});
108+
109+
async function subject(): Promise<any> {
110+
return wrapModule.connect(subjectCaller.wallet).wrap(
111+
subjectSetToken,
112+
subjectUnderlyingToken,
113+
subjectWrappedToken,
114+
subjectUnderlyingUnits,
115+
subjectIntegrationName,
116+
);
117+
}
118+
119+
it("should reduce the zero out the AGI unit and remove token from components", async () => {
120+
const previousUnderlyingBalance = await agiToken.balanceOf(setToken.address);
121+
122+
await subject();
123+
124+
const underlyingBalance = await agiToken.balanceOf(setToken.address);
125+
const agiTokenUnit = await setToken.getDefaultPositionRealUnit(agiToken.address);
126+
const agxTokenUnit = await setToken.getDefaultPositionRealUnit(agixToken.address);
127+
const components = await setToken.getComponents();
128+
129+
const expectedUnderlyingBalance = previousUnderlyingBalance.sub(setTokensIssued.div(10 ** 10));
130+
expect(underlyingBalance).to.eq(expectedUnderlyingBalance);
131+
expect(agiTokenUnit).to.eq(ZERO);
132+
expect(agxTokenUnit).to.eq(ZERO);
133+
expect(components.length).to.eq(ZERO);
134+
});
135+
});
136+
});
137+
});

0 commit comments

Comments
 (0)