Skip to content

Commit

Permalink
Implement IOracle to SortedOracles & add FeeCurrencyDirectory to devc…
Browse files Browse the repository at this point in the history
…hain (#10993)

* FeeCurrencyDirectory to devchain

* Mento Fee Currency adapter

* MentoFeeCurrencyAdapter test

* lint

* Update of SortedOracles

* tests added

* fix test release

* lint

* removal of MentoFeeCurrencyAdapterV1
  • Loading branch information
pahor167 authored May 16, 2024
1 parent 894fd26 commit 43a3146
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 41 deletions.
8 changes: 6 additions & 2 deletions packages/protocol/contractPackages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ export const SOLIDITY_08_PACKAGE = {
proxiesPath: '/', // Proxies are still with 0.5 contracts
// Proxies shouldn't have to be added to a list manually
// https://github.com/celo-org/celo-monorepo/issues/10555
contracts: ['GasPriceMinimum'],
proxyContracts: ['GasPriceMinimumProxy'],
contracts: ['GasPriceMinimum', 'FeeCurrencyDirectory'],
proxyContracts: [
'GasPriceMinimumProxy',
'FeeCurrencyDirectoryProxy',
'MentoFeeCurrencyAdapterV1',
],
truffleConfig: 'truffle-config0.8.js',
} satisfies ContractPackage
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma solidity >=0.5.13 <0.9.0;

/// Possibly not final version
interface IOracle {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pragma solidity ^0.5.13;

import "../Proxy.sol";

/* solhint-disable-next-line no-empty-blocks */
contract FeeCurrencyDirectoryProxy is Proxy {}
37 changes: 25 additions & 12 deletions packages/protocol/contracts/stability/SortedOracles.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import "../common/FixidityLib.sol";
import "../common/Initializable.sol";
import "../common/linkedlists/AddressSortedLinkedListWithMedian.sol";
import "../common/linkedlists/SortedLinkedListWithMedian.sol";
import "../../contracts-0.8/common/interfaces/IOracle.sol";

/**
* @title SortedOracles
Expand Down Expand Up @@ -319,6 +320,29 @@ contract SortedOracles is ISortedOracles, ICeloVersionedContract, Ownable, Initi
return rates[token].getElements();
}

/**
* @notice Returns the exchange rate for a specified token.
* @param token The token for which the exchange rate is being retrieved.
* @return uint256 The exchange rate for the specified token.
* @return uint256 The denominator for the exchange rate.
*/
function getExchangeRate(
address token
) external view returns (uint256 numerator, uint256 denominator) {
(numerator, denominator) = medianRate(token);
}

/**
* @notice Returns the storage, major, minor, and patch version of the contract.
* @return Storage version of the contract.
* @return Major version of the contract.
* @return Minor version of the contract.
* @return Patch version of the contract.
*/
function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256) {
return (1, 1, 4, 0);
}

/**
* @notice Returns the median of the currently stored rates for a specified rateFeedId.
* @dev Please note that this function respects the equivalentToken mapping, and so may
Expand All @@ -327,7 +351,7 @@ contract SortedOracles is ISortedOracles, ICeloVersionedContract, Ownable, Initi
* @return uint256 The median exchange rate for rateFeedId (fixidity).
* @return uint256 denominator
*/
function medianRate(address token) external view returns (uint256, uint256) {
function medianRate(address token) public view returns (uint256, uint256) {
EquivalentToken storage equivalentToken = equivalentTokens[token];
if (equivalentToken.token != address(0)) {
(uint256 equivalentMedianRate, uint256 denominator) = medianRateWithoutEquivalentMapping(
Expand All @@ -339,17 +363,6 @@ contract SortedOracles is ISortedOracles, ICeloVersionedContract, Ownable, Initi
return medianRateWithoutEquivalentMapping(token);
}

/**
* @notice Returns the storage, major, minor, and patch version of the contract.
* @return Storage version of the contract.
* @return Major version of the contract.
* @return Minor version of the contract.
* @return Patch version of the contract.
*/
function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256) {
return (1, 1, 3, 0);
}

/**
* @notice Sets the report expiry parameter.
* @param _reportExpirySeconds The number of seconds before a report is considered expired.
Expand Down
20 changes: 12 additions & 8 deletions packages/protocol/lib/compatibility/verify-bytecode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from '@celo/protocol/lib/bytecode'
import { verifyProxyStorageProof } from '@celo/protocol/lib/proxy-utils'
import { ProposalTx } from '@celo/protocol/scripts/truffle/make-release'
import { ZERO_ADDRESS } from '@celo/protocol/test/constants'
import { BuildArtifacts } from '@openzeppelin/upgrades'
import { ProxyInstance, RegistryInstance } from 'types'
import Web3 from 'web3'
Expand All @@ -24,7 +25,7 @@ let ignoredContracts = [
]

interface VerificationContext {
artifacts: BuildArtifacts
artifacts: BuildArtifacts[]
libraryAddresses: LibraryAddresses
registry: RegistryInstance
governanceAddress: string
Expand Down Expand Up @@ -76,8 +77,8 @@ export const getProposedProxyAddress = (contract: string, proposal: ProposalTx[]
return relevantTx.args[1]
}

const getSourceBytecodeFromArtifacts = (contract: string, artifacts: BuildArtifacts): string =>
stripMetadata(artifacts.getArtifactByName(contract).deployedBytecode)
const getSourceBytecodeFromArtifacts = (contract: string, artifacts: BuildArtifacts[]): string =>
stripMetadata(artifacts.map(a => a.getArtifactByName(contract)).find(a => a).deployedBytecode)

const getSourceBytecode = (contract: string, context: VerificationContext): string =>
getSourceBytecodeFromArtifacts(contract, context.artifacts)
Expand Down Expand Up @@ -110,7 +111,6 @@ const dfsStep = async (queue: string[], visited: Set<string>, context: Verificat
throw new Error(`Proposed ${contract}Proxy does not match compiled proxy bytecode`)
}

console.log(`Proxy deployed at ${proxyAddress} matches ${contract}Proxy (bytecode and storage)`)
}

// check implementation deployment
Expand All @@ -124,6 +124,10 @@ const dfsStep = async (queue: string[], visited: Set<string>, context: Verificat
implementationAddress = ensureLeading0x(context.libraryAddresses.addresses[contract])
} else {
const proxyAddress = await context.registry.getAddressForString(contract)
if (proxyAddress === ZERO_ADDRESS) {
console.log(`Contract ${contract} is not in registry - skipping bytecode verification`)
return;
}
const proxy = await context.Proxy.at(proxyAddress) // necessary await
implementationAddress = await proxy._getImplementation()
}
Expand Down Expand Up @@ -166,7 +170,7 @@ const assertValidProposalTransactions = (proposal: ProposalTx[]) => {
}

const assertValidInitializationData = (
artifacts: BuildArtifacts,
artifacts: BuildArtifacts[],
proposal: ProposalTx[],
web3: Web3,
initializationData: InitializationData
Expand All @@ -182,7 +186,7 @@ const assertValidInitializationData = (
)
}

const contract = artifacts.getArtifactByName(contractName)
const contract = artifacts.map(a => a.getArtifactByName(contractName)).find(a => a)
const initializeAbi = contract.abi.find(
(abi: any) => abi.type === 'function' && abi.name === 'initialize'
)
Expand Down Expand Up @@ -216,7 +220,7 @@ const assertValidInitializationData = (
*/
export const verifyBytecodes = async (
contracts: string[],
artifacts: BuildArtifacts,
artifacts: BuildArtifacts[],
registry: RegistryInstance,
proposal: ProposalTx[],
Proxy: Truffle.Contract<ProxyInstance>,
Expand All @@ -228,7 +232,7 @@ export const verifyBytecodes = async (
assertValidProposalTransactions(proposal)
assertValidInitializationData(artifacts, proposal, _web3, initializationData)

const compiledContracts = artifacts.listArtifacts().map((a) => a.contractName)
const compiledContracts = Array.prototype.concat.apply([], artifacts.map(a => a.listArtifacts())).map((a) => a.contractName)

if (version > 9) {
ignoredContracts = [...ignoredContracts, ...ignoredContractsV9]
Expand Down
7 changes: 4 additions & 3 deletions packages/protocol/lib/registry-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export enum CeloContractName {
FeeCurrencyWhitelist = 'FeeCurrencyWhitelist',
Freezer = 'Freezer',
GasPriceMinimum = 'GasPriceMinimum',
FeeCurrencyDirectory = 'FeeCurrencyDirectory',
GoldToken = 'GoldToken',
Governance = 'Governance',
GovernanceSlasher = 'GovernanceSlasher',
Expand All @@ -52,10 +53,10 @@ export const usesRegistry = [
CeloContractName.StableToken,
]

export const hasEntryInRegistry: ContractPackage[]= [
export const hasEntryInRegistry: ContractPackage[] = [
{
name: "default",
contracts:[
contracts: [
CeloContractName.Accounts,
CeloContractName.Attestations,
CeloContractName.BlockchainParameters,
Expand All @@ -78,7 +79,7 @@ export const hasEntryInRegistry: ContractPackage[]= [
{
...MENTO_PACKAGE,
// not all Mentro contracts are supposed to be in the Registry
contracts:[
contracts: [
CeloContractName.Exchange,
CeloContractName.GrandaMento,
CeloContractName.Reserve,
Expand Down
52 changes: 52 additions & 0 deletions packages/protocol/migrations_ts/26_100_fee_currency_directory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { ArtifactsSingleton } from '@celo/protocol/lib/artifactsSingleton'
import { CeloContractName } from '@celo/protocol/lib/registry-utils'
import {
deploymentForCoreContract,
getDeployedProxiedContract,
} from '@celo/protocol/lib/web3-utils'
import { SortedOraclesInstance, StableTokenInstance } from '@celo/protocol/types/typechain-mento'
import { FeeCurrencyDirectoryInstance } from 'types/08'
import { MENTO_PACKAGE, SOLIDITY_08_PACKAGE } from '../contractPackages'

const initializeArgs = async (): Promise<any[]> => {
return []
}

module.exports = deploymentForCoreContract<FeeCurrencyDirectoryInstance>(
web3,
artifacts,
CeloContractName.FeeCurrencyDirectory,
initializeArgs,
async (feeCurrencyDirectory: FeeCurrencyDirectoryInstance, _web3: Web3, networkName: string) => {
const sortedOracles = await getDeployedProxiedContract<SortedOraclesInstance>(
'SortedOracles',
artifacts
)

for (const token of ['StableToken', 'StableTokenEUR', 'StableTokenBRL']) {
const stableToken: StableTokenInstance =
await getDeployedProxiedContract<StableTokenInstance>(
token,
ArtifactsSingleton.getInstance(MENTO_PACKAGE)
)
console.log(
'setting currency config for',
token,
'with address',
stableToken.address,
'and adapter address',
sortedOracles.address,
'on network',
networkName
)
await feeCurrencyDirectory.setCurrencyConfig(stableToken.address, sortedOracles.address, 1)
}

console.log(
'Fee currency directory deployed and registered!!!',
feeCurrencyDirectory.address,
networkName
)
},
SOLIDITY_08_PACKAGE
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"FeeCurrencyDirectory": []
}
6 changes: 5 additions & 1 deletion packages/protocol/scripts/truffle/verify-bytecode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ const argv = require('minimist')(process.argv.slice(2), {
})

const artifactsDirectory = argv.build_artifacts ? argv.build_artifacts : './build/contracts'
const artifacts08Directory = argv.build_artifacts08
? argv.build_artifacts08
: './build/contracts-0.8'
const branch = (argv.branch ? argv.branch : '') as string
const network = argv.network ?? 'development'
const proposal = argv.proposal ? readJsonSync(argv.proposal) : []
Expand All @@ -48,9 +51,10 @@ module.exports = async (callback: (error?: any) => number) => {

const registry = await Registry.at(celoRegistryAddress)
const buildArtifacts = getBuildArtifacts(artifactsDirectory)
const artifacts08 = getBuildArtifacts(artifacts08Directory)
const libraryAddresses = await verifyBytecodes(
Object.keys(CeloContractName),
buildArtifacts,
[buildArtifacts, artifacts08],
registry,
proposal,
Proxy,
Expand Down
3 changes: 3 additions & 0 deletions packages/protocol/test-sol/stability/SortedOracles.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -476,8 +476,11 @@ contract RemoveOracle is SortedOraclesTest {

sortedOracle.removeOracle(aToken, oracleAccount, 0);
(uint256 newMedianRate, uint256 newNumOfRates) = sortedOracle.medianRate(aToken);
(uint256 exchangeRate, uint256 exchangeRateDenominator) = sortedOracle.getExchangeRate(aToken);
assertEq(originalMedianRate, newMedianRate);
assertEq(originalNumOfRates, newNumOfRates);
assertEq(exchangeRate, newMedianRate);
assertEq(exchangeRateDenominator, FIXED1);
}

function test_ShouldNotDecreaseTheNumberOfTimestamps_WhenThereIsASingleReportLeft() public {
Expand Down
Loading

0 comments on commit 43a3146

Please sign in to comment.