Skip to content

Commit

Permalink
Add protocol id disambiguator to AaveLinearPoolFactory (#2115)
Browse files Browse the repository at this point in the history
* feat: add protocolId to differentiate between different protocols using the same Aave Linear Pool factory

* feat: add protocol id registration

* docs: add comments

* test: fix AaveLinearPool tests

* docs: improve event comment

* docs: replace slang with common word

* refactor: add protocol ID registration event

* docs: fix comment
  • Loading branch information
EndymionJkb authored Dec 7, 2022
1 parent c7b1989 commit 5329a2c
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 24 deletions.
84 changes: 68 additions & 16 deletions pkg/pool-linear/contracts/aave/AaveLinearPoolFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ contract AaveLinearPoolFactory is
ReentrancyGuard,
FactoryWidePauseWindow
{
// Associate a name with each registered protocol that uses this factory.
struct ProtocolIdData {
string name;
bool registered;
}

// Used for create2 deployments
uint256 private _nextRebalancerSalt;

Expand All @@ -46,6 +52,16 @@ contract AaveLinearPoolFactory is
address private _lastCreatedPool;
string private _poolVersion;

// Maintain a set of recognized protocolIds.
mapping(uint256 => ProtocolIdData) private _protocolIds;

// This event allows off-chain tools to differentiate between different protocols that use this factory
// to deploy Aave Linear Pools.
event AaveLinearPoolCreated(address indexed pool, uint256 protocolId);

// Record protocol ID registrations.
event AaveLinearPoolProtocolIdRegistered(uint256 indexed protocolId, string name);

constructor(
IVault vault,
IProtocolFeePercentagesProvider protocolFeeProvider,
Expand All @@ -57,14 +73,31 @@ contract AaveLinearPoolFactory is
_poolVersion = poolVersion;
}

/**
* @dev Return the address of the most recently created pool.
*/
function getLastCreatedPool() external view override returns (address) {
return _lastCreatedPool;
}

/**
* @dev Return the pool version deployed by this factory.
*/
function getPoolVersion() public view override returns (string memory) {
return _poolVersion;
}

/**
* @dev Return the name associated with the given protocolId, if registered.
*/
function getProtocolName(uint256 protocolId) external view returns (string memory) {
ProtocolIdData memory protocolIdData = _protocolIds[protocolId];

require(protocolIdData.registered, "Protocol ID not registered");

return protocolIdData.name;
}

function _create(bytes memory constructorArgs) internal virtual override returns (address) {
address pool = super._create(constructorArgs);
_lastCreatedPool = pool;
Expand All @@ -73,7 +106,7 @@ contract AaveLinearPoolFactory is
}

/**
* @dev Deploys a new `AaveLinearPool`.
* @dev Deploys a new `AaveLinearPool` with a given protocolId.
*/
function create(
string memory name,
Expand All @@ -82,7 +115,8 @@ contract AaveLinearPoolFactory is
IERC20 wrappedToken,
uint256 upperTarget,
uint256 swapFeePercentage,
address owner
address owner,
uint256 protocolId
) external nonReentrant returns (AaveLinearPool) {
// We are going to deploy both an AaveLinearPool and an AaveLinearPoolRebalancer set as its Asset Manager, but
// this creates a circular dependency problem: the Pool must know the Asset Manager's address in order to call
Expand All @@ -109,20 +143,19 @@ contract AaveLinearPoolFactory is

(uint256 pauseWindowDuration, uint256 bufferPeriodDuration) = getPauseConfiguration();

AaveLinearPool.ConstructorArgs memory args = AaveLinearPool.ConstructorArgs({
vault: getVault(),
name: name,
symbol: symbol,
mainToken: mainToken,
wrappedToken: wrappedToken,
assetManager: expectedRebalancerAddress,
upperTarget: upperTarget,
swapFeePercentage: swapFeePercentage,
pauseWindowDuration: pauseWindowDuration,
bufferPeriodDuration: bufferPeriodDuration,
owner: owner,
version: getPoolVersion()
});
AaveLinearPool.ConstructorArgs memory args;
args.vault = getVault();
args.name = name;
args.symbol = symbol;
args.mainToken = mainToken;
args.wrappedToken = wrappedToken;
args.assetManager = expectedRebalancerAddress;
args.upperTarget = upperTarget;
args.swapFeePercentage = swapFeePercentage;
args.pauseWindowDuration = pauseWindowDuration;
args.bufferPeriodDuration = bufferPeriodDuration;
args.owner = owner;
args.version = getPoolVersion();

AaveLinearPool pool = AaveLinearPool(_create(abi.encode(args)));

Expand All @@ -135,7 +168,26 @@ contract AaveLinearPoolFactory is
address actualRebalancerAddress = Create2.deploy(0, rebalancerSalt, rebalancerCreationCode);
require(expectedRebalancerAddress == actualRebalancerAddress, "Rebalancer deployment failed");

// Identify the protocolId associated with this pool. We do not require that the protocolId be registered.
emit AaveLinearPoolCreated(address(pool), protocolId);

// We don't return the Rebalancer's address, but that can be queried in the Vault by calling `getPoolTokenInfo`.
return pool;
}

/**
* @notice Register an id (and name) to differentiate between multiple protocols using this factory.
* @dev This is a permissioned function. Protocol ids cannot be deregistered.
*/
function registerProtocolId(uint256 protocolId, string memory name) external authenticate {
require(!_protocolIds[protocolId].registered, "Protocol ID already registered");

_registerProtocolId(protocolId, name);
}

function _registerProtocolId(uint256 protocolId, string memory name) private {
_protocolIds[protocolId] = ProtocolIdData({ name: name, registered: true });

emit AaveLinearPoolProtocolIdRegistered(protocolId, name);
}
}
7 changes: 5 additions & 2 deletions pkg/pool-linear/test/AaveLinearPool.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ describe('AaveLinearPool', function () {
let trader: SignerWithAddress, lp: SignerWithAddress, owner: SignerWithAddress;

const POOL_SWAP_FEE_PERCENTAGE = fp(0.01);
const AAVE_PROTOCOL_ID = 0;

before('setup', async () => {
[, lp, trader, owner] = await ethers.getSigners();
Expand Down Expand Up @@ -66,7 +67,8 @@ describe('AaveLinearPool', function () {
wrappedToken.address,
bn(0),
POOL_SWAP_FEE_PERCENTAGE,
owner.address
owner.address,
AAVE_PROTOCOL_ID
);

const receipt = await tx.wait();
Expand All @@ -87,7 +89,8 @@ describe('AaveLinearPool', function () {
wrappedToken.address,
bn(0),
POOL_SWAP_FEE_PERCENTAGE,
owner.address
owner.address,
AAVE_PROTOCOL_ID
)
).to.be.revertedWith('TOKENS_MISMATCH');
});
Expand Down
77 changes: 71 additions & 6 deletions pkg/pool-linear/test/AaveLinearPoolFactory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import { deploy, deployedAt } from '@balancer-labs/v2-helpers/src/contract';
import { MAX_UINT112 } from '@balancer-labs/v2-helpers/src/constants';
import { advanceTime, currentTimestamp, MONTH } from '@balancer-labs/v2-helpers/src/time';
import Token from '@balancer-labs/v2-helpers/src/models/tokens/Token';
import { sharedBeforeEach } from '@balancer-labs/v2-common/sharedBeforeEach';
import { actionId } from '@balancer-labs/v2-helpers/src/models/misc/actions';

describe('AaveLinearPoolFactory', function () {
let vault: Vault, tokens: TokenList, factory: Contract;
let creationTime: BigNumber, owner: SignerWithAddress;
let creationTime: BigNumber, admin: SignerWithAddress, owner: SignerWithAddress;
let factoryVersion: string, poolVersion: string;

const NAME = 'Balancer Linear Pool Token';
Expand All @@ -24,12 +26,20 @@ describe('AaveLinearPoolFactory', function () {
const BASE_PAUSE_WINDOW_DURATION = MONTH * 3;
const BASE_BUFFER_PERIOD_DURATION = MONTH;

const AAVE_PROTOCOL_ID = 0;
const BEEFY_PROTOCOL_ID = 1;
const STURDY_PROTOCOL_ID = 2;

const AAVE_PROTOCOL_NAME = 'AAVE';
const BEEFY_PROTOCOL_NAME = 'Beefy';
const STURDY_PROTOCOL_NAME = 'Sturdy';

before('setup signers', async () => {
[, owner] = await ethers.getSigners();
[, admin, owner] = await ethers.getSigners();
});

sharedBeforeEach('deploy factory & tokens', async () => {
vault = await Vault.create();
vault = await Vault.create({ admin });
const queries = await deploy('v2-standalone-utils/BalancerQueries', { args: [vault.address] });
factoryVersion = JSON.stringify({
name: 'AaveLinearPoolFactory',
Expand Down Expand Up @@ -58,17 +68,24 @@ describe('AaveLinearPoolFactory', function () {
});

async function createPool(): Promise<Contract> {
const receipt = await factory.create(
const tx = await factory.create(
NAME,
SYMBOL,
tokens.DAI.address,
tokens.CDAI.address,
UPPER_TARGET,
POOL_SWAP_FEE_PERCENTAGE,
owner.address
owner.address,
AAVE_PROTOCOL_ID
);

const event = expectEvent.inReceipt(await receipt.wait(), 'PoolCreated');
const receipt = await tx.wait();
const event = expectEvent.inReceipt(receipt, 'PoolCreated');
expectEvent.inReceipt(receipt, 'AaveLinearPoolCreated', {
pool: event.args.pool,
protocolId: AAVE_PROTOCOL_ID,
});

return deployedAt('AaveLinearPool', event.args.pool);
}

Expand Down Expand Up @@ -202,4 +219,52 @@ describe('AaveLinearPoolFactory', function () {
expect(bufferPeriodEndTime).to.equal(now);
});
});

describe('protocol id', () => {
it('should not allow adding protocols without permission', async () => {
await expect(factory.registerProtocolId(AAVE_PROTOCOL_ID, 'AAVE')).to.be.revertedWith('SENDER_NOT_ALLOWED');
});

context('with no registered protocols', () => {
it('should revert when asking for an unregistered protocol name', async () => {
await expect(factory.getProtocolName(AAVE_PROTOCOL_ID)).to.be.revertedWith('Protocol ID not registered');
});
});

context('with registered protocols', () => {
sharedBeforeEach('grant permissions', async () => {
const action = await actionId(factory, 'registerProtocolId');
await vault.authorizer.connect(admin).grantPermissions([action], admin.address, [factory.address]);
});

sharedBeforeEach('register some protocols', async () => {
await factory.connect(admin).registerProtocolId(AAVE_PROTOCOL_ID, AAVE_PROTOCOL_NAME);
await factory.connect(admin).registerProtocolId(BEEFY_PROTOCOL_ID, BEEFY_PROTOCOL_NAME);
await factory.connect(admin).registerProtocolId(STURDY_PROTOCOL_ID, STURDY_PROTOCOL_NAME);
});

it('protocol ID registration should emit an event', async () => {
const OTHER_PROTOCOL_ID = 57;
const OTHER_PROTOCOL_NAME = 'Protocol 57';

const tx = await factory.connect(admin).registerProtocolId(OTHER_PROTOCOL_ID, OTHER_PROTOCOL_NAME);
expectEvent.inReceipt(await tx.wait(), 'AaveLinearPoolProtocolIdRegistered', {
protocolId: OTHER_PROTOCOL_ID,
name: OTHER_PROTOCOL_NAME,
});
});

it('should register protocols', async () => {
expect(await factory.getProtocolName(AAVE_PROTOCOL_ID)).to.equal(AAVE_PROTOCOL_NAME);
expect(await factory.getProtocolName(BEEFY_PROTOCOL_ID)).to.equal(BEEFY_PROTOCOL_NAME);
expect(await factory.getProtocolName(STURDY_PROTOCOL_ID)).to.equal(STURDY_PROTOCOL_NAME);
});

it('should fail when a protocol is already registered', async () => {
await expect(
factory.connect(admin).registerProtocolId(STURDY_PROTOCOL_ID, 'Random protocol')
).to.be.revertedWith('Protocol ID already registered');
});
});
});
});

0 comments on commit 5329a2c

Please sign in to comment.