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