diff --git a/.changeset/tough-ears-study.md b/.changeset/tough-ears-study.md new file mode 100644 index 000000000..dd137256a --- /dev/null +++ b/.changeset/tough-ears-study.md @@ -0,0 +1,5 @@ +--- +'@celo/celocli': minor +--- + +Add ability to build and view governance proposals which interact with contracts verified on celoscan diff --git a/.changeset/young-foxes-compete.md b/.changeset/young-foxes-compete.md new file mode 100644 index 000000000..78b309f60 --- /dev/null +++ b/.changeset/young-foxes-compete.md @@ -0,0 +1,5 @@ +--- +'@celo/explorer': minor +--- + +Adds Celoscan as source for fetching verified contract metadata diff --git a/.gitignore b/.gitignore index 11d572ad7..5cc62897e 100644 --- a/.gitignore +++ b/.gitignore @@ -91,3 +91,4 @@ package.json-e # Ignore generated credentials from google-github-actions/auth gha-creds-*.json transactions.json +transactions2.json diff --git a/docs/sdk/explorer/classes/block_explorer.BlockExplorer.md b/docs/sdk/explorer/classes/block_explorer.BlockExplorer.md index e2f65c432..d12bd4e66 100644 --- a/docs/sdk/explorer/classes/block_explorer.BlockExplorer.md +++ b/docs/sdk/explorer/classes/block_explorer.BlockExplorer.md @@ -180,7 +180,7 @@ ___ ▸ **getContractMappingFromSourcify**(`address`): `Promise`\<`undefined` \| [`ContractMapping`](../interfaces/base.ContractMapping.md)\> Returns the ContractMapping for the contract at that address, or undefined -by looking up the contract address in Sourcify. +by looking up the contract address on various contract verification services. #### Parameters diff --git a/docs/sdk/explorer/classes/sourcify.Metadata.md b/docs/sdk/explorer/classes/sourcify.Metadata.md index e9c860c75..e655a7ca8 100644 --- a/docs/sdk/explorer/classes/sourcify.Metadata.md +++ b/docs/sdk/explorer/classes/sourcify.Metadata.md @@ -19,6 +19,7 @@ light runtime verification. - [abi](sourcify.Metadata.md#abi) - [contractName](sourcify.Metadata.md#contractname) - [fnMapping](sourcify.Metadata.md#fnmapping) +- [implementationAddress](sourcify.Metadata.md#implementationaddress) ### Accessors @@ -42,7 +43,7 @@ light runtime verification. | :------ | :------ | | `connection` | `Connection` | | `address` | `string` | -| `response` | `any` | +| `response` | [`MetadataResponse`](../interfaces/sourcify.MetadataResponse.md) | #### Returns @@ -50,7 +51,7 @@ light runtime verification. #### Defined in -[sourcify.ts:73](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/explorer/src/sourcify.ts#L73) +[sourcify.ts:76](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/explorer/src/sourcify.ts#L76) ## Properties @@ -60,7 +61,7 @@ light runtime verification. #### Defined in -[sourcify.ts:65](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/explorer/src/sourcify.ts#L65) +[sourcify.ts:67](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/explorer/src/sourcify.ts#L67) ___ @@ -70,7 +71,7 @@ ___ #### Defined in -[sourcify.ts:66](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/explorer/src/sourcify.ts#L66) +[sourcify.ts:68](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/explorer/src/sourcify.ts#L68) ___ @@ -80,7 +81,17 @@ ___ #### Defined in -[sourcify.ts:67](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/explorer/src/sourcify.ts#L67) +[sourcify.ts:70](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/explorer/src/sourcify.ts#L70) + +___ + +### implementationAddress + +• **implementationAddress**: ``null`` \| `string` = `null` + +#### Defined in + +[sourcify.ts:69](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/explorer/src/sourcify.ts#L69) ## Accessors @@ -100,7 +111,7 @@ ___ #### Defined in -[sourcify.ts:83](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/explorer/src/sourcify.ts#L83) +[sourcify.ts:88](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/explorer/src/sourcify.ts#L88) ## Methods @@ -127,7 +138,7 @@ and array of AbiItems matching the query #### Defined in -[sourcify.ts:152](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/explorer/src/sourcify.ts#L152) +[sourcify.ts:158](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/explorer/src/sourcify.ts#L158) ___ @@ -151,7 +162,7 @@ an AbiItem if found or null #### Defined in -[sourcify.ts:136](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/explorer/src/sourcify.ts#L136) +[sourcify.ts:142](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/explorer/src/sourcify.ts#L142) ___ @@ -167,4 +178,4 @@ Turn the ABI into a mapping of function selectors to ABI items. #### Defined in -[sourcify.ts:119](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/explorer/src/sourcify.ts#L119) +[sourcify.ts:125](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/explorer/src/sourcify.ts#L125) diff --git a/docs/sdk/explorer/interfaces/sourcify.MetadataResponse.md b/docs/sdk/explorer/interfaces/sourcify.MetadataResponse.md index b3245eb8f..9f17c131d 100644 --- a/docs/sdk/explorer/interfaces/sourcify.MetadataResponse.md +++ b/docs/sdk/explorer/interfaces/sourcify.MetadataResponse.md @@ -42,6 +42,8 @@ ___ | Name | Type | | :------ | :------ | | `compilationTarget?` | `Record`\<`string`, `string`\> | +| `implementation?` | `string` | +| `name?` | `string` | #### Defined in diff --git a/docs/sdk/explorer/modules/sourcify.md b/docs/sdk/explorer/modules/sourcify.md index 3d5d61dfc..9c270e313 100644 --- a/docs/sdk/explorer/modules/sourcify.md +++ b/docs/sdk/explorer/modules/sourcify.md @@ -23,8 +23,8 @@ ▸ **fetchMetadata**(`connection`, `contract`, `strict?`): `Promise`\<[`Metadata`](../classes/sourcify.Metadata.md) \| ``null``\> -Fetch the sourcify response and instantiate a Metadata wrapper class around it. -Try a full_match but fallback to partial_match when not strict. +Fetch the sourcify or celoscan response and instantiate a Metadata wrapper class around it. +Try a full_match but fallback to partial_match when not strict. (only valid for sourcify) #### Parameters @@ -42,7 +42,7 @@ Metadata or null #### Defined in -[sourcify.ts:179](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/explorer/src/sourcify.ts#L179) +[sourcify.ts:185](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/explorer/src/sourcify.ts#L185) ___ @@ -73,4 +73,4 @@ the implementation address or null #### Defined in -[sourcify.ts:228](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/explorer/src/sourcify.ts#L228) +[sourcify.ts:300](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/explorer/src/sourcify.ts#L300) diff --git a/docs/sdk/governance/classes/proposals.InteractiveProposalBuilder.md b/docs/sdk/governance/classes/proposals.InteractiveProposalBuilder.md index d2fe9c299..bd162138b 100644 --- a/docs/sdk/governance/classes/proposals.InteractiveProposalBuilder.md +++ b/docs/sdk/governance/classes/proposals.InteractiveProposalBuilder.md @@ -33,7 +33,7 @@ #### Defined in -[proposals.ts:466](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/governance/src/proposals.ts#L466) +[proposals.ts:464](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/governance/src/proposals.ts#L464) ## Methods @@ -47,7 +47,7 @@ #### Defined in -[proposals.ts:468](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/governance/src/proposals.ts#L468) +[proposals.ts:466](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/governance/src/proposals.ts#L466) ___ @@ -61,4 +61,4 @@ ___ #### Defined in -[proposals.ts:473](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/governance/src/proposals.ts#L473) +[proposals.ts:471](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/governance/src/proposals.ts#L471) diff --git a/docs/sdk/governance/classes/proposals.ProposalBuilder.md b/docs/sdk/governance/classes/proposals.ProposalBuilder.md index 5a123e945..52b78ebac 100644 --- a/docs/sdk/governance/classes/proposals.ProposalBuilder.md +++ b/docs/sdk/governance/classes/proposals.ProposalBuilder.md @@ -144,7 +144,7 @@ ___ #### Defined in -[proposals.ts:460](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/governance/src/proposals.ts#L460) +[proposals.ts:458](https://github.com/celo-org/developer-tooling/blob/master/packages/sdk/governance/src/proposals.ts#L458) ___ diff --git a/packages/cli/.gitignore b/packages/cli/.gitignore index d9e7a3389..dbd16f4d7 100644 --- a/packages/cli/.gitignore +++ b/packages/cli/.gitignore @@ -10,3 +10,5 @@ oclif.manifest.json src/generated .devchain/ .devchain.tar.gz +transactions.json +transactions2.json diff --git a/packages/cli/jest.config.js b/packages/cli/jest.config.js index c9608b009..1bb232a34 100644 --- a/packages/cli/jest.config.js +++ b/packages/cli/jest.config.js @@ -5,4 +5,7 @@ module.exports = { setupFilesAfterEnv: ['@celo/dev-utils/lib/matchers', '/src/test-utils/setupAfterEnv.ts'], globalSetup: '/src/test-utils/setup.global.ts', globalTeardown: '/src/test-utils/teardown.global.ts', + testTimeout: 10 * 1000, // set default timeout to 10 seconds + slowTestThreshold: 10, // this is also 10 seconds. + openHandlesTimeout: 0, // disables } diff --git a/packages/cli/package.json b/packages/cli/package.json index 40706cfd3..7a1b11254 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -83,6 +83,7 @@ "@types/ledgerhq__hw-transport-node-hid": "^4.22.5", "@types/node": "^18.7.16", "@types/prompts": "^1.1.1", + "fetch-mock": "^9.11.0", "jest": "^29.7.0", "oclif": "^4.3.4", "prettier": "1.19.1", diff --git a/packages/cli/src/base.ts b/packages/cli/src/base.ts index 83aa26ba0..d1cb06fd3 100644 --- a/packages/cli/src/base.ts +++ b/packages/cli/src/base.ts @@ -7,6 +7,7 @@ import { LocalWallet } from '@celo/wallet-local' import _TransportNodeHid from '@ledgerhq/hw-transport-node-hid' import { Command, Flags } from '@oclif/core' import chalk from 'chalk' +import debugFactory from 'debug' import net from 'net' import Web3 from 'web3' import { CustomFlags } from './utils/command' @@ -14,6 +15,8 @@ import { getNodeUrl } from './utils/config' import { getFeeCurrencyContractWrapper } from './utils/fee-currency' import { requireNodeIsSynced } from './utils/helpers' +const debug = debugFactory('cli:base') + export abstract class BaseCommand extends Command { static flags = { privateKey: Flags.string({ @@ -126,10 +129,14 @@ export abstract class BaseCommand extends Command { } async getKit() { - if (!this._kit) { - this._kit = newKitFromWeb3(await this.getWeb3()) + // return now if kit already has been built instead of doing all that parsing and awaiting + // because this.getKit is called when closing down the command by not returning early this.parse would be called and weird logs occure + if (this._kit) { + return this._kit } + this._kit = newKitFromWeb3(await this.getWeb3()) + const res = await this.parse() if (res.flags && res.flags.privateKey && !res.flags.useLedger && !res.flags.useAKV) { this._kit.connection.addAccount(res.flags.privateKey) @@ -231,10 +238,11 @@ export abstract class BaseCommand extends Command { async finally(arg: Error | undefined): Promise { try { if (arg) { - console.error('received error while cleaning up', arg) + debug('received error while cleaning up:', arg) } - const kit = await this.getKit() - kit.connection.stop() + // we only need to stop the kit if one exists. if there is an error on this.parse for flags then there is no kit and thus none to stop + // if we use this.getKit then this.parse is called and errors out. throwing and logging failed to close the connection + this._kit && this._kit.connection.stop() } catch (error) { this.log(`Failed to close the connection: ${error}`) } diff --git a/packages/cli/src/commands/election/list.test.ts b/packages/cli/src/commands/election/list.test.ts index 137fc0402..1c2302711 100644 --- a/packages/cli/src/commands/election/list.test.ts +++ b/packages/cli/src/commands/election/list.test.ts @@ -52,5 +52,5 @@ describe('election:list cmd', () => { ], ] `) - }) + }, 4000) }) diff --git a/packages/cli/src/commands/election/revoke.test.ts b/packages/cli/src/commands/election/revoke.test.ts index 32e132d09..b5f40b345 100644 --- a/packages/cli/src/commands/election/revoke.test.ts +++ b/packages/cli/src/commands/election/revoke.test.ts @@ -8,7 +8,7 @@ import { setupGroupAndAffiliateValidator, voteForGroupFromAndActivateVotes, } from '../../test-utils/chain-setup' -import { testLocally } from '../../test-utils/cliUtils' +import { EXTRA_LONG_TIMEOUT_MS, testLocally } from '../../test-utils/cliUtils' import Revoke from './revoke' process.env.NO_SYNCCHECK = 'true' @@ -34,73 +34,85 @@ testWithGanache('election:revoke', (web3: Web3) => { ) }) - it('fails when trying to revoke more votes than voted', async () => { - const kit = newKitFromWeb3(web3) - const [fromAddress, groupAddress] = await web3.eth.getAccounts() - - await registerAccount(kit, fromAddress) - - await expect( - testLocally(Revoke, ['--from', fromAddress, '--for', groupAddress, '--value', '1']) - ).rejects.toThrow( - `can't revoke more votes for ${groupAddress} than have been made by ${fromAddress}` - ) - }) - - it('successfuly revokes all votes', async () => { - const kit = newKitFromWeb3(web3) - const election = await kit.contracts.getElection() - const amount = new BigNumber(12345) - const [fromAddress, validatorAddress, groupAddress] = await web3.eth.getAccounts() - - await registerAccountWithLockedGold(kit, fromAddress) - await setupGroupAndAffiliateValidator(kit, groupAddress, validatorAddress) - await voteForGroupFromAndActivateVotes(kit, fromAddress, groupAddress, amount) - - expect((await election.getVotesForGroupByAccount(fromAddress, groupAddress)).active).toEqual( - amount - ) - - await testLocally(Revoke, [ - '--from', - fromAddress, - '--for', - groupAddress, - '--value', - amount.toFixed(), - ]) - - expect((await election.getVotesForGroupByAccount(fromAddress, groupAddress)).active).toEqual( - new BigNumber(0) - ) - }) - - it('successfuly revokes votes partially', async () => { - const kit = newKitFromWeb3(web3) - const election = await kit.contracts.getElection() - const amount = new BigNumber(54321) - const revokeAmount = new BigNumber(4321) - const [fromAddress, validatorAddress, groupAddress] = await web3.eth.getAccounts() - - await registerAccountWithLockedGold(kit, fromAddress) - await setupGroupAndAffiliateValidator(kit, groupAddress, validatorAddress) - await voteForGroupFromAndActivateVotes(kit, fromAddress, groupAddress, amount) - - expect((await election.getVotesForGroupByAccount(fromAddress, groupAddress)).active).toEqual( - amount - ) - - await testLocally(Revoke, [ - '--from', - fromAddress, - '--for', - groupAddress, - '--value', - revokeAmount.toFixed(), - ]) - - expect((await election.getVotesForGroupByAccount(fromAddress, groupAddress)).active).toEqual( - amount.minus(revokeAmount) - ) - }) + it( + 'fails when trying to revoke more votes than voted', + async () => { + const kit = newKitFromWeb3(web3) + const [fromAddress, groupAddress] = await web3.eth.getAccounts() + + await registerAccount(kit, fromAddress) + + await expect( + testLocally(Revoke, ['--from', fromAddress, '--for', groupAddress, '--value', '1']) + ).rejects.toThrow( + `can't revoke more votes for ${groupAddress} than have been made by ${fromAddress}` + ) + }, + EXTRA_LONG_TIMEOUT_MS + ) + + it( + 'successfuly revokes all votes', + async () => { + const kit = newKitFromWeb3(web3) + const election = await kit.contracts.getElection() + const amount = new BigNumber(12345) + const [fromAddress, validatorAddress, groupAddress] = await web3.eth.getAccounts() + + await registerAccountWithLockedGold(kit, fromAddress) + await setupGroupAndAffiliateValidator(kit, groupAddress, validatorAddress) + await voteForGroupFromAndActivateVotes(kit, fromAddress, groupAddress, amount) + + expect((await election.getVotesForGroupByAccount(fromAddress, groupAddress)).active).toEqual( + amount + ) + + await testLocally(Revoke, [ + '--from', + fromAddress, + '--for', + groupAddress, + '--value', + amount.toFixed(), + ]) + + expect((await election.getVotesForGroupByAccount(fromAddress, groupAddress)).active).toEqual( + new BigNumber(0) + ) + }, + EXTRA_LONG_TIMEOUT_MS + ) + + it( + 'successfuly revokes votes partially', + async () => { + const kit = newKitFromWeb3(web3) + const election = await kit.contracts.getElection() + const amount = new BigNumber(54321) + const revokeAmount = new BigNumber(4321) + const [fromAddress, validatorAddress, groupAddress] = await web3.eth.getAccounts() + + await registerAccountWithLockedGold(kit, fromAddress) + await setupGroupAndAffiliateValidator(kit, groupAddress, validatorAddress) + await voteForGroupFromAndActivateVotes(kit, fromAddress, groupAddress, amount) + + expect((await election.getVotesForGroupByAccount(fromAddress, groupAddress)).active).toEqual( + amount + ) + + await testLocally(Revoke, [ + '--from', + fromAddress, + '--for', + groupAddress, + '--value', + revokeAmount.toFixed(), + ]) + + expect((await election.getVotesForGroupByAccount(fromAddress, groupAddress)).active).toEqual( + amount.minus(revokeAmount) + ) + }, + EXTRA_LONG_TIMEOUT_MS + ) }) diff --git a/packages/cli/src/commands/election/run.test.ts b/packages/cli/src/commands/election/run.test.ts index 5de3462d6..a194b264b 100644 --- a/packages/cli/src/commands/election/run.test.ts +++ b/packages/cli/src/commands/election/run.test.ts @@ -6,7 +6,7 @@ import { ux } from '@oclif/core' import BigNumber from 'bignumber.js' import Web3 from 'web3' import { setupGroupAndAffiliateValidator } from '../../test-utils/chain-setup' -import { stripAnsiCodes, testLocally } from '../../test-utils/cliUtils' +import { EXTRA_LONG_TIMEOUT_MS, stripAnsiCodes, testLocally } from '../../test-utils/cliUtils' import Run from './run' process.env.NO_SYNCCHECK = 'true' @@ -41,54 +41,59 @@ testWithGanache('election:run', (web3: Web3) => { `) }) - it('runs election', async () => { - const kit = newKitFromWeb3(web3) - const logMock = jest.spyOn(console, 'log') - const warnMock = jest.spyOn(console, 'warn') - const writeMock = jest.spyOn(ux.write, 'stdout') - const [ - validatorAddress, - anotherValidatorAddress, - groupAddress, - anotherGroupAddress, - signerAddress, - anotherSignerAddress, - ] = await web3.eth.getAccounts() + it( + 'runs election', + async () => { + const kit = newKitFromWeb3(web3) + const logMock = jest.spyOn(console, 'log') + const warnMock = jest.spyOn(console, 'warn') + const writeMock = jest.spyOn(ux.write, 'stdout') + const [ + validatorAddress, + anotherValidatorAddress, + groupAddress, + anotherGroupAddress, + signerAddress, + anotherSignerAddress, + ] = await web3.eth.getAccounts() - await setupGroupAndAffiliateValidator(kit, groupAddress, validatorAddress) - await setupGroupAndAffiliateValidator(kit, anotherGroupAddress, anotherValidatorAddress) + await setupGroupAndAffiliateValidator(kit, groupAddress, validatorAddress) + await setupGroupAndAffiliateValidator(kit, anotherGroupAddress, anotherValidatorAddress) - const electValidatorSignersMock = jest.spyOn(ElectionWrapper.prototype, 'electValidatorSigners') - const getValidatorMock = jest.spyOn(ValidatorsWrapper.prototype, 'getValidator') + const electValidatorSignersMock = jest.spyOn( + ElectionWrapper.prototype, + 'electValidatorSigners' + ) + const getValidatorMock = jest.spyOn(ValidatorsWrapper.prototype, 'getValidator') - electValidatorSignersMock.mockImplementation(async () => [ - validatorAddress, - anotherValidatorAddress, - ]) + electValidatorSignersMock.mockImplementation(async () => [ + validatorAddress, + anotherValidatorAddress, + ]) - getValidatorMock - .mockImplementationOnce(async () => ({ - address: validatorAddress, - affiliation: groupAddress, - blsPublicKey: '0x-bls-public-key-1', - ecdsaPublicKey: '0x-ecdsa-public-key-1', - name: 'Validator #1', - score: new BigNumber(85), - signer: signerAddress, - })) - .mockImplementationOnce(async () => ({ - address: anotherValidatorAddress, - affiliation: anotherGroupAddress, - blsPublicKey: '0x-bls-public-key-2', - ecdsaPublicKey: '0x-ecdsa-public-key-2', - name: 'Validator #2', - score: new BigNumber(100), - signer: anotherSignerAddress, - })) + getValidatorMock + .mockImplementationOnce(async () => ({ + address: validatorAddress, + affiliation: groupAddress, + blsPublicKey: '0x-bls-public-key-1', + ecdsaPublicKey: '0x-ecdsa-public-key-1', + name: 'Validator #1', + score: new BigNumber(85), + signer: signerAddress, + })) + .mockImplementationOnce(async () => ({ + address: anotherValidatorAddress, + affiliation: anotherGroupAddress, + blsPublicKey: '0x-bls-public-key-2', + ecdsaPublicKey: '0x-ecdsa-public-key-2', + name: 'Validator #2', + score: new BigNumber(100), + signer: anotherSignerAddress, + })) - await testLocally(Run, ['--csv']) + await testLocally(Run, ['--csv']) - expect(writeMock.mock.calls).toMatchInlineSnapshot(` + expect(writeMock.mock.calls).toMatchInlineSnapshot(` [ [ "Address,Name,Affiliation,Score,Ecdsapublickey,Blspublickey,Signer @@ -104,7 +109,11 @@ testWithGanache('election:run', (web3: Web3) => { ], ] `) - expect(logMock.mock.calls.map((args) => args.map(stripAnsiCodes))).toMatchInlineSnapshot(`[]`) - expect(warnMock.mock.calls.map((args) => args.map(stripAnsiCodes))).toMatchInlineSnapshot(`[]`) - }) + expect(logMock.mock.calls.map((args) => args.map(stripAnsiCodes))).toMatchInlineSnapshot(`[]`) + expect(warnMock.mock.calls.map((args) => args.map(stripAnsiCodes))).toMatchInlineSnapshot( + `[]` + ) + }, + EXTRA_LONG_TIMEOUT_MS + ) }) diff --git a/packages/cli/src/commands/election/vote.test.ts b/packages/cli/src/commands/election/vote.test.ts index 9aabf7d27..b21d0a154 100644 --- a/packages/cli/src/commands/election/vote.test.ts +++ b/packages/cli/src/commands/election/vote.test.ts @@ -8,7 +8,7 @@ import { registerAccountWithLockedGold, setupGroupAndAffiliateValidator, } from '../../test-utils/chain-setup' -import { stripAnsiCodes, testLocally } from '../../test-utils/cliUtils' +import { EXTRA_LONG_TIMEOUT_MS, stripAnsiCodes, testLocally } from '../../test-utils/cliUtils' import Vote from './vote' process.env.NO_SYNCCHECK = 'true' @@ -51,44 +51,59 @@ testWithGanache('election:vote', (web3: Web3) => { ) }) - it('fails when value is too high', async () => { - const kit = newKitFromWeb3(web3) - const logMock = jest.spyOn(console, 'log') - const [fromAddress, groupAddress, validatorAddress] = await web3.eth.getAccounts() - - await registerAccount(kit, fromAddress) - await setupGroupAndAffiliateValidator(kit, groupAddress, validatorAddress) - - await expect( - testLocally(Vote, ['--from', fromAddress, '--for', groupAddress, '--value', '1']) - ).rejects.toThrow() - - expect(stripAnsiCodes(logMock.mock.calls[3][0])).toContain( - `✘ Account has at least 0.000000000000000001 non-voting Locked Gold` - ) - }) - - it('successfuly votes for a group', async () => { - const kit = newKitFromWeb3(web3) - const logMock = jest.spyOn(console, 'log') - const writeMock = jest.spyOn(ux.write, 'stdout') - const [fromAddress, groupAddress, validatorAddress] = await web3.eth.getAccounts() - const amount = new BigNumber(12345) - const election = await kit.contracts.getElection() - - await registerAccountWithLockedGold(kit, fromAddress) - await setupGroupAndAffiliateValidator(kit, groupAddress, validatorAddress) - - expect(await election.getTotalVotesForGroupByAccount(groupAddress, fromAddress)).toEqual( - new BigNumber(0) - ) - - await expect( - testLocally(Vote, ['--from', fromAddress, '--for', groupAddress, '--value', amount.toFixed()]) - ).resolves.not.toThrow() - - expect(await election.getTotalVotesForGroupByAccount(groupAddress, fromAddress)).toEqual(amount) - expect(logMock.mock.calls.map((args) => args.map(stripAnsiCodes))).toMatchInlineSnapshot(` + it( + 'fails when value is too high', + async () => { + const kit = newKitFromWeb3(web3) + const logMock = jest.spyOn(console, 'log') + const [fromAddress, groupAddress, validatorAddress] = await web3.eth.getAccounts() + + await registerAccount(kit, fromAddress) + await setupGroupAndAffiliateValidator(kit, groupAddress, validatorAddress) + + await expect( + testLocally(Vote, ['--from', fromAddress, '--for', groupAddress, '--value', '1']) + ).rejects.toThrow() + + expect(stripAnsiCodes(logMock.mock.calls[3][0])).toContain( + `✘ Account has at least 0.000000000000000001 non-voting Locked Gold` + ) + }, + EXTRA_LONG_TIMEOUT_MS + ) + + it( + 'successfuly votes for a group', + async () => { + const kit = newKitFromWeb3(web3) + const logMock = jest.spyOn(console, 'log') + const writeMock = jest.spyOn(ux.write, 'stdout') + const [fromAddress, groupAddress, validatorAddress] = await web3.eth.getAccounts() + const amount = new BigNumber(12345) + const election = await kit.contracts.getElection() + + await registerAccountWithLockedGold(kit, fromAddress) + await setupGroupAndAffiliateValidator(kit, groupAddress, validatorAddress) + + expect(await election.getTotalVotesForGroupByAccount(groupAddress, fromAddress)).toEqual( + new BigNumber(0) + ) + + await expect( + testLocally(Vote, [ + '--from', + fromAddress, + '--for', + groupAddress, + '--value', + amount.toFixed(), + ]) + ).resolves.not.toThrow() + + expect(await election.getTotalVotesForGroupByAccount(groupAddress, fromAddress)).toEqual( + amount + ) + expect(logMock.mock.calls.map((args) => args.map(stripAnsiCodes))).toMatchInlineSnapshot(` [ [ "Running Checks:", @@ -113,6 +128,8 @@ testWithGanache('election:vote', (web3: Web3) => { ], ] `) - expect(writeMock.mock.calls).toMatchInlineSnapshot(`[]`) - }) + expect(writeMock.mock.calls).toMatchInlineSnapshot(`[]`) + }, + EXTRA_LONG_TIMEOUT_MS + ) }) diff --git a/packages/cli/src/commands/governance/propose.test.ts b/packages/cli/src/commands/governance/propose.test.ts index 621e80d47..3899ee2af 100644 --- a/packages/cli/src/commands/governance/propose.test.ts +++ b/packages/cli/src/commands/governance/propose.test.ts @@ -147,6 +147,8 @@ testWithAnvil('governance:propose cmd', (web3: Web3) => { let accounts: StrongAddress[] = [] beforeEach(async () => { + fetchMock.reset() + fetchMock.catch(404) accounts = (await web3.eth.getAccounts()) as StrongAddress[] kit.defaultAccount = accounts[0] governance = await kit.contracts.getGovernance() @@ -157,6 +159,15 @@ testWithAnvil('governance:propose cmd', (web3: Web3) => { test( 'will successfully create proposal based on Core contract', async () => { + fetchMock + .get( + 'https://repo.sourcify.dev/contracts/full_match/1101/0x8726C7414ac023D23348326B47AF3205185Fd035/metadata.json', + 400 + ) + .get( + 'https://repo.sourcify.dev/contracts/partial_match/1101/0x8726C7414ac023D23348326B47AF3205185Fd035/metadata.json', + 400 + ) const transactionsToBeSaved = JSON.stringify(transactions) fs.writeFileSync('transactions.json', transactionsToBeSaved, { flag: 'w' }) @@ -245,7 +256,7 @@ testWithAnvil('governance:propose cmd', (web3: Web3) => { '--for', multisigWithOneSigner, '--descriptionURL', - 'https://dummyurl.com', + 'https://example.com', ], web3 ) @@ -310,7 +321,7 @@ testWithAnvil('governance:propose cmd', (web3: Web3) => { '--for', multisigWithTwoSigners, '--descriptionURL', - 'https://dummyurl.com', + 'https://example.com', ], web3 ) @@ -365,7 +376,7 @@ testWithAnvil('governance:propose cmd', (web3: Web3) => { '--from', accounts[0], '--descriptionURL', - 'https://dummyurl.com', + 'https://example.com', '--force', '--noInfo', ], @@ -412,7 +423,7 @@ testWithAnvil('governance:propose cmd', (web3: Web3) => { '--from', accounts[0], '--descriptionURL', - 'https://dummyurl.com', + 'https://example.com', '--force', '--noInfo', ], @@ -518,4 +529,133 @@ testWithAnvil('governance:propose cmd', (web3: Web3) => { }, EXTRA_LONG_TIMEOUT_MS ) + + const transactionsForContractsVerifiedOnCeloScan = [ + { + contract: 'Vyper_contract', + address: '0xf4cab10dC19695AaCe14b7A16d7705b600ad5F73', + function: 'transfer(address,uint256)', + args: ['0x87647780180B8f55980C7D3fFeFe08a9B29e9aE1', '20001239154911011864219072'], + value: '0', + }, + ] + + test( + 'when proposal contains transactions for contracts not verified', + async () => { + fetchMock + .get( + 'https://repo.sourcify.dev/contracts/full_match/1101/0x37f750B7cC259A2f741AF45294f6a16572CF5cAd/metadata.json', + 404 + ) + .get( + 'https://repo.sourcify.dev/contracts/partial_match/1101/0x37f750B7cC259A2f741AF45294f6a16572CF5cAd/metadata.json', + 404 + ) + .get( + 'https://api.celoscan.io/api?module=contract&action=getsourcecode&address=0x37f750B7cC259A2f741AF45294f6a16572CF5cAd', + { + status: '0', + message: 'NOTOK', + } + ) + const transactionsForUnverifiedContracts = [ + { + contract: '0x552b9AA0eEe500c60f09456e49FBc1096322714C', + address: '0x37f750B7cC259A2f741AF45294f6a16572CF5cAd', + function: 'approve(address,uint256)', + args: ['0xFa3df877F98ac5ecd87456a7AcCaa948462412f0', '10000000000000000000000000'], + value: '0', + }, + ] + const transactionsToBeSaved = JSON.stringify(transactionsForUnverifiedContracts) + fs.writeFileSync('transactions.json', transactionsToBeSaved, { flag: 'w' }) + await expect( + testLocallyWithWeb3Node( + Propose, + [ + '--jsonTransactions', + 'transactions.json', + '--deposit', + '1000000000000000000', + '--from', + accounts[0] as StrongAddress, + '--descriptionURL', + 'https://example.com', + ], + web3 + ) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unable to parse {"input":"0x095ea7b3000000000000000000000000fa3df877f98ac5ecd87456a7accaa948462412f0000000000000000000000000000000000000000000084595161401484a000000","to":"0x37f750B7cC259A2f741AF45294f6a16572CF5cAd","value":"0"} with block explorer"` + ) + }, + EXTRA_LONG_TIMEOUT_MS + ) + + test( + 'succeeds when proposal contains transactions for contracts verified on celoScan', + async () => { + fetchMock + .get( + 'https://repo.sourcify.dev/contracts/full_match/1101/0xf4cab10dC19695AaCe14b7A16d7705b600ad5F73/metadata.json', + 404 + ) + .get( + 'https://api.celoscan.io/api?module=contract&action=getsourcecode&address=0xf4cab10dC19695AaCe14b7A16d7705b600ad5F73', + MOCK_CONTRACT_RESPONSE + ) + const transactionsToBeSaved = JSON.stringify(transactionsForContractsVerifiedOnCeloScan) + fs.writeFileSync('transactions2.json', transactionsToBeSaved, { flag: 'w' }) + await testLocallyWithWeb3Node( + Propose, + [ + '--jsonTransactions', + 'transactions2.json', + '--deposit', + '1000000000000000000', + '--from', + accounts[0], + '--descriptionURL', + 'https://example.com', + ], + web3 + ) + const proposal = await governance.getProposal(1) + expect(proposal.length).toEqual(transactionsForContractsVerifiedOnCeloScan.length) + expect(proposal[0].to).toEqual('0xf4cab10dC19695AaCe14b7A16d7705b600ad5F73') + expect(proposal[0].value).toEqual(transactionsForContractsVerifiedOnCeloScan[0].value) + expect(proposal).toMatchInlineSnapshot(` + [ + { + "input": "0xa9059cbb00000000000000000000000087647780180b8f55980c7d3ffefe08a9b29e9ae1000000000000000000000000000000000000000000108b6d58e29cce52f28dc0", + "to": "0xf4cab10dC19695AaCe14b7A16d7705b600ad5F73", + "value": "0", + }, + ] + `) + }, + EXTRA_LONG_TIMEOUT_MS + ) }) + +const MOCK_CONTRACT_RESPONSE = { + status: '1', + message: 'OK', + result: [ + { + SourceCode: '', + ABI: '[{"name":"Transfer","inputs":[{"name":"sender","type":"address","indexed":true},{"name":"receiver","type":"address","indexed":true},{"name":"value","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"Approval","inputs":[{"name":"owner","type":"address","indexed":true},{"name":"spender","type":"address","indexed":true},{"name":"value","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"TokenExchange","inputs":[{"name":"buyer","type":"address","indexed":true},{"name":"sold_id","type":"int128","indexed":false},{"name":"tokens_sold","type":"uint256","indexed":false},{"name":"bought_id","type":"int128","indexed":false},{"name":"tokens_bought","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"AddLiquidity","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amounts","type":"uint256[2]","indexed":false},{"name":"fees","type":"uint256[2]","indexed":false},{"name":"invariant","type":"uint256","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveLiquidity","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amounts","type":"uint256[2]","indexed":false},{"name":"fees","type":"uint256[2]","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveLiquidityOne","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amount","type":"uint256","indexed":false},{"name":"coin_amount","type":"uint256","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RemoveLiquidityImbalance","inputs":[{"name":"provider","type":"address","indexed":true},{"name":"token_amounts","type":"uint256[2]","indexed":false},{"name":"fees","type":"uint256[2]","indexed":false},{"name":"invariant","type":"uint256","indexed":false},{"name":"token_supply","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"RampA","inputs":[{"name":"old_A","type":"uint256","indexed":false},{"name":"new_A","type":"uint256","indexed":false},{"name":"initial_time","type":"uint256","indexed":false},{"name":"future_time","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"name":"StopRampA","inputs":[{"name":"A","type":"uint256","indexed":false},{"name":"t","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"stateMutability":"nonpayable","type":"constructor","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"initialize","inputs":[{"name":"_name","type":"string"},{"name":"_symbol","type":"string"},{"name":"_coins","type":"address[4]"},{"name":"_rate_multipliers","type":"uint256[4]"},{"name":"_A","type":"uint256"},{"name":"_fee","type":"uint256"}],"outputs":[],"gas":516829},{"stateMutability":"view","type":"function","name":"decimals","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":390},{"stateMutability":"nonpayable","type":"function","name":"transfer","inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"outputs":[{"name":"","type":"bool"}],"gas":79005},{"stateMutability":"nonpayable","type":"function","name":"transferFrom","inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"outputs":[{"name":"","type":"bool"}],"gas":116985},{"stateMutability":"nonpayable","type":"function","name":"approve","inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"outputs":[{"name":"","type":"bool"}],"gas":39211},{"stateMutability":"nonpayable","type":"function","name":"permit","inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"},{"name":"_deadline","type":"uint256"},{"name":"_v","type":"uint8"},{"name":"_r","type":"bytes32"},{"name":"_s","type":"bytes32"}],"outputs":[{"name":"","type":"bool"}],"gas":102281},{"stateMutability":"view","type":"function","name":"get_balances","inputs":[],"outputs":[{"name":"","type":"uint256[2]"}],"gas":4782},{"stateMutability":"view","type":"function","name":"admin_fee","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":570},{"stateMutability":"view","type":"function","name":"A","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":10508},{"stateMutability":"view","type":"function","name":"A_precise","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":10508},{"stateMutability":"view","type":"function","name":"get_virtual_price","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":649135},{"stateMutability":"view","type":"function","name":"calc_token_amount","inputs":[{"name":"_amounts","type":"uint256[2]"},{"name":"_is_deposit","type":"bool"}],"outputs":[{"name":"","type":"uint256"}],"gas":1284256},{"stateMutability":"nonpayable","type":"function","name":"add_liquidity","inputs":[{"name":"_amounts","type":"uint256[2]"},{"name":"_min_mint_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}],"gas":2144257},{"stateMutability":"nonpayable","type":"function","name":"add_liquidity","inputs":[{"name":"_amounts","type":"uint256[2]"},{"name":"_min_mint_amount","type":"uint256"},{"name":"_receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}],"gas":2144257},{"stateMutability":"view","type":"function","name":"get_dy","inputs":[{"name":"i","type":"int128"},{"name":"j","type":"int128"},{"name":"dx","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}],"gas":995830},{"stateMutability":"nonpayable","type":"function","name":"exchange","inputs":[{"name":"i","type":"int128"},{"name":"j","type":"int128"},{"name":"_dx","type":"uint256"},{"name":"_min_dy","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}],"gas":1150187},{"stateMutability":"nonpayable","type":"function","name":"exchange","inputs":[{"name":"i","type":"int128"},{"name":"j","type":"int128"},{"name":"_dx","type":"uint256"},{"name":"_min_dy","type":"uint256"},{"name":"_receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}],"gas":1150187},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity","inputs":[{"name":"_burn_amount","type":"uint256"},{"name":"_min_amounts","type":"uint256[2]"}],"outputs":[{"name":"","type":"uint256[2]"}],"gas":241198},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity","inputs":[{"name":"_burn_amount","type":"uint256"},{"name":"_min_amounts","type":"uint256[2]"},{"name":"_receiver","type":"address"}],"outputs":[{"name":"","type":"uint256[2]"}],"gas":241198},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity_imbalance","inputs":[{"name":"_amounts","type":"uint256[2]"},{"name":"_max_burn_amount","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}],"gas":2144337},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity_imbalance","inputs":[{"name":"_amounts","type":"uint256[2]"},{"name":"_max_burn_amount","type":"uint256"},{"name":"_receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}],"gas":2144337},{"stateMutability":"view","type":"function","name":"calc_withdraw_one_coin","inputs":[{"name":"_burn_amount","type":"uint256"},{"name":"i","type":"int128"}],"outputs":[{"name":"","type":"uint256"}],"gas":1229},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity_one_coin","inputs":[{"name":"_burn_amount","type":"uint256"},{"name":"i","type":"int128"},{"name":"_min_received","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}],"gas":1535032},{"stateMutability":"nonpayable","type":"function","name":"remove_liquidity_one_coin","inputs":[{"name":"_burn_amount","type":"uint256"},{"name":"i","type":"int128"},{"name":"_min_received","type":"uint256"},{"name":"_receiver","type":"address"}],"outputs":[{"name":"","type":"uint256"}],"gas":1535032},{"stateMutability":"nonpayable","type":"function","name":"ramp_A","inputs":[{"name":"_future_A","type":"uint256"},{"name":"_future_time","type":"uint256"}],"outputs":[],"gas":161164},{"stateMutability":"nonpayable","type":"function","name":"stop_ramp_A","inputs":[],"outputs":[],"gas":157387},{"stateMutability":"view","type":"function","name":"admin_balances","inputs":[{"name":"i","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}],"gas":7829},{"stateMutability":"nonpayable","type":"function","name":"withdraw_admin_fees","inputs":[],"outputs":[],"gas":28911},{"stateMutability":"pure","type":"function","name":"version","inputs":[],"outputs":[{"name":"","type":"string"}],"gas":6677},{"stateMutability":"view","type":"function","name":"coins","inputs":[{"name":"arg0","type":"uint256"}],"outputs":[{"name":"","type":"address"}],"gas":3225},{"stateMutability":"view","type":"function","name":"balances","inputs":[{"name":"arg0","type":"uint256"}],"outputs":[{"name":"","type":"uint256"}],"gas":3255},{"stateMutability":"view","type":"function","name":"fee","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3240},{"stateMutability":"view","type":"function","name":"initial_A","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3270},{"stateMutability":"view","type":"function","name":"future_A","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3300},{"stateMutability":"view","type":"function","name":"initial_A_time","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3330},{"stateMutability":"view","type":"function","name":"future_A_time","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3360},{"stateMutability":"view","type":"function","name":"name","inputs":[],"outputs":[{"name":"","type":"string"}],"gas":13679},{"stateMutability":"view","type":"function","name":"symbol","inputs":[],"outputs":[{"name":"","type":"string"}],"gas":11438},{"stateMutability":"view","type":"function","name":"balanceOf","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"uint256"}],"gas":3716},{"stateMutability":"view","type":"function","name":"allowance","inputs":[{"name":"arg0","type":"address"},{"name":"arg1","type":"address"}],"outputs":[{"name":"","type":"uint256"}],"gas":4012},{"stateMutability":"view","type":"function","name":"totalSupply","inputs":[],"outputs":[{"name":"","type":"uint256"}],"gas":3510},{"stateMutability":"view","type":"function","name":"DOMAIN_SEPARATOR","inputs":[],"outputs":[{"name":"","type":"bytes32"}],"gas":3540},{"stateMutability":"view","type":"function","name":"nonces","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"uint256"}],"gas":3836}]', + ContractName: 'Vyper_contract', + CompilerVersion: 'vyper:0.3.1', + OptimizationUsed: '0', + Runs: '0', + ConstructorArguments: '', + EVMVersion: 'Default', + Library: '', + LicenseType: 'None', + Proxy: '0', + Implementation: '', + SwarmSource: '', + }, + ], +} diff --git a/packages/cli/src/commands/governance/upvote.test.ts b/packages/cli/src/commands/governance/upvote.test.ts index d647d629d..9222d56fb 100644 --- a/packages/cli/src/commands/governance/upvote.test.ts +++ b/packages/cli/src/commands/governance/upvote.test.ts @@ -5,7 +5,7 @@ import { testWithAnvil } from '@celo/dev-utils/lib/anvil-test' import { timeTravel } from '@celo/dev-utils/lib/ganache-test' import BigNumber from 'bignumber.js' import Web3 from 'web3' -import { testLocallyWithWeb3Node } from '../../test-utils/cliUtils' +import { EXTRA_LONG_TIMEOUT_MS, testLocallyWithWeb3Node } from '../../test-utils/cliUtils' import Register from '../account/register' import Lock from '../lockedgold/lock' import Dequeue from './dequeue' @@ -70,14 +70,18 @@ testWithAnvil('governance:upvote cmd', (web3: Web3) => { expect(dequeue).toEqual([proposalID, proposalID2, proposalID3, proposalID4]) }) - test('can upvote proposal which cannot be dequeued', async () => { - await testLocallyWithWeb3Node( - Upvote, - ['--proposalID', proposalID5.toString(10), '--from', accounts[0]], - web3 - ) + test( + 'can upvote proposal which cannot be dequeued', + async () => { + await testLocallyWithWeb3Node( + Upvote, + ['--proposalID', proposalID5.toString(10), '--from', accounts[0]], + web3 + ) - const queue = await governance.getQueue() - expect(queue).toEqual([{ proposalID: proposalID5, upvotes: new BigNumber(100) }]) - }) + const queue = await governance.getQueue() + expect(queue).toEqual([{ proposalID: proposalID5, upvotes: new BigNumber(100) }]) + }, + EXTRA_LONG_TIMEOUT_MS + ) }) diff --git a/packages/cli/src/commands/lockedgold/lock.test.ts b/packages/cli/src/commands/lockedgold/lock.test.ts index eb457556c..c2aef51b3 100644 --- a/packages/cli/src/commands/lockedgold/lock.test.ts +++ b/packages/cli/src/commands/lockedgold/lock.test.ts @@ -1,4 +1,5 @@ import { newKitFromWeb3 } from '@celo/contractkit' +import { testWithAnvil } from '@celo/dev-utils/lib/anvil-test' import { ux } from '@oclif/core' import BigNumber from 'bignumber.js' import Web3 from 'web3' @@ -10,7 +11,6 @@ import { import Register from '../account/register' import Lock from './lock' import Unlock from './unlock' -import { testWithAnvil } from '@celo/dev-utils/lib/anvil-test' process.env.NO_SYNCCHECK = 'true' diff --git a/packages/cli/src/commands/lockedgold/unlock.test.ts b/packages/cli/src/commands/lockedgold/unlock.test.ts index fb76c326e..1626f5637 100644 --- a/packages/cli/src/commands/lockedgold/unlock.test.ts +++ b/packages/cli/src/commands/lockedgold/unlock.test.ts @@ -1,4 +1,5 @@ import { newKitFromWeb3 } from '@celo/contractkit' +import { testWithAnvil } from '@celo/dev-utils/lib/anvil-test' import { addressToPublicKey } from '@celo/utils/lib/signatureUtils' import Web3 from 'web3' import { LONG_TIMEOUT_MS, testLocallyWithWeb3Node } from '../../test-utils/cliUtils' @@ -10,7 +11,6 @@ import ValidatorGroupMember from '../validatorgroup/member' import ValidatorGroupRegister from '../validatorgroup/register' import Lock from './lock' import Unlock from './unlock' -import { testWithAnvil } from '@celo/dev-utils/lib/anvil-test' process.env.NO_SYNCCHECK = 'true' diff --git a/packages/cli/src/commands/lockedgold/update-delegated-amount.test.ts b/packages/cli/src/commands/lockedgold/update-delegated-amount.test.ts index e0131bbf4..5dd562dc9 100644 --- a/packages/cli/src/commands/lockedgold/update-delegated-amount.test.ts +++ b/packages/cli/src/commands/lockedgold/update-delegated-amount.test.ts @@ -1,10 +1,10 @@ +import { testWithAnvil } from '@celo/dev-utils/lib/anvil-test' import Web3 from 'web3' import { LONG_TIMEOUT_MS, testLocallyWithWeb3Node } from '../../test-utils/cliUtils' import Register from '../account/register' import Delegate from './delegate' import Lock from './lock' import UpdateDelegatedAmount from './update-delegated-amount' -import { testWithAnvil } from '@celo/dev-utils/lib/anvil-test' process.env.NO_SYNCCHECK = 'true' diff --git a/packages/cli/src/commands/network/contracts.test.ts b/packages/cli/src/commands/network/contracts.test.ts index 4e8d1ab68..2d2e806f6 100644 --- a/packages/cli/src/commands/network/contracts.test.ts +++ b/packages/cli/src/commands/network/contracts.test.ts @@ -7,7 +7,7 @@ process.env.NO_SYNCCHECK = 'true' testWithGanache('network:contracts', () => { test('runs', async () => { const spy = jest.spyOn(write, 'stdout') - await testLocally(Contracts, ['--output', 'json']) + await testLocally(Contracts, ['--output', 'csv']) expect(spy.mock.calls).toMatchSnapshot() }) }) diff --git a/packages/cli/src/commands/releasecelo/locked-gold.test.ts b/packages/cli/src/commands/releasecelo/locked-gold.test.ts index 25bb7be83..3578685f4 100644 --- a/packages/cli/src/commands/releasecelo/locked-gold.test.ts +++ b/packages/cli/src/commands/releasecelo/locked-gold.test.ts @@ -1,7 +1,7 @@ import { newKitFromWeb3 } from '@celo/contractkit' import { getContractFromEvent, testWithGanache } from '@celo/dev-utils/lib/ganache-test' import Web3 from 'web3' -import { LONG_TIMEOUT_MS, testLocally } from '../../test-utils/cliUtils' +import { testLocally } from '../../test-utils/cliUtils' import CreateAccount from './create-account' import LockedGold from './locked-gold' @@ -20,47 +20,43 @@ testWithGanache('releasegold:locked-gold cmd', (web3: Web3) => { await testLocally(CreateAccount, ['--contract', contractAddress]) }) - test( - 'can lock celo with pending withdrawals', - async () => { - const lockedGold = await kit.contracts.getLockedGold() - await testLocally(LockedGold, [ - '--contract', - contractAddress, - '--action', - 'lock', - '--value', - '100', - ]) - await testLocally(LockedGold, [ - '--contract', - contractAddress, - '--action', - 'unlock', - '--value', - '50', - ]) - await testLocally(LockedGold, [ - '--contract', - contractAddress, - '--action', - 'lock', - '--value', - '75', - ]) - await testLocally(LockedGold, [ - '--contract', - contractAddress, - '--action', - 'unlock', - '--value', - '50', - ]) - const pendingWithdrawalsTotalValue = await lockedGold.getPendingWithdrawalsTotalValue( - contractAddress - ) - expect(pendingWithdrawalsTotalValue.toFixed()).toBe('50') - }, - LONG_TIMEOUT_MS * 2 - ) + test('can lock celo with pending withdrawals', async () => { + const lockedGold = await kit.contracts.getLockedGold() + await testLocally(LockedGold, [ + '--contract', + contractAddress, + '--action', + 'lock', + '--value', + '100', + ]) + await testLocally(LockedGold, [ + '--contract', + contractAddress, + '--action', + 'unlock', + '--value', + '50', + ]) + await testLocally(LockedGold, [ + '--contract', + contractAddress, + '--action', + 'lock', + '--value', + '75', + ]) + await testLocally(LockedGold, [ + '--contract', + contractAddress, + '--action', + 'unlock', + '--value', + '50', + ]) + const pendingWithdrawalsTotalValue = await lockedGold.getPendingWithdrawalsTotalValue( + contractAddress + ) + expect(pendingWithdrawalsTotalValue.toFixed()).toBe('50') + }) }) diff --git a/packages/cli/src/commands/releasecelo/locked-gold.ts b/packages/cli/src/commands/releasecelo/locked-gold.ts index 7122b6d62..485717a35 100644 --- a/packages/cli/src/commands/releasecelo/locked-gold.ts +++ b/packages/cli/src/commands/releasecelo/locked-gold.ts @@ -43,7 +43,7 @@ export default class LockedGold extends ReleaseGoldBaseCommand { const releaseOwner = await this.releaseGoldWrapper.getReleaseOwner() const lockedGold = await kit.contracts.getLockedGold() kit.defaultAccount = isRevoked ? releaseOwner : beneficiary - + console.log(`Using account ${kit.defaultAccount}`) if (flags.action === 'lock') { // Must verify contract is account before checking pending withdrawals await checkBuilder.addCheck('Is not revoked', () => !isRevoked).runChecks() diff --git a/packages/cli/src/commands/releasecelo/transfer-dollars.test.ts b/packages/cli/src/commands/releasecelo/transfer-dollars.test.ts index e2e554a60..044d02ae9 100644 --- a/packages/cli/src/commands/releasecelo/transfer-dollars.test.ts +++ b/packages/cli/src/commands/releasecelo/transfer-dollars.test.ts @@ -1,3 +1,4 @@ +import { OFAC_SANCTIONS_LIST_URL, SANCTIONED_ADDRESSES } from '@celo/compliance' import { ContractKit, newKitFromWeb3 } from '@celo/contractkit' import { getContractFromEvent, testWithGanache } from '@celo/dev-utils/lib/ganache-test' import Web3 from 'web3' @@ -24,10 +25,13 @@ testWithGanache('releasegold:transfer-dollars cmd', (web3: Web3) => { { index: 1 } // canValidate = false ) kit = newKitFromWeb3(web3) + fetchMock.get(OFAC_SANCTIONS_LIST_URL, SANCTIONED_ADDRESSES) + accounts = await web3.eth.getAccounts() await testLocally(Register, ['--from', accounts[0]]) await testLocally(CreateAccount, ['--contract', contractAddress]) }) + afterEach(() => fetchMock.reset()) test('can transfer dollars out of the ReleaseGold contract', async () => { const balanceBefore = await kit.getTotalBalance(accounts[0]) diff --git a/packages/cli/src/commands/transfer/celo.test.ts b/packages/cli/src/commands/transfer/celo.test.ts index 28184fa42..41c12e853 100644 --- a/packages/cli/src/commands/transfer/celo.test.ts +++ b/packages/cli/src/commands/transfer/celo.test.ts @@ -1,4 +1,8 @@ -import { COMPLIANT_ERROR_RESPONSE, SANCTIONED_ADDRESSES } from '@celo/compliance' +import { + COMPLIANT_ERROR_RESPONSE, + OFAC_SANCTIONS_LIST_URL, + SANCTIONED_ADDRESSES, +} from '@celo/compliance' import { ContractKit, StableToken, newKitFromWeb3 } from '@celo/contractkit' import Web3 from 'web3' import { testLocallyWithWeb3Node } from '../../test-utils/cliUtils' @@ -17,7 +21,9 @@ testWithGanache('transfer:celo cmd', (web3: Web3) => { beforeEach(async () => { kit = newKitFromWeb3(web3) accounts = await web3.eth.getAccounts() + fetchMock.get(OFAC_SANCTIONS_LIST_URL, SANCTIONED_ADDRESSES) }) + afterEach(() => fetchMock.reset()) test('can transfer celo', async () => { const balanceBefore = await kit.getTotalBalance(accounts[0]) diff --git a/packages/cli/src/commands/transfer/dollars.test.ts b/packages/cli/src/commands/transfer/dollars.test.ts index 3c57109b6..4a8d4eb6a 100644 --- a/packages/cli/src/commands/transfer/dollars.test.ts +++ b/packages/cli/src/commands/transfer/dollars.test.ts @@ -1,11 +1,15 @@ -import { COMPLIANT_ERROR_RESPONSE, SANCTIONED_ADDRESSES } from '@celo/compliance' +import { + COMPLIANT_ERROR_RESPONSE, + OFAC_SANCTIONS_LIST_URL, + SANCTIONED_ADDRESSES, +} from '@celo/compliance' import { ContractKit, StableToken, newKitFromWeb3 } from '@celo/contractkit' +import { testWithAnvil } from '@celo/dev-utils/lib/anvil-test' +import BigNumber from 'bignumber.js' import Web3 from 'web3' +import { topUpWithToken } from '../../test-utils/chain-setup' import { testLocallyWithWeb3Node } from '../../test-utils/cliUtils' import TransferCUSD from './dollars' -import { testWithAnvil } from '@celo/dev-utils/lib/anvil-test' -import { topUpWithToken } from '../../test-utils/chain-setup' -import BigNumber from 'bignumber.js' process.env.NO_SYNCCHECK = 'true' @@ -19,7 +23,9 @@ testWithAnvil('transfer:dollars cmd', (web3: Web3) => { beforeEach(async () => { kit = newKitFromWeb3(web3) accounts = await web3.eth.getAccounts() + fetchMock.get(OFAC_SANCTIONS_LIST_URL, SANCTIONED_ADDRESSES) }) + afterEach(() => fetchMock.reset()) test('can transfer cusd', async () => { await topUpWithToken( @@ -67,7 +73,7 @@ testWithAnvil('transfer:dollars cmd', (web3: Web3) => { ['--from', accounts[1], '--to', SANCTIONED_ADDRESSES[0], '--value', '1'], web3 ) - ).rejects.toThrow() + ).rejects.toThrow(`"Some checks didn't pass!"`) expect(spy).toHaveBeenCalledWith(expect.stringContaining(COMPLIANT_ERROR_RESPONSE)) }) }) diff --git a/packages/cli/src/commands/transfer/euros.test.ts b/packages/cli/src/commands/transfer/euros.test.ts index 0947a3697..749fb55b4 100644 --- a/packages/cli/src/commands/transfer/euros.test.ts +++ b/packages/cli/src/commands/transfer/euros.test.ts @@ -1,11 +1,15 @@ -import { COMPLIANT_ERROR_RESPONSE, SANCTIONED_ADDRESSES } from '@celo/compliance' +import { + COMPLIANT_ERROR_RESPONSE, + OFAC_SANCTIONS_LIST_URL, + SANCTIONED_ADDRESSES, +} from '@celo/compliance' import { ContractKit, StableToken, newKitFromWeb3 } from '@celo/contractkit' +import { testWithAnvil } from '@celo/dev-utils/lib/anvil-test' +import BigNumber from 'bignumber.js' import Web3 from 'web3' +import { topUpWithToken } from '../../test-utils/chain-setup' import { testLocallyWithWeb3Node } from '../../test-utils/cliUtils' import TransferEURO from './euros' -import { testWithAnvil } from '@celo/dev-utils/lib/anvil-test' -import { topUpWithToken } from '../../test-utils/chain-setup' -import BigNumber from 'bignumber.js' process.env.NO_SYNCCHECK = 'true' @@ -19,7 +23,9 @@ testWithAnvil('transfer:euros cmd', (web3: Web3) => { beforeEach(async () => { kit = newKitFromWeb3(web3) accounts = await web3.eth.getAccounts() + fetchMock.get(OFAC_SANCTIONS_LIST_URL, SANCTIONED_ADDRESSES) }) + afterEach(() => fetchMock.reset()) test('can transfer ceur', async () => { await topUpWithToken( @@ -67,7 +73,7 @@ testWithAnvil('transfer:euros cmd', (web3: Web3) => { ['--from', accounts[1], '--to', SANCTIONED_ADDRESSES[0], '--value', '1'], web3 ) - ).rejects.toThrow() + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Some checks didn't pass!"`) expect(spy).toHaveBeenCalledWith(expect.stringContaining(COMPLIANT_ERROR_RESPONSE)) }) }) diff --git a/packages/cli/src/test-utils/setup.ts b/packages/cli/src/test-utils/setup.ts index 5dbb83c4f..aafcd6394 100644 --- a/packages/cli/src/test-utils/setup.ts +++ b/packages/cli/src/test-utils/setup.ts @@ -1,3 +1,21 @@ +const fetchMockSandbox = require('fetch-mock').sandbox() +jest.mock('cross-fetch', () => ({ + ...jest.requireActual('cross-fetch'), + __esModule: true, + default: fetchMockSandbox, + fetch: fetchMockSandbox, +})) + +// @ts-ignore +global.fetchMock = fetchMockSandbox + +/* eslint import/no-extraneous-dependencies:off */ +import { FetchMockSandbox } from 'fetch-mock' + +declare global { + const fetchMock: FetchMockSandbox +} + jest.mock('@ledgerhq/hw-transport-node-hid', () => { return { default: { diff --git a/packages/cli/src/utils/checks.ts b/packages/cli/src/utils/checks.ts index 7e3f009f7..da7a1c13f 100644 --- a/packages/cli/src/utils/checks.ts +++ b/packages/cli/src/utils/checks.ts @@ -15,7 +15,7 @@ import { isValidAddress } from '@celo/utils/lib/address' import { verifySignature } from '@celo/utils/lib/signatureUtils' import BigNumber from 'bignumber.js' import chalk from 'chalk' -import { fetch } from 'cross-fetch' +import fetch from 'cross-fetch' import utils from 'web3-utils' import { BaseCommand } from '../base' import { printValueMapRecursive } from './cli' diff --git a/packages/sdk/explorer/src/block-explorer.ts b/packages/sdk/explorer/src/block-explorer.ts index 2d4743ac5..552ec9364 100644 --- a/packages/sdk/explorer/src/block-explorer.ts +++ b/packages/sdk/explorer/src/block-explorer.ts @@ -308,7 +308,7 @@ export class BlockExplorer { /** * Returns the ContractMapping for the contract at that address, or undefined - * by looking up the contract address in Sourcify. + * by looking up the contract address on various contract verification services. * @param address * @returns The ContractMapping for the contract at that address, or undefined */ @@ -380,14 +380,11 @@ export class BlockExplorer { this.getContractMappingFromSourcifyAsProxy, ] ): Promise { - const mappings = await Promise.all( - strategies.map(async (strategy) => { - const contractMapping = await strategy(address) - if (contractMapping && contractMapping.fnMapping.get(selector)) { - return contractMapping - } - }) - ) - return mappings.find((mapping) => mapping !== undefined) + for (const strategy of strategies) { + const contractMapping = await strategy(address) + if (contractMapping && contractMapping.fnMapping.get(selector)) { + return contractMapping + } + } } } diff --git a/packages/sdk/explorer/src/sourcify.test.ts b/packages/sdk/explorer/src/sourcify.test.ts index 08d0cf481..686df482f 100644 --- a/packages/sdk/explorer/src/sourcify.test.ts +++ b/packages/sdk/explorer/src/sourcify.test.ts @@ -12,7 +12,7 @@ import { Metadata, fetchMetadata, tryGetProxyImplementation } from './sourcify' // This is taken from protocol/contracts/build/Account.json const CONTRACT_METADATA = require('../fixtures/contract.metadata.json') -describe('sourcify helpers', () => { +describe('sourcify verified helpers', () => { let connection: Connection const web3: Web3 = new Web3() const address: Address = web3.utils.randomHex(20) @@ -56,6 +56,26 @@ describe('sourcify helpers', () => { }) describe('when a full match does not exist', () => { + describe('when contract has been verified on celoscan', () => { + it('returns the metadata from celoscan', async () => { + fetchMock + .get( + `https://repo.sourcify.dev/contracts/full_match/42220/${address}/metadata.json`, + 400 + ) + .get( + `https://api.celoscan.io/api?module=contract&action=getsourcecode&address=${address}`, + PROX_CONTRACT_VIA_CELLOSCAN + ) + .get( + `https://api.celoscan.io/api?module=contract&action=getsourcecode&address=0x796dff6d74f3e27060b71255fe517bfb23c93eed`, + IMPLEMENATATION_CONTRACT + ) + const result = await fetchMetadata(connection, address) + expect(result).toBeInstanceOf(Metadata) + expect(result?.abi).toBeDefined() + }) + }) describe('but a partial match exists', () => { it('returns the metadata from the partial match', async () => { fetchMock @@ -67,6 +87,14 @@ describe('sourcify helpers', () => { `https://repo.sourcify.dev/contracts/partial_match/42220/${address}/metadata.json`, {} ) + .get( + `https://api.celoscan.io/api?module=contract&action=getsourcecode&address=${address}`, + { + status: '0', + message: 'NOTOK', + } + ) + const metadata = await fetchMetadata(connection, address) expect(metadata).toBeInstanceOf(Metadata) }) @@ -83,6 +111,13 @@ describe('sourcify helpers', () => { `https://repo.sourcify.dev/contracts/partial_match/42220/${address}/metadata.json`, 400 ) + .get( + `https://api.celoscan.io/api?module=contract&action=getsourcecode&address=${address}`, + { + status: '0', + message: 'NOTOK', + } + ) const metadata = await fetchMetadata(connection, address) expect(metadata).toEqual(null) }) @@ -93,6 +128,7 @@ describe('sourcify helpers', () => { describe('Metadata', () => { describe('get abi', () => { it('returns the abi when it finds it', () => { + // @ts-expect-error -- test is purposefully incorrect const metadata = new Metadata(connection, address, { output: { abi: [{}] } }) const abi = metadata.abi expect(abi).not.toBeNull() @@ -100,6 +136,7 @@ describe('sourcify helpers', () => { }) it('returns null when there is no abi', () => { + // @ts-expect-error -- test is purposefully incorrect const metadata = new Metadata(connection, address, { output: { other: [{}] } }) const abi = metadata.abi expect(abi).toBeNull() @@ -107,8 +144,23 @@ describe('sourcify helpers', () => { }) describe('get contractName', () => { + describe('when name is passed in as setting', () => { + it('is used', () => { + const metadata = new Metadata(connection, address, { + settings: { + name: 'SpiceContract', + compilationTarget: { + 'somefile.sol': 'SomeContract', + 'otherfile.sol': 'OtherContract', + }, + }, + }) + expect(metadata.contractName).toEqual('SpiceContract') + }) + }) describe('when the structure does not contain it', () => { it('returns null', () => { + // @ts-expect-error -- test is purposefully incorrect const metadata = new Metadata(connection, address, { output: { abi: [{}] } }) const name = metadata.contractName expect(name).toBeNull() @@ -251,3 +303,51 @@ describe('sourcify helpers', () => { }) }) }) + +const PROX_CONTRACT_VIA_CELLOSCAN = { + status: '1', + message: 'OK', + result: [ + { + SourceCode: + '/**\r\n *Submitted for verification at Etherscan.io on 2022-07-08\r\n*/\r\n\r\n// File: @openzeppelin/contracts/proxy/beacon/IBeacon.sol\r\n\r\n// SPDX-License-Identifier: MIT\r\n\r\npragma solidity ^0.8.0;\r\n\r\n/**\r\n * @dev This is the interface that {BeaconProxy} expects of its beacon.\r\n */\r\ninterface IBeacon {\r\n /**\r\n * @dev Must return an address that can be used as a delegate call target.\r\n *\r\n * {BeaconProxy} will check that this address is a contract.\r\n */\r\n function implementation() external view returns (address);\r\n}\r\n\r\n// File: @openzeppelin/contracts/proxy/Proxy.sol\r\n\r\n\r\npragma solidity ^0.8.0;\r\n\r\n/**\r\n * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM\r\n * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to\r\n * be specified by overriding the virtual {_implementation} function.\r\n *\r\n * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a\r\n * different contract through the {_delegate} function.\r\n *\r\n * The success and return data of the delegated call will be returned back to the caller of the proxy.\r\n */\r\nabstract contract Proxy {\r\n /**\r\n * @dev Delegates the current call to `implementation`.\r\n *\r\n * This function does not return to its internall call site, it will return directly to the external caller.\r\n */\r\n function _delegate(address implementation) internal virtual {\r\n assembly {\r\n // Copy msg.data. We take full control of memory in this inline assembly\r\n // block because it will not return to Solidity code. We overwrite the\r\n // Solidity scratch pad at memory position 0.\r\n calldatacopy(0, 0, calldatasize())\r\n\r\n // Call the implementation.\r\n // out and outsize are 0 because we don\'t know the size yet.\r\n let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)\r\n\r\n // Copy the returned data.\r\n returndatacopy(0, 0, returndatasize())\r\n\r\n switch result\r\n // delegatecall returns 0 on error.\r\n case 0 {\r\n revert(0, returndatasize())\r\n }\r\n default {\r\n return(0, returndatasize())\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function\r\n * and {_fallback} should delegate.\r\n */\r\n function _implementation() internal view virtual returns (address);\r\n\r\n /**\r\n * @dev Delegates the current call to the address returned by `_implementation()`.\r\n *\r\n * This function does not return to its internall call site, it will return directly to the external caller.\r\n */\r\n function _fallback() internal virtual {\r\n _beforeFallback();\r\n _delegate(_implementation());\r\n }\r\n\r\n /**\r\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other\r\n * function in the contract matches the call data.\r\n */\r\n fallback() external payable virtual {\r\n _fallback();\r\n }\r\n\r\n /**\r\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data\r\n * is empty.\r\n */\r\n receive() external payable virtual {\r\n _fallback();\r\n }\r\n\r\n /**\r\n * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`\r\n * call, or as part of the Solidity `fallback` or `receive` functions.\r\n *\r\n * If overriden should call `super._beforeFallback()`.\r\n */\r\n function _beforeFallback() internal virtual {}\r\n}\r\n\r\n// File: @openzeppelin/contracts/utils/Address.sol\r\n\r\n\r\npragma solidity ^0.8.0;\r\n\r\n/**\r\n * @dev Collection of functions related to the address type\r\n */\r\nlibrary Address {\r\n /**\r\n * @dev Returns true if `account` is a contract.\r\n *\r\n * [IMPORTANT]\r\n * ====\r\n * It is unsafe to assume that an address for which this function returns\r\n * false is an externally-owned account (EOA) and not a contract.\r\n *\r\n * Among others, `isContract` will return false for the following\r\n * types of addresses:\r\n *\r\n * - an externally-owned account\r\n * - a contract in construction\r\n * - an address where a contract will be created\r\n * - an address where a contract lived, but was destroyed\r\n * ====\r\n */\r\n function isContract(address account) internal view returns (bool) {\r\n // This method relies on extcodesize, which returns 0 for contracts in\r\n // construction, since the code is only stored at the end of the\r\n // constructor execution.\r\n\r\n uint256 size;\r\n assembly {\r\n size := extcodesize(account)\r\n }\r\n return size > 0;\r\n }\r\n\r\n /**\r\n * @dev Replacement for Solidity\'s `transfer`: sends `amount` wei to\r\n * `recipient`, forwarding all available gas and reverting on errors.\r\n *\r\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\r\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\r\n * imposed by `transfer`, making them unable to receive funds via\r\n * `transfer`. {sendValue} removes this limitation.\r\n *\r\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\r\n *\r\n * IMPORTANT: because control is transferred to `recipient`, care must be\r\n * taken to not create reentrancy vulnerabilities. Consider using\r\n * {ReentrancyGuard} or the\r\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\r\n */\r\n function sendValue(address payable recipient, uint256 amount) internal {\r\n require(address(this).balance >= amount, "Address: insufficient balance");\r\n\r\n (bool success, ) = recipient.call{value: amount}("");\r\n require(success, "Address: unable to send value, recipient may have reverted");\r\n }\r\n\r\n /**\r\n * @dev Performs a Solidity function call using a low level `call`. A\r\n * plain `call` is an unsafe replacement for a function call: use this\r\n * function instead.\r\n *\r\n * If `target` reverts with a revert reason, it is bubbled up by this\r\n * function (like regular Solidity function calls).\r\n *\r\n * Returns the raw returned data. To convert to the expected return value,\r\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\r\n *\r\n * Requirements:\r\n *\r\n * - `target` must be a contract.\r\n * - calling `target` with `data` must not revert.\r\n *\r\n * _Available since v3.1._\r\n */\r\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\r\n return functionCall(target, data, "Address: low-level call failed");\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\r\n * `errorMessage` as a fallback revert reason when `target` reverts.\r\n *\r\n * _Available since v3.1._\r\n */\r\n function functionCall(\r\n address target,\r\n bytes memory data,\r\n string memory errorMessage\r\n ) internal returns (bytes memory) {\r\n return functionCallWithValue(target, data, 0, errorMessage);\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\r\n * but also transferring `value` wei to `target`.\r\n *\r\n * Requirements:\r\n *\r\n * - the calling contract must have an ETH balance of at least `value`.\r\n * - the called Solidity function must be `payable`.\r\n *\r\n * _Available since v3.1._\r\n */\r\n function functionCallWithValue(\r\n address target,\r\n bytes memory data,\r\n uint256 value\r\n ) internal returns (bytes memory) {\r\n return functionCallWithValue(target, data, value, "Address: low-level call with value failed");\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\r\n * with `errorMessage` as a fallback revert reason when `target` reverts.\r\n *\r\n * _Available since v3.1._\r\n */\r\n function functionCallWithValue(\r\n address target,\r\n bytes memory data,\r\n uint256 value,\r\n string memory errorMessage\r\n ) internal returns (bytes memory) {\r\n require(address(this).balance >= value, "Address: insufficient balance for call");\r\n require(isContract(target), "Address: call to non-contract");\r\n\r\n (bool success, bytes memory returndata) = target.call{value: value}(data);\r\n return verifyCallResult(success, returndata, errorMessage);\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\r\n * but performing a static call.\r\n *\r\n * _Available since v3.3._\r\n */\r\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\r\n return functionStaticCall(target, data, "Address: low-level static call failed");\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\r\n * but performing a static call.\r\n *\r\n * _Available since v3.3._\r\n */\r\n function functionStaticCall(\r\n address target,\r\n bytes memory data,\r\n string memory errorMessage\r\n ) internal view returns (bytes memory) {\r\n require(isContract(target), "Address: static call to non-contract");\r\n\r\n (bool success, bytes memory returndata) = target.staticcall(data);\r\n return verifyCallResult(success, returndata, errorMessage);\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\r\n * but performing a delegate call.\r\n *\r\n * _Available since v3.4._\r\n */\r\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\r\n return functionDelegateCall(target, data, "Address: low-level delegate call failed");\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\r\n * but performing a delegate call.\r\n *\r\n * _Available since v3.4._\r\n */\r\n function functionDelegateCall(\r\n address target,\r\n bytes memory data,\r\n string memory errorMessage\r\n ) internal returns (bytes memory) {\r\n require(isContract(target), "Address: delegate call to non-contract");\r\n\r\n (bool success, bytes memory returndata) = target.delegatecall(data);\r\n return verifyCallResult(success, returndata, errorMessage);\r\n }\r\n\r\n /**\r\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn\'t, either by bubbling the\r\n * revert reason using the provided one.\r\n *\r\n * _Available since v4.3._\r\n */\r\n function verifyCallResult(\r\n bool success,\r\n bytes memory returndata,\r\n string memory errorMessage\r\n ) internal pure returns (bytes memory) {\r\n if (success) {\r\n return returndata;\r\n } else {\r\n // Look for revert reason and bubble it up if present\r\n if (returndata.length > 0) {\r\n // The easiest way to bubble the revert reason is using memory via assembly\r\n\r\n assembly {\r\n let returndata_size := mload(returndata)\r\n revert(add(32, returndata), returndata_size)\r\n }\r\n } else {\r\n revert(errorMessage);\r\n }\r\n }\r\n }\r\n}\r\n\r\n// File: @openzeppelin/contracts/utils/StorageSlot.sol\r\n\r\n\r\npragma solidity ^0.8.0;\r\n\r\n/**\r\n * @dev Library for reading and writing primitive types to specific storage slots.\r\n *\r\n * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.\r\n * This library helps with reading and writing to such slots without the need for inline assembly.\r\n *\r\n * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.\r\n *\r\n * Example usage to set ERC1967 implementation slot:\r\n * ```\r\n * contract ERC1967 {\r\n * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\r\n *\r\n * function _getImplementation() internal view returns (address) {\r\n * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\r\n * }\r\n *\r\n * function _setImplementation(address newImplementation) internal {\r\n * require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");\r\n * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\r\n * }\r\n * }\r\n * ```\r\n *\r\n * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._\r\n */\r\nlibrary StorageSlot {\r\n struct AddressSlot {\r\n address value;\r\n }\r\n\r\n struct BooleanSlot {\r\n bool value;\r\n }\r\n\r\n struct Bytes32Slot {\r\n bytes32 value;\r\n }\r\n\r\n struct Uint256Slot {\r\n uint256 value;\r\n }\r\n\r\n /**\r\n * @dev Returns an `AddressSlot` with member `value` located at `slot`.\r\n */\r\n function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {\r\n assembly {\r\n r.slot := slot\r\n }\r\n }\r\n\r\n /**\r\n * @dev Returns an `BooleanSlot` with member `value` located at `slot`.\r\n */\r\n function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {\r\n assembly {\r\n r.slot := slot\r\n }\r\n }\r\n\r\n /**\r\n * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.\r\n */\r\n function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {\r\n assembly {\r\n r.slot := slot\r\n }\r\n }\r\n\r\n /**\r\n * @dev Returns an `Uint256Slot` with member `value` located at `slot`.\r\n */\r\n function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {\r\n assembly {\r\n r.slot := slot\r\n }\r\n }\r\n}\r\n\r\n// File: @openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol\r\n\r\n\r\npragma solidity ^0.8.2;\r\n\r\n\r\n\r\n/**\r\n * @dev This abstract contract provides getters and event emitting update functions for\r\n * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.\r\n *\r\n * _Available since v4.1._\r\n *\r\n * @custom:oz-upgrades-unsafe-allow delegatecall\r\n */\r\nabstract contract ERC1967Upgrade {\r\n // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1\r\n bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;\r\n\r\n /**\r\n * @dev Storage slot with the address of the current implementation.\r\n * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is\r\n * validated in the constructor.\r\n */\r\n bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\r\n\r\n /**\r\n * @dev Emitted when the implementation is upgraded.\r\n */\r\n event Upgraded(address indexed implementation);\r\n\r\n /**\r\n * @dev Returns the current implementation address.\r\n */\r\n function _getImplementation() internal view returns (address) {\r\n return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\r\n }\r\n\r\n /**\r\n * @dev Stores a new address in the EIP1967 implementation slot.\r\n */\r\n function _setImplementation(address newImplementation) private {\r\n require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");\r\n StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\r\n }\r\n\r\n /**\r\n * @dev Perform implementation upgrade\r\n *\r\n * Emits an {Upgraded} event.\r\n */\r\n function _upgradeTo(address newImplementation) internal {\r\n _setImplementation(newImplementation);\r\n emit Upgraded(newImplementation);\r\n }\r\n\r\n /**\r\n * @dev Perform implementation upgrade with additional setup call.\r\n *\r\n * Emits an {Upgraded} event.\r\n */\r\n function _upgradeToAndCall(\r\n address newImplementation,\r\n bytes memory data,\r\n bool forceCall\r\n ) internal {\r\n _upgradeTo(newImplementation);\r\n if (data.length > 0 || forceCall) {\r\n Address.functionDelegateCall(newImplementation, data);\r\n }\r\n }\r\n\r\n /**\r\n * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.\r\n *\r\n * Emits an {Upgraded} event.\r\n */\r\n function _upgradeToAndCallSecure(\r\n address newImplementation,\r\n bytes memory data,\r\n bool forceCall\r\n ) internal {\r\n address oldImplementation = _getImplementation();\r\n\r\n // Initial upgrade and setup call\r\n _setImplementation(newImplementation);\r\n if (data.length > 0 || forceCall) {\r\n Address.functionDelegateCall(newImplementation, data);\r\n }\r\n\r\n // Perform rollback test if not already in progress\r\n StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);\r\n if (!rollbackTesting.value) {\r\n // Trigger rollback using upgradeTo from the new implementation\r\n rollbackTesting.value = true;\r\n Address.functionDelegateCall(\r\n newImplementation,\r\n abi.encodeWithSignature("upgradeTo(address)", oldImplementation)\r\n );\r\n rollbackTesting.value = false;\r\n // Check rollback was effective\r\n require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");\r\n // Finally reset to the new implementation and log the upgrade\r\n _upgradeTo(newImplementation);\r\n }\r\n }\r\n\r\n /**\r\n * @dev Storage slot with the admin of the contract.\r\n * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is\r\n * validated in the constructor.\r\n */\r\n bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\r\n\r\n /**\r\n * @dev Emitted when the admin account has changed.\r\n */\r\n event AdminChanged(address previousAdmin, address newAdmin);\r\n\r\n /**\r\n * @dev Returns the current admin.\r\n */\r\n function _getAdmin() internal view returns (address) {\r\n return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;\r\n }\r\n\r\n /**\r\n * @dev Stores a new address in the EIP1967 admin slot.\r\n */\r\n function _setAdmin(address newAdmin) private {\r\n require(newAdmin != address(0), "ERC1967: new admin is the zero address");\r\n StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;\r\n }\r\n\r\n /**\r\n * @dev Changes the admin of the proxy.\r\n *\r\n * Emits an {AdminChanged} event.\r\n */\r\n function _changeAdmin(address newAdmin) internal {\r\n emit AdminChanged(_getAdmin(), newAdmin);\r\n _setAdmin(newAdmin);\r\n }\r\n\r\n /**\r\n * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.\r\n * This is bytes32(uint256(keccak256(\'eip1967.proxy.beacon\')) - 1)) and is validated in the constructor.\r\n */\r\n bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;\r\n\r\n /**\r\n * @dev Emitted when the beacon is upgraded.\r\n */\r\n event BeaconUpgraded(address indexed beacon);\r\n\r\n /**\r\n * @dev Returns the current beacon.\r\n */\r\n function _getBeacon() internal view returns (address) {\r\n return StorageSlot.getAddressSlot(_BEACON_SLOT).value;\r\n }\r\n\r\n /**\r\n * @dev Stores a new beacon in the EIP1967 beacon slot.\r\n */\r\n function _setBeacon(address newBeacon) private {\r\n require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");\r\n require(\r\n Address.isContract(IBeacon(newBeacon).implementation()),\r\n "ERC1967: beacon implementation is not a contract"\r\n );\r\n StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;\r\n }\r\n\r\n /**\r\n * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does\r\n * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).\r\n *\r\n * Emits a {BeaconUpgraded} event.\r\n */\r\n function _upgradeBeaconToAndCall(\r\n address newBeacon,\r\n bytes memory data,\r\n bool forceCall\r\n ) internal {\r\n _setBeacon(newBeacon);\r\n emit BeaconUpgraded(newBeacon);\r\n if (data.length > 0 || forceCall) {\r\n Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);\r\n }\r\n }\r\n}\r\n\r\n// File: @openzeppelin/contracts/proxy/beacon/BeaconProxy.sol\r\n\r\n\r\npragma solidity ^0.8.0;\r\n\r\n\r\n\r\n/**\r\n * @dev This contract implements a proxy that gets the implementation address for each call from a {UpgradeableBeacon}.\r\n *\r\n * The beacon address is stored in storage slot `uint256(keccak256(\'eip1967.proxy.beacon\')) - 1`, so that it doesn\'t\r\n * conflict with the storage layout of the implementation behind the proxy.\r\n *\r\n * _Available since v3.4._\r\n */\r\ncontract BeaconProxy is Proxy, ERC1967Upgrade {\r\n /**\r\n * @dev Initializes the proxy with `beacon`.\r\n *\r\n * If `data` is nonempty, it\'s used as data in a delegate call to the implementation returned by the beacon. This\r\n * will typically be an encoded function call, and allows initializating the storage of the proxy like a Solidity\r\n * constructor.\r\n *\r\n * Requirements:\r\n *\r\n * - `beacon` must be a contract with the interface {IBeacon}.\r\n */\r\n constructor(address beacon, bytes memory data) payable {\r\n assert(_BEACON_SLOT == bytes32(uint256(keccak256("eip1967.proxy.beacon")) - 1));\r\n _upgradeBeaconToAndCall(beacon, data, false);\r\n }\r\n\r\n /**\r\n * @dev Returns the current beacon address.\r\n */\r\n function _beacon() internal view virtual returns (address) {\r\n return _getBeacon();\r\n }\r\n\r\n /**\r\n * @dev Returns the current implementation address of the associated beacon.\r\n */\r\n function _implementation() internal view virtual override returns (address) {\r\n return IBeacon(_getBeacon()).implementation();\r\n }\r\n\r\n /**\r\n * @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}.\r\n *\r\n * If `data` is nonempty, it\'s used as data in a delegate call to the implementation returned by the beacon.\r\n *\r\n * Requirements:\r\n *\r\n * - `beacon` must be a contract.\r\n * - The implementation returned by `beacon` must be a contract.\r\n */\r\n function _setBeacon(address beacon, bytes memory data) internal virtual {\r\n _upgradeBeaconToAndCall(beacon, data, false);\r\n }\r\n}\r\n\r\n// File: contracts/bridge/token/Token.sol\r\n\r\n// contracts/Structs.sol\r\n\r\npragma solidity ^0.8.0;\r\n\r\ncontract BridgeToken is BeaconProxy {\r\n constructor(address beacon, bytes memory data) BeaconProxy(beacon, data) {\r\n\r\n }\r\n}', + ABI: '[{"inputs":[{"internalType":"address","name":"beacon","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beacon","type":"address"}],"name":"BeaconUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"stateMutability":"payable","type":"fallback"},{"stateMutability":"payable","type":"receive"}]', + ContractName: 'BridgeToken', + CompilerVersion: 'v0.8.4+commit.c7e474f2', + OptimizationUsed: '1', + Runs: '200', + ConstructorArguments: + '000000000000000000000000796dff6d74f3e27060b71255fe517bfb23c93eed00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000164c71f461500000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000010e51000000000000000000000000796dff6d74f3e27060b71255fe517bfb23c93eed0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000d57726170706564204574686572000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004574554480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + EVMVersion: 'Default', + Library: '', + LicenseType: 'Apache-2.0', + Proxy: '1', + Implementation: '0x796dff6d74f3e27060b71255fe517bfb23c93eed', + SwarmSource: 'ipfs://3e23ac4f79a95247999f8e0df94ea5bd5e38561ccfd051b0ea50ba9aea59b8f1', + }, + ], +} + +const IMPLEMENATATION_CONTRACT = { + status: '1', + message: 'OK', + result: [ + { + SourceCode: + '// File: @openzeppelin/contracts/proxy/Proxy.sol\r\n\r\n// SPDX-License-Identifier: MIT\r\n\r\npragma solidity ^0.8.0;\r\n\r\n/**\r\n * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM\r\n * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to\r\n * be specified by overriding the virtual {_implementation} function.\r\n *\r\n * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a\r\n * different contract through the {_delegate} function.\r\n *\r\n * The success and return data of the delegated call will be returned back to the caller of the proxy.\r\n */\r\nabstract contract Proxy {\r\n /**\r\n * @dev Delegates the current call to `implementation`.\r\n *\r\n * This function does not return to its internall call site, it will return directly to the external caller.\r\n */\r\n function _delegate(address implementation) internal virtual {\r\n assembly {\r\n // Copy msg.data. We take full control of memory in this inline assembly\r\n // block because it will not return to Solidity code. We overwrite the\r\n // Solidity scratch pad at memory position 0.\r\n calldatacopy(0, 0, calldatasize())\r\n\r\n // Call the implementation.\r\n // out and outsize are 0 because we don\'t know the size yet.\r\n let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)\r\n\r\n // Copy the returned data.\r\n returndatacopy(0, 0, returndatasize())\r\n\r\n switch result\r\n // delegatecall returns 0 on error.\r\n case 0 {\r\n revert(0, returndatasize())\r\n }\r\n default {\r\n return(0, returndatasize())\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function\r\n * and {_fallback} should delegate.\r\n */\r\n function _implementation() internal view virtual returns (address);\r\n\r\n /**\r\n * @dev Delegates the current call to the address returned by `_implementation()`.\r\n *\r\n * This function does not return to its internall call site, it will return directly to the external caller.\r\n */\r\n function _fallback() internal virtual {\r\n _beforeFallback();\r\n _delegate(_implementation());\r\n }\r\n\r\n /**\r\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other\r\n * function in the contract matches the call data.\r\n */\r\n fallback() external payable virtual {\r\n _fallback();\r\n }\r\n\r\n /**\r\n * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data\r\n * is empty.\r\n */\r\n receive() external payable virtual {\r\n _fallback();\r\n }\r\n\r\n /**\r\n * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`\r\n * call, or as part of the Solidity `fallback` or `receive` functions.\r\n *\r\n * If overriden should call `super._beforeFallback()`.\r\n */\r\n function _beforeFallback() internal virtual {}\r\n}\r\n\r\n// File: @openzeppelin/contracts/proxy/beacon/IBeacon.sol\r\n\r\npragma solidity ^0.8.0;\r\n\r\n/**\r\n * @dev This is the interface that {BeaconProxy} expects of its beacon.\r\n */\r\ninterface IBeacon {\r\n /**\r\n * @dev Must return an address that can be used as a delegate call target.\r\n *\r\n * {BeaconProxy} will check that this address is a contract.\r\n */\r\n function implementation() external view returns (address);\r\n}\r\n\r\n// File: @openzeppelin/contracts/utils/Address.sol\r\n\r\npragma solidity ^0.8.0;\r\n\r\n/**\r\n * @dev Collection of functions related to the address type\r\n */\r\nlibrary Address {\r\n /**\r\n * @dev Returns true if `account` is a contract.\r\n *\r\n * [IMPORTANT]\r\n * ====\r\n * It is unsafe to assume that an address for which this function returns\r\n * false is an externally-owned account (EOA) and not a contract.\r\n *\r\n * Among others, `isContract` will return false for the following\r\n * types of addresses:\r\n *\r\n * - an externally-owned account\r\n * - a contract in construction\r\n * - an address where a contract will be created\r\n * - an address where a contract lived, but was destroyed\r\n * ====\r\n */\r\n function isContract(address account) internal view returns (bool) {\r\n // This method relies on extcodesize, which returns 0 for contracts in\r\n // construction, since the code is only stored at the end of the\r\n // constructor execution.\r\n\r\n uint256 size;\r\n assembly {\r\n size := extcodesize(account)\r\n }\r\n return size > 0;\r\n }\r\n\r\n /**\r\n * @dev Replacement for Solidity\'s `transfer`: sends `amount` wei to\r\n * `recipient`, forwarding all available gas and reverting on errors.\r\n *\r\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\r\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\r\n * imposed by `transfer`, making them unable to receive funds via\r\n * `transfer`. {sendValue} removes this limitation.\r\n *\r\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\r\n *\r\n * IMPORTANT: because control is transferred to `recipient`, care must be\r\n * taken to not create reentrancy vulnerabilities. Consider using\r\n * {ReentrancyGuard} or the\r\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\r\n */\r\n function sendValue(address payable recipient, uint256 amount) internal {\r\n require(address(this).balance >= amount, "Address: insufficient balance");\r\n\r\n (bool success, ) = recipient.call{value: amount}("");\r\n require(success, "Address: unable to send value, recipient may have reverted");\r\n }\r\n\r\n /**\r\n * @dev Performs a Solidity function call using a low level `call`. A\r\n * plain `call` is an unsafe replacement for a function call: use this\r\n * function instead.\r\n *\r\n * If `target` reverts with a revert reason, it is bubbled up by this\r\n * function (like regular Solidity function calls).\r\n *\r\n * Returns the raw returned data. To convert to the expected return value,\r\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\r\n *\r\n * Requirements:\r\n *\r\n * - `target` must be a contract.\r\n * - calling `target` with `data` must not revert.\r\n *\r\n * _Available since v3.1._\r\n */\r\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\r\n return functionCall(target, data, "Address: low-level call failed");\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\r\n * `errorMessage` as a fallback revert reason when `target` reverts.\r\n *\r\n * _Available since v3.1._\r\n */\r\n function functionCall(\r\n address target,\r\n bytes memory data,\r\n string memory errorMessage\r\n ) internal returns (bytes memory) {\r\n return functionCallWithValue(target, data, 0, errorMessage);\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\r\n * but also transferring `value` wei to `target`.\r\n *\r\n * Requirements:\r\n *\r\n * - the calling contract must have an ETH balance of at least `value`.\r\n * - the called Solidity function must be `payable`.\r\n *\r\n * _Available since v3.1._\r\n */\r\n function functionCallWithValue(\r\n address target,\r\n bytes memory data,\r\n uint256 value\r\n ) internal returns (bytes memory) {\r\n return functionCallWithValue(target, data, value, "Address: low-level call with value failed");\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\r\n * with `errorMessage` as a fallback revert reason when `target` reverts.\r\n *\r\n * _Available since v3.1._\r\n */\r\n function functionCallWithValue(\r\n address target,\r\n bytes memory data,\r\n uint256 value,\r\n string memory errorMessage\r\n ) internal returns (bytes memory) {\r\n require(address(this).balance >= value, "Address: insufficient balance for call");\r\n require(isContract(target), "Address: call to non-contract");\r\n\r\n (bool success, bytes memory returndata) = target.call{value: value}(data);\r\n return verifyCallResult(success, returndata, errorMessage);\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\r\n * but performing a static call.\r\n *\r\n * _Available since v3.3._\r\n */\r\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\r\n return functionStaticCall(target, data, "Address: low-level static call failed");\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\r\n * but performing a static call.\r\n *\r\n * _Available since v3.3._\r\n */\r\n function functionStaticCall(\r\n address target,\r\n bytes memory data,\r\n string memory errorMessage\r\n ) internal view returns (bytes memory) {\r\n require(isContract(target), "Address: static call to non-contract");\r\n\r\n (bool success, bytes memory returndata) = target.staticcall(data);\r\n return verifyCallResult(success, returndata, errorMessage);\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\r\n * but performing a delegate call.\r\n *\r\n * _Available since v3.4._\r\n */\r\n function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\r\n return functionDelegateCall(target, data, "Address: low-level delegate call failed");\r\n }\r\n\r\n /**\r\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\r\n * but performing a delegate call.\r\n *\r\n * _Available since v3.4._\r\n */\r\n function functionDelegateCall(\r\n address target,\r\n bytes memory data,\r\n string memory errorMessage\r\n ) internal returns (bytes memory) {\r\n require(isContract(target), "Address: delegate call to non-contract");\r\n\r\n (bool success, bytes memory returndata) = target.delegatecall(data);\r\n return verifyCallResult(success, returndata, errorMessage);\r\n }\r\n\r\n /**\r\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn\'t, either by bubbling the\r\n * revert reason using the provided one.\r\n *\r\n * _Available since v4.3._\r\n */\r\n function verifyCallResult(\r\n bool success,\r\n bytes memory returndata,\r\n string memory errorMessage\r\n ) internal pure returns (bytes memory) {\r\n if (success) {\r\n return returndata;\r\n } else {\r\n // Look for revert reason and bubble it up if present\r\n if (returndata.length > 0) {\r\n // The easiest way to bubble the revert reason is using memory via assembly\r\n\r\n assembly {\r\n let returndata_size := mload(returndata)\r\n revert(add(32, returndata), returndata_size)\r\n }\r\n } else {\r\n revert(errorMessage);\r\n }\r\n }\r\n }\r\n}\r\n\r\n// File: @openzeppelin/contracts/utils/StorageSlot.sol\r\n\r\npragma solidity ^0.8.0;\r\n\r\n/**\r\n * @dev Library for reading and writing primitive types to specific storage slots.\r\n *\r\n * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.\r\n * This library helps with reading and writing to such slots without the need for inline assembly.\r\n *\r\n * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.\r\n *\r\n * Example usage to set ERC1967 implementation slot:\r\n * ```\r\n * contract ERC1967 {\r\n * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\r\n *\r\n * function _getImplementation() internal view returns (address) {\r\n * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\r\n * }\r\n *\r\n * function _setImplementation(address newImplementation) internal {\r\n * require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");\r\n * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\r\n * }\r\n * }\r\n * ```\r\n *\r\n * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._\r\n */\r\nlibrary StorageSlot {\r\n struct AddressSlot {\r\n address value;\r\n }\r\n\r\n struct BooleanSlot {\r\n bool value;\r\n }\r\n\r\n struct Bytes32Slot {\r\n bytes32 value;\r\n }\r\n\r\n struct Uint256Slot {\r\n uint256 value;\r\n }\r\n\r\n /**\r\n * @dev Returns an `AddressSlot` with member `value` located at `slot`.\r\n */\r\n function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {\r\n assembly {\r\n r.slot := slot\r\n }\r\n }\r\n\r\n /**\r\n * @dev Returns an `BooleanSlot` with member `value` located at `slot`.\r\n */\r\n function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {\r\n assembly {\r\n r.slot := slot\r\n }\r\n }\r\n\r\n /**\r\n * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.\r\n */\r\n function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {\r\n assembly {\r\n r.slot := slot\r\n }\r\n }\r\n\r\n /**\r\n * @dev Returns an `Uint256Slot` with member `value` located at `slot`.\r\n */\r\n function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {\r\n assembly {\r\n r.slot := slot\r\n }\r\n }\r\n}\r\n\r\n// File: @openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol\r\n\r\npragma solidity ^0.8.2;\r\n\r\n\r\n\r\n/**\r\n * @dev This abstract contract provides getters and event emitting update functions for\r\n * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.\r\n *\r\n * _Available since v4.1._\r\n *\r\n * @custom:oz-upgrades-unsafe-allow delegatecall\r\n */\r\nabstract contract ERC1967Upgrade {\r\n // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1\r\n bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;\r\n\r\n /**\r\n * @dev Storage slot with the address of the current implementation.\r\n * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is\r\n * validated in the constructor.\r\n */\r\n bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\r\n\r\n /**\r\n * @dev Emitted when the implementation is upgraded.\r\n */\r\n event Upgraded(address indexed implementation);\r\n\r\n /**\r\n * @dev Returns the current implementation address.\r\n */\r\n function _getImplementation() internal view returns (address) {\r\n return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;\r\n }\r\n\r\n /**\r\n * @dev Stores a new address in the EIP1967 implementation slot.\r\n */\r\n function _setImplementation(address newImplementation) private {\r\n require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");\r\n StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;\r\n }\r\n\r\n /**\r\n * @dev Perform implementation upgrade\r\n *\r\n * Emits an {Upgraded} event.\r\n */\r\n function _upgradeTo(address newImplementation) internal {\r\n _setImplementation(newImplementation);\r\n emit Upgraded(newImplementation);\r\n }\r\n\r\n /**\r\n * @dev Perform implementation upgrade with additional setup call.\r\n *\r\n * Emits an {Upgraded} event.\r\n */\r\n function _upgradeToAndCall(\r\n address newImplementation,\r\n bytes memory data,\r\n bool forceCall\r\n ) internal {\r\n _upgradeTo(newImplementation);\r\n if (data.length > 0 || forceCall) {\r\n Address.functionDelegateCall(newImplementation, data);\r\n }\r\n }\r\n\r\n /**\r\n * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.\r\n *\r\n * Emits an {Upgraded} event.\r\n */\r\n function _upgradeToAndCallSecure(\r\n address newImplementation,\r\n bytes memory data,\r\n bool forceCall\r\n ) internal {\r\n address oldImplementation = _getImplementation();\r\n\r\n // Initial upgrade and setup call\r\n _setImplementation(newImplementation);\r\n if (data.length > 0 || forceCall) {\r\n Address.functionDelegateCall(newImplementation, data);\r\n }\r\n\r\n // Perform rollback test if not already in progress\r\n StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);\r\n if (!rollbackTesting.value) {\r\n // Trigger rollback using upgradeTo from the new implementation\r\n rollbackTesting.value = true;\r\n Address.functionDelegateCall(\r\n newImplementation,\r\n abi.encodeWithSignature("upgradeTo(address)", oldImplementation)\r\n );\r\n rollbackTesting.value = false;\r\n // Check rollback was effective\r\n require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");\r\n // Finally reset to the new implementation and log the upgrade\r\n _upgradeTo(newImplementation);\r\n }\r\n }\r\n\r\n /**\r\n * @dev Storage slot with the admin of the contract.\r\n * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is\r\n * validated in the constructor.\r\n */\r\n bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\r\n\r\n /**\r\n * @dev Emitted when the admin account has changed.\r\n */\r\n event AdminChanged(address previousAdmin, address newAdmin);\r\n\r\n /**\r\n * @dev Returns the current admin.\r\n */\r\n function _getAdmin() internal view returns (address) {\r\n return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;\r\n }\r\n\r\n /**\r\n * @dev Stores a new address in the EIP1967 admin slot.\r\n */\r\n function _setAdmin(address newAdmin) private {\r\n require(newAdmin != address(0), "ERC1967: new admin is the zero address");\r\n StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;\r\n }\r\n\r\n /**\r\n * @dev Changes the admin of the proxy.\r\n *\r\n * Emits an {AdminChanged} event.\r\n */\r\n function _changeAdmin(address newAdmin) internal {\r\n emit AdminChanged(_getAdmin(), newAdmin);\r\n _setAdmin(newAdmin);\r\n }\r\n\r\n /**\r\n * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.\r\n * This is bytes32(uint256(keccak256(\'eip1967.proxy.beacon\')) - 1)) and is validated in the constructor.\r\n */\r\n bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;\r\n\r\n /**\r\n * @dev Emitted when the beacon is upgraded.\r\n */\r\n event BeaconUpgraded(address indexed beacon);\r\n\r\n /**\r\n * @dev Returns the current beacon.\r\n */\r\n function _getBeacon() internal view returns (address) {\r\n return StorageSlot.getAddressSlot(_BEACON_SLOT).value;\r\n }\r\n\r\n /**\r\n * @dev Stores a new beacon in the EIP1967 beacon slot.\r\n */\r\n function _setBeacon(address newBeacon) private {\r\n require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");\r\n require(\r\n Address.isContract(IBeacon(newBeacon).implementation()),\r\n "ERC1967: beacon implementation is not a contract"\r\n );\r\n StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;\r\n }\r\n\r\n /**\r\n * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does\r\n * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).\r\n *\r\n * Emits a {BeaconUpgraded} event.\r\n */\r\n function _upgradeBeaconToAndCall(\r\n address newBeacon,\r\n bytes memory data,\r\n bool forceCall\r\n ) internal {\r\n _setBeacon(newBeacon);\r\n emit BeaconUpgraded(newBeacon);\r\n if (data.length > 0 || forceCall) {\r\n Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);\r\n }\r\n }\r\n}\r\n\r\n// File: @openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol\r\n\r\npragma solidity ^0.8.0;\r\n\r\n\r\n/**\r\n * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an\r\n * implementation address that can be changed. This address is stored in storage in the location specified by\r\n * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn\'t conflict with the storage layout of the\r\n * implementation behind the proxy.\r\n */\r\ncontract ERC1967Proxy is Proxy, ERC1967Upgrade {\r\n /**\r\n * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.\r\n *\r\n * If `_data` is nonempty, it\'s used as data in a delegate call to `_logic`. This will typically be an encoded\r\n * function call, and allows initializating the storage of the proxy like a Solidity constructor.\r\n */\r\n constructor(address _logic, bytes memory _data) payable {\r\n assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));\r\n _upgradeToAndCall(_logic, _data, false);\r\n }\r\n\r\n /**\r\n * @dev Returns the current implementation address.\r\n */\r\n function _implementation() internal view virtual override returns (address impl) {\r\n return ERC1967Upgrade._getImplementation();\r\n }\r\n}\r\n\r\n// File: contracts/bridge/TokenBridge.sol\r\n\r\n// contracts/Wormhole.sol\r\n\r\npragma solidity ^0.8.0;\r\n\r\ncontract TokenBridge is ERC1967Proxy {\r\n constructor (address implementation, bytes memory initData)\r\n ERC1967Proxy(\r\n implementation,\r\n initData\r\n )\r\n {}\r\n}', + ABI: '[{"inputs":[{"internalType":"address","name":"implementation","type":"address"},{"internalType":"bytes","name":"initData","type":"bytes"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beacon","type":"address"}],"name":"BeaconUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"stateMutability":"payable","type":"fallback"},{"stateMutability":"payable","type":"receive"}]', + ContractName: 'TokenBridge', + CompilerVersion: 'v0.8.4+commit.c7e474f2', + OptimizationUsed: '1', + Runs: '200', + ConstructorArguments: + '00000000000000000000000051b5123a7b0f9b2ba265f9c4c8de7d78d52f510f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e43bfa0638000000000000000000000000b91e3638f82a1facb28690b37e3aae45d2c33808000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000a321448d90d4e5b0a732867c18ea198e75cac48e00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000004000000000000000000000000a5f208e072434bc67592e4c49c1b991ba79bca46000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + EVMVersion: 'Default', + Library: '', + LicenseType: 'Apache-2.0', + Proxy: '1', + Implementation: '0x99dd86b81080e1cc794695b9e740b979d0286649', + SwarmSource: 'ipfs://c651652cc0041da7a6ccfbb62ab4d69a33d006e787e6a41c09cd2b8fb857905d', + }, + ], +} diff --git a/packages/sdk/explorer/src/sourcify.ts b/packages/sdk/explorer/src/sourcify.ts index ad572934f..58843ce37 100644 --- a/packages/sdk/explorer/src/sourcify.ts +++ b/packages/sdk/explorer/src/sourcify.ts @@ -53,6 +53,8 @@ export interface MetadataResponse { } settings?: { compilationTarget?: Record + implementation?: string + name?: string } } @@ -64,16 +66,19 @@ export interface MetadataResponse { export class Metadata { public abi: AbiItem[] | null = null public contractName: string | null = null + public implementationAddress: string | null = null public fnMapping: Map = new Map() private abiCoder: AbiCoder private jsonInterfaceMethodToString: (item: AbiItem) => string private address: Address - constructor(connection: Connection, address: Address, response: any) { + constructor(connection: Connection, address: Address, response: MetadataResponse) { this.abiCoder = connection.getAbiCoder() - + this.contractName = response.settings?.name || null + // note setting this.response has a side affect of setting this.contractName if it is empty this.response = response as MetadataResponse + this.implementationAddress = response.settings?.implementation || null // XXX: For some reason this isn't exported as it should be // @ts-ignore this.jsonInterfaceMethodToString = connection.web3.utils._jsonInterfaceMethodToString @@ -109,7 +114,8 @@ export class Metadata { // XXX: Not sure when there are multiple compilationTargets and what should // happen then but defaulting to this for now. const contracts = Object.values(value.settings.compilationTarget) - this.contractName = contracts[0] + + this.contractName = this.contractName ?? contracts[0] } } @@ -169,8 +175,8 @@ export class Metadata { } /** - * Fetch the sourcify response and instantiate a Metadata wrapper class around it. - * Try a full_match but fallback to partial_match when not strict. + * Fetch the sourcify or celoscan response and instantiate a Metadata wrapper class around it. + * Try a full_match but fallback to partial_match when not strict. (only valid for sourcify) * @param connection @celo/connect instance * @param contract the address of the contract to query * @param strict only allow full matches https://docs.sourcify.dev/docs/full-vs-partial-match/ @@ -184,7 +190,18 @@ export async function fetchMetadata( const fullMatchMetadata = await querySourcify(connection, 'full_match', contract) if (fullMatchMetadata !== null) { return fullMatchMetadata - } else if (strict) { + } + console.debug('None found on full match on celo explorer, trying celoScan') + const fullMatchFromCeloScan = await queryCeloScan(connection, contract) + if (fullMatchFromCeloScan !== null) { + if (fullMatchFromCeloScan.implementationAddress) { + console.info('Implementation found', fullMatchFromCeloScan.implementationAddress) + return queryCeloScan(connection, fullMatchFromCeloScan.implementationAddress) + } + return fullMatchFromCeloScan + } + console.debug('No full match found, trying partial match') + if (strict) { return null } else { return querySourcify(connection, 'partial_match', contract) @@ -208,7 +225,64 @@ async function querySourcify( `https://repo.sourcify.dev/contracts/${matchType}/${chainID}/${contract}/metadata.json` ) if (resp.ok) { - return new Metadata(connection, contract, await resp.json()) + return new Metadata(connection, contract, (await resp.json()) as MetadataResponse) + } + return null +} + +type CeloScanResponse = + | { + status: '1' + message: 'OK' + result: + | [ + { + SourceCode: string + ABI: string + ContractName: string + Implementation: `0x${string}` + // More + } + ] + | [ + { + SourceCode: '' + ABI: 'Contract source code not verified' + ContractName: '' + EVMVersion: 'Default' + Proxy: '0' + Implementation: '' + } + ] + } + | { + status: '0' + message: 'NOTOK' + result: string + } + +/** + * Fetch the celoScan response and instantiate a Metadata wrapper class around it. + * @param connection @celo/connect instance + * @param contract the address of the contract to query + * @returns Metadata + */ +async function queryCeloScan(connection: Connection, contract: Address): Promise { + const resp = await fetch( + `https://api.celoscan.io/api?module=contract&action=getsourcecode&address=${contract}` + ) + if (resp.ok) { + const json = (await resp.json()) as CeloScanResponse + if (json.message === 'OK' && json.result[0].ABI.length > 2) { + const info = json.result[0] + const data = JSON.parse(info.ABI) as AbiItem[] + return new Metadata(connection, contract, { + output: { abi: data }, + settings: { name: info.ContractName, implementation: info.Implementation }, + }) + } else { + console.warn(json.status, json.result) + } } return null } diff --git a/packages/sdk/governance/src/proposals.ts b/packages/sdk/governance/src/proposals.ts index 77234b5fa..ba7c293f9 100644 --- a/packages/sdk/governance/src/proposals.ts +++ b/packages/sdk/governance/src/proposals.ts @@ -443,9 +443,7 @@ export class ProposalBuilder { console.log(tx.address + ' is a proxy, repointing to ' + tx.args[0]) this.externalCallProxyRepoint.set(tx.address || tx.contract, tx.args[0] as string) } - const strategies = [this.buildCallToCoreContract, this.buildCallToExternalContract] - for (const strategy of strategies) { try { return await strategy(tx) @@ -453,7 +451,7 @@ export class ProposalBuilder { debug("Couldn't build transaction with strategy %s: %O", strategy.name, e) } } - + // this throws when every strategy fails. so you must run in debug mode or add another log to see to real error throw new Error(`Couldn't build call for transaction: ${JSON.stringify(tx)}`) } diff --git a/packages/sdk/network-utils/jestSetup.ts b/packages/sdk/network-utils/jestSetup.ts index 69170cbb3..174feeb42 100644 --- a/packages/sdk/network-utils/jestSetup.ts +++ b/packages/sdk/network-utils/jestSetup.ts @@ -2,11 +2,12 @@ import { URL } from 'node:url' // @ts-ignore global.URL = URL -// @ts-ignore -const fetchMock = require('fetch-mock') - -const fetchMockSandbox = fetchMock.sandbox() -jest.mock('cross-fetch', () => fetchMockSandbox) - +const fetchMockSandbox = require('fetch-mock').sandbox() +jest.mock('cross-fetch', () => ({ + ...jest.requireActual('cross-fetch'), + __esModule: true, + default: fetchMockSandbox, + fetch: fetchMockSandbox, +})) // @ts-ignore global.fetchMock = fetchMockSandbox diff --git a/yarn.lock b/yarn.lock index 71f64a4ee..dcb0030e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1040,6 +1040,16 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/code-frame@npm:7.24.7" + dependencies: + "@babel/highlight": "npm:^7.24.7" + picocolors: "npm:^1.0.0" + checksum: 4812e94885ba7e3213d49583a155fdffb05292330f0a9b2c41b49288da70cf3c746a3fda0bf1074041a6d741c33f8d7be24be5e96f41ef77395eeddc5c9ff624 + languageName: node + linkType: hard + "@babel/compat-data@npm:^7.21.4": version: 7.21.4 resolution: "@babel/compat-data@npm:7.21.4" @@ -1047,6 +1057,36 @@ __metadata: languageName: node linkType: hard +"@babel/compat-data@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/compat-data@npm:7.24.7" + checksum: 6edc09152ca51a22c33741c441f33f9475598fa59edc53369edb74b49f4ea4bef1281f5b0ed2b9b67fb66faef2da2069e21c4eef83405d8326e524b301f4e7e2 + languageName: node + linkType: hard + +"@babel/core@npm:^7.0.0": + version: 7.24.7 + resolution: "@babel/core@npm:7.24.7" + dependencies: + "@ampproject/remapping": "npm:^2.2.0" + "@babel/code-frame": "npm:^7.24.7" + "@babel/generator": "npm:^7.24.7" + "@babel/helper-compilation-targets": "npm:^7.24.7" + "@babel/helper-module-transforms": "npm:^7.24.7" + "@babel/helpers": "npm:^7.24.7" + "@babel/parser": "npm:^7.24.7" + "@babel/template": "npm:^7.24.7" + "@babel/traverse": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" + convert-source-map: "npm:^2.0.0" + debug: "npm:^4.1.0" + gensync: "npm:^1.0.0-beta.2" + json5: "npm:^2.2.3" + semver: "npm:^6.3.1" + checksum: ef8cc1afa3ccecee6d1f5660c487ccc2a3f25106830ea9040e80ef4b2092e053607ee4ddd03493e4f7ef2f9967a956ca53b830d54c5bee738eeb58cce679dd4a + languageName: node + linkType: hard + "@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3": version: 7.21.4 resolution: "@babel/core@npm:7.21.4" @@ -1094,6 +1134,18 @@ __metadata: languageName: node linkType: hard +"@babel/generator@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/generator@npm:7.24.7" + dependencies: + "@babel/types": "npm:^7.24.7" + "@jridgewell/gen-mapping": "npm:^0.3.5" + "@jridgewell/trace-mapping": "npm:^0.3.25" + jsesc: "npm:^2.5.1" + checksum: c71d24a4b41b19c10d2f2eb819f27d4cf94220e2322f7c8fed8bfbbb115b2bebbdd6dc1f27dac78a175e90604def58d763af87e0fa81ce4ab1582858162cf768 + languageName: node + linkType: hard + "@babel/generator@npm:^7.7.2": version: 7.23.6 resolution: "@babel/generator@npm:7.23.6" @@ -1121,6 +1173,19 @@ __metadata: languageName: node linkType: hard +"@babel/helper-compilation-targets@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-compilation-targets@npm:7.24.7" + dependencies: + "@babel/compat-data": "npm:^7.24.7" + "@babel/helper-validator-option": "npm:^7.24.7" + browserslist: "npm:^4.22.2" + lru-cache: "npm:^5.1.1" + semver: "npm:^6.3.1" + checksum: 8f8bc89af70a606ccb208513aa25d83e19b88f91b64a33174f7701a9479e67ddbb0a9c89033265070375cd24e690b93380b3a3ea11e4b3a711d742f0f4699ee7 + languageName: node + linkType: hard + "@babel/helper-environment-visitor@npm:^7.18.9": version: 7.18.9 resolution: "@babel/helper-environment-visitor@npm:7.18.9" @@ -1135,6 +1200,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-environment-visitor@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-environment-visitor@npm:7.24.7" + dependencies: + "@babel/types": "npm:^7.24.7" + checksum: 079d86e65701b29ebc10baf6ed548d17c19b808a07aa6885cc141b690a78581b180ee92b580d755361dc3b16adf975b2d2058b8ce6c86675fcaf43cf22f2f7c6 + languageName: node + linkType: hard + "@babel/helper-function-name@npm:^7.23.0": version: 7.23.0 resolution: "@babel/helper-function-name@npm:7.23.0" @@ -1145,6 +1219,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-function-name@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-function-name@npm:7.24.7" + dependencies: + "@babel/template": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" + checksum: 2ceb3d9b2b35a0fc4100fc06ed7be3bc38f03ff0bf128ff0edbc0cc7dd842967b1496fc70b5c616c747d7711c2b87e7d025c8888f48740631d6148a9d3614f85 + languageName: node + linkType: hard + "@babel/helper-hoist-variables@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-hoist-variables@npm:7.22.5" @@ -1154,6 +1238,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-hoist-variables@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-hoist-variables@npm:7.24.7" + dependencies: + "@babel/types": "npm:^7.24.7" + checksum: 6cfdcf2289cd12185dcdbdf2435fa8d3447b797ac75851166de9fc8503e2fd0021db6baf8dfbecad3753e582c08e6a3f805c8d00cbed756060a877d705bd8d8d + languageName: node + linkType: hard + "@babel/helper-module-imports@npm:^7.18.6": version: 7.21.4 resolution: "@babel/helper-module-imports@npm:7.21.4" @@ -1163,6 +1256,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-imports@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-module-imports@npm:7.24.7" + dependencies: + "@babel/traverse": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" + checksum: df8bfb2bb18413aa151ecd63b7d5deb0eec102f924f9de6bc08022ced7ed8ca7fed914562d2f6fa5b59b74a5d6e255dc35612b2bc3b8abf361e13f61b3704770 + languageName: node + linkType: hard + "@babel/helper-module-transforms@npm:^7.21.2": version: 7.21.2 resolution: "@babel/helper-module-transforms@npm:7.21.2" @@ -1179,6 +1282,21 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-transforms@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-module-transforms@npm:7.24.7" + dependencies: + "@babel/helper-environment-visitor": "npm:^7.24.7" + "@babel/helper-module-imports": "npm:^7.24.7" + "@babel/helper-simple-access": "npm:^7.24.7" + "@babel/helper-split-export-declaration": "npm:^7.24.7" + "@babel/helper-validator-identifier": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 4f2b232bf6d1be8d3a72b084a2a7ac1b0b93ea85717411a11ae1fb6375d4392019e781d8cc155789e649a2caa7eec378dd1404210603d6d4230f042c5feacffb + languageName: node + linkType: hard + "@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.8.0": version: 7.22.5 resolution: "@babel/helper-plugin-utils@npm:7.22.5" @@ -1202,6 +1320,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-simple-access@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-simple-access@npm:7.24.7" + dependencies: + "@babel/traverse": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" + checksum: 5083e190186028e48fc358a192e4b93ab320bd016103caffcfda81302a13300ccce46c9cd255ae520c25d2a6a9b47671f93e5fe5678954a2329dc0a685465c49 + languageName: node + linkType: hard + "@babel/helper-split-export-declaration@npm:^7.18.6": version: 7.18.6 resolution: "@babel/helper-split-export-declaration@npm:7.18.6" @@ -1220,6 +1348,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-split-export-declaration@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-split-export-declaration@npm:7.24.7" + dependencies: + "@babel/types": "npm:^7.24.7" + checksum: ff04a3071603c87de0d6ee2540b7291ab36305b329bd047cdbb6cbd7db335a12f9a77af1cf708779f75f13c4d9af46093c00b34432e50b2411872c658d1a2e5e + languageName: node + linkType: hard + "@babel/helper-string-parser@npm:^7.19.4": version: 7.19.4 resolution: "@babel/helper-string-parser@npm:7.19.4" @@ -1241,6 +1378,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-string-parser@npm:7.24.7" + checksum: 603d8d962bbe89907aa99a8f19a006759ab7b2464615f20a6a22e3e2e8375af37ddd0e5175c9e622e1c4b2d83607ffb41055a59d0ce34404502af30fde573a5c + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.18.6, @babel/helper-validator-identifier@npm:^7.19.1": version: 7.19.1 resolution: "@babel/helper-validator-identifier@npm:7.19.1" @@ -1255,6 +1399,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-validator-identifier@npm:7.24.7" + checksum: 86875063f57361471b531dbc2ea10bbf5406e12b06d249b03827d361db4cad2388c6f00936bcd9dc86479f7e2c69ea21412c2228d4b3672588b754b70a449d4b + languageName: node + linkType: hard + "@babel/helper-validator-option@npm:^7.21.0": version: 7.21.0 resolution: "@babel/helper-validator-option@npm:7.21.0" @@ -1262,6 +1413,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-option@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-validator-option@npm:7.24.7" + checksum: 9689166bf3f777dd424c026841c8cd651e41b21242dbfd4569a53086179a3e744c8eddd56e9d10b54142270141c91581b53af0d7c00c82d552d2540e2a919f7e + languageName: node + linkType: hard + "@babel/helpers@npm:^7.21.0": version: 7.21.0 resolution: "@babel/helpers@npm:7.21.0" @@ -1273,6 +1431,16 @@ __metadata: languageName: node linkType: hard +"@babel/helpers@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helpers@npm:7.24.7" + dependencies: + "@babel/template": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" + checksum: f7496f0d7a0b13ea86136ac2053371027125734170328215f8a90eac96fafaaae4e5398c0729bdadf23261c00582a31e14bc70113427653b718220641a917f9d + languageName: node + linkType: hard + "@babel/highlight@npm:^7.18.6": version: 7.18.6 resolution: "@babel/highlight@npm:7.18.6" @@ -1295,6 +1463,18 @@ __metadata: languageName: node linkType: hard +"@babel/highlight@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/highlight@npm:7.24.7" + dependencies: + "@babel/helper-validator-identifier": "npm:^7.24.7" + chalk: "npm:^2.4.2" + js-tokens: "npm:^4.0.0" + picocolors: "npm:^1.0.0" + checksum: 69b73f38cdd4f881b09b939a711e76646da34f4834f4ce141d7a49a6bb1926eab1c594148970a8aa9360398dff800f63aade4e81fafdd7c8d8a8489ea93bfec1 + languageName: node + linkType: hard + "@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.4": version: 7.21.4 resolution: "@babel/parser@npm:7.21.4" @@ -1313,6 +1493,15 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/parser@npm:7.24.7" + bin: + parser: ./bin/babel-parser.js + checksum: ef9ebce60e13db560ccc7af9235d460f6726bb7e23ae2d675098c1fc43d5249067be60d4118889dad33b1d4f85162cf66baf554719e1669f29bb20e71322568e + languageName: node + linkType: hard + "@babel/plugin-syntax-async-generators@npm:^7.8.4": version: 7.8.4 resolution: "@babel/plugin-syntax-async-generators@npm:7.8.4" @@ -1467,6 +1656,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.0.0": + version: 7.24.7 + resolution: "@babel/runtime@npm:7.24.7" + dependencies: + regenerator-runtime: "npm:^0.14.0" + checksum: 7b77f566165dee62db3db0296e71d08cafda3f34e1b0dcefcd68427272e17c1704f4e4369bff76651b07b6e49d3ea5a0ce344818af9116e9292e4381e0918c76 + languageName: node + linkType: hard + "@babel/runtime@npm:^7.20.1": version: 7.23.2 resolution: "@babel/runtime@npm:7.23.2" @@ -1516,6 +1714,17 @@ __metadata: languageName: node linkType: hard +"@babel/template@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/template@npm:7.24.7" + dependencies: + "@babel/code-frame": "npm:^7.24.7" + "@babel/parser": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" + checksum: 5975d404ef51cf379515eb0f80b115981d0b9dff5539e53a47516644abb8c83d7559f5b083eb1d4977b20d8359ebb2f911ccd4f729143f8958fdc465f976d843 + languageName: node + linkType: hard + "@babel/traverse@npm:^7.21.0, @babel/traverse@npm:^7.21.2, @babel/traverse@npm:^7.21.4": version: 7.23.3 resolution: "@babel/traverse@npm:7.23.3" @@ -1534,6 +1743,24 @@ __metadata: languageName: node linkType: hard +"@babel/traverse@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/traverse@npm:7.24.7" + dependencies: + "@babel/code-frame": "npm:^7.24.7" + "@babel/generator": "npm:^7.24.7" + "@babel/helper-environment-visitor": "npm:^7.24.7" + "@babel/helper-function-name": "npm:^7.24.7" + "@babel/helper-hoist-variables": "npm:^7.24.7" + "@babel/helper-split-export-declaration": "npm:^7.24.7" + "@babel/parser": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" + debug: "npm:^4.3.1" + globals: "npm:^11.1.0" + checksum: 785cf26383a992740e492efba7016de964cd06c05c9d7146fa1b5ead409e054c444f50b36dc37856884a56e32cf9d3105ddf1543486b6df68300bffb117a245a + languageName: node + linkType: hard + "@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.23.6, @babel/types@npm:^7.3.3, @babel/types@npm:^7.8.3": version: 7.23.6 resolution: "@babel/types@npm:7.23.6" @@ -1567,6 +1794,17 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/types@npm:7.24.7" + dependencies: + "@babel/helper-string-parser": "npm:^7.24.7" + "@babel/helper-validator-identifier": "npm:^7.24.7" + to-fast-properties: "npm:^2.0.0" + checksum: ad3c8c0d6fb4acb0bb74bb5b4bb849b181bf6185677ef9c59c18856c81e43628d0858253cf232f0eca806f02e08eff85a1d3e636a3e94daea737597796b0b430 + languageName: node + linkType: hard + "@bcoe/v8-coverage@npm:^0.2.3": version: 0.2.3 resolution: "@bcoe/v8-coverage@npm:0.2.3" @@ -1679,6 +1917,7 @@ __metadata: cross-fetch: "npm:3.1.5" debug: "npm:^4.1.1" ethers: "npm:5" + fetch-mock: "npm:^9.11.0" fs-extra: "npm:^8.1.0" humanize-duration: "npm:^3.29.0" jest: "npm:^29.7.0" @@ -3542,6 +3781,17 @@ __metadata: languageName: node linkType: hard +"@jridgewell/gen-mapping@npm:^0.3.5": + version: 0.3.5 + resolution: "@jridgewell/gen-mapping@npm:0.3.5" + dependencies: + "@jridgewell/set-array": "npm:^1.2.1" + "@jridgewell/sourcemap-codec": "npm:^1.4.10" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 81587b3c4dd8e6c60252122937cea0c637486311f4ed208b52b62aae2e7a87598f63ec330e6cd0984af494bfb16d3f0d60d3b21d7e5b4aedd2602ff3fe9d32e2 + languageName: node + linkType: hard + "@jridgewell/resolve-uri@npm:3.1.0": version: 3.1.0 resolution: "@jridgewell/resolve-uri@npm:3.1.0" @@ -3563,6 +3813,13 @@ __metadata: languageName: node linkType: hard +"@jridgewell/set-array@npm:^1.2.1": + version: 1.2.1 + resolution: "@jridgewell/set-array@npm:1.2.1" + checksum: 832e513a85a588f8ed4f27d1279420d8547743cc37fcad5a5a76fc74bb895b013dfe614d0eed9cb860048e6546b798f8f2652020b4b2ba0561b05caa8c654b10 + languageName: node + linkType: hard + "@jridgewell/sourcemap-codec@npm:1.4.14": version: 1.4.14 resolution: "@jridgewell/sourcemap-codec@npm:1.4.14" @@ -3607,6 +3864,16 @@ __metadata: languageName: node linkType: hard +"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25": + version: 0.3.25 + resolution: "@jridgewell/trace-mapping@npm:0.3.25" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.1.0" + "@jridgewell/sourcemap-codec": "npm:^1.4.14" + checksum: dced32160a44b49d531b80a4a2159dceab6b3ddf0c8e95a0deae4b0e894b172defa63d5ac52a19c2068e1fe7d31ea4ba931fbeec103233ecb4208953967120fc + languageName: node + linkType: hard + "@ledgerhq/cryptoassets@npm:^13.0.0": version: 13.0.0 resolution: "@ledgerhq/cryptoassets@npm:13.0.0" @@ -8872,6 +9139,13 @@ __metadata: languageName: node linkType: hard +"core-js@npm:^3.0.0": + version: 3.37.1 + resolution: "core-js@npm:3.37.1" + checksum: 25d6bd15fcc6ffd2a0ec0be57a78ff3358b3e1fdffdb6800fc93dcfdb3854037aee41f3d101aed8c37905d107daf98218b3e7ee95cec383710d2a66a5d9e541b + languageName: node + linkType: hard + "core-util-is@npm:1.0.2": version: 1.0.2 resolution: "core-util-is@npm:1.0.2" @@ -10675,6 +10949,29 @@ __metadata: languageName: node linkType: hard +"fetch-mock@npm:^9.11.0": + version: 9.11.0 + resolution: "fetch-mock@npm:9.11.0" + dependencies: + "@babel/core": "npm:^7.0.0" + "@babel/runtime": "npm:^7.0.0" + core-js: "npm:^3.0.0" + debug: "npm:^4.1.1" + glob-to-regexp: "npm:^0.4.0" + is-subset: "npm:^0.1.1" + lodash.isequal: "npm:^4.5.0" + path-to-regexp: "npm:^2.2.1" + querystring: "npm:^0.2.0" + whatwg-url: "npm:^6.5.0" + peerDependencies: + node-fetch: "*" + peerDependenciesMeta: + node-fetch: + optional: true + checksum: 4bbd312eafd960b787596ab587d00f84967c009a5b00d66566cba9312171bc1f8a8f8d85898d9d158c4fdabbd26913ad360ef4363ec85153463893ae761f2a8c + languageName: node + linkType: hard + "figures@npm:^3.0.0": version: 3.2.0 resolution: "figures@npm:3.2.0" @@ -17127,7 +17424,7 @@ __metadata: languageName: node linkType: hard -"querystring@npm:^0.2.1": +"querystring@npm:^0.2.0, querystring@npm:^0.2.1": version: 0.2.1 resolution: "querystring@npm:0.2.1" checksum: 5ae2eeb8c6d70263a3d13ffaf234ce9593ae0e95ad8ea04aa540e14ff66679347420817aeb4fe6fdfa2aaa7fac86e311b6f1d3da2187f433082ad9125c808c14 @@ -19151,6 +19448,15 @@ __metadata: languageName: node linkType: hard +"tr46@npm:^1.0.1": + version: 1.0.1 + resolution: "tr46@npm:1.0.1" + dependencies: + punycode: "npm:^2.1.0" + checksum: 6e80d75480cb6658f7f283c15f5f41c2d4dfa243ca99a0e1baf3de6cc823fc4c829f89782a7a11e029905781fccfea42d08d8a6674ba7948c7dbc595b6f27dd3 + languageName: node + linkType: hard + "tr46@npm:~0.0.3": version: 0.0.3 resolution: "tr46@npm:0.0.3" @@ -20623,6 +20929,13 @@ __metadata: languageName: node linkType: hard +"webidl-conversions@npm:^4.0.2": + version: 4.0.2 + resolution: "webidl-conversions@npm:4.0.2" + checksum: 594187c36f2d7898f89c0ed3b9248a095fa549ecc1befb10a97bc884b5680dc96677f58df5579334d8e0d1018e5ef075689cfa2a6c459f45a61a9deb512cb59e + languageName: node + linkType: hard + "websocket@npm:^1.0.32": version: 1.0.34 resolution: "websocket@npm:1.0.34" @@ -20647,6 +20960,17 @@ __metadata: languageName: node linkType: hard +"whatwg-url@npm:^6.5.0": + version: 6.5.0 + resolution: "whatwg-url@npm:6.5.0" + dependencies: + lodash.sortby: "npm:^4.7.0" + tr46: "npm:^1.0.1" + webidl-conversions: "npm:^4.0.2" + checksum: fbe8e9d81f8f07343f60bc135962ff346d7d19efc9e2f842f54b5c525837dcf1a36d13be21902311a2922b6342799dd82454e552b4e460d74451794f187ba0a5 + languageName: node + linkType: hard + "which-boxed-primitive@npm:^1.0.2": version: 1.0.2 resolution: "which-boxed-primitive@npm:1.0.2"