diff --git a/packages/protocol/contracts/identity/FederatedAttestations.sol b/packages/protocol/contracts/identity/FederatedAttestations.sol new file mode 100644 index 00000000000..3483cf0b12f --- /dev/null +++ b/packages/protocol/contracts/identity/FederatedAttestations.sol @@ -0,0 +1,60 @@ +pragma solidity ^0.5.13; + +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; +import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol"; +import "openzeppelin-solidity/contracts/utils/SafeCast.sol"; + +import "./interfaces/IFederatedAttestations.sol"; +import "../common/interfaces/IAccounts.sol"; +import "../common/interfaces/ICeloVersionedContract.sol"; + +import "../common/Initializable.sol"; +import "../common/UsingRegistry.sol"; +import "../common/Signatures.sol"; +import "../common/UsingPrecompiles.sol"; +import "../common/libraries/ReentrancyGuard.sol"; + +/** + * @title Contract mapping identifiers to accounts + */ +contract FederatedAttestations is + IFederatedAttestations, + ICeloVersionedContract, + Ownable, + Initializable, + UsingRegistry, + ReentrancyGuard, + UsingPrecompiles +{ + using SafeMath for uint256; + using SafeCast for uint256; + + // TODO ASv2 State var declarations + + // TODO ASv2 Event declarations + + /** + * @notice Sets initialized == true on implementation contracts + * @param test Set to true to skip implementation initialization + */ + constructor(bool test) public Initializable(test) {} + + /** + * @notice Used in place of the constructor to allow the contract to be upgradable via proxy. + * @param registryAddress The address of the registry core smart contract. + */ + function initialize(address registryAddress) external initializer { + _transferOwnership(msg.sender); + setRegistry(registryAddress); + // TODO ASv2 initialize any other variables here + } + + /** + * @notice Returns the storage, major, minor, and patch version of the contract. + * @return The storage, major, minor, and patch version of the contract. + */ + function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256) { + return (1, 1, 0, 0); + } +} diff --git a/packages/protocol/contracts/identity/interfaces/IFederatedAttestations.sol b/packages/protocol/contracts/identity/interfaces/IFederatedAttestations.sol new file mode 100644 index 00000000000..d72986621d7 --- /dev/null +++ b/packages/protocol/contracts/identity/interfaces/IFederatedAttestations.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.5.13; + +// TODO ASv2 add external, view, and only owner function sigs +// separated into these three groups for clarity +interface IFederatedAttestations {} diff --git a/packages/protocol/contracts/identity/proxies/FederatedAttestationsProxy.sol b/packages/protocol/contracts/identity/proxies/FederatedAttestationsProxy.sol new file mode 100644 index 00000000000..6edd4884f1a --- /dev/null +++ b/packages/protocol/contracts/identity/proxies/FederatedAttestationsProxy.sol @@ -0,0 +1,6 @@ +pragma solidity ^0.5.13; + +import "../../common/Proxy.sol"; + +/* solhint-disable no-empty-blocks */ +contract FederatedAttestationsProxy is Proxy {} diff --git a/packages/protocol/lib/registry-utils.ts b/packages/protocol/lib/registry-utils.ts index d6dae2db095..5d4aa040bff 100644 --- a/packages/protocol/lib/registry-utils.ts +++ b/packages/protocol/lib/registry-utils.ts @@ -20,6 +20,7 @@ export enum CeloContractName { Exchange = 'Exchange', ExchangeEUR = 'ExchangeEUR', ExchangeBRL = 'ExchangeBRL', + FederatedAttestations = 'FederatedAttestations', FeeCurrencyWhitelist = 'FeeCurrencyWhitelist', Freezer = 'Freezer', GasPriceMinimum = 'GasPriceMinimum', @@ -55,6 +56,8 @@ export const hasEntryInRegistry: string[] = [ CeloContractName.Election, CeloContractName.Escrow, CeloContractName.Exchange, + // TODO ASv2 revisit this + CeloContractName.FederatedAttestations, CeloContractName.FeeCurrencyWhitelist, CeloContractName.Freezer, CeloContractName.GasPriceMinimum, diff --git a/packages/protocol/lib/test-utils.ts b/packages/protocol/lib/test-utils.ts index be796bb82ae..9a5c58237c5 100644 --- a/packages/protocol/lib/test-utils.ts +++ b/packages/protocol/lib/test-utils.ts @@ -375,6 +375,7 @@ export const isSameAddress = (minerAddress, otherAddress) => { // TODO(amy): Pull this list from the build artifacts instead export const proxiedContracts: string[] = [ 'Attestations', + // TODO ASv2 revisit if we need to update test-utils 'Escrow', 'GoldToken', 'Registry', @@ -386,6 +387,7 @@ export const proxiedContracts: string[] = [ // TODO(asa): Pull this list from the build artifacts instead export const ownedContracts: string[] = [ 'Attestations', + // TODO ASv2 revisit if we need to update test-utils 'Escrow', 'Exchange', 'Registry', diff --git a/packages/protocol/migrations/25_governance.ts b/packages/protocol/migrations/25_governance.ts index 78a731eb053..e91ad42fc0e 100644 --- a/packages/protocol/migrations/25_governance.ts +++ b/packages/protocol/migrations/25_governance.ts @@ -83,6 +83,7 @@ module.exports = deploymentForCoreContract( 'Escrow', 'Exchange', 'ExchangeEUR', + // TODO ASv2 revisit 'FeeCurrencyWhitelist', 'Freezer', 'GasPriceMinimum', diff --git a/packages/protocol/migrations/27_federated_attestations.ts b/packages/protocol/migrations/27_federated_attestations.ts new file mode 100644 index 00000000000..ad25500c0a6 --- /dev/null +++ b/packages/protocol/migrations/27_federated_attestations.ts @@ -0,0 +1,15 @@ +import { CeloContractName } from '@celo/protocol/lib/registry-utils' +import { deploymentForCoreContract } from '@celo/protocol/lib/web3-utils' +import { config } from '@celo/protocol/migrationsConfig' +import { FederatedAttestationsInstance } from 'types' + +const initializeArgs = async (): Promise<[string]> => { + return [config.registry.predeployedProxyAddress] +} + +module.exports = deploymentForCoreContract( + web3, + artifacts, + CeloContractName.FederatedAttestations, + initializeArgs +) diff --git a/packages/protocol/migrationsConfig.js b/packages/protocol/migrationsConfig.js index 6df3ee5d3f2..799b124efd3 100644 --- a/packages/protocol/migrationsConfig.js +++ b/packages/protocol/migrationsConfig.js @@ -26,6 +26,8 @@ const DAY = 24 * HOUR const WEEK = 7 * DAY const YEAR = 365 * DAY +// TODO ASv2 + const DefaultConfig = { attestations: { attestationExpiryBlocks: HOUR / 5, // ~1 hour, diff --git a/packages/protocol/scripts/bash/backupmigrations.sh b/packages/protocol/scripts/bash/backupmigrations.sh index e7005a1f8e7..a5cfedf501e 100755 --- a/packages/protocol/scripts/bash/backupmigrations.sh +++ b/packages/protocol/scripts/bash/backupmigrations.sh @@ -45,4 +45,5 @@ else # cp migrations.bak/23_governance_approver_multisig.* migrations/ # cp migrations.bak/24_governance.* migrations/ # cp migrations.bak/25_elect_validators.* migrations/ + # cp migrations.bak/27_federated_attestations.* migrations/ fi \ No newline at end of file diff --git a/packages/protocol/scripts/build.ts b/packages/protocol/scripts/build.ts index 1f4f78240f7..0a15471c3be 100644 --- a/packages/protocol/scripts/build.ts +++ b/packages/protocol/scripts/build.ts @@ -20,6 +20,7 @@ export const ProxyContracts = [ 'ExchangeBRLProxy', 'ExchangeEURProxy', 'ExchangeProxy', + 'FederatedAttestationsProxy', 'FeeCurrencyWhitelistProxy', 'GasPriceMinimumProxy', 'GoldTokenProxy', @@ -66,6 +67,7 @@ export const CoreContracts = [ // identity 'Attestations', 'Escrow', + 'FederatedAttestations', 'Random', // stability diff --git a/packages/protocol/test/identity/federatedattestations.ts b/packages/protocol/test/identity/federatedattestations.ts new file mode 100644 index 00000000000..54070cb21e9 --- /dev/null +++ b/packages/protocol/test/identity/federatedattestations.ts @@ -0,0 +1,43 @@ +import { CeloContractName } from '@celo/protocol/lib/registry-utils' +// import { getPhoneHash } from '@celo/utils/lib/phoneNumbers' +import { + AccountsContract, + AccountsInstance, + FederatedAttestationsContract, + FederatedAttestationsInstance, + RegistryContract, + RegistryInstance, +} from 'types' + +const Accounts: AccountsContract = artifacts.require('Accounts') +const FederatedAttestations: FederatedAttestationsContract = artifacts.require( + 'FederatedAttestations' +) +const Registry: RegistryContract = artifacts.require('Registry') + +contract('Attestations', (accounts: string[]) => { + let accountsInstance: AccountsInstance + let federatedAttestations: FederatedAttestationsInstance + let registry: RegistryInstance + + const caller: string = accounts[0] + // const phoneNumber: string = '+18005551212' + // const phoneHash: string = getPhoneHash(phoneNumber) + + beforeEach('FederatedAttestations setup', async () => { + accountsInstance = await Accounts.new(true) + federatedAttestations = await FederatedAttestations.new(true) + registry = await Registry.new(true) + await accountsInstance.initialize(registry.address) + await registry.setAddressFor(CeloContractName.Accounts, accountsInstance.address) + await federatedAttestations.initialize(registry.address) + }) + + describe('#initialize()', () => { + it('TODO ASv2', async () => { + // TODO ASv2 + assert(caller) + assert(federatedAttestations) + }) + }) +}) diff --git a/packages/sdk/contractkit/src/base.ts b/packages/sdk/contractkit/src/base.ts index d4b0657bfb0..082358d2f69 100644 --- a/packages/sdk/contractkit/src/base.ts +++ b/packages/sdk/contractkit/src/base.ts @@ -11,6 +11,7 @@ export enum CeloContract { Exchange = 'Exchange', ExchangeEUR = 'ExchangeEUR', ExchangeBRL = 'ExchangeBRL', + FederatedAttestations = 'FederatedAttestations', FeeCurrencyWhitelist = 'FeeCurrencyWhitelist', Freezer = 'Freezer', GasPriceMinimum = 'GasPriceMinimum', diff --git a/packages/sdk/contractkit/src/contract-cache.ts b/packages/sdk/contractkit/src/contract-cache.ts index 088e20af3a9..2aa58931444 100644 --- a/packages/sdk/contractkit/src/contract-cache.ts +++ b/packages/sdk/contractkit/src/contract-cache.ts @@ -15,6 +15,7 @@ import { EpochRewardsWrapper } from './wrappers/EpochRewards' import { Erc20Wrapper } from './wrappers/Erc20Wrapper' import { EscrowWrapper } from './wrappers/Escrow' import { ExchangeWrapper } from './wrappers/Exchange' +import { FederatedAttestationsWrapper } from './wrappers/FederatedAttestations' import { FreezerWrapper } from './wrappers/Freezer' import { GasPriceMinimumWrapper } from './wrappers/GasPriceMinimum' import { GoldTokenWrapper } from './wrappers/GoldTokenWrapper' @@ -38,6 +39,7 @@ const WrapperFactories = { [CeloContract.Exchange]: ExchangeWrapper, [CeloContract.ExchangeEUR]: ExchangeWrapper, [CeloContract.ExchangeBRL]: ExchangeWrapper, + [CeloContract.FederatedAttestations]: FederatedAttestationsWrapper, // [CeloContract.FeeCurrencyWhitelist]: FeeCurrencyWhitelistWrapper, [CeloContract.Freezer]: FreezerWrapper, [CeloContract.GasPriceMinimum]: GasPriceMinimumWrapper, @@ -91,6 +93,7 @@ interface WrapperCacheMap { [CeloContract.Exchange]?: ExchangeWrapper [CeloContract.ExchangeEUR]?: ExchangeWrapper [CeloContract.ExchangeBRL]?: ExchangeWrapper + [CeloContract.FederatedAttestations]?: FederatedAttestationsWrapper // [CeloContract.FeeCurrencyWhitelist]?: FeeCurrencyWhitelistWrapper, [CeloContract.Freezer]?: FreezerWrapper [CeloContract.GasPriceMinimum]?: GasPriceMinimumWrapper @@ -166,6 +169,14 @@ export class WrapperCache implements ContractCacheType { return this.getContract(CeloContract.Freezer) } + getFederatedAttestations() { + return this.getContract(CeloContract.FederatedAttestations) + } + + // getFeeCurrencyWhitelist() { + // return this.getWrapper(CeloContract.FeeCurrencyWhitelist, newFeeCurrencyWhitelist) + // } + getGasPriceMinimum() { return this.getContract(CeloContract.GasPriceMinimum) } diff --git a/packages/sdk/contractkit/src/kit.ts b/packages/sdk/contractkit/src/kit.ts index 3d4e72c681e..06a20913736 100644 --- a/packages/sdk/contractkit/src/kit.ts +++ b/packages/sdk/contractkit/src/kit.ts @@ -27,6 +27,7 @@ import { BlockchainParametersConfig } from './wrappers/BlockchainParameters' import { DowntimeSlasherConfig } from './wrappers/DowntimeSlasher' import { ElectionConfig } from './wrappers/Election' import { ExchangeConfig } from './wrappers/Exchange' +import { FederatedAttestationsConfig } from './wrappers/FederatedAttestations' import { GasPriceMinimumConfig } from './wrappers/GasPriceMinimum' import { GovernanceConfig } from './wrappers/Governance' import { GrandaMentoConfig } from './wrappers/GrandaMento' @@ -72,6 +73,8 @@ export interface NetworkConfig { stableTokens: EachCeloToken election: ElectionConfig attestations: AttestationsConfig + // TODO ASv2 + federatedattestations: FederatedAttestationsConfig governance: GovernanceConfig lockedGold: LockedGoldConfig sortedOracles: SortedOraclesConfig @@ -146,6 +149,8 @@ export class ContractKit { const configContracts: ValidWrappers[] = [ CeloContract.Election, CeloContract.Attestations, + // TODO ASv2 + CeloContract.FederatedAttestations, CeloContract.Governance, CeloContract.LockedGold, CeloContract.SortedOracles, diff --git a/packages/sdk/contractkit/src/proxy.ts b/packages/sdk/contractkit/src/proxy.ts index 427bc363e1f..c9a4867c713 100644 --- a/packages/sdk/contractkit/src/proxy.ts +++ b/packages/sdk/contractkit/src/proxy.ts @@ -9,6 +9,7 @@ import { ABI as ElectionABI } from './generated/Election' import { ABI as EpochRewardsABI } from './generated/EpochRewards' import { ABI as EscrowABI } from './generated/Escrow' import { ABI as ExchangeABI } from './generated/Exchange' +import { ABI as FederatedAttestationsABI } from './generated/FederatedAttestations' import { ABI as FeeCurrencyWhitelistABI } from './generated/FeeCurrencyWhitelist' import { ABI as FreezerABI } from './generated/Freezer' import { ABI as GasPriceMinimumABI } from './generated/GasPriceMinimum' @@ -104,6 +105,7 @@ const initializeAbiMap = { ExchangeProxy: findInitializeAbi(ExchangeABI), ExchangeEURProxy: findInitializeAbi(ExchangeABI), ExchangeBRLProxy: findInitializeAbi(ExchangeABI), + FederatedAttestationsProxy: findInitializeAbi(FederatedAttestationsABI), FeeCurrencyWhitelistProxy: findInitializeAbi(FeeCurrencyWhitelistABI), FreezerProxy: findInitializeAbi(FreezerABI), GasPriceMinimumProxy: findInitializeAbi(GasPriceMinimumABI), diff --git a/packages/sdk/contractkit/src/web3-contract-cache.ts b/packages/sdk/contractkit/src/web3-contract-cache.ts index ef2b8712c6f..fc9989fe661 100644 --- a/packages/sdk/contractkit/src/web3-contract-cache.ts +++ b/packages/sdk/contractkit/src/web3-contract-cache.ts @@ -13,6 +13,7 @@ import { newEscrow } from './generated/Escrow' import { newExchange } from './generated/Exchange' import { newExchangeBrl } from './generated/ExchangeBRL' import { newExchangeEur } from './generated/ExchangeEUR' +import { newFederatedAttestations } from './generated/FederatedAttestations' import { newFeeCurrencyWhitelist } from './generated/FeeCurrencyWhitelist' import { newFreezer } from './generated/Freezer' import { newGasPriceMinimum } from './generated/GasPriceMinimum' @@ -48,6 +49,7 @@ export const ContractFactories = { [CeloContract.Exchange]: newExchange, [CeloContract.ExchangeEUR]: newExchangeEur, [CeloContract.ExchangeBRL]: newExchangeBrl, + [CeloContract.FederatedAttestations]: newFederatedAttestations, [CeloContract.FeeCurrencyWhitelist]: newFeeCurrencyWhitelist, [CeloContract.Freezer]: newFreezer, [CeloContract.GasPriceMinimum]: newGasPriceMinimum, @@ -126,6 +128,9 @@ export class Web3ContractCache { getExchange(stableToken: StableToken = StableToken.cUSD) { return this.getContract(StableToExchange[stableToken]) } + getFederatedAttestations() { + return this.getContract(CeloContract.FederatedAttestations) + } getFeeCurrencyWhitelist() { return this.getContract(CeloContract.FeeCurrencyWhitelist) } diff --git a/packages/sdk/contractkit/src/wrappers/FederatedAttestations.test.ts b/packages/sdk/contractkit/src/wrappers/FederatedAttestations.test.ts new file mode 100644 index 00000000000..de69785bb59 --- /dev/null +++ b/packages/sdk/contractkit/src/wrappers/FederatedAttestations.test.ts @@ -0,0 +1,26 @@ +import { testWithGanache } from '@celo/dev-utils/lib/ganache-test' +// import { PhoneNumberUtils } from '@celo/utils' +import { newKitFromWeb3 } from '../kit' +import { FederatedAttestationsWrapper } from './FederatedAttestations' + +testWithGanache('FederatedAttestations Wrapper', (web3) => { + // const PHONE_NUMBER = '+15555555555' + // const IDENTIFIER = PhoneNumberUtils.getPhoneHash(PHONE_NUMBER) + + const kit = newKitFromWeb3(web3) + let accounts: string[] = [] + let federatedAttestations: FederatedAttestationsWrapper + + beforeAll(async () => { + accounts = await web3.eth.getAccounts() + kit.defaultAccount = accounts[0] + }) + + describe('TODO ASv2', () => { + it('TODO ASv2', async () => { + expect(accounts) + federatedAttestations = await kit.contracts.getFederatedAttestations() + expect(federatedAttestations) + }) + }) +}) diff --git a/packages/sdk/contractkit/src/wrappers/FederatedAttestations.ts b/packages/sdk/contractkit/src/wrappers/FederatedAttestations.ts new file mode 100644 index 00000000000..dbb665ce97a --- /dev/null +++ b/packages/sdk/contractkit/src/wrappers/FederatedAttestations.ts @@ -0,0 +1,8 @@ +import { FederatedAttestations } from '../generated/FederatedAttestations' +import { BaseWrapper } from './BaseWrapper' + +export interface FederatedAttestationsConfig { + // TODO ASv2 +} + +export class FederatedAttestationsWrapper extends BaseWrapper {}