Skip to content

Commit

Permalink
Feat/migration refactor (#581)
Browse files Browse the repository at this point in the history
* feat: migration refactor with populated transaction

* feat: deleted token owner

* feat: added test for migration builder

* feat: added user e mode to user summary

* feat: fixed faulty indexing

* feat: changed to approval types
  • Loading branch information
JoaquinBattilana authored May 31, 2024
1 parent 9363b73 commit d61aa11
Show file tree
Hide file tree
Showing 3 changed files with 434 additions and 4 deletions.
173 changes: 170 additions & 3 deletions packages/contract-helpers/src/v3-migration-contract/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import BigNumber from 'bignumber.js';
import { constants, providers, utils } from 'ethers';
import {
PopulatedTransaction,
constants,
providers,
utils,
BigNumber as BigNumberEthers,
} from 'ethers';
import {
ApproveDelegationType,
BaseDebtToken,
BaseDebtTokenInterface,
} from '../baseDebtToken-contract';
Expand All @@ -13,11 +20,15 @@ import {
tEthereumAddress,
transactionType,
} from '../commons/types';
import { gasLimitRecommendations } from '../commons/utils';
import { V3MigratorValidator } from '../commons/validators/methodValidators';
import { isEthAddress } from '../commons/validators/paramValidators';
import { ERC20Service } from '../erc20-contract';
import { ApproveType, ERC20Service } from '../erc20-contract';
import { Pool } from '../v3-pool-contract';
import { IMigrationHelper } from './typechain/IMigrationHelper';
import {
IMigrationHelper,
IMigrationHelperInterface,
} from './typechain/IMigrationHelper';
import { IMigrationHelper__factory } from './typechain/IMigrationHelper__factory';
import {
MigrationDelegationApproval,
Expand All @@ -34,6 +45,31 @@ export interface V3MigrationHelperInterface {
) => Promise<EthereumTransactionTypeExtended[]>;
}

interface ApproveWithTx extends ApproveType {
tx: PopulatedTransaction;
}

interface ApproveDelegationWithTx extends ApproveDelegationType {
tx: PopulatedTransaction;
}

interface MigrationNeededApprovals {
supplyApprovalTxs: ApproveWithTx[];
borrowCreditDelegationApprovalTxs: ApproveDelegationWithTx[];
}

export type MigrationTxBuilder = {
generateApprovalsTxs: (
params: Pick<
V3MigrationType,
'supplyAssets' | 'user' | 'creditDelegationApprovals'
>,
) => Promise<MigrationNeededApprovals>;
generateTxData: (
params: Omit<V3MigrationType, 'creditDelegationApprovals'>,
) => PopulatedTransaction;
};

export class V3MigrationHelperService
extends BaseService<IMigrationHelper>
implements V3MigrationHelperInterface
Expand All @@ -43,6 +79,8 @@ export class V3MigrationHelperService
readonly MIGRATOR_ADDRESS: tEthereumAddress;
readonly erc20Service: ERC20Service;
readonly pool: Pool;
readonly contractInterface: IMigrationHelperInterface;
readonly migrationTxBuilder: MigrationTxBuilder;
constructor(
provider: providers.Provider,
MIGRATOR_ADDRESS: tEthereumAddress,
Expand All @@ -53,6 +91,135 @@ export class V3MigrationHelperService
this.erc20Service = new ERC20Service(provider);
this.baseDebtTokenService = new BaseDebtToken(provider, this.erc20Service);
this.pool = pool;
this.contractInterface = IMigrationHelper__factory.createInterface();

this.migrationTxBuilder = {
generateApprovalsTxs: async ({
supplyAssets,
user,
creditDelegationApprovals,
}) => {
const supplyApprovalTxs: Array<ApproveWithTx | undefined> =
await Promise.all(
supplyAssets.map(async ({ amount, aToken }) => {
const isApproved = await this.erc20Service.isApproved({
amount,
spender: this.MIGRATOR_ADDRESS,
token: aToken,
user,
nativeDecimals: true,
});
if (isApproved) {
return undefined;
}

const tx = this.erc20Service.approveTxData({
user,
token: aToken,
spender: this.MIGRATOR_ADDRESS,
amount,
});
return {
amount,
spender: this.MIGRATOR_ADDRESS,
token: aToken,
user,
tx,
};
}),
);
const borrowCreditDelegationApprovalTxs: Array<
ApproveDelegationWithTx | undefined
> = await Promise.all(
creditDelegationApprovals.map(
async ({ amount, debtTokenAddress }) => {
const isApproved =
await this.baseDebtTokenService.isDelegationApproved({
debtTokenAddress,
allowanceGiver: user,
allowanceReceiver: this.MIGRATOR_ADDRESS,
amount,
nativeDecimals: true,
});
if (isApproved) {
return undefined;
}

const tx =
this.baseDebtTokenService.generateApproveDelegationTxData({
user,
delegatee: this.MIGRATOR_ADDRESS,
debtTokenAddress,
amount,
});
return {
tx,
amount,
delegatee: this.MIGRATOR_ADDRESS,
debtTokenAddress,
user,
};
},
),
);
return {
supplyApprovalTxs: supplyApprovalTxs.filter(
(elem): elem is ApproveWithTx => Boolean(elem),
),
borrowCreditDelegationApprovalTxs:
borrowCreditDelegationApprovalTxs.filter(
(elem): elem is ApproveDelegationWithTx => Boolean(elem),
),
};
},
generateTxData: ({
supplyAssets,
user,
repayAssets,
signedSupplyPermits,
signedCreditDelegationPermits,
}) => {
const actionTx: PopulatedTransaction = {};
let permits: IMigrationHelper.PermitInputStruct[] = [];
let creditDelegationPermits: IMigrationHelper.CreditDelegationInputStruct[] =
[];
if (signedSupplyPermits && signedSupplyPermits.length > 0) {
permits = this.splitSignedPermits(signedSupplyPermits);
}

if (
signedCreditDelegationPermits &&
signedCreditDelegationPermits.length > 0
) {
creditDelegationPermits = this.splitSignedCreditDelegationPermits(
signedCreditDelegationPermits,
);
}

const assetsToMigrate = supplyAssets.map(
supplyAsset => supplyAsset.underlyingAsset,
);
const assetsToRepay = repayAssets.map(repayAsset => {
return {
asset: repayAsset.underlyingAsset,
rateMode: repayAsset.rateMode === InterestRate.Stable ? 1 : 2,
};
});
const txData = this.contractInterface.encodeFunctionData('migrate', [
assetsToMigrate,
assetsToRepay,
permits,
creditDelegationPermits,
]);
actionTx.to = this.MIGRATOR_ADDRESS;
actionTx.from = user;
actionTx.data = txData;
actionTx.gasLimit = BigNumberEthers.from(
gasLimitRecommendations[ProtocolAction.migrateV3].recommended,
);
return actionTx;
},
};
}

@V3MigratorValidator
Expand Down
Loading

0 comments on commit d61aa11

Please sign in to comment.