Update optics-provider with governance capabilities (#21)
* Update optics-provider with governance capabilities * Update mainnet domains * Update staging community domains * Update staging community domains * update mainnet domains * Update staging domain * Add domain update scripts * Re-add pagination * address commentspull/29/head
parent
632cc53ee1
commit
d63e38123d
@ -0,0 +1,16 @@ |
|||||||
|
import * as celo from '../../config/mainnets/celo'; |
||||||
|
import * as ethereum from '../../config/mainnets/ethereum'; |
||||||
|
import * as avalanche from '../../config/mainnets/avalanche'; |
||||||
|
import * as polygon from '../../config/mainnets/polygon'; |
||||||
|
import { updateProviderDomain } from '../../src/provider'; |
||||||
|
import { makeAllConfigs } from '../../src/config'; |
||||||
|
|
||||||
|
|
||||||
|
const configPath = '../../rust/config/production-community'; |
||||||
|
updateProviderDomain('mainnetCommunity', configPath, [ |
||||||
|
makeAllConfigs(celo, (_) => _.config), |
||||||
|
makeAllConfigs(ethereum, (_) => _.config), |
||||||
|
makeAllConfigs(avalanche, (_) => _.config), |
||||||
|
makeAllConfigs(polygon, (_) => _.config), |
||||||
|
]); |
||||||
|
|
@ -0,0 +1,14 @@ |
|||||||
|
import * as celo from '../../config/mainnets/celo'; |
||||||
|
import * as ethereum from '../../config/mainnets/ethereum'; |
||||||
|
import * as polygon from '../../config/mainnets/polygon'; |
||||||
|
import { updateProviderDomain } from '../../src/provider'; |
||||||
|
import { makeAllConfigs } from '../../src/config'; |
||||||
|
|
||||||
|
|
||||||
|
const configPath = '../../rust/config/mainnet'; |
||||||
|
updateProviderDomain('mainnet', configPath, [ |
||||||
|
makeAllConfigs(ethereum, (_) => _.config), |
||||||
|
makeAllConfigs(polygon, (_) => _.config), |
||||||
|
makeAllConfigs(celo, (_) => _.config), |
||||||
|
]); |
||||||
|
|
@ -0,0 +1,16 @@ |
|||||||
|
import * as alfajores from '../../config/testnets/alfajores'; |
||||||
|
import * as kovan from '../../config/testnets/kovan'; |
||||||
|
import * as gorli from '../../config/testnets/gorli'; |
||||||
|
import * as ropsten from '../../config/testnets/ropsten'; |
||||||
|
import { updateProviderDomain } from '../../src/provider'; |
||||||
|
import { makeAllConfigs } from '../../src/config'; |
||||||
|
|
||||||
|
|
||||||
|
const configPath = '../../rust/config/staging-community'; |
||||||
|
updateProviderDomain('stagingCommunity', configPath, [ |
||||||
|
makeAllConfigs(alfajores, (_) => _.devConfig), |
||||||
|
makeAllConfigs(ropsten, (_) => _.devConfig), |
||||||
|
makeAllConfigs(kovan, (_) => _.devConfig), |
||||||
|
makeAllConfigs(gorli, (_) => _.devConfig), |
||||||
|
]); |
||||||
|
|
@ -0,0 +1,14 @@ |
|||||||
|
import * as alfajores from '../../config/testnets/alfajores'; |
||||||
|
import * as kovan from '../../config/testnets/kovan'; |
||||||
|
import * as rinkeby from '../../config/testnets/rinkeby'; |
||||||
|
import { updateProviderDomain } from '../../src/provider'; |
||||||
|
import { makeAllConfigs } from '../../src/config'; |
||||||
|
|
||||||
|
|
||||||
|
const configPath = '../../rust/config/staging'; |
||||||
|
updateProviderDomain('staging', configPath, [ |
||||||
|
makeAllConfigs(alfajores, (_) => _.devConfig), |
||||||
|
makeAllConfigs(kovan, (_) => _.devConfig), |
||||||
|
makeAllConfigs(rinkeby, (_) => _.devConfig), |
||||||
|
]); |
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,105 @@ |
|||||||
|
import { ethers } from 'ethers'; |
||||||
|
import { CoreContracts } from '../contracts'; |
||||||
|
|
||||||
|
|
||||||
|
import * as utils from './utils'; |
||||||
|
|
||||||
|
export type Address = string; |
||||||
|
|
||||||
|
export interface Call { |
||||||
|
to: Address; |
||||||
|
data: ethers.utils.BytesLike; |
||||||
|
} |
||||||
|
|
||||||
|
export class CallBatch { |
||||||
|
readonly local: Readonly<Call>[]; |
||||||
|
readonly remote: Map<number, Readonly<Call>[]>; |
||||||
|
private core: CoreContracts; |
||||||
|
private built?: ethers.PopulatedTransaction[]; |
||||||
|
|
||||||
|
constructor(core: CoreContracts) { |
||||||
|
this.core = core; |
||||||
|
this.remote = new Map(); |
||||||
|
this.local = []; |
||||||
|
} |
||||||
|
|
||||||
|
static async fromCore(core: CoreContracts): Promise<CallBatch> { |
||||||
|
const governor = await core.governor(); |
||||||
|
if (!governor.local) |
||||||
|
throw new Error( |
||||||
|
'Cannot create call batch on a chain without governance rights. Use the governing chain.', |
||||||
|
); |
||||||
|
return new CallBatch(core); |
||||||
|
} |
||||||
|
|
||||||
|
pushLocal(call: Call): void { |
||||||
|
if (this.built) |
||||||
|
throw new Error('Batch has been built. Cannot push more calls'); |
||||||
|
this.local.push(utils.normalizeCall(call)); |
||||||
|
} |
||||||
|
|
||||||
|
pushRemote(domain: number, call: Call): void { |
||||||
|
if (this.built) |
||||||
|
throw new Error('Batch has been built. Cannot push more calls'); |
||||||
|
const calls = this.remote.get(domain); |
||||||
|
const normalized = utils.normalizeCall(call); |
||||||
|
if (!calls) { |
||||||
|
this.remote.set(domain, [normalized]); |
||||||
|
} else { |
||||||
|
calls.push(normalized); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Build governance transactions from this callbatch
|
||||||
|
async build( |
||||||
|
overrides?: ethers.Overrides, |
||||||
|
): Promise<ethers.PopulatedTransaction[]> { |
||||||
|
if (this.built && overrides) |
||||||
|
throw new Error('Cannot rebuild batch with new overrides') |
||||||
|
if (this.built) return this.built; |
||||||
|
const [domains, remoteCalls] = utils.associateRemotes(this.remote); |
||||||
|
const local = await this.core.governanceRouter.populateTransaction.callLocal(this.local) |
||||||
|
const remotes = await Promise.all( |
||||||
|
domains.map((domain: number, i: number) => this.core.governanceRouter.populateTransaction.callRemote(domain, remoteCalls[i], overrides)) |
||||||
|
) |
||||||
|
this.built = remotes.concat(local) |
||||||
|
return this.built; |
||||||
|
} |
||||||
|
|
||||||
|
// Sign each governance transaction and dispatch them to the chain
|
||||||
|
async execute( |
||||||
|
overrides?: ethers.Overrides, |
||||||
|
): Promise<ethers.providers.TransactionReceipt[]> { |
||||||
|
const transactions = await this.build(overrides); |
||||||
|
const signer = await this.governorSigner() |
||||||
|
const receipts = [] |
||||||
|
for (const tx of transactions) { |
||||||
|
const response = await signer.sendTransaction(tx) |
||||||
|
receipts.push(await response.wait()) |
||||||
|
} |
||||||
|
return receipts |
||||||
|
} |
||||||
|
|
||||||
|
async estimateGas( |
||||||
|
overrides?: ethers.Overrides, |
||||||
|
): Promise<any[]> { |
||||||
|
const transactions = await this.build(overrides); |
||||||
|
const signer = await this.governorSigner() |
||||||
|
const responses = [] |
||||||
|
for (const tx of transactions) { |
||||||
|
responses.push(await signer.estimateGas(tx)) |
||||||
|
} |
||||||
|
return responses |
||||||
|
} |
||||||
|
|
||||||
|
async governorSigner(): Promise<ethers.Signer> { |
||||||
|
const signer = this.core.governanceRouter.signer; |
||||||
|
const governor = await this.core.governor() |
||||||
|
const signerAddress = await signer.getAddress() |
||||||
|
if (!governor.local) |
||||||
|
throw new Error('Governor is not local'); |
||||||
|
if (signerAddress !== governor.identifier) |
||||||
|
throw new Error('Signer is not Governor'); |
||||||
|
return signer |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
import { ethers } from 'ethers'; |
||||||
|
import { Call } from '.'; |
||||||
|
import { canonizeId } from '../../utils'; |
||||||
|
|
||||||
|
// Returns the length (in bytes) of a BytesLike.
|
||||||
|
export function byteLength(bytesLike: ethers.utils.BytesLike): number { |
||||||
|
return ethers.utils.arrayify(bytesLike).length; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Serialize a call to its packed Optics governance representation |
||||||
|
* @param call The function call to serialize |
||||||
|
* @returns The serialized function call, as a '0x'-prepended hex string |
||||||
|
*/ |
||||||
|
export function serializeCall(call: Call): string { |
||||||
|
const { to, data } = call; |
||||||
|
const dataLen = byteLength(data); |
||||||
|
|
||||||
|
if (!to || !data) { |
||||||
|
throw new Error(`Missing data in Call: \n ${call}`); |
||||||
|
} |
||||||
|
|
||||||
|
return ethers.utils.solidityPack( |
||||||
|
['bytes32', 'uint32', 'bytes'], |
||||||
|
[to, dataLen, data], |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
export function associateRemotes( |
||||||
|
remoteCalls: Map<number, Call[]>, |
||||||
|
): [number[], Call[][]] { |
||||||
|
const domains = []; |
||||||
|
const calls = []; |
||||||
|
for (const [key, value] of remoteCalls) { |
||||||
|
domains.push(key); |
||||||
|
calls.push(value); |
||||||
|
} |
||||||
|
return [domains, calls]; |
||||||
|
} |
||||||
|
|
||||||
|
export function normalizeCall(partial: Partial<Call>): Readonly<Call> { |
||||||
|
const to = ethers.utils.hexlify(canonizeId(partial.to!)); |
||||||
|
const data = partial.data ?? '0x'; |
||||||
|
|
||||||
|
return Object.freeze({ |
||||||
|
to, |
||||||
|
data, |
||||||
|
}); |
||||||
|
} |
Loading…
Reference in new issue