Skip to content

Commit

Permalink
Always deploy with governor as admin and transfer ownership after (#173)
Browse files Browse the repository at this point in the history
* Set signature bridge sides on the anchor after all initialization has occurred

* SignatureBridgeSides always governed by admin

* Update connect to existing SignatureBridgeSides

* Set the admin of the bridge on static initialization

* Fix tests and address PR comments
  • Loading branch information
nepoche authored Aug 2, 2022
1 parent 0eb172f commit f2bc27b
Show file tree
Hide file tree
Showing 10 changed files with 238 additions and 140 deletions.
6 changes: 6 additions & 0 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { HardhatUserConfig } from 'hardhat/types';
import { HARDHAT_ACCOUNTS } from './hardhatAccounts.js';
import 'hardhat-artifactor';
import 'hardhat-gas-reporter'
import '@typechain/hardhat';
Expand All @@ -25,6 +26,11 @@ subtask('typechain-generate-types',

const config: HardhatUserConfig = {
defaultNetwork: 'hardhat',
networks: {
'hardhat': {
accounts: HARDHAT_ACCOUNTS,
}
},
solidity: {
compilers: [{
version: '0.8.5',
Expand Down
48 changes: 48 additions & 0 deletions hardhatAccounts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
exports.HARDHAT_PK_0 = '0000000000000000000000000000000000000000000000000000000000000010';


exports.HARDHAT_PK_1 = '0000000000000000000000000000000000000000000000000000000000000001';
exports.HARDHAT_PK_2 = '0000000000000000000000000000000000000000000000000000000000000002';

exports.HARDHAT_ACCOUNTS = [
{
privateKey: '0000000000000000000000000000000000000000000000000000000000000010',
balance: '1000000000000000000000',
},
{
privateKey: '0000000000000000000000000000000000000000000000000000000000000001',
balance: '1000000000000000000000',
},
{
privateKey: '0000000000000000000000000000000000000000000000000000000000000002',
balance: '1000000000000000000000',
},
{
privateKey: '0000000000000000000000000000000000000000000000000000000000000003',
balance: '1000000000000000000000',
},
{
privateKey: '0000000000000000000000000000000000000000000000000000000000000004',
balance: '1000000000000000000000',
},
{
privateKey: '0000000000000000000000000000000000000000000000000000000000000005',
balance: '1000000000000000000000',
},
{
privateKey: '0000000000000000000000000000000000000000000000000000000000000006',
balance: '1000000000000000000000',
},
{
privateKey: '0000000000000000000000000000000000000000000000000000000000000007',
balance: '1000000000000000000000',
},
{
privateKey: '0000000000000000000000000000000000000000000000000000000000000008',
balance: '1000000000000000000000',
},
{
privateKey: '0000000000000000000000000000000000000000000000000000000000000009',
balance: '1000000000000000000000',
},
];
11 changes: 8 additions & 3 deletions packages/bridges/src/SignatureBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export class SignatureBridge {
const initialGovernor = initialGovernors[chainID];

// Create the bridgeSide
const bridgeInstance = await SignatureBridgeSide.createBridgeSide(initialGovernor, deployers[chainID]);
const bridgeInstance = await SignatureBridgeSide.createBridgeSide(deployers[chainID]);

const handler = await AnchorHandler.createAnchorHandler(
bridgeInstance.contract.address,
Expand All @@ -129,8 +129,6 @@ export class SignatureBridge {
);
await bridgeInstance.setAnchorHandler(handler);

bridgeSides.set(chainID, bridgeInstance);

// Create Treasury and TreasuryHandler
const treasuryHandler = await TreasuryHandler.createTreasuryHandler(
bridgeInstance.contract.address,
Expand Down Expand Up @@ -216,8 +214,15 @@ export class SignatureBridge {
chainGroupedAnchors.push(anchorInstance);
anchors.set(SignatureBridge.createAnchorIdString({ anchorSize, chainId: chainID }), anchorInstance);
}

// Transfer ownership of the bridge to the initialGovernor
const tx = await bridgeInstance.transferOwnership(initialGovernor, 0);
await tx.wait();

await SignatureBridge.setPermissions(bridgeInstance, chainGroupedAnchors);
createdAnchors.push(chainGroupedAnchors);

bridgeSides.set(chainID, bridgeInstance);
}

// All anchors created, massage data to group anchors which should be linked together
Expand Down
92 changes: 58 additions & 34 deletions packages/bridges/src/SignatureBridgeSide.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { IAnchor, IBridgeSide, Proposal } from '@webb-tools/interfaces';
import { TreasuryHandler } from '@webb-tools/tokens';
import { getChainIdType } from '@webb-tools/utils';
import { signMessage, toHex } from '@webb-tools/sdk-core';
import EC from 'elliptic';
const ec = new EC.ec('secp256k1');

type SystemSigningFn = (data: any) => Promise<string>;

export class SignatureBridgeSide implements IBridgeSide {
contract: SignatureBridge;
Expand All @@ -18,60 +18,84 @@ export class SignatureBridgeSide implements IBridgeSide {
tokenHandler: TokenWrapperHandler;
treasuryHandler: TreasuryHandler;
proposals: Proposal[];
signingSystemSignFn: (data: any) => Promise<string>;
signingSystemSignFn: SystemSigningFn;

ANCHOR_HANDLER_MISSING_ERROR = new Error('Cannot connect an anchor without a handler');
TOKEN_HANDLER_MISSING_ERROR = new Error('Cannot connect to a token wrapper without a handler');
TREASURY_HANDLER_MISSING_ERROR = new Error('Cannot connect to treasury without handler');

private constructor(
contract: SignatureBridge,
governor: ethers.Wallet | string,
signer: ethers.Signer,
signingSystemSignFn?: (data: any) => Promise<string>
systemSigningFn: SystemSigningFn,
) {
this.contract = contract;
this.admin = signer;
this.governor = governor;
this.anchorHandler = null;
this.tokenHandler = null;
this.treasuryHandler = null;
this.proposals = [];
if (signingSystemSignFn) {
// The signing system here is an asynchronous function that
// potentially dispatches a message for a signature and waits
// to receive it. It is potentially a long-running process.
this.signingSystemSignFn = signingSystemSignFn;
} else {
if (typeof governor === 'string') {
throw new Error('Cannot sign with signing system without a governor wallet');
}

this.signingSystemSignFn = (data: any) => {
return Promise.resolve(signMessage(governor, data));
};
}

this.signingSystemSignFn = systemSigningFn
}

/**
* When a bridgeSide is created, the admin is set as the governor.
* Ownership of the bridge can then be transferred to another entity.
*
* @param admin - The deployer and governor upon creation.
*/
public static async createBridgeSide(
initialGovernor: ethers.Wallet | string,
admin: ethers.Signer,
signingSystemSignFn?: (data: any) => Promise<string>
admin: ethers.Wallet
): Promise<SignatureBridgeSide> {
const bridgeFactory = new SignatureBridge__factory(admin);
const deployedBridge = (typeof initialGovernor === 'string')
? await bridgeFactory.deploy(initialGovernor, 0)
: await bridgeFactory.deploy(initialGovernor.address, 0);
const deployedBridge = await bridgeFactory.deploy(admin.address, 0);
await deployedBridge.deployed();
const bridgeSide = (typeof initialGovernor === 'string')
? new SignatureBridgeSide(deployedBridge, initialGovernor, admin, signingSystemSignFn)
: new SignatureBridgeSide(deployedBridge, initialGovernor, admin, signingSystemSignFn);
const bridgeSide = new SignatureBridgeSide(deployedBridge, (data: any) => {
return Promise.resolve(signMessage(admin,data));
});
bridgeSide.admin = admin;
bridgeSide.governor = admin;
return bridgeSide;
}

public static async connect(address: string, governor: ethers.Wallet, admin: ethers.Wallet) {
const deployedBridge = SignatureBridge__factory.connect(address, admin);
const bridgeSide = new SignatureBridgeSide(deployedBridge, governor, admin);
/**
* When an existing SignatureBridge is connected, the governor must be configured.
* In the case of connectMocked, a wallet address is passed which will act as the governor.
*
* connectMocked is particularly useful for integration testing
*
* @param contractAddress - The contract address of the SignatureBridge contract instance.
* @param mockedGovernor - The ethers.Wallet which will sign messages before execution on the bridgeSide.
*/
public static async connectMocked(contractAddress: string, mockedGovernor: ethers.Wallet) {
const deployedBridge = SignatureBridge__factory.connect(contractAddress, mockedGovernor);
const bridgeSide = new SignatureBridgeSide(deployedBridge, (data: string) => {
return Promise.resolve(signMessage(mockedGovernor,data));
});
bridgeSide.governor = mockedGovernor;
bridgeSide.admin = mockedGovernor;
return bridgeSide;
}

/**
* When an existing SignatureBridge is connected, the governor must be configured.
* In the case of connectGovernor, a network is passed for querying the chain as well
* as a signing function which can keep this class generic.
*
* connectGovernor is necessary for interacting with this class when the private key
* of the governor is unavailable, but signed proposals are available.
*
* @param contractAddress - The contract address of the SignatureBridge contract instance.
* @param provider - The network which the contract address exists upon.
* @param systemSigningFn - a function which will produce a signature that verifies as
* coming from the configured governor on chain.
*/
public static async connectGovernor(
contractAddress: string,
provider: ethers.providers.Provider,
systemSigningFn: SystemSigningFn
) {
const deployedBridge = SignatureBridge__factory.connect(contractAddress, provider);
const bridgeSide = new SignatureBridgeSide(deployedBridge, systemSigningFn);
return bridgeSide;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/interfaces/src/bridge/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { IAnchor } from '..';
import { IBridgeSide } from '../IBridgeSide';

// Deployer config matches the chainId to the signer for that chain
export type DeployerConfig = Record<number, ethers.Signer>;
export type DeployerConfig = Record<number, ethers.Wallet>;

// Initial Governor config the chainId to the initial governor for that chain
export type GovernorConfig = Record<number, ethers.Wallet>;
export type GovernorConfig = Record<number, string>;

export type AnchorIdentifier = {
anchorSize?: ethers.BigNumberish;
Expand Down
17 changes: 12 additions & 5 deletions packages/vbridge/src/VBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,15 @@ export class VBridge {
return linkedVAnchorMap;
}

// Deployments of all contracts for the bridge will be done with the DeployerConfig.
// After deployments, the wallet in the DeployerConfig will transfer ownership
// to the initialGovernor
public static async deployVariableAnchorBridge(
vBridgeInput: VBridgeInput,
deployers: DeployerConfig,
initialGovernors: GovernorConfig,
smallCircuitZkComponents: ZkComponents,
largeCircuitZkComponents: ZkComponents
largeCircuitZkComponents: ZkComponents,
): Promise<VBridge> {
let webbTokenAddresses: Map<number, string> = new Map();
let vBridgeSides: Map<number, SignatureBridgeSide> = new Map();
Expand All @@ -125,7 +128,7 @@ export class VBridge {
for (let chainID of vBridgeInput.chainIDs) {
const initialGovernor = initialGovernors[chainID];
// Create the bridgeSide
let vBridgeInstance = await SignatureBridgeSide.createBridgeSide(initialGovernor, deployers[chainID]);
let vBridgeInstance = await SignatureBridgeSide.createBridgeSide(deployers[chainID]);

const handler = await AnchorHandler.createAnchorHandler(
vBridgeInstance.contract.address,
Expand All @@ -135,8 +138,6 @@ export class VBridge {
);
vBridgeInstance.setAnchorHandler(handler);

vBridgeSides.set(chainID, vBridgeInstance);

// Create Treasury and TreasuryHandler
const treasuryHandler = await TreasuryHandler.createTreasuryHandler(
vBridgeInstance.contract.address,
Expand Down Expand Up @@ -226,6 +227,11 @@ export class VBridge {

await VBridge.setPermissions(vBridgeInstance, chainGroupedVAnchors);
createdVAnchors.push(chainGroupedVAnchors);

// Transfer ownership of the bridge to the initialGovernor
const tx = await vBridgeInstance.transferOwnership(initialGovernor, 0);
await tx.wait();
vBridgeSides.set(chainID, vBridgeInstance);
}

// All anchors created, massage data to group anchors which should be linked together
Expand All @@ -240,8 +246,9 @@ export class VBridge {
groupLinkedVAnchors.push(linkedAnchors);
}

// finally, link the anchors
// link the anchors
const linkedVAnchorMap = await VBridge.createLinkedVAnchorMap(groupLinkedVAnchors);

return new VBridge(vBridgeSides, webbTokenAddresses, linkedVAnchorMap, vAnchors);
}

Expand Down
Loading

0 comments on commit f2bc27b

Please sign in to comment.