Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
ee65afb
current dev
MishaShWoof Jul 25, 2025
7e08ec9
fix: working dev
MishaShWoof Jul 25, 2025
2bb3587
fix: scenarios
MishaShWoof Jul 28, 2025
9f5564c
fix
MishaShWoof Jul 28, 2025
88fe803
fix
MishaShWoof Jul 28, 2025
8b5ee76
Update index.ts
MishaShWoof Jul 28, 2025
700a4c6
Update DeploymentManager.ts
MishaShWoof Jul 29, 2025
4cbf863
fix: unit tests
MishaShWoof Aug 14, 2025
2bc2dbf
fix: working linea scenarios and prepare workflow fix
MishaShWoof Aug 20, 2025
0313418
Merge branch 'main' of github.com:woof-software/comet into develop
MishaShWoof Sep 16, 2025
688d515
fix: remove linea markets from dev
MishaShWoof Sep 16, 2025
df8d724
fix: remove localhost deployment
MishaShWoof Sep 19, 2025
0001bd6
fix: unify import
MishaShWoof Sep 22, 2025
d04a40d
Merge branch 'main' of github.com:woof-software/comet into develop
MishaShWoof Sep 25, 2025
2621df6
Merge branch 'main' of github.com:woof-software/comet into develop
MishaShWoof Nov 11, 2025
aceaf43
fixes from recent prs
MishaShWoof Nov 11, 2025
3f9caef
fix: change base rpc
MishaShWoof Nov 12, 2025
68f6aab
fix: change rpc
MishaShWoof Nov 14, 2025
d2801ea
fix: replace ankr with quicknode
MishaShWoof Nov 17, 2025
001b60c
Merge branch 'main' of github.com:woof-software/comet into develop
MishaShWoof Nov 24, 2025
38a9d9e
fix: working tests
MishaShWoof Nov 24, 2025
525e5a8
fix: working forge test
MishaShWoof Nov 24, 2025
d73df39
Update run-forge-tests.yaml
MishaShWoof Nov 24, 2025
640d956
Update run-forge-tests.yaml
MishaShWoof Nov 24, 2025
49ac136
Update run-scenarios.yaml
MishaShWoof Nov 27, 2025
a16813a
fix: env in scenario
MishaShWoof Nov 27, 2025
0407aa6
fix: use different keys for etherscan
MishaShWoof Nov 27, 2025
1a368d4
fix: strict order
MishaShWoof Nov 27, 2025
a34434f
fix: pass the prepared state
MishaShWoof Nov 27, 2025
9d6c077
fix: remove matrix
MishaShWoof Nov 27, 2025
58ed478
fix: pass node modules
MishaShWoof Nov 27, 2025
0e67e92
fix: add contracts to cache before scenario run
MishaShWoof Nov 28, 2025
bc0871e
fix: cache path fix
MishaShWoof Nov 28, 2025
5f4fc6f
fix: use hidden files
MishaShWoof Nov 28, 2025
5a77147
fix: fuji
MishaShWoof Nov 28, 2025
b287ff2
feat: rotate apikeys and some arbitrum fixes
MishaShWoof Dec 2, 2025
070fda2
Merge pull request #279 from woof-software/speed-up-scenarios
MishaShWoof Dec 3, 2025
98e6783
fix: working arbitrum scenarios
MishaShWoof Dec 3, 2025
b2b05a2
fix: teth symbol fix
MishaShWoof Dec 3, 2025
a0fdad7
fix: add debug for spider
MishaShWoof Dec 3, 2025
0cf69cd
Merge branch 'main' of github.com:woof-software/comet into develop
MishaShWoof Dec 22, 2025
e7f8ce6
fix: ronin and forge test fixes
MishaShWoof Dec 22, 2025
b3456da
fix: working arbitrum scenarios
MishaShWoof Dec 24, 2025
8b2ea24
new fixes
MishaShWoof Feb 23, 2026
73cb01d
fix
MishaShWoof Feb 23, 2026
7d4a913
fix: precision fix
MishaShWoof Feb 23, 2026
f3ebdba
fix: change scroll rpc
MishaShWoof Apr 9, 2026
ccc4855
fix: update relations and cctp addresses
MishaShWoof Apr 24, 2026
e5144db
fix
MishaShWoof Apr 24, 2026
c980175
fix: handle max gaslimit per tx
MishaShWoof May 1, 2026
750ede2
fix: slither
MishaShWoof May 1, 2026
e02ba0d
fix: change capo branch
MishaShWoof May 13, 2026
e18d7a8
fix: supply and withdraw scenario fixes
MishaShWoof May 19, 2026
a9747c9
feat: recent proposals
MishaShWoof May 21, 2026
c1c411a
fix: speed up gas tests
MishaShWoof May 21, 2026
2c7d198
fix: set enacted to true
MishaShWoof May 21, 2026
7b738e5
fix: remove redundant migrations
MishaShWoof May 22, 2026
eadf6f4
fix: undo delition
MishaShWoof May 22, 2026
feef68f
Merge branch 'main' of github.com:woof-software/comet into develop
MishaShWoof Jun 1, 2026
8bea7de
Next Develop Update (#433)
MishaShWoof Jun 1, 2026
e24f281
fix: remove Comet.sol, drop Sepolia and Fuji deployments, and update …
MishaShWoof Jun 1, 2026
877935b
fix: remove deleted param from deploy scripts
MishaShWoof Jun 1, 2026
f4d74bd
Delete ScrollComet.sol
MishaShWoof Jun 1, 2026
38e68d9
fix: revert multichain proposal simulation from develop
VladyslavBudiukWOOF Jun 8, 2026
f780802
feat: New migration
MishaShWoof Jun 15, 2026
2b51ee2
fix: Use tenderly params from .env
MishaShWoof Jun 15, 2026
875df2c
feat: description
MishaShWoof Jun 16, 2026
776380a
fix: Update description and contract abi and address
MishaShWoof Jun 18, 2026
befea91
Merge branch 'main' of github.com:woof-software/comet into woof-softw…
MishaShWoof Jul 6, 2026
53c9c8b
feat: new deployment and audit link
MishaShWoof Jul 6, 2026
04aeb9d
fix: new description and updated logic for proposal
MishaShWoof Jul 7, 2026
131e04c
Modified migration from GitHub Actions
Jul 14, 2026
4b992e2
feat: change mock address to actual
MishaShWoof Jul 14, 2026
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
260 changes: 260 additions & 0 deletions deployments/mainnet/usdc/migrations/1781520280_setup_franchiser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
import { expect } from 'chai';
import { DeploymentManager } from '../../../../plugins/deployment_manager/DeploymentManager';
import { migration } from '../../../../plugins/deployment_manager/Migration';
import { exp, proposal } from '../../../../src/deploy';
import { Contract } from 'ethers';

const OLD_FRANCHISER_FACTORY = '0xE696d89f4F378772f437F01FaaD70240abdf1854';
const FRANCHISER_POOL_FACTORY = '0x8421c35b1235741e00d2475f365A434d4b476A19';

const poolConfig = {
coordinator: '0xfd947c72f09703210eeCbcab9c9206fE5e1Bb6e2',
guardian: '0xbbf3f1421D886E9b2c5D716B5192aC998af2012c',
maxDelegatees: 30,
freezePeriod: 10 * 24 * 60 * 60, // 10 days in seconds
amount: exp(610000, 18), // 610,000 COMP
};

const oldFranchisers = [
'0xDff7Ea1B9c6c23d63B9038428767231439321D12',
'0x99e0705d0C93f1cb982bCE4c14ce7feD7e23Cf83',
'0x1e76b5bcf86967d5555ECDEDDd67248F949dc5BC',
'0x2F845447F1040176091Ed16C72E9c4ddC292F14B',
'0xC623e9bc66cF498ABdC3c7297e9Df95f20C2F0a8',
'0xca038273e110c8e39543E7FAB9279FB42fE32c63',
'0x59dC38b5CEe74ca7189394eddAf5A13611DF0B63',
'0xe8B2B94612240d57D830944a668E4e48eDF9F9D8',
'0xEC8604670F07dB6E40f84147F4297b7f58775350',
'0x603657fDEc47a9dF15A5661cE011E13C0D449982',
'0x1543D57471A5F168cBabb5922246a77013c9d2E4',
'0xBbc353508e782d44fFbD28d2869bd527070b24F2',
'0x78BE1f3dd735bB4F20decb62A1beCE31DA452126',
];

const delegateesToSkip = [
'0x13BDaE8c5F0fC40231F0E6A4ad70196F59138548', // Michigan Blockchain | 29,999.88 COMP
'0x4f894Bfc9481110278C356adE1473eBe2127Fd3C', // Reservoir / AlphaGrowth | 50,000.00 COMP
'0x72C58877ef744b86F6ef416a3bE26Ec19d587708', // Sharp | 1,178.70 COMP
];

const delegateesToReallocate = [
{ delegatee: '0x070341aA5Ed571f0FB2c4a5641409B1A46b4961b', amount: exp(8262.46, 18) }, // FranklinDAO
{ delegatee: '0xc5547B4907418C2EC0C2A95beC6fEE8354657759', amount: exp(40000, 18) }, // DAOplomats
{ delegatee: '0x1F3D3A7A9c548bE39539b39D7400302753E20591', amount: exp(32916.12, 18) }, // blockful
];

const delegatePowerBefore: { [delegatee: string]: bigint } = {};
const delegateesConfig: {delegatee: string, balance: bigint}[] = [];

export default migration('1781520280_setup_franchiser', {
async prepare() {
return {};
},

async enact(deploymentManager: DeploymentManager) {
const trace = deploymentManager.tracer();

const {
governor,
timelock,
COMP,
} = await deploymentManager.getContracts();

const oldFranchiserFactory = await deploymentManager.existing('oldFranchiserFactory', OLD_FRANCHISER_FACTORY);
const franchiserPoolFactory = await deploymentManager.existing('franchiserPoolFactory', FRANCHISER_POOL_FACTORY);

let totalBalance = BigInt(0);

for (const franchiser of oldFranchisers) {
const balance = await COMP.balanceOf(franchiser);

const franchiserContract = new Contract(
franchiser,
['function delegatee() view returns (address)'],
await deploymentManager.getSigner()
);
const delegatee = await franchiserContract.delegatee();

trace(`Franchiser ${franchiser}, (delegatee ${delegatee}) has balance ${balance.toString()}`);
delegateesConfig.push({ delegatee, balance: balance.toBigInt() });
totalBalance += balance.toBigInt();

const delegatePower = await COMP.getCurrentVotes(delegatee);
delegatePowerBefore[delegatee] = delegatePower.toBigInt();
}

trace(`Total balance of old franchisers: ${totalBalance}`);

// Capture baseline voting power for reallocated delegatees that have no old franchiser (e.g. brand-new delegates).
for (const { delegatee } of delegateesToReallocate) {
if (!(delegatee in delegatePowerBefore)) {
delegatePowerBefore[delegatee] = (await COMP.getCurrentVotes(delegatee)).toBigInt();
}
}

const delegatees = delegateesConfig.map(({ delegatee }) => delegatee);

for (const { delegatee, amount } of delegateesToReallocate) {
const existing = delegateesConfig.find((c) => c.delegatee === delegatee);
if (existing) {
existing.balance += amount;
} else {
delegateesConfig.push({ delegatee, balance: amount });
}
}

const delegateesForRenewal = delegateesConfig.filter(({ delegatee }) => !delegateesToSkip.includes(delegatee));
trace(`Delegatees for renewal: ${delegateesForRenewal.map(({ delegatee }) => delegatee).join(', ')}`);

const mainnetActions = [
// 1. Recall COMP tokens from old franchisers back to the timelock
{
contract: oldFranchiserFactory,
signature: 'recallMany(address[],address[])',
args: [
delegatees,
Array(delegatees.length).fill(timelock.address),
],
},
// 2. Approve factory to transfer COMP tokens on behalf of the timelock
{
contract: COMP,
signature: 'approve(address,uint256)',
args: [franchiserPoolFactory.address, poolConfig.amount],
},
// 3. Create a new pool for the franchisers
{
contract: franchiserPoolFactory,
signature: 'createPoolAndFund(address,address,uint256,uint256,uint256,address[],uint256[])',
args: [
poolConfig.coordinator,
poolConfig.guardian,
poolConfig.maxDelegatees,
poolConfig.freezePeriod,
poolConfig.amount,
delegateesForRenewal.map(({ delegatee }) => delegatee),
delegateesForRenewal.map(({ balance }) => balance.toString())
],
},
];

const description = `# Franchiser Operational Upgrade and Rebalance

## Summary

This proposal upgrades Compound's treasury-delegation (Franchiser) system to a new pool-based architecture and, in the same step, carries out the first scheduled biannual delegate rebalancing under **Proposal 504 — [Compound Delegate Race (Cycle 2)](https://www.comp.xyz/t/compound-delegate-race-cycle-2/7302)**.

The total COMP delegated through the program does not change. Existing delegations migrate to the new pool with no interruption to voting power, and the pool reclaimed from under-participating delegates is redistributed to qualified active delegates. COMP remains in Governance-owned contracts throughout.

## Background

Proposal 504 established a standing six-monthly review of treasury-delegated voting power, with a uniform on-chain participation standard and a defined reallocation procedure. The first review window (Proposals 505–586) has closed and participation has been verified on-chain. The full review, application window, and waterfall are documented on the forum: [comp.xyz/t/7862](https://www.comp.xyz/t/7862).

## What this proposal does

1. **Upgrade.** Migrate the treasury-delegation program to the upgraded Franchiser pool: existing delegations move into a new Governance-funded pool, with the CGWG multisig set as Coordinator and a Guardian set for emergency oversight. Governance retains sole authority to fund, halt, or reclaim the pool in full.

2. **Rebalance.** As part of the migration, three delegations that fell below the participation standard are not renewed — releasing **81,178.58 COMP** — which is redistributed to the qualified applicants per the waterfall.

**Not renewed — 81,178.58 COMP reclaimed:**

| Delegatee | Address | COMP |
|---|---|---:|
| Michigan Blockchain | \`0x13BD…8548\` | 29,999.88 |
| Reservoir / AlphaGrowth | \`0x4f89…fd3c\` | 50,000.00 |
| Sharp | \`0x72C5…7708\` | 1,178.70 |

**Reallocated — 81,178.58 COMP:**

| Delegate | Address | COMP |
|---|---|---:|
| FranklinDAO | \`0x0703…961b\` | 8,262.46 |
| DAOplomats | \`0xc554…7759\` | 40,000.00 |
| blockful | \`0x1F3D…0591\` | 32,916.12 |

## The upgraded architecture

The upgrade replaces per-delegation Franchiser deployments with a single pool, split across three role-gated contracts:

- **FranchiserPoolFactory** — the Governance-only entry point. Governance creates, funds, and can halt a pool; halting recalls all delegates and returns the full COMP balance to the Timelock (the only path for COMP to leave the program).
- **FranchiserPool** — holds the program's idle COMP. A **Coordinator** (the CGWG multisig) can \`delegate\`, \`recall\`, and \`reassign\` voting power among delegates within Governance-set limits. A separate **Guardian** can emergency-\`recall\` a delegate or \`freeze\` the pool (10-day minimum), but can never move or delegate COMP.
- **Franchiser** — unchanged from V1: holds delegated COMP and grants voting power to a delegatee (who may name up to one sub-delegate).

Governance (the Timelock) sets each pool's parameters — delegate cap, Coordinator, Guardian, freeze period — and can change them or halt the program at any time by on-chain vote.

## Guarantees

- **Delegation only, never custody.** COMP stays in Governance-owned contracts at every step (Timelock → pool → Franchiser); no working group, Coordinator, or Guardian ever takes custody of the tokens.
- **Total set by governance.** This proposal does not change the program's total delegated COMP — it migrates and reallocates the existing pool. Any change to the total returns to an on-chain vote, and Governance can recall or reassign any delegation at any time.
`;

const txn = await deploymentManager.retry(async () =>
trace(
await governor.propose(...(await proposal(mainnetActions, description)))
), 0, 300_000
);

const event = txn.events.find(
(event: { event: string }) => event.event === 'ProposalCreated'
);
const [proposalId] = event.args;
trace(`Created proposal ${proposalId}.`);
},

async enacted(): Promise<boolean> {
return false;
},

async verify(deploymentManager: DeploymentManager) {

const { COMP } = await deploymentManager.getContracts();

const franchiserPoolFactory = await deploymentManager.existing('franchiserPoolFactory', FRANCHISER_POOL_FACTORY);

const pools = await franchiserPoolFactory.getAllPools();
expect(pools.length).to.equal(1);

const pool = new Contract(
pools[0],
[
'function coordinator() view returns (address)',
'function guardian() view returns (address)',
'function maxDelegatees() view returns (uint256)',
'function freezePeriod() view returns (uint256)',
],
await deploymentManager.getSigner()
);

expect(await pool.coordinator()).to.equal(poolConfig.coordinator);
expect(await pool.guardian()).to.equal(poolConfig.guardian);
expect(await pool.maxDelegatees()).to.equal(poolConfig.maxDelegatees);
expect(await pool.freezePeriod()).to.equal(poolConfig.freezePeriod);

for (const franchiser of oldFranchisers) {
expect(await COMP.balanceOf(franchiser)).to.equal(0);
}

// Check that all COMP is used for franchisers and none is left in the pool contract.
expect(await COMP.balanceOf(pools[0])).to.equal(0);

// Verify that delegatees have the expected voting power after migration.
for (const delegatee of Object.keys(delegatePowerBefore)) {
const delegatePowerAfter = await COMP.getCurrentVotes(delegatee);
const reallocation = delegateesToReallocate.find(({ delegatee: d }) => d === delegatee);

if (delegateesToSkip.includes(delegatee)) {
// Delegatees that were skipped for reallocation should have 0 voting power after migration.
expect(delegatePowerBefore[delegatee] - delegatePowerAfter.toBigInt())
.to.equal(delegateesConfig.find(({ delegatee: d }) => d === delegatee)?.balance);
}
else if (reallocation) {
// Delegatees that received a reallocation should gain exactly the reallocated amount.
expect(delegatePowerAfter.toBigInt() - delegatePowerBefore[delegatee]).to.equal(reallocation.amount);
}
else {
// Delegatees that were renewed should have the same voting power as before migration.
expect(delegatePowerAfter.toBigInt()).to.equal(delegatePowerBefore[delegatee]);
}
}
},
});
15 changes: 11 additions & 4 deletions scenario/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,8 @@ export async function isValidAssetIndex(
if (assetNum >= MAX_ASSETS) return false;
// Asset info checks. If any of these are false, the asset is invalid. This means that the asset is deprecated.
const comet = await ctx.getComet();
const numAssets = await comet.numAssets();
if (assetNum >= numAssets) return false;
const assetInfo = await comet.getAssetInfo(assetNum);
if (assetInfo.borrowCollateralFactor.toBigInt() == 0n) return false;
if (assetInfo.supplyCap.toBigInt() == 0n) return false;
Expand Down Expand Up @@ -1001,9 +1003,11 @@ async function simulateBundle(
const rollingStateChanges = {};
const results = [];

for (const sim of simulations) {
const { username, project, accessKey } = (dm.hre.config as any).tenderly;
const project = 'comet';
const username = process.env.TENDERLY_USERNAME || '';
const accessKey = process.env.TENDERLY_ACCESS_KEY || '';

for (const sim of simulations) {
// Merge rolling state changes with simulation's own state_objects
const stateObjects = sim.state_objects
? { ...rollingStateChanges, ...sim.state_objects }
Expand Down Expand Up @@ -1059,8 +1063,11 @@ async function simulateBundle(
return results;
}

async function shareSimulation(dm: DeploymentManager, simulationId: string) {
const { username, project, accessKey } = (dm.hre.config as any).tenderly;
async function shareSimulation(dm: DeploymentManager, simulationId: string) {
const project = 'comet';
const username = process.env.TENDERLY_USERNAME || '';
const accessKey = process.env.TENDERLY_ACCESS_KEY || '';

return axios.post(
`https://api.tenderly.co/api/v1/account/${username}/project/${project}/simulations/${simulationId}/share`,
{},
Expand Down