Skip to content

Commit

Permalink
Layer2 AA Migration (#406)
Browse files Browse the repository at this point in the history
* chore: AA migration

* chore: small fix

* chore: gas optimization

* chore: refactor for deploy

* chore: fix lint

* chore: fix

* chore: support user migration

* chore: fix format

* chore: fix script and format

* chore: some optimization

* chore: remove batch migration

* chore: update script

* chore: use memory struct to cache local variable

* chore: fix lint

* chore: remove default parameter
  • Loading branch information
zhoujia6139 authored Oct 24, 2023
1 parent 86c289c commit eb89afb
Show file tree
Hide file tree
Showing 17 changed files with 571 additions and 30 deletions.
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,10 @@ zksync-bytecode-hashes:
redeploy-market:
make SCRIPT_PATH=./scripts/dev/15.redeploy-market.ts run

.PHONY: upgrade-pool-aa-position-mover
upgrade-pool-aa-position-mover:
make TASK_NAME=upgrade:pool-aa-position-mover run-task

.PHONY: transfer-tokens
transfer-tokens:
make SCRIPT_PATH=./scripts/dev/2.transfer-tokens.ts run
Expand Down
9 changes: 9 additions & 0 deletions contracts/interfaces/IAccountFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.10;

interface IAccountFactory {
function createAccount(
address owner,
uint256 salt
) external returns (address);
}
3 changes: 2 additions & 1 deletion contracts/interfaces/INToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {IERC721} from "../dependencies/openzeppelin/contracts/IERC721.sol";
import {IERC721Receiver} from "../dependencies/openzeppelin/contracts/IERC721Receiver.sol";
import {IERC721Enumerable} from "../dependencies/openzeppelin/contracts/IERC721Enumerable.sol";
import {IERC1155Receiver} from "../dependencies/openzeppelin/contracts/IERC1155Receiver.sol";

import {ICollateralizableERC721} from "./ICollateralizableERC721.sol";
import {IInitializableNToken} from "./IInitializableNToken.sol";
import {IXTokenType} from "./IXTokenType.sol";
import {DataTypes} from "../protocol/libraries/types/DataTypes.sol";
Expand All @@ -16,6 +16,7 @@ import {DataTypes} from "../protocol/libraries/types/DataTypes.sol";
* @notice Defines the basic interface for an NToken.
**/
interface INToken is
ICollateralizableERC721,
IERC721Enumerable,
IInitializableNToken,
IERC721Receiver,
Expand Down
4 changes: 3 additions & 1 deletion contracts/interfaces/IPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {IPoolMarketplace} from "./IPoolMarketplace.sol";
import {IPoolParameters} from "./IPoolParameters.sol";
import {IParaProxyInterfaces} from "./IParaProxyInterfaces.sol";
import {IPoolPositionMover} from "./IPoolPositionMover.sol";
import {IPoolAAPositionMover} from "./IPoolAAPositionMover.sol";
import "./IPoolApeStaking.sol";
import "./IPoolBorrowAndStake.sol";

Expand All @@ -21,7 +22,8 @@ interface IPool is
IPoolApeStaking,
IParaProxyInterfaces,
IPoolPositionMover,
IPoolBorrowAndStake
IPoolBorrowAndStake,
IPoolAAPositionMover
{

}
13 changes: 13 additions & 0 deletions contracts/interfaces/IPoolAAPositionMover.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.0;

/**
* @title IPool
*
* @notice Defines the basic interface for an ParaSpace Pool.
**/
interface IPoolAAPositionMover {
event PositionMovedToAA(address indexed user, address aaAccount);

function positionMoveToAA(address aaAccount) external;
}
1 change: 1 addition & 0 deletions contracts/protocol/libraries/helpers/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,5 @@ library Errors {
string public constant TOKEN_NOT_ALLOW_RESCUE = "140"; // token is not allow rescue

string public constant INVALID_PARAMETER = "170"; //invalid parameter
string public constant INVALID_CALLER = "171"; //invalid callser
}
209 changes: 209 additions & 0 deletions contracts/protocol/pool/PoolAAPositionMove.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import {ParaVersionedInitializable} from "../libraries/paraspace-upgradeability/ParaVersionedInitializable.sol";
import {DataTypes} from "../libraries/types/DataTypes.sol";
import {IPoolAAPositionMover} from "../../interfaces/IPoolAAPositionMover.sol";
import {IAccount} from "../../interfaces/IAccount.sol";
import {PoolStorage} from "./PoolStorage.sol";
import {ParaReentrancyGuard} from "../libraries/paraspace-upgradeability/ParaReentrancyGuard.sol";
import {IPool} from "../../interfaces/IPool.sol";
import {INToken} from "../../interfaces/INToken.sol";
import {IPToken} from "../../interfaces/IPToken.sol";
import {Errors} from "../libraries/helpers/Errors.sol";
import {ReserveConfiguration} from "../../protocol/libraries/configuration/ReserveConfiguration.sol";
import {UserConfiguration} from "../../protocol/libraries/configuration/UserConfiguration.sol";
import {ReserveLogic} from "../libraries/logic/ReserveLogic.sol";
import {SafeCast} from "../../dependencies/openzeppelin/contracts/SafeCast.sol";
import {IAccountFactory} from "../../interfaces/IAccountFactory.sol";
import {Errors} from "../libraries/helpers/Errors.sol";
import {IVariableDebtToken} from "../../interfaces/IVariableDebtToken.sol";
import {MathUtils} from "../libraries/math/MathUtils.sol";
import {WadRayMath} from "../libraries/math/WadRayMath.sol";

/**
* @title Pool PositionMover contract
*
**/
contract PoolAAPositionMover is
ParaVersionedInitializable,
ParaReentrancyGuard,
PoolStorage,
IPoolAAPositionMover
{
uint256 internal constant POOL_REVISION = 200;

event ReserveUsedAsCollateralEnabled(
address indexed reserve,
address indexed user
);
event ReserveUsedAsCollateralDisabled(
address indexed reserve,
address indexed user
);

using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
using UserConfiguration for DataTypes.UserConfigurationMap;
using ReserveLogic for DataTypes.ReserveData;
using SafeCast for uint256;
using WadRayMath for uint256;

struct MigrationCacheVars {
uint256 reservesCount;
bool isTokenCollateral;
bool isCollectionCollateral;
DataTypes.TimeLockParams timeLockParams;
}

function positionMoveToAA(address aaAccount) external nonReentrant {
require(
IAccount(aaAccount).owner() == msg.sender,
Errors.NOT_THE_OWNER
);

DataTypes.PoolStorage storage ps = poolStorage();
DataTypes.UserConfigurationMap storage userConfig = ps._usersConfig[
msg.sender
];
DataTypes.UserConfigurationMap storage aaConfig = ps._usersConfig[
aaAccount
];

MigrationCacheVars memory cacheVars;
cacheVars.reservesCount = ps._reservesCount;
for (uint256 j = 0; j < cacheVars.reservesCount; j++) {
address currentReserveAddress = ps._reservesList[j];
if (currentReserveAddress == address(0)) {
continue;
}

DataTypes.ReserveCache memory reserveCache = ps
._reserves[currentReserveAddress]
.cache();
if (
reserveCache.reserveConfiguration.getAssetType() ==
DataTypes.AssetType.ERC20
) {
//handle ptoken
{
IPToken pToken = IPToken(reserveCache.xTokenAddress);
uint256 balance = pToken.balanceOf(msg.sender);
if (balance > 0) {
uint256 nextLiquidityIndex = _calculateLiquidityIndex(
reserveCache
);
pToken.burn(
msg.sender,
reserveCache.xTokenAddress,
balance,
nextLiquidityIndex,
cacheVars.timeLockParams
);
pToken.mint(
aaAccount,
aaAccount,
balance,
nextLiquidityIndex
);
if (userConfig.isUsingAsCollateral(j)) {
aaConfig.setUsingAsCollateral(j, true);
emit ReserveUsedAsCollateralEnabled(
currentReserveAddress,
aaAccount
);
userConfig.setUsingAsCollateral(j, false);
emit ReserveUsedAsCollateralDisabled(
currentReserveAddress,
msg.sender
);
}
}
}

//handle debt token
{
IVariableDebtToken debtToken = IVariableDebtToken(
ps
._reserves[currentReserveAddress]
.variableDebtTokenAddress
);
uint256 balance = debtToken.balanceOf(msg.sender);
if (balance > 0) {
uint256 debtIndex = _calculateDebtIndex(reserveCache);
debtToken.burn(msg.sender, balance, debtIndex);
debtToken.mint(
aaAccount,
aaAccount,
balance,
debtIndex
);
aaConfig.setBorrowing(j, true);
userConfig.setBorrowing(j, false);
}
}
} else {
INToken nToken = INToken(reserveCache.xTokenAddress);
uint256 balance = nToken.balanceOf(msg.sender);
cacheVars.isCollectionCollateral = false;
for (uint256 k = 0; k < balance; k++) {
uint256 tokenId = nToken.tokenOfOwnerByIndex(msg.sender, k);
cacheVars.isTokenCollateral = nToken.isUsedAsCollateral(
tokenId
);
nToken.transferOnLiquidation(
msg.sender,
aaAccount,
tokenId
);
if (cacheVars.isTokenCollateral) {
nToken.setIsUsedAsCollateral(tokenId, true, aaAccount);
cacheVars.isCollectionCollateral = true;
}
}
if (cacheVars.isCollectionCollateral) {
aaConfig.setUsingAsCollateral(j, true);
emit ReserveUsedAsCollateralEnabled(
currentReserveAddress,
aaAccount
);
userConfig.setUsingAsCollateral(j, false);
emit ReserveUsedAsCollateralDisabled(
currentReserveAddress,
msg.sender
);
}
}
}

emit PositionMovedToAA(msg.sender, aaAccount);
}

function getRevision() internal pure virtual override returns (uint256) {
return POOL_REVISION;
}

function _calculateLiquidityIndex(
DataTypes.ReserveCache memory reserveCache
) internal view returns (uint256) {
uint256 cumulatedLiquidityInterest = MathUtils.calculateLinearInterest(
reserveCache.currLiquidityRate,
reserveCache.reserveLastUpdateTimestamp
);
return
cumulatedLiquidityInterest.rayMul(reserveCache.currLiquidityIndex);
}

function _calculateDebtIndex(
DataTypes.ReserveCache memory reserveCache
) internal view returns (uint256) {
uint256 cumulatedVariableBorrowInterest = MathUtils
.calculateCompoundedInterest(
reserveCache.currVariableBorrowRate,
reserveCache.reserveLastUpdateTimestamp
);
return
cumulatedVariableBorrowInterest.rayMul(
reserveCache.currVariableBorrowIndex
);
}
}
42 changes: 38 additions & 4 deletions helpers/contracts-deployments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ import {
WstETHMocked,
X2Y2Adapter,
X2Y2R1,
PoolAAPositionMover__factory,
PoolBorrowAndStake__factory,
PoolBorrowAndStake,
} from "../types";
Expand Down Expand Up @@ -671,6 +672,27 @@ export const deployPoolParaProxyInterfaces = async (verify?: boolean) => {
};
};

export const deployAAPoolPositionMover = async (verify?: boolean) => {
const {poolAAPositionMoverSelectors} = await getPoolSignatures();

const poolAAPositionMover = (await withSaveAndVerify(
await getContractFactory("PoolAAPositionMover"),
eContractid.PoolAAPositionMoverImpl,
[],
verify,
false,
undefined,
poolAAPositionMoverSelectors
)) as PoolPositionMover;

return {
poolAAPositionMover,
poolAAPositionMoverSelectors: poolAAPositionMoverSelectors.map(
(s) => s.signature
),
};
};

export const deployPoolPositionMover = async (
provider: tEthereumAddress,
bendDaoLendPoolLoan: tEthereumAddress,
Expand Down Expand Up @@ -786,6 +808,10 @@ export const getPoolSignatures = () => {
PoolPositionMover__factory.abi
);

const poolAAPositionMoverSelectors = getFunctionSignatures(
PoolAAPositionMover__factory.abi
);

const poolProxySelectors = getFunctionSignatures(ParaProxy__factory.abi);

const poolParaProxyInterfacesSelectors = getFunctionSignatures(
Expand All @@ -802,6 +828,7 @@ export const getPoolSignatures = () => {
...poolProxySelectors,
...poolParaProxyInterfacesSelectors,
...poolPositionMoverSelectors,
...poolAAPositionMoverSelectors,
];
for (const selector of poolSelectors) {
if (!allSelectors[selector.signature]) {
Expand All @@ -823,6 +850,7 @@ export const getPoolSignatures = () => {
poolBorrowAndStakeSelectors,
poolParaProxyInterfacesSelectors,
poolPositionMoverSelectors,
poolAAPositionMoverSelectors,
};
};

Expand Down Expand Up @@ -3210,15 +3238,21 @@ export const deployAccount = async (
) as Promise<Account>;

export const deployAccountFactory = async (
accountRegistry: tEthereumAddress,
entryPoint: tEthereumAddress,
verify?: boolean
) =>
withSaveAndVerify(
) => {
const accountImpl = await deployAccount(entryPoint, verify);
const accountRegistry = await deployAccountRegistry(
accountImpl.address,
verify
);
return withSaveAndVerify(
await getContractFactory("AccountFactory"),
eContractid.AccountFactory,
[accountRegistry],
[accountRegistry.address],
verify
) as Promise<AccountFactory>;
};

export const deployAccountRegistry = async (
impl: tEthereumAddress,
Expand Down
Loading

0 comments on commit eb89afb

Please sign in to comment.