Skip to content

Commit

Permalink
Merge pull request #26 from pnetwork-association/refactor/move-roles
Browse files Browse the repository at this point in the history
  • Loading branch information
oliviera9 authored Feb 7, 2024
2 parents 769d8dd + 1465971 commit 6a1d854
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 143 deletions.
2 changes: 2 additions & 0 deletions tasks/deploy_forwarder_bsc.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const { task } = require('hardhat/config')

const { PNT_ON_BSC_ADDRESS, ZERO_ADDRESS } = require('../tasks/config')

const deploy = async (_args, _hre) => {
Expand Down
2 changes: 2 additions & 0 deletions tasks/deploy_forwarder_polygon.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const { task } = require('hardhat/config')

const { PNT_ON_POLYGON_ADDRESS, ZERO_ADDRESS } = require('../tasks/config')

const deploy = async (_args, _hre) => {
Expand Down
22 changes: 11 additions & 11 deletions tasks/set_permissions.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,19 @@ const setPermissions = async (_args, _hre) => {
console.log('Setting ACL permissions ...')
await acl.setPermissionManager(signer.address, TOKEN_MANAGER_ADDRESS, getRole('MINT_ROLE'))
await acl.setPermissionManager(signer.address, TOKEN_MANAGER_ADDRESS, getRole('BURN_ROLE'))
await acl.grantPermission(stakingManager.address, TOKEN_MANAGER_ADDRESS, getRole('MINT_ROLE'))
await acl.grantPermission(stakingManager.address, TOKEN_MANAGER_ADDRESS, getRole('BURN_ROLE'))
await acl.grantPermission(stakingManagerLM.address, TOKEN_MANAGER_ADDRESS, getRole('MINT_ROLE'))
await acl.grantPermission(stakingManagerLM.address, TOKEN_MANAGER_ADDRESS, getRole('BURN_ROLE'))
await acl.grantPermission(stakingManagerRM.address, TOKEN_MANAGER_ADDRESS, getRole('MINT_ROLE'))
await acl.grantPermission(stakingManagerRM.address, TOKEN_MANAGER_ADDRESS, getRole('BURN_ROLE'))
await stakingManagerLM.grantRole(getRole('STAKE_ROLE'), lendingManager.address)
await stakingManagerLM.grantRole(getRole('INCREASE_DURATION_ROLE'), lendingManager.address)
await stakingManagerRM.grantRole(getRole('STAKE_ROLE'), registrationManager.address)
await stakingManagerRM.grantRole(getRole('INCREASE_DURATION_ROLE'), registrationManager.address)
await acl.grantPermission(await stakingManager.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('MINT_ROLE'))
await acl.grantPermission(await stakingManager.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('BURN_ROLE'))
await acl.grantPermission(await stakingManagerLM.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('MINT_ROLE'))
await acl.grantPermission(await stakingManagerLM.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('BURN_ROLE'))
await acl.grantPermission(await stakingManagerRM.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('MINT_ROLE'))
await acl.grantPermission(await stakingManagerRM.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('BURN_ROLE'))
await stakingManagerLM.grantRole(getRole('STAKE_ROLE'), await lendingManager.getAddress())
await stakingManagerLM.grantRole(getRole('INCREASE_DURATION_ROLE'), await lendingManager.getAddress())
await stakingManagerRM.grantRole(getRole('STAKE_ROLE'), await registrationManager.getAddress())
await stakingManagerRM.grantRole(getRole('INCREASE_DURATION_ROLE'), await registrationManager.getAddress())

console.log('Assigning roles and whitelisting origin addresses ...')
await lendingManager.grantRole(getRole('BORROW_ROLE'), registrationManager.address)
await lendingManager.grantRole(getRole('BORROW_ROLE'), await registrationManager.getAddress())
// await forwarder.whitelistOriginAddress(FORWARDER_ON_MAINNET)
// await forwarder.whitelistOriginAddress(FORWARDER_ON_BSC)

Expand Down
4 changes: 2 additions & 2 deletions test/EpochsManager.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const { expect } = require('chai')
const { ethers, upgrades } = require('hardhat')

const { ONE_DAY } = require('./constants')
const { getRole } = require('./utils')
const { UPGRADE_ROLE } = require('./roles')

const EPOCH_DURATION = ONE_DAY // 1 day

Expand All @@ -17,7 +17,7 @@ describe('EpochsManager', () => {
initializer: 'initialize',
kind: 'uups'
})
await epochsManager.grantRole(getRole('UPGRADE_ROLE'), signer.address)
await epochsManager.grantRole(UPGRADE_ROLE, signer.address)
})

it('admin should be able to handle correctly a contract upgrade', async () => {
Expand Down
36 changes: 15 additions & 21 deletions test/FeesManager.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,19 @@ const {
PNT_MAX_TOTAL_SUPPLY,
TOKEN_MANAGER_ADDRESS
} = require('./constants')
const { getRole, getSentinelIdentity } = require('./utils')
const {
BORROW_ROLE,
RELEASE_ROLE,
STAKE_ROLE,
INCREASE_DURATION_ROLE,
UPDATE_GUARDIAN_REGISTRATION_ROLE,
REDIRECT_CLAIM_TO_CHALLENGER_BY_EPOCH_ROLE,
MINT_ROLE,
BURN_ROLE
} = require('./roles')
const { getSentinelIdentity } = require('./utils')
const { hardhatReset } = require('./utils/hardhat-reset')

// roles
const BORROW_ROLE = getRole('BORROW_ROLE')
const RELEASE_ROLE = getRole('RELEASE_ROLE')
const STAKE_ROLE = getRole('STAKE_ROLE')
const INCREASE_DURATION_ROLE = getRole('INCREASE_DURATION_ROLE')
const REDIRECT_CLAIM_TO_CHALLENGER_BY_EPOCH_ROLE = getRole('REDIRECT_CLAIM_TO_CHALLENGER_BY_EPOCH_ROLE')
const UPDATE_GUARDIAN_REGISTRATION_ROLE = getRole('UPDATE_GUARDIAN_REGISTRATION_ROLE')

let stakingManagerLM,
stakingManagerRM,
epochsManager,
Expand Down Expand Up @@ -172,18 +174,10 @@ describe('FeesManager', () => {
await stakingManagerRM.grantRole(INCREASE_DURATION_ROLE, await registrationManager.getAddress())
await registrationManager.grantRole(UPDATE_GUARDIAN_REGISTRATION_ROLE, fakeDandelionVoting.address)
await feesManager.grantRole(REDIRECT_CLAIM_TO_CHALLENGER_BY_EPOCH_ROLE, fakeRegistrationManager.address)
await acl
.connect(daoRoot)
.grantPermission(await stakingManagerRM.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('MINT_ROLE'))
await acl
.connect(daoRoot)
.grantPermission(await stakingManagerRM.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('BURN_ROLE'))
await acl
.connect(daoRoot)
.grantPermission(await stakingManagerLM.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('MINT_ROLE'))
await acl
.connect(daoRoot)
.grantPermission(await stakingManagerLM.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('BURN_ROLE'))
await acl.connect(daoRoot).grantPermission(await stakingManagerRM.getAddress(), TOKEN_MANAGER_ADDRESS, MINT_ROLE)
await acl.connect(daoRoot).grantPermission(await stakingManagerRM.getAddress(), TOKEN_MANAGER_ADDRESS, BURN_ROLE)
await acl.connect(daoRoot).grantPermission(await stakingManagerLM.getAddress(), TOKEN_MANAGER_ADDRESS, MINT_ROLE)
await acl.connect(daoRoot).grantPermission(await stakingManagerLM.getAddress(), TOKEN_MANAGER_ADDRESS, BURN_ROLE)

await owner.sendTransaction({
to: pntHolder1.address,
Expand Down
57 changes: 27 additions & 30 deletions test/Forwarders.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,16 @@ const {
TOKEN_MANAGER_ADDRESS,
ZERO_ADDRESS
} = require('./constants')
const { getRole, encode, getSentinelIdentity, getUserDataGeneratedByForwarder } = require('./utils')
const {
BORROW_ROLE,
STAKE_ROLE,
INCREASE_DURATION_ROLE,
UPGRADE_ROLE,
MINT_ROLE,
BURN_ROLE,
SET_FORWARDER_ROLE
} = require('./roles')
const { encode, getSentinelIdentity, getUserDataGeneratedByForwarder } = require('./utils')

let acl,
forwarderNative,
Expand Down Expand Up @@ -153,32 +162,20 @@ describe('Forwarders', () => {
await voting.setForwarder(await forwarderHost.getAddress())

// set permissions
await acl
.connect(root)
.grantPermission(await stakingManager.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('MINT_ROLE'))
await acl
.connect(root)
.grantPermission(await stakingManager.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('BURN_ROLE'))
await acl
.connect(root)
.grantPermission(await stakingManagerRM.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('MINT_ROLE'))
await acl
.connect(root)
.grantPermission(await stakingManagerRM.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('BURN_ROLE'))
await acl
.connect(root)
.grantPermission(await stakingManagerLM.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('MINT_ROLE'))
await acl
.connect(root)
.grantPermission(await stakingManagerLM.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('BURN_ROLE'))
await lendingManager.grantRole(getRole('BORROW_ROLE'), await registrationManager.getAddress())
await stakingManagerLM.grantRole(getRole('STAKE_ROLE'), await lendingManager.getAddress())
await stakingManagerLM.grantRole(getRole('INCREASE_DURATION_ROLE'), await lendingManager.getAddress())
await stakingManagerRM.grantRole(getRole('STAKE_ROLE'), await registrationManager.getAddress())
await stakingManagerRM.grantRole(getRole('INCREASE_DURATION_ROLE'), await registrationManager.getAddress())
await stakingManager.grantRole(getRole('UPGRADE_ROLE'), owner.address)
await lendingManager.grantRole(getRole('UPGRADE_ROLE'), owner.address)
await registrationManager.grantRole(getRole('UPGRADE_ROLE'), owner.address)
await acl.connect(root).grantPermission(await stakingManager.getAddress(), TOKEN_MANAGER_ADDRESS, MINT_ROLE)
await acl.connect(root).grantPermission(await stakingManager.getAddress(), TOKEN_MANAGER_ADDRESS, BURN_ROLE)
await acl.connect(root).grantPermission(await stakingManagerRM.getAddress(), TOKEN_MANAGER_ADDRESS, MINT_ROLE)
await acl.connect(root).grantPermission(await stakingManagerRM.getAddress(), TOKEN_MANAGER_ADDRESS, BURN_ROLE)
await acl.connect(root).grantPermission(await stakingManagerLM.getAddress(), TOKEN_MANAGER_ADDRESS, MINT_ROLE)
await acl.connect(root).grantPermission(await stakingManagerLM.getAddress(), TOKEN_MANAGER_ADDRESS, BURN_ROLE)
await lendingManager.grantRole(BORROW_ROLE, await registrationManager.getAddress())
await stakingManagerLM.grantRole(STAKE_ROLE, await lendingManager.getAddress())
await stakingManagerLM.grantRole(INCREASE_DURATION_ROLE, await lendingManager.getAddress())
await stakingManagerRM.grantRole(STAKE_ROLE, await registrationManager.getAddress())
await stakingManagerRM.grantRole(INCREASE_DURATION_ROLE, await registrationManager.getAddress())
await stakingManager.grantRole(UPGRADE_ROLE, owner.address)
await lendingManager.grantRole(UPGRADE_ROLE, owner.address)
await registrationManager.grantRole(UPGRADE_ROLE, owner.address)

await stakingManager.setForwarder(await forwarderHost.getAddress())
await lendingManager.setForwarder(await forwarderHost.getAddress())
Expand Down Expand Up @@ -217,22 +214,22 @@ describe('Forwarders', () => {
describe('ForwarderRecipientUpgradeable', () => {
it('should not be able to change the forwarder without the corresponding role', async () => {
for (const { contract } of forwarderRecipientUpgradeableTestData) {
const expectedError = `AccessControl: account ${pntHolder1.address.toLowerCase()} is missing role ${getRole('SET_FORWARDER_ROLE')}`
const expectedError = `AccessControl: account ${pntHolder1.address.toLowerCase()} is missing role ${SET_FORWARDER_ROLE}`
await expect(contract.connect(pntHolder1).setForwarder(fakeForwarder.address)).to.be.revertedWith(expectedError)
}
})

it('should be able to change the forwarder', async () => {
for (const { contract } of forwarderRecipientUpgradeableTestData) {
await contract.grantRole(getRole('SET_FORWARDER_ROLE'), pntHolder1.address)
await contract.grantRole(SET_FORWARDER_ROLE, pntHolder1.address)
await contract.connect(pntHolder1).setForwarder(fakeForwarder.address)
expect(await contract.forwarder()).to.be.eq(fakeForwarder.address)
}
})

it('should be able to change the forwarder after a contract upgrade', async () => {
for (const { artifact, contract } of forwarderRecipientUpgradeableTestData) {
await contract.grantRole(getRole('SET_FORWARDER_ROLE'), pntHolder1.address)
await contract.grantRole(SET_FORWARDER_ROLE, pntHolder1.address)
await contract.connect(pntHolder1).setForwarder(fakeForwarder.address)
expect(await contract.forwarder()).to.be.eq(fakeForwarder.address)
await upgrades.upgradeProxy(await contract.getAddress(), artifact, {
Expand Down
25 changes: 12 additions & 13 deletions test/LendingManager.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,18 @@ const {
PNT_MAX_TOTAL_SUPPLY,
TOKEN_MANAGER_ADDRESS
} = require('./constants')
const { getRole, truncateWithPrecision } = require('./utils')
const {
BORROW_ROLE,
RELEASE_ROLE,
UPGRADE_ROLE,
STAKE_ROLE,
INCREASE_DURATION_ROLE,
MINT_ROLE,
BURN_ROLE
} = require('./roles')
const { truncateWithPrecision } = require('./utils')
const { hardhatReset } = require('./utils/hardhat-reset')

const BORROW_ROLE = getRole('BORROW_ROLE')
const RELEASE_ROLE = getRole('RELEASE_ROLE')
const STAKE_ROLE = getRole('STAKE_ROLE')
const INCREASE_DURATION_ROLE = getRole('INCREASE_DURATION_ROLE')
const UPGRADE_ROLE = getRole('UPGRADE_ROLE')

describe('LendingManager', () => {
let daoRoot,
acl,
Expand Down Expand Up @@ -107,12 +110,8 @@ describe('LendingManager', () => {
await lendingManager.grantRole(UPGRADE_ROLE, owner.address)
await stakingManager.grantRole(STAKE_ROLE, await lendingManager.getAddress())
await stakingManager.grantRole(INCREASE_DURATION_ROLE, await lendingManager.getAddress())
await acl
.connect(daoRoot)
.grantPermission(await stakingManager.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('MINT_ROLE'))
await acl
.connect(daoRoot)
.grantPermission(await stakingManager.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('BURN_ROLE'))
await acl.connect(daoRoot).grantPermission(await stakingManager.getAddress(), TOKEN_MANAGER_ADDRESS, MINT_ROLE)
await acl.connect(daoRoot).grantPermission(await stakingManager.getAddress(), TOKEN_MANAGER_ADDRESS, BURN_ROLE)

await owner.sendTransaction({
to: pntHolder1.address,
Expand Down
45 changes: 20 additions & 25 deletions test/RegistrationManager.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,24 @@ const {
MINIMUM_BORROWING_FEE,
ONE_HOUR_IN_S
} = require('./constants')
const { getRole, getSentinelIdentity, truncateWithPrecision } = require('./utils')
const {
BORROW_ROLE,
RELEASE_ROLE,
STAKE_ROLE,
INCREASE_DURATION_ROLE,
SLASH_ROLE,
INCREASE_AMOUNT_ROLE,
UPGRADE_ROLE,
UPDATE_GUARDIAN_REGISTRATION_ROLE,
SET_GOVERNANCE_MESSAGE_EMITTER_ROLE,
SET_FEES_MANAGER_ROLE,
REDIRECT_CLAIM_TO_CHALLENGER_BY_EPOCH_ROLE,
MINT_ROLE,
BURN_ROLE
} = require('./roles')
const { getSentinelIdentity, truncateWithPrecision } = require('./utils')
const { hardhatReset } = require('./utils/hardhat-reset')

const BORROW_ROLE = getRole('BORROW_ROLE')
const RELEASE_ROLE = getRole('RELEASE_ROLE')
const SLASH_ROLE = getRole('SLASH_ROLE')
const STAKE_ROLE = getRole('STAKE_ROLE')
const INCREASE_DURATION_ROLE = getRole('INCREASE_DURATION_ROLE')
const UPGRADE_ROLE = getRole('UPGRADE_ROLE')
const UPDATE_GUARDIAN_REGISTRATION_ROLE = getRole('UPDATE_GUARDIAN_REGISTRATION_ROLE')
const SET_FEES_MANAGER_ROLE = getRole('SET_FEES_MANAGER_ROLE')
const SET_GOVERNANCE_MESSAGE_EMITTER_ROLE = getRole('SET_GOVERNANCE_MESSAGE_EMITTER_ROLE')
const REDIRECT_CLAIM_TO_CHALLENGER_BY_EPOCH_ROLE = getRole('REDIRECT_CLAIM_TO_CHALLENGER_BY_EPOCH_ROLE')
const INCREASE_AMOUNT_ROLE = getRole('INCREASE_AMOUNT_ROLE')

let signers,
stakingManagerRM,
stakingManagerLM,
Expand Down Expand Up @@ -200,18 +203,10 @@ describe('RegistrationManager', () => {
await registrationManager.grantRole(SET_GOVERNANCE_MESSAGE_EMITTER_ROLE, owner.address)
await registrationManager.grantRole(SET_FEES_MANAGER_ROLE, owner.address)
await feesManager.grantRole(REDIRECT_CLAIM_TO_CHALLENGER_BY_EPOCH_ROLE, await registrationManager.getAddress())
await acl
.connect(daoRoot)
.grantPermission(await stakingManagerRM.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('MINT_ROLE'))
await acl
.connect(daoRoot)
.grantPermission(await stakingManagerRM.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('BURN_ROLE'))
await acl
.connect(daoRoot)
.grantPermission(await stakingManagerLM.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('MINT_ROLE'))
await acl
.connect(daoRoot)
.grantPermission(await stakingManagerLM.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('BURN_ROLE'))
await acl.connect(daoRoot).grantPermission(await stakingManagerRM.getAddress(), TOKEN_MANAGER_ADDRESS, MINT_ROLE)
await acl.connect(daoRoot).grantPermission(await stakingManagerRM.getAddress(), TOKEN_MANAGER_ADDRESS, BURN_ROLE)
await acl.connect(daoRoot).grantPermission(await stakingManagerLM.getAddress(), TOKEN_MANAGER_ADDRESS, MINT_ROLE)
await acl.connect(daoRoot).grantPermission(await stakingManagerLM.getAddress(), TOKEN_MANAGER_ADDRESS, BURN_ROLE)

await registrationManager.setFeesManager(await feesManager.getAddress())
await registrationManager.setGovernanceMessageEmitter(await governanceMessageEmitter.getAddress())
Expand Down
22 changes: 9 additions & 13 deletions test/StakingManager.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const {
TOKEN_MANAGER_ADDRESS,
ZERO_ADDRESS
} = require('./constants')
const { getRole } = require('./utils/index')
const { CHANGE_MAX_TOTAL_SUPPLY_ROLE, UPGRADE_ROLE, SLASH_ROLE, MINT_ROLE, BURN_ROLE } = require('./roles')

describe('StakingManager', () => {
let pntHolder1, root, owner, stakingManager, StakingManager, fakeForwarder, challenger, acl, pnt, daoPnt
Expand Down Expand Up @@ -52,15 +52,11 @@ describe('StakingManager', () => {
value: ethers.parseEther('10')
})

await stakingManager.grantRole(getRole('UPGRADE_ROLE'), owner.address)
await stakingManager.grantRole(getRole('SLASH_ROLE'), owner.address)
await stakingManager.grantRole(getRole('CHANGE_MAX_TOTAL_SUPPLY_ROLE'), owner.address)
await acl
.connect(root)
.grantPermission(await stakingManager.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('MINT_ROLE'))
await acl
.connect(root)
.grantPermission(await stakingManager.getAddress(), TOKEN_MANAGER_ADDRESS, getRole('BURN_ROLE'))
await stakingManager.grantRole(UPGRADE_ROLE, owner.address)
await stakingManager.grantRole(SLASH_ROLE, owner.address)
await stakingManager.grantRole(CHANGE_MAX_TOTAL_SUPPLY_ROLE, owner.address)
await acl.connect(root).grantPermission(await stakingManager.getAddress(), TOKEN_MANAGER_ADDRESS, MINT_ROLE)
await acl.connect(root).grantPermission(await stakingManager.getAddress(), TOKEN_MANAGER_ADDRESS, BURN_ROLE)

await owner.sendTransaction({
to: pntHolder1.address,
Expand Down Expand Up @@ -371,19 +367,19 @@ describe('StakingManager', () => {
})

it('should not be able to update the maximun supply without the corresponding role', async () => {
const expectedError = `AccessControl: account ${pntHolder1.address.toLowerCase()} is missing role ${getRole('CHANGE_MAX_TOTAL_SUPPLY_ROLE')}`
const expectedError = `AccessControl: account ${pntHolder1.address.toLowerCase()} is missing role ${CHANGE_MAX_TOTAL_SUPPLY_ROLE}`
await expect(stakingManager.connect(pntHolder1).changeMaxTotalSupply('1')).to.be.revertedWith(expectedError)
})

it('should be able to change the max total supply', async () => {
await stakingManager.grantRole(getRole('CHANGE_MAX_TOTAL_SUPPLY_ROLE'), owner.address)
await stakingManager.grantRole(CHANGE_MAX_TOTAL_SUPPLY_ROLE, owner.address)
await expect(stakingManager.changeMaxTotalSupply('1')).to.emit(stakingManager, 'MaxTotalSupplyChanged').withArgs(1)
})

it('should not be able to mint more than the max total supply', async () => {
const duration = ONE_DAY * 10
const newMaxTotalSupply = ethers.parseEther('1000')
await stakingManager.grantRole(getRole('CHANGE_MAX_TOTAL_SUPPLY_ROLE'), owner.address)
await stakingManager.grantRole(CHANGE_MAX_TOTAL_SUPPLY_ROLE, owner.address)
await stakingManager.changeMaxTotalSupply(newMaxTotalSupply)
await pnt.connect(pntHolder1).approve(await stakingManager.getAddress(), newMaxTotalSupply + 1n)
await expect(
Expand Down
Loading

0 comments on commit 6a1d854

Please sign in to comment.