Skip to content
This repository has been archived by the owner on Dec 20, 2022. It is now read-only.

v1-core migration changes #54

Open
wants to merge 51 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
efc1c99
wip: pools not found
0xdewy Apr 12, 2022
56ca840
check when sensitive to slippage
georgercarder Apr 12, 2022
12c12ec
consider slippage
georgercarder Apr 12, 2022
c95cc6c
consolidate changes to MigrationController
georgercarder Apr 13, 2022
67e1503
update ETH2X test
georgercarder Apr 13, 2022
4a11546
until v1 branch is merged, package.json depends on v1 branch
georgercarder Apr 13, 2022
373fdf9
wip: update tests to live migration contracts
0xdewy Apr 14, 2022
a53dde9
lint
0xdewy Apr 14, 2022
513a448
get holders with balance
0xdewy Apr 14, 2022
b6287e3
feat: get erc20 holders script
0xdewy Apr 15, 2022
32e41c0
feat: add balance to impersonated accounts. wip: fix tests
0xdewy Apr 15, 2022
881d487
wip: debugging
0xdewy Apr 15, 2022
7ab62c7
wip: debugging
0xdewy Apr 15, 2022
4dc5da5
snapshot
georgercarder Apr 15, 2022
21567b9
bugfix: enum casting bug
georgercarder Apr 16, 2022
383979b
update BTC2X createStrategy
georgercarder Apr 16, 2022
ba3da6d
IMigrationController must have oracle and whitelist
georgercarder Apr 16, 2022
d9c6e75
migrateAll with Leverage2XAdapter function invocation successful. TOD…
georgercarder Apr 16, 2022
78efb61
BTC2X with Leverage2XAdapter works ONLY when first aave borrow is 50p…
georgercarder Apr 16, 2022
5bb6474
wip: migrate all
0xdewy Apr 18, 2022
bf4febe
update ETH2X test to use liqmigV2 and leverageAdapter2X
georgercarder Apr 18, 2022
d31ee13
gas used for migrateAll
georgercarder Apr 18, 2022
ab7ab45
update constants
georgercarder Apr 18, 2022
bb5e28c
spelling
georgercarder Apr 18, 2022
cb2b18d
del fixme
georgercarder Apr 18, 2022
64cb198
del space changes to be consistent with deployed contracts.
georgercarder Apr 18, 2022
f675b13
remove approval and router after deposit
georgercarder Apr 18, 2022
e1dca5a
Merge pull request #51 from EnsoFinance/george/slippage_check_in_migrate
PeterMPhillips Apr 18, 2022
c8d6fed
Merge branch 'contract_fixes' into george/test_leverage2XAdapter_update
georgercarder Apr 18, 2022
c92abcf
package.json uses branch of v1
georgercarder Apr 18, 2022
6166800
update ts import
georgercarder Apr 18, 2022
04edabd
fix leveraged token tests
PeterMPhillips Apr 19, 2022
3392741
script to get current stakes to migrate
0xdewy Apr 19, 2022
df8cf05
update package.json
PeterMPhillips Apr 19, 2022
349d759
Merge pull request #52 from EnsoFinance/george/test_leverage2XAdapter…
PeterMPhillips Apr 19, 2022
6b9afa1
merge
0xdewy Apr 19, 2022
22c07ee
wip: migrate all tokens
0xdewy Apr 19, 2022
12cfd1c
fix: piedao estimates. update blocknumber
0xdewy Apr 19, 2022
9cf3c98
cleanup messages
0xdewy Apr 19, 2022
851c389
integration fixes
PeterMPhillips Apr 20, 2022
5265be2
deploy migration controller
PeterMPhillips Apr 20, 2022
9fd18db
wip: deploy all strategies script
0xdewy Apr 20, 2022
bd5cf16
wip: wait for transaction
0xdewy Apr 20, 2022
c071f46
output deployed strategies + testing scripts
PeterMPhillips Apr 21, 2022
f754303
update token positions
PeterMPhillips Apr 21, 2022
f018d61
MigrationController fix
PeterMPhillips Apr 21, 2022
93fbe80
update token positions
PeterMPhillips Apr 22, 2022
2c5d17b
mainnet strategies
PeterMPhillips Apr 22, 2022
10119d0
setStrategy tx builder
PeterMPhillips Apr 22, 2022
e388b7c
migrateAll builder
PeterMPhillips Apr 22, 2022
df9653d
batching
PeterMPhillips Apr 22, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
2 changes: 1 addition & 1 deletion .solhint.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"extends": "solhint:recommended",
"plugins": ["prettier"],
"plugins": [],
"rules": {
"prettier/prettier": "error",
"compiler-version": ["error", "^0.8.0"],
Expand Down
54 changes: 42 additions & 12 deletions contracts/migration/MigrationController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma experimental ABIEncoderV2;

import "@ensofinance/v1-core/contracts/StrategyControllerStorage.sol";
import "@ensofinance/v1-core/contracts/interfaces/IOracle.sol";
import "@ensofinance/v1-core/contracts/interfaces/IStrategyController.sol";
import "@ensofinance/v1-core/contracts/interfaces/registries/ITokenRegistry.sol";
import "@ensofinance/v1-core/contracts/helpers/StrategyTypes.sol";
import "./libraries/SignedSafeMath.sol";
Expand All @@ -21,12 +22,15 @@ contract MigrationController is IMigrationController, StrategyTypes, StrategyCon
address internal immutable _liquidityMigration;
address internal immutable _ensoManager;

address private constant ETH2X = 0xAa6E8127831c9DE45ae56bB1b0d4D4Da6e5665BD;
address private constant BTC2X = 0x0B498ff89709d3838a063f1dFA463091F9801c2b;

event Withdraw(address indexed strategy, address indexed account, uint256 value, uint256 amount);
event Deposit(address indexed strategy, address indexed account, uint256 value, uint256 amount);
event Balanced(address indexed strategy, uint256 total);
event Balanced(address indexed strategy, uint256 totalBefore, uint256 totalAfter);
event NewStructure(address indexed strategy, StrategyItem[] items, bool indexed finalized);
event NewValue(address indexed strategy, TimelockCategory category, uint256 newValue, bool indexed finalized);
event StrategyOpen(address indexed strategy, uint256 performanceFee);
event StrategyOpen(address indexed strategy);
event StrategySet(address indexed strategy);

constructor(
Expand All @@ -50,12 +54,31 @@ contract MigrationController is IMigrationController, StrategyTypes, StrategyCon
require(strategy.totalSupply() == 0, "Strategy cannot be migrated to");
require(amount > 0, "No amount");
require(lpToken.balanceOf(address(genericRouter)) >= amount, "Wrong balance"); // Funds should have been sent to GenericRouter
bool sensitiveToSlippage = _sensitiveToSlippage(address(lpToken));
int256 estimateBefore = (sensitiveToSlippage) ? oracle().estimateItem(amount, address(lpToken)) : 0;
if (strategy.supportsDebt()) {
// approve and set ONLY for the deposit
IStrategy(strategy).approveDebt(IStrategy(strategy).debt(), address(genericRouter), uint256(-1));
IStrategy(strategy).setRouter(address(genericRouter));
}
bytes memory migrationData = abi.encode(
adapter.encodeMigration(address(genericRouter), address(strategy), address(lpToken), amount)
);
genericRouter.deposit(address(strategy), migrationData);
// At this point the underlying tokens should be in the Strategy. Estimate strategy value
if (strategy.supportsDebt()) {
// remove approval and router after deposit
IStrategy(strategy).approveDebt(IStrategy(strategy).debt(), address(genericRouter), 0);
IStrategy(strategy).setRouter(address(0));
}
(uint256 total, ) = oracle().estimateStrategy(strategy);
if (sensitiveToSlippage) {
int256 slippage = int256(_strategyStates[address(strategy)].restructureSlippage);
require(
int256(total) >= estimateBefore.mul(slippage).div(int256(DIVISOR)),
"migrate: value lost to slippage."
);
}
// Migration is a one-time function and cannot be called unless Strategy's total
// supply is zero. So we can trust that `amount` will be the new total supply
strategy.updateTokenValue(total, amount);
Expand Down Expand Up @@ -98,9 +121,10 @@ contract MigrationController is IMigrationController, StrategyTypes, StrategyCon
require(percentage >= 0, "Token cannot be negative");
require(percentage <= PERCENTAGE_BOUND, "Out of bounds");
}
EstimatorCategory category = EstimatorCategory(registry.estimatorCategories(item));
require(category != EstimatorCategory.BLOCKED, "Token blocked");
if (category == EstimatorCategory.STRATEGY) _checkCyclicDependency(strategy, IStrategy(item), registry);
uint256 category = registry.estimatorCategories(item);
require(category != uint256(EstimatorCategory.BLOCKED), "Token blocked");
if (category == uint256(EstimatorCategory.STRATEGY))
_checkCyclicDependency(strategy, IStrategy(item), registry);
total = total.add(percentage);
}
require(total == int256(DIVISOR), "Total percentage wrong");
Expand All @@ -111,11 +135,16 @@ contract MigrationController is IMigrationController, StrategyTypes, StrategyCon
return _initialized[strategy] > 0;
}

function oracle() public view returns (IOracle) {
function oracle() public view override returns (IOracle) {
return IOracle(_oracle);
}

function whitelist() external view override returns (IWhitelist) {
return IWhitelist(_whitelist);
}

function _setInitialState(address strategy, InitialState memory state) private {
_checkAndEmit(strategy, TimelockCategory.PERFORMANCE, uint256(state.performanceFee), true);
_checkAndEmit(strategy, TimelockCategory.THRESHOLD, uint256(state.rebalanceThreshold), true);
_checkAndEmit(strategy, TimelockCategory.REBALANCE_SLIPPAGE, uint256(state.rebalanceSlippage), true);
_checkAndEmit(strategy, TimelockCategory.RESTRUCTURE_SLIPPAGE, uint256(state.restructureSlippage), true);
Expand All @@ -127,12 +156,9 @@ contract MigrationController is IMigrationController, StrategyTypes, StrategyCon
state.social,
state.set
);
IStrategy(strategy).updatePerformanceFee(state.performanceFee);
IStrategy(strategy).updateRebalanceThreshold(state.rebalanceThreshold);
if (state.social) {
_checkDivisor(uint256(state.performanceFee));
IStrategy(strategy).updatePerformanceFee(state.performanceFee);
emit StrategyOpen(strategy, state.performanceFee);
}
if (state.social) emit StrategyOpen(strategy);
if (state.set) emit StrategySet(strategy);
emit NewValue(strategy, TimelockCategory.TIMELOCK, uint256(state.timelock), true);
}
Expand All @@ -146,7 +172,7 @@ contract MigrationController is IMigrationController, StrategyTypes, StrategyCon
require(!strategy.supportsSynths(), "Synths not supported");
address[] memory strategyItems = strategy.items();
for (uint256 i = 0; i < strategyItems.length; i++) {
if (EstimatorCategory(registry.estimatorCategories(strategyItems[i])) == EstimatorCategory.STRATEGY)
if (registry.estimatorCategories(strategyItems[i]) == uint256(EstimatorCategory.STRATEGY))
_checkCyclicDependency(test, IStrategy(strategyItems[i]), registry);
}
}
Expand All @@ -172,4 +198,8 @@ contract MigrationController is IMigrationController, StrategyTypes, StrategyCon
function _removeStrategyLock(IStrategy strategy) private {
strategy.unlock();
}

function _sensitiveToSlippage(address token) private pure returns (bool) {
return token == ETH2X || token == BTC2X;
}
}
6 changes: 6 additions & 0 deletions contracts/migration/interfaces/IMigrationController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ pragma solidity >=0.6.0 <0.9.0;

import "@ensofinance/v1-core/contracts/interfaces/IStrategy.sol";
import "@ensofinance/v1-core/contracts/interfaces/IStrategyRouter.sol";
import "@ensofinance/v1-core/contracts/interfaces/IOracle.sol";
import "@ensofinance/v1-core/contracts/interfaces/IWhitelist.sol";
import "../../interfaces/IAdapter.sol";
import "../libraries/SafeERC20Transfer.sol";

Expand All @@ -16,4 +18,8 @@ interface IMigrationController {
) external;

function initialized(address strategy) external view returns (bool);

function oracle() external view returns (IOracle);

function whitelist() external view returns (IWhitelist);
}
31 changes: 31 additions & 0 deletions contracts/test/Multicall.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.6.0 <0.9.0;
pragma experimental ABIEncoderV2;

/// @title Multicall - Aggregate internal calls + show revert message
contract Multicall {
struct Call {
address target;
bytes callData;
}

/**
* @notice Aggregate calls and return a list of the return data
*/
function aggregate(Call[] memory calls) internal returns (bytes[] memory returnData) {
returnData = new bytes[](calls.length);
for (uint256 i = 0; i < calls.length; i++) {
Call memory internalTx = calls[i];
(bool success, bytes memory ret) = internalTx.target.call(internalTx.callData);
if (!success) {
assembly {
let ptr := mload(0x40)
let size := returndatasize()
returndatacopy(ptr, 0, size)
revert(ptr, size)
}
}
returnData[i] = ret;
}
}
}
105 changes: 105 additions & 0 deletions contracts/test/MulticallRouter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
//SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

import "@ensofinance/v1-core/contracts/libraries/SafeERC20.sol";
import "./Multicall.sol";
import "@ensofinance/v1-core/contracts/routers/StrategyRouter.sol";

/**
* @notice An experimental contract to allow for flexible trading strategies by aggregating calldata to accomplish a rebalance
*/
contract MulticallRouter is StrategyRouter, Multicall {
using SafeERC20 for IERC20;

/**
* @notice Setup StrategyRouter
*/
constructor(address controller_) public StrategyRouter(RouterCategory.GENERIC, controller_) {}

/**
* @notice Executes provided calldata to achieve a deposit for the Strategy
*/
// Receive call from controller
function deposit(address, bytes memory data) external override onlyController {
Call[] memory callStructs = abi.decode(data, (Call[]));
aggregate(callStructs);
}

function withdraw(address, bytes calldata data) external override onlyController {
Call[] memory callStructs = abi.decode(data, (Call[]));
aggregate(callStructs);
}

/**
* @notice Executes provided calldata to achieve a rebalance for the Strategy
*/
// Receive call from controller
function rebalance(address, bytes memory data) external override onlyController {
Call[] memory callStructs = abi.decode(data, (Call[]));
aggregate(callStructs);
}

function restructure(address, bytes memory data) external override onlyController {
Call[] memory callStructs = abi.decode(data, (Call[]));
aggregate(callStructs);
}

/**
* @notice Helper function to encode typed struct into bytes
*/
function encodeCalls(Call[] calldata calls) external pure returns (bytes memory data) {
data = abi.encode(calls);
}

/**
* @notice Uses delegate call to swap tokens
* @dev Delegate call to avoid redundant token transfers
*/
function delegateSwap(
address adapter,
uint256 amount,
uint256 expected,
address tokenIn,
address tokenOut,
address from,
address to
) public {
_onlyInternal();
_delegateSwap(adapter, amount, expected, tokenIn, tokenOut, from, to);
}

function settleSwap(
address adapter,
address tokenIn,
address tokenOut,
address from,
address to
) public {
_onlyInternal();
uint256 amount = IERC20(tokenIn).balanceOf(from);
if (amount > 0) _delegateSwap(adapter, amount, 0, tokenIn, tokenOut, from, to);
}

function settleTransfer(address token, address to) public {
_onlyInternal();
IERC20 erc20 = IERC20(token);
uint256 amount = erc20.balanceOf(address(this));
if (amount > 0) erc20.safeTransfer(to, amount);
}

function settleTransferFrom(
address token,
address from,
address to
) public {
_onlyInternal();
IERC20 erc20 = IERC20(token);
uint256 amount = erc20.balanceOf(from);
if (amount > 0) erc20.safeTransferFrom(from, to, amount);
}

function _onlyInternal() internal view {
require(msg.sender == address(this), "Only internal");
}
}
9 changes: 6 additions & 3 deletions deployments.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"Claimable": "0xE39d9712F3749b9663627331d9BCa15Ac69b6170",
"LiquidityMigrationV2": "0x0c6D898ac945E493D25751Ea43BE2c8Beb881D8C",
"MigrationAdapter": "0xAf8d0523660A94494c56d5445e7BfE8C61802B59",
"MigrationCoordinator": "0x6CD0df59370B38261E251Bd786B2f320595005d1"
"MigrationCoordinator": "0x6CD0df59370B38261E251Bd786B2f320595005d1",
"MigrationControllerImplementation": "0x81c8bE47b09CAF806ac5B757990B0725ff6cefc8"
},
"hardhat": {
"IndexCoopAdapter": "0x9c9326C521895c78414BD3C2945e47AFC4Ef16cc",
Expand All @@ -36,7 +37,8 @@
"Claimable": "0xE39d9712F3749b9663627331d9BCa15Ac69b6170",
"LiquidityMigrationV2": "0x0c6D898ac945E493D25751Ea43BE2c8Beb881D8C",
"MigrationAdapter": "0xAf8d0523660A94494c56d5445e7BfE8C61802B59",
"MigrationCoordinator": "0x6CD0df59370B38261E251Bd786B2f320595005d1"
"MigrationCoordinator": "0x6CD0df59370B38261E251Bd786B2f320595005d1",
"MigrationControllerImplementation": "0x81c8bE47b09CAF806ac5B757990B0725ff6cefc8"
},
"mainnet": {
"IndexCoopAdapter": "0x9c9326C521895c78414BD3C2945e47AFC4Ef16cc",
Expand All @@ -50,6 +52,7 @@
"Claimable": "0xE39d9712F3749b9663627331d9BCa15Ac69b6170",
"LiquidityMigrationV2": "0x0c6D898ac945E493D25751Ea43BE2c8Beb881D8C",
"MigrationAdapter": "0xAf8d0523660A94494c56d5445e7BfE8C61802B59",
"MigrationCoordinator": "0x6CD0df59370B38261E251Bd786B2f320595005d1"
"MigrationCoordinator": "0x6CD0df59370B38261E251Bd786B2f320595005d1",
"MigrationControllerImplementation": "0x81c8bE47b09CAF806ac5B757990B0725ff6cefc8"
}
}
2 changes: 1 addition & 1 deletion hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ function getNetworks(): NetworksUserConfig {
if (archiveNode)
networks.hardhat.forking = {
url: archiveNode,
blockNumber: 14034913,
blockNumber: 14636125,
};
}
if (mnemonic && infuraApiKey) {
Expand Down
11 changes: 4 additions & 7 deletions migration-test/6_FullMigrationFork.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ethers, network } from "hardhat";
import { expect } from "chai";
import { BigNumber, Event } from "ethers";
import { BigNumber, Event, constants } from "ethers";
import { Signers } from "../types";
import { AcceptedProtocols, LiquidityMigrationBuilder } from "../src/liquiditymigration";
import { IERC20__factory } from "../typechain";
Expand All @@ -11,6 +11,8 @@ import { EnsoBuilder, InitialState, StrategyItem, ITEM_CATEGORY, ESTIMATOR_CATEG
import { WETH, SUSD } from "../src/constants";
import { setupStrategyItems, getBlockTime } from "../src/utils";
import deployments from "../deployments.json";
import { impersonateWithEth } from "../src/mainnet";
const { WeiPerEther } = constants;

const migrator = "0x007A8CFf81A9FCca63E8a05Acb41A8292F4b353e";

Expand All @@ -22,12 +24,7 @@ describe("MigrationCoordinator tests: ", function () {
const allSigners = await ethers.getSigners();
signers.default = allSigners[0];
signers.secondary = allSigners[1];

await network.provider.request({
method: "hardhat_impersonateAccount",
params: [migrator],
});
signers.admin = await ethers.getSigner(migrator);
signers.admin = await impersonateWithEth(migrator, WeiPerEther.mul(10));

const LiquidityMigration = await ethers.getContractFactory("LiquidityMigration");
liquidityMigration = LiquidityMigration.attach(deployments.mainnet.LiquidityMigration);
Expand Down
25 changes: 25 additions & 0 deletions out/deployed_strategies.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"0x1494CA1F11D487c2bBe4543E90080AeBa4BA3C2b": "0x890ed1Ee6d435a35d51081ded97Ff7CE53Be5942",
"0x2aF1dF3AB0ab157e1E2Ad8F88A7D04fbea0c7dc6": "0xAcA8C8927D7bC553AEAF9D1c9eA9C1D8c88da243",
"0x33d63Ba1E57E54779F7dDAeaA7109349344cf5F1": "0x87E7A3CB9549fc88dEA4d9c35A9dd47a2F53AD80",
"0x26607aC599266b21d13c7aCF7942c7701a8b699c": "0xaDD1eDE9b828f23FDa9da724092004c38642b5F1",
"0x9ba60bA98413A60dB4C651D4afE5C937bbD8044B": "0xb41A7a429c73Aa68683da1389051893Fe290F614",
"0x907FeB27f8cc5b003Db7e62dfc2f9B01ce3FADd6": "0x5067C1D15467ad102E7b6fA4ab824a9B62566fF9",
"0xAa6E8127831c9DE45ae56bB1b0d4D4Da6e5665BD": "0x81CDDBF4A9d21CF52ef49Bda5e5d5C4aE2e40B3e",
"0xD6cb2aDF47655B1bABdDc214d79257348CBC39A7": "0x4BB3b93D8E394CFe0f465A3581AE0b527ae33921",
"0x0f4c00139602AB502Bc7c1c0e71D6CB72A9FB0e7": "0x0CF65Dcf23c3a67D1A220A2732B5c2F7921A30c4",
"0xfDC4a3FC36df16a78edCAf1B837d3ACAaeDB2CB4": "0x75deAA256730Cf43ad6E12E591Be77F22813EBAB",
"0xb4bebD34f6DaaFd808f73De0d10235a92Fbb6c3D": "0xA6A6550CbAf8CCd944f3Dd41F2527d441999238c",
"0x0ac1DBa8252240589266194F9C27a42229E84B19": "0xEA9E30b15248ed3cf71b95C7FfA4c61b8EC77638",
"0xd076b9865feb49A43Aa38c06b0432dF6b6cBCA9E": "0xB48BD102c6e318286cE1A00601ae1971Fb9BEE15",
"0x3A4851597F36F459b58e65C55c8f3a8710313Fc7": "0x86736159120Ca443CeA1424a8335a28cED370215",
"0xe8e8486228753E01Dbc222dA262Aa706Bd67e601": "0x0C0dFf3ACB7278f9d2A8F009d8324bfD61d47114",
"0x9A48BD0EC040ea4f1D3147C025cd4076A2e71e3e": "0x57203696a9bDE98bA56b1e667841e85c2c60ce59",
"0x68bB81B3F67f7AAb5fd1390ECB0B8e1a806F2465": "0x16F7a9c3449F9C67E8c7e8F30ae1ee5D7b8Ed10d",
"0x391603b1C3b03A0133AD82E91692790e58f73570": "0x3a6f78566a29a478269F93EF1600bEefc8f5f36a",
"0x0B498ff89709d3838a063f1dFA463091F9801c2b": "0x2B7bD86a2633146d6A63334b0130e756327141fa",
"0x63aE7457b8Be660DAaf308a07db6bccB733B92Df": "0x7b5dd414E5E80F12cB29589492451F3AA989a4DD",
"0x23687D9d40F9Ecc86E7666DDdB820e700F954526": "0xB572c9a3EB3aDFe72c175D6A6be3aC0051D21372",
"0xBbA8120b355bC70E771F28e151a141A126843CdF": "0x0b1281c92ea713E544ca29D7C68d9c3f5F6d4Ae5",
"0xF059aFA5239eD6463a00FC06a447c14Fe26406e1": "0x33dEB629ea0fADbfF489790e7FA8177c6c5c799e"
}
Loading