diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1cf26ad36..03a722404 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -39,33 +39,32 @@ jobs: - uses: ./.github/actions/install-dependencies - run: pnpm --filter abi test - tests-api: - name: Run api tests + test-account: + name: Run account tests runs-on: ubuntu-latest needs: [install] steps: - uses: actions/checkout@v3 - uses: ./.github/actions/install-dependencies - - run: pnpm --filter api test + - run: pnpm --filter account test - tests-auth: - name: Run auth tests + tests-api: + name: Run api tests runs-on: ubuntu-latest needs: [install] steps: - uses: actions/checkout@v3 - uses: ./.github/actions/install-dependencies - - run: pnpm --filter auth test + - run: pnpm --filter api test - tests-config: - name: Run config tests + tests-auth: + name: Run auth tests runs-on: ubuntu-latest needs: [install] steps: - uses: actions/checkout@v3 - uses: ./.github/actions/install-dependencies - - run: pnpm --filter config test - + - run: pnpm --filter auth test tests-deployer: name: Run deployer tests runs-on: ubuntu-latest @@ -111,6 +110,15 @@ jobs: - uses: ./.github/actions/install-dependencies - run: pnpm --filter metadata test + tests-migration: + name: Run migrations tests + runs-on: ubuntu-latest + needs: [install] + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/install-dependencies + - run: pnpm --filter migration test + tests-multicall: name: Run multicall tests runs-on: ubuntu-latest @@ -147,23 +155,41 @@ jobs: - uses: ./.github/actions/install-dependencies - run: pnpm --filter relayer test - tests-simulator: - name: Run simulator tests + tests-replacer: + name: Run replacer tests runs-on: ubuntu-latest needs: [install] steps: - uses: actions/checkout@v3 - uses: ./.github/actions/install-dependencies - - run: pnpm --filter simulator test + - run: pnpm --filter replacer test + + tests-sessions: + name: Run sessions tests + runs-on: ubuntu-latest + needs: [install] + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/install-dependencies + - run: pnpm --filter sessions test - tests-transactions: - name: Run transactions tests + tests-signhub: + name: Run signhub tests runs-on: ubuntu-latest needs: [install] steps: - uses: actions/checkout@v3 - uses: ./.github/actions/install-dependencies - - run: pnpm --filter transactions test + - run: pnpm --filter signhub test + + tests-simulator: + name: Run simulator tests + runs-on: ubuntu-latest + needs: [install] + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/install-dependencies + - run: pnpm --filter simulator test tests-utils: name: Run utils tests diff --git a/README.md b/README.md index 165484181..333dc7034 100644 --- a/README.md +++ b/README.md @@ -22,14 +22,16 @@ or - [@0xsequence/abi](./packages/abi) - [@0xsequence/api](./packages/api) - [@0xsequence/auth](./packages/auth) -- [@0xsequence/config](./packages/config) +- [@0xsequence/core](./packages/core) - [@0xsequence/deployer](./packages/deployer) - [@0xsequence/guard](./packages/guard) - [@0xsequence/multicall](./packages/multicall) - [@0xsequence/network](./packages/network) - [@0xsequence/provider](./packages/provider) - [@0xsequence/relayer](./packages/relayer) -- [@0xsequence/transactions](./packages/transactions) +- [@0xsequence/replacer](./packages/replacer) +- [@0xsequence/sessions](./packages/sessions) +- [@0xsequence/signhub](./packages/signhub) - [@0xsequence/utils](./packages/utils) - [@0xsequence/wallet](./packages/wallet) diff --git a/package.json b/package.json index 381a991c9..ba1564363 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "dev": "preconstruct dev", "postinstall": "preconstruct dev", "coverage": "rimraf ./coverage && rimraf ./.nyc_output && nyc pnpm test", - "prepare": "husky-run install" + "prepare": "husky install" }, "husky": { "hooks": { @@ -33,7 +33,6 @@ "@0xsequence/abi": "workspace:*", "@0xsequence/api": "workspace:*", "@0xsequence/auth": "workspace:*", - "@0xsequence/config": "workspace:*", "@0xsequence/deployer": "workspace:*", "@0xsequence/estimator": "workspace:*", "@0xsequence/guard": "workspace:*", @@ -44,40 +43,39 @@ "@0xsequence/provider": "workspace:*", "@0xsequence/relayer": "workspace:*", "@0xsequence/simulator": "workspace:*", - "@0xsequence/transactions": "workspace:*", "@0xsequence/utils": "workspace:*", "@0xsequence/wallet": "workspace:*", - "@babel/core": "^7.20.2", + "@babel/core": "^7.21.4", "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/preset-env": "^7.20.2", - "@babel/preset-typescript": "^7.18.6", - "@babel/runtime": "^7.20.1", + "@babel/preset-env": "^7.21.4", + "@babel/preset-typescript": "^7.21.4", + "@babel/runtime": "^7.21.0", "@changesets/changelog-github": "^0.4.7", - "@changesets/cli": "^2.25.2", + "@changesets/cli": "^2.26.1", "@preconstruct/cli": "^2.2.2", "@types/chai": "^4.2.22", "@types/chai-as-promised": "^7.1.4", "@types/mocha": "^10.0.0", - "@types/node": "^18.11.17", - "@typescript-eslint/eslint-plugin": "^5.43.0", - "@typescript-eslint/parser": "^5.43.0", + "@types/node": "^18.16.1", + "@typescript-eslint/eslint-plugin": "^5.59.1", + "@typescript-eslint/parser": "^5.59.1", "ava": "^3.15.0", "chai": "^4.3.4", "chai-as-promised": "^7.1.1", "concurrently": "^7.5.0", - "eslint": "^8.27.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-import": "^2.26.0", + "eslint": "^8.39.0", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-import": "^2.27.5", "eslint-plugin-prettier": "^4.2.1", "ethers": "^5.7.2", "express": "^4.18.2", "hardhat": "^2.12.2", - "husky": "4.3.8", + "husky": "^8.0.0", "mocha": "^10.1.0", "nyc": "^15.1.0", - "prettier": "^2.7.1", + "prettier": "^2.8.8", "puppeteer": "^19.7.2", - "rimraf": "^3.0.2", + "rimraf": "^5.0.0", "ts-node": "^10.9.1", "tsx": "^3.12.1", "typescript": "~4.9.4", diff --git a/packages/0xsequence/package.json b/packages/0xsequence/package.json index ae94cb439..a837fc212 100644 --- a/packages/0xsequence/package.json +++ b/packages/0xsequence/package.json @@ -28,26 +28,30 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@0xsequence/abi": "^0.43.34", - "@0xsequence/api": "^0.43.34", - "@0xsequence/auth": "^0.43.34", - "@0xsequence/config": "^0.43.34", - "@0xsequence/guard": "^0.43.34", - "@0xsequence/indexer": "^0.43.34", - "@0xsequence/metadata": "^0.43.34", - "@0xsequence/multicall": "^0.43.34", - "@0xsequence/network": "^0.43.34", - "@0xsequence/provider": "^0.43.34", - "@0xsequence/relayer": "^0.43.34", - "@0xsequence/transactions": "^0.43.34", - "@0xsequence/utils": "^0.43.34", - "@0xsequence/wallet": "^0.43.34" + "@0xsequence/abi": "workspace:*", + "@0xsequence/account": "workspace:*", + "@0xsequence/api": "workspace:*", + "@0xsequence/auth": "workspace:*", + "@0xsequence/core": "workspace:*", + "@0xsequence/guard": "workspace:*", + "@0xsequence/indexer": "workspace:*", + "@0xsequence/metadata": "workspace:*", + "@0xsequence/migration": "workspace:*", + "@0xsequence/multicall": "workspace:*", + "@0xsequence/network": "workspace:*", + "@0xsequence/provider": "workspace:*", + "@0xsequence/relayer": "workspace:*", + "@0xsequence/sessions": "workspace:*", + "@0xsequence/signhub": "workspace:*", + "@0xsequence/utils": "workspace:*", + "@0xsequence/wallet": "workspace:*" }, "peerDependencies": { "ethers": ">=5.5 < 6" }, "devDependencies": { - "@0xsequence/wallet-contracts": "1.10.0", + "@0xsequence/tests": "workspace:*", + "@0xsequence/wallet-contracts": "^1.10.0", "@babel/plugin-transform-runtime": "^7.19.6", "babel-loader": "^9.1.0", "ethers": "^5.7.2", diff --git a/packages/0xsequence/src/account.ts b/packages/0xsequence/src/account.ts new file mode 100644 index 000000000..5378d5293 --- /dev/null +++ b/packages/0xsequence/src/account.ts @@ -0,0 +1 @@ +export * from '@0xsequence/account' diff --git a/packages/0xsequence/src/config.ts b/packages/0xsequence/src/config.ts deleted file mode 100644 index da3e9cd82..000000000 --- a/packages/0xsequence/src/config.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from '@0xsequence/config' - -export type { - WalletConfig, - WalletState -} from '@0xsequence/config' diff --git a/packages/0xsequence/src/core.ts b/packages/0xsequence/src/core.ts new file mode 100644 index 000000000..c9df6528a --- /dev/null +++ b/packages/0xsequence/src/core.ts @@ -0,0 +1,6 @@ +import { commons } from '@0xsequence/core' + +export * from '@0xsequence/core' + +export type Config = commons.config.Config +export type WalletContext = commons.context.WalletContext diff --git a/packages/0xsequence/src/migration.ts b/packages/0xsequence/src/migration.ts new file mode 100644 index 000000000..029dfd709 --- /dev/null +++ b/packages/0xsequence/src/migration.ts @@ -0,0 +1 @@ +export * from '@0xsequence/migration' diff --git a/packages/0xsequence/src/network.ts b/packages/0xsequence/src/network.ts index 191e1de0f..7d9c318c1 100644 --- a/packages/0xsequence/src/network.ts +++ b/packages/0xsequence/src/network.ts @@ -11,6 +11,5 @@ export type { JsonRpcMiddlewareHandler, NetworkConfig, ChainId, - ChainIdLike, - WalletContext + ChainIdLike } from '@0xsequence/network' diff --git a/packages/0xsequence/src/sequence.ts b/packages/0xsequence/src/sequence.ts index 1554ed48f..f361de27e 100644 --- a/packages/0xsequence/src/sequence.ts +++ b/packages/0xsequence/src/sequence.ts @@ -1,7 +1,6 @@ export * as abi from './abi' export * as api from './api' export * as auth from './auth' -export * as config from './config' export * as guard from './guard' export * as indexer from './indexer' export * as metadata from './metadata' @@ -11,6 +10,11 @@ export * as provider from './provider' export * as relayer from './relayer' export * as transactions from './transactions' export * as utils from './utils' +export * as core from './core' +export * as signhub from './signhub' +export * as sessions from './sessions' +export * as migration from './migration' +export * as account from './account' export { initWallet, diff --git a/packages/0xsequence/src/sessions.ts b/packages/0xsequence/src/sessions.ts new file mode 100644 index 000000000..1eaf93bf5 --- /dev/null +++ b/packages/0xsequence/src/sessions.ts @@ -0,0 +1 @@ +export * from '@0xsequence/sessions' \ No newline at end of file diff --git a/packages/0xsequence/src/signhub.ts b/packages/0xsequence/src/signhub.ts new file mode 100644 index 000000000..acab93090 --- /dev/null +++ b/packages/0xsequence/src/signhub.ts @@ -0,0 +1,2 @@ + +export * from '@0xsequence/signhub' diff --git a/packages/0xsequence/src/transactions.ts b/packages/0xsequence/src/transactions.ts index 050692807..f2b08c462 100644 --- a/packages/0xsequence/src/transactions.ts +++ b/packages/0xsequence/src/transactions.ts @@ -1,11 +1,10 @@ -export * from '@0xsequence/transactions' +import { commons } from '@0xsequence/core' -export type { - Transaction, - TransactionEncoded, - TransactionRequest, - TransactionResponse, - NonceDependency, - Transactionish, - SignedTransactions, -} from '@0xsequence/transactions' +export const transactions = commons.transaction + +export type Transaction = commons.transaction.Transaction +export type TransactionEncoded = commons.transaction.TransactionEncoded +export type TransactionResponse = commons.transaction.TransactionResponse +export type Transactionish = commons.transaction.Transactionish +export type SignedTransactionBundle = commons.transaction.SignedTransactionBundle +export type RelayReadyTransactionBundle = commons.transaction.RelayReadyTransactionBundle diff --git a/packages/0xsequence/src/utils.ts b/packages/0xsequence/src/utils.ts index 3a13450ae..8ed1ee47d 100644 --- a/packages/0xsequence/src/utils.ts +++ b/packages/0xsequence/src/utils.ts @@ -2,9 +2,9 @@ export * from '@0xsequence/utils' export { isValidSignature, - isValidMessageSignature, - isValidTypedDataSignature, - recoverWalletConfig, + // isValidMessageSignature, + // isValidTypedDataSignature, + // recoverWalletConfig, isWalletUpToDate } from '@0xsequence/provider' diff --git a/packages/0xsequence/tests/browser/mock-wallet/mock-wallet.test.ts b/packages/0xsequence/tests/browser/mock-wallet/mock-wallet.test.ts index 7b9698fb7..84da83e03 100644 --- a/packages/0xsequence/tests/browser/mock-wallet/mock-wallet.test.ts +++ b/packages/0xsequence/tests/browser/mock-wallet/mock-wallet.test.ts @@ -1,12 +1,15 @@ import { ethers } from 'ethers' import { WalletRequestHandler, WindowMessageHandler } from '@0xsequence/provider' -import { Wallet, Account } from '@0xsequence/wallet' -import { NetworkConfig, JsonRpcProvider } from '@0xsequence/network' +import { Account } from '@0xsequence/account' +import { NetworkConfig } from '@0xsequence/network' import { LocalRelayer } from '@0xsequence/relayer' import { configureLogger } from '@0xsequence/utils' -import { testAccounts, getEOAWallet, deployWalletContext, testWalletContext } from '../testutils' +import { testAccounts, getEOAWallet } from '../testutils' import { test, assert } from '../../utils/assert' +import * as utils from '@0xsequence/tests' +import { Orchestrator } from '@0xsequence/signhub' +import { trackers } from '@0xsequence/sessions' configureLogger({ logLevel: 'DEBUG', silence: false }) @@ -18,22 +21,24 @@ const main = async () => { // // Providers // - const provider = new JsonRpcProvider('http://localhost:8545', { blockCache: true }) - const provider2 = new JsonRpcProvider('http://localhost:9545', { blockCache: true }) + const provider = new ethers.providers.JsonRpcProvider('http://localhost:8545') + const provider2 = new ethers.providers.JsonRpcProvider('http://localhost:9545') // // Deploy Sequence WalletContext (deterministic) // - const deployedWalletContext = await deployWalletContext(provider, provider2) + const deployedWalletContext = await utils.context.deploySequenceContexts(provider.getSigner()) + await utils.context.deploySequenceContexts(provider2.getSigner()) + console.log('walletContext:', deployedWalletContext) // assert testWalletContext value is correct - if ( - deployedWalletContext.factory !== testWalletContext.factory || - deployedWalletContext.guestModule !== testWalletContext.guestModule - ) { - throw new Error('deployedWalletContext and testWalletContext do not match. check or regen.') - } + // if ( + // deployedWalletContext.factory !== testWalletContext.factory || + // deployedWalletContext.guestModule !== testWalletContext.guestModule + // ) { + // throw new Error('deployedWalletContext and testWalletContext do not match. check or regen.') + // } // // Setup single owner Sequence wallet @@ -48,7 +53,7 @@ const main = async () => { const relayer2 = new LocalRelayer(getEOAWallet(testAccounts[5].privateKey, provider2)) // wallet account address: 0xa91Ab3C5390A408DDB4a322510A4290363efcEE9 based on the chainId - const wallet = (await Wallet.singleOwner(owner, deployedWalletContext)).connect(provider, relayer) + // const wallet = (await Wallet.singleOwner(owner, deployedWalletContext)).connect(provider, relayer) // Network available list const networks: NetworkConfig[] = [ @@ -59,28 +64,40 @@ const main = async () => { provider: provider, relayer: relayer, isDefaultChain: true - // isAuthChain: true }, { name: 'hardhat2', chainId: 31338, rpcUrl: provider2.connection.url, provider: provider2, - relayer: relayer2, - isAuthChain: true + relayer: relayer2 } ] // Account for managing multi-network wallets // TODO: make this a 3-key multisig with threshold of 2 - const account = new Account( - { - initialConfig: wallet.config, - networks, - context: deployedWalletContext + // const account = new Account( + // { + // initialConfig: wallet.config, + // networks, + // context: deployedWalletContext + // }, + // owner + // ) + const account = await Account.new({ + config: { + threshold: 2, + checkpoint: 0, + signers: [{ + address: owner.address, + weight: 2 + }] }, - owner - ) + networks, + contexts: deployedWalletContext, + orchestrator: new Orchestrator([owner]), + tracker: new trackers.local.LocalConfigTracker(provider) + }) // the json-rpc signer via the wallet const walletRequestHandler = new WalletRequestHandler(undefined, null, networks) diff --git a/packages/0xsequence/tests/browser/mux-transport/mux.test.ts b/packages/0xsequence/tests/browser/mux-transport/mux.test.ts index 686f8fb89..3af81f0f0 100644 --- a/packages/0xsequence/tests/browser/mux-transport/mux.test.ts +++ b/packages/0xsequence/tests/browser/mux-transport/mux.test.ts @@ -1,22 +1,21 @@ import { - ProxyMessageProvider, - ProviderMessageTransport, - ProviderMessage, WalletRequestHandler, ProxyMessageChannel, ProxyMessageHandler, Wallet, - DefaultProviderConfig, - Web3Provider, WindowMessageHandler } from '@0xsequence/provider' -import { providers } from 'ethers' +import { ethers } from 'ethers' import { test, assert } from '../../utils/assert' -import { NetworkConfig, WalletContext, JsonRpcProvider } from '@0xsequence/network' -import { Wallet as SequenceWallet, Account as SequenceAccount, isValidSignature, recoverConfig } from '@0xsequence/wallet' +import { NetworkConfig } from '@0xsequence/network' import { LocalRelayer } from '@0xsequence/relayer' -import { configureLogger, packMessageData } from '@0xsequence/utils' -import { testAccounts, getEOAWallet, testWalletContext } from '../testutils' +import { configureLogger } from '@0xsequence/utils' +import { testAccounts, getEOAWallet } from '../testutils' +import * as utils from '@0xsequence/tests' +import { Account } from '@0xsequence/account' +import { Orchestrator } from '@0xsequence/signhub' +import { trackers } from '@0xsequence/sessions' +import { commons } from '@0xsequence/core' configureLogger({ logLevel: 'DEBUG', silence: false }) @@ -26,14 +25,14 @@ export const tests = async () => { // // Providers // - const provider1 = new JsonRpcProvider('http://localhost:8545') - const provider2 = new JsonRpcProvider('http://localhost:9545') + const provider1 = new ethers.providers.JsonRpcProvider('http://localhost:8545') + const provider2 = new ethers.providers.JsonRpcProvider('http://localhost:9545') // - // Deploy Sequence WalletContext (deterministic). We skip deployment - // as we rely on mock-wallet to deploy it. + // Deploy Sequence WalletContext (deterministic). // - const deployedWalletContext = testWalletContext + const deployedWalletContext = await utils.context.deploySequenceContexts(provider1.getSigner()) + await utils.context.deploySequenceContexts(provider2.getSigner()) console.log('walletContext:', deployedWalletContext) // @@ -53,9 +52,6 @@ export const tests = async () => { const relayer1 = new LocalRelayer(getEOAWallet(testAccounts[5].privateKey)) const relayer2 = new LocalRelayer(getEOAWallet(testAccounts[5].privateKey, provider2)) - // wallet account address: 0xa91Ab3C5390A408DDB4a322510A4290363efcEE9 based on the chainId - const swallet = (await SequenceWallet.singleOwner(owner, deployedWalletContext)).connect(provider1, relayer1) - // Network available list const networks: NetworkConfig[] = [ { @@ -65,27 +61,31 @@ export const tests = async () => { provider: provider1, relayer: relayer1, isDefaultChain: true - // isAuthChain: true }, { name: 'hardhat2', chainId: 31338, rpcUrl: provider2.connection.url, provider: provider2, - relayer: relayer2, - isAuthChain: true + relayer: relayer2 } ] // Account for managing multi-network wallets - const saccount = new SequenceAccount( - { - initialConfig: swallet.config, - networks, - context: deployedWalletContext + const saccount = await Account.new({ + networks, + contexts: deployedWalletContext, + config: { + threshold: 1, + checkpoint: 0, + signers: [{ + address: owner.address, + weight: 1 + }] }, - owner - ) + orchestrator: new Orchestrator([owner]), + tracker: new trackers.local.LocalConfigTracker(provider1) + }) // the rpc signer via the wallet const walletRequestHandler = new WalletRequestHandler(saccount, null, networks) @@ -149,11 +149,11 @@ export const tests = async () => { assert.true(opened, 'wallet is opened') }) - let walletContext: WalletContext + let walletContext: commons.context.VersionedContext await test('getWalletContext', async () => { walletContext = await wallet.getWalletContext() - assert.equal(walletContext.factory, deployedWalletContext.factory, 'wallet context factory') - assert.equal(walletContext.guestModule, deployedWalletContext.guestModule, 'wallet context guestModule') + assert.equal(walletContext[2].factory, deployedWalletContext[2].factory, 'wallet context factory') + assert.equal(walletContext[2].guestModule, deployedWalletContext[2].guestModule, 'wallet context guestModule') }) await test('getChainId', async () => { diff --git a/packages/0xsequence/tests/browser/proxy-transport/channel.test.ts b/packages/0xsequence/tests/browser/proxy-transport/channel.test.ts index 8b8e926df..6006ab4df 100644 --- a/packages/0xsequence/tests/browser/proxy-transport/channel.test.ts +++ b/packages/0xsequence/tests/browser/proxy-transport/channel.test.ts @@ -1,20 +1,21 @@ import { Web3Provider, ProxyMessageProvider, - WalletSession, WalletRequestHandler, ProxyMessageChannel, ProxyMessageHandler, prefixEIP191Message } from '@0xsequence/provider' -import { ethers, Wallet as EOAWallet } from 'ethers' +import { ethers } from 'ethers' import { test, assert } from '../../utils/assert' -import { sequenceContext, testnetNetworks } from '@0xsequence/network' -import { Wallet, isValidSignature, recoverConfig } from '@0xsequence/wallet' -import { addressOf } from '@0xsequence/config' import { LocalRelayer } from '@0xsequence/relayer' -import { configureLogger, encodeMessageDigest, packMessageData } from '@0xsequence/utils' +import { configureLogger, encodeMessageDigest } from '@0xsequence/utils' import { testAccounts, getEOAWallet } from '../testutils' +import { Account } from '@0xsequence/account' +import * as utils from '@0xsequence/tests' +import { Orchestrator } from '@0xsequence/signhub' +import { trackers } from '@0xsequence/sessions' +import { commons } from '@0xsequence/core' configureLogger({ logLevel: 'DEBUG', silence: false }) @@ -62,10 +63,8 @@ export const tests = async () => { // relayer account is same as owner here const relayer = new LocalRelayer(owner) - - // wallet account address: 0xa91Ab3C5390A408DDB4a322510A4290363efcEE9 based on the chainId const rpcProvider = new ethers.providers.JsonRpcProvider('http://localhost:8545') - const wallet = (await Wallet.singleOwner(owner)).connect(rpcProvider, relayer) + const contexts = await utils.context.deploySequenceContexts(rpcProvider.getSigner()) const networks = [ { @@ -75,17 +74,32 @@ export const tests = async () => { provider: rpcProvider, relayer: relayer, isDefaultChain: true - // isAuthChain: true } ] + // wallet account address: 0x91A858FbBa42E7EE200b4303b1A8B2F0BD139663 based on the chainId + const account = await Account.new({ + config: { + threshold: 1, + checkpoint: 1674142220, + signers: [{ + address: owner.address, + weight: 1 + }] + }, + networks, + contexts, + orchestrator: new Orchestrator([owner]), + tracker: new trackers.local.LocalConfigTracker(rpcProvider) + }) + // the rpc signer via the wallet const walletRequestHandler = new WalletRequestHandler(undefined, null, networks) // fake/force an async wallet initialization for the wallet-request handler. This is the behaviour // of the wallet-webapp, so lets ensure the mock wallet does the same thing too. setTimeout(() => { - walletRequestHandler.signIn(wallet) + walletRequestHandler.signIn(account) }, 1000) // register wallet message handler, in this case using the ProxyMessage transport. @@ -102,12 +116,12 @@ export const tests = async () => { await walletProvider.waitUntilOpened() // setup web3 provider - const provider = new Web3Provider(walletProvider) + const provider = new Web3Provider(walletProvider, 31337) const signer = provider.getSigner() const address = await signer.getAddress() await test('verifying getAddress result', async () => { - assert.equal(address, ethers.utils.getAddress('0xa91Ab3C5390A408DDB4a322510A4290363efcEE9'), 'wallet address') + assert.equal(address, ethers.utils.getAddress('0x91A858FbBa42E7EE200b4303b1A8B2F0BD139663'), 'wallet address') }) await test('sending a json-rpc request', async () => { @@ -138,34 +152,20 @@ export const tests = async () => { const sig = await signer.signMessage(message) assert.equal( sig, - '0x00010001230f8b68557d982f26234c9c7ce4ff35a449392c1e7cbc9a1129268ce2acea40529252535b1caa300e30d53d5c24009cb6f2fafd0e132944016f9472c1a0cc8b1b02', + '0x000000000000000000000000e35a6b88704b08f944f37d0c709f8cb548594883000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002047a9a16280000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000050eb6e88efa415aeb63baca79db74c112bef2e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000004432c02a14000000000000000000000000eda2d9b473e7dcf61d18606c459e06e0635c351372a16f43c162cf3b238b87c35fccdbebab1b4d1e42e2c835e30471dd311fe67900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a000163c9620c0001045ea593a25d0053816f2cfb0239eb04c30cc08fd26193927bf6cf68f7f31a8239ecbcbd1365f18a6bf2bf3b13d544c91d85e35503696a28fcb96a4078a7556a1c02000000000000000000000000000000000000000000006492649264926492649264926492649264926492649264926492649264926492', 'signature match' ) const chainId = await signer.getChainId() + const reader = new commons.reader.OnChainReader(rpcProvider) // // Verify the message signature // - // const messageDigest = ethers.utils.arrayify(ethers.utils.keccak256(message)) + await account.doBootstrap(31337) const messageDigest = encodeMessageDigest(prefixEIP191Message(message)) - const isValid = await isValidSignature(address, messageDigest, sig, provider, sequenceContext, chainId) + const isValid = await reader.isValidSignature(address, messageDigest, sig) assert.true(isValid, 'signature is valid - 1') - - // also compute the subDigest of the message, to be provided to the end-user - // in order to recover the config properly, the subDigest + sig is required. - const subDigest = packMessageData(address, chainId, messageDigest) - - // - // Recover config / address - // - const walletConfig = await recoverConfig(subDigest, sig) - - const recoveredWalletAddress = addressOf(walletConfig, sequenceContext) - assert.true(recoveredWalletAddress === address, 'recover address - 1') - - const singleSignerAddress = ethers.utils.getAddress('0x4e37E14f5d5AAC4DF1151C6E8DF78B7541680853') // expected from mock-wallet owner - assert.true(singleSignerAddress === walletConfig.signers[0].address, 'owner address check') }) walletProvider.closeWallet() diff --git a/packages/0xsequence/tests/browser/testutils/deploy-wallet-context.ts b/packages/0xsequence/tests/browser/testutils/deploy-wallet-context.ts index 166cd4963..f7d93b4cf 100644 --- a/packages/0xsequence/tests/browser/testutils/deploy-wallet-context.ts +++ b/packages/0xsequence/tests/browser/testutils/deploy-wallet-context.ts @@ -1,80 +1,80 @@ -import { ethers } from 'ethers' -import { UniversalDeployer } from '@0xsequence/deployer' -import { WalletContext } from '@0xsequence/network' -import { testAccounts, getEOAWallet } from './accounts' +// import { ethers } from 'ethers' +// import { UniversalDeployer } from '@0xsequence/deployer' +// import { WalletContext } from '@0xsequence/network' +// import { testAccounts, getEOAWallet } from './accounts' -// TODO/NOTE: it should be possible to import below from just '@0xsequence/wallet-contracts' -// however, experiencing a strange JS packaging/module resolution issue which leads to: -// -// mock-wallet.test.js:70822 Uncaught (in promise) TypeError: Class constructor ContractFactory cannot be invoked without 'new' -// -// by importing from '@0xsequence/wallet-contracts/gen/typechain', this issue goes away +// // TODO/NOTE: it should be possible to import below from just '@0xsequence/wallet-contracts' +// // however, experiencing a strange JS packaging/module resolution issue which leads to: +// // +// // mock-wallet.test.js:70822 Uncaught (in promise) TypeError: Class constructor ContractFactory cannot be invoked without 'new' +// // +// // by importing from '@0xsequence/wallet-contracts/gen/typechain', this issue goes away -import { - Factory__factory, - MainModule__factory, - MainModuleUpgradable__factory, - GuestModule__factory, - SequenceUtils__factory, - RequireFreshSigner__factory, -} from '@0xsequence/wallet-contracts' +// import { +// Factory__factory, +// MainModule__factory, +// MainModuleUpgradable__factory, +// GuestModule__factory, +// SequenceUtils__factory, +// RequireFreshSigner__factory, +// } from '@0xsequence/wallet-contracts' -const deployWalletContextCache: WalletContext[] = [] +// const deployWalletContextCache: WalletContext[] = [] -// deployWalletContext will deploy the Sequence WalletContext via the UniversalDeployer -// which will return deterministic contract addresses between calls. -export const deployWalletContext = async (...providers: ethers.providers.JsonRpcProvider[]): Promise => { - if (!providers || providers.length === 0) { - providers.push(new ethers.providers.JsonRpcProvider('http://localhost:8545')) - } +// // deployWalletContext will deploy the Sequence WalletContext via the UniversalDeployer +// // which will return deterministic contract addresses between calls. +// export const deployWalletContext = async (...providers: ethers.providers.JsonRpcProvider[]): Promise => { +// if (!providers || providers.length === 0) { +// providers.push(new ethers.providers.JsonRpcProvider('http://localhost:8545')) +// } - // Memoize the result. Even though its universal/deterministic, caching the result - // offers greater efficiency between calls - if (deployWalletContextCache.length === providers.length) { - return deployWalletContextCache[0] - } +// // Memoize the result. Even though its universal/deterministic, caching the result +// // offers greater efficiency between calls +// if (deployWalletContextCache.length === providers.length) { +// return deployWalletContextCache[0] +// } - await Promise.all(providers.map(async provider => { - // Deploying test accounts with the first test account - const wallet = getEOAWallet(testAccounts[0].privateKey, provider) +// await Promise.all(providers.map(async provider => { +// // Deploying test accounts with the first test account +// const wallet = getEOAWallet(testAccounts[0].privateKey, provider) - // Universal deployer for deterministic contract addresses - const universalDeployer = new UniversalDeployer('local', wallet.provider as ethers.providers.JsonRpcProvider) - const txParams = { gasLimit: 8000000, gasPrice: ethers.BigNumber.from(10).pow(9).mul(10) } +// // Universal deployer for deterministic contract addresses +// const universalDeployer = new UniversalDeployer('local', wallet.provider as ethers.providers.JsonRpcProvider) +// const txParams = { gasLimit: 8000000, gasPrice: ethers.BigNumber.from(10).pow(9).mul(10) } - const walletFactory = await universalDeployer.deploy('WalletFactory', Factory__factory as any, txParams) - const mainModule = await universalDeployer.deploy('MainModule', MainModule__factory as any, txParams, 0, walletFactory.address) +// const walletFactory = await universalDeployer.deploy('WalletFactory', Factory__factory as any, txParams) +// const mainModule = await universalDeployer.deploy('MainModule', MainModule__factory as any, txParams, 0, walletFactory.address) - await universalDeployer.deploy('MainModuleUpgradable', MainModuleUpgradable__factory as any, txParams) - await universalDeployer.deploy('GuestModule', GuestModule__factory as any, txParams) +// await universalDeployer.deploy('MainModuleUpgradable', MainModuleUpgradable__factory as any, txParams) +// await universalDeployer.deploy('GuestModule', GuestModule__factory as any, txParams) - const sequenceUtils = await universalDeployer.deploy('SequenceUtils', SequenceUtils__factory as any, txParams, 0, walletFactory.address, mainModule.address) - await universalDeployer.deploy('RequireFreshSignerLib', RequireFreshSigner__factory as any, txParams, 0, sequenceUtils.address) +// const sequenceUtils = await universalDeployer.deploy('SequenceUtils', SequenceUtils__factory as any, txParams, 0, walletFactory.address, mainModule.address) +// await universalDeployer.deploy('RequireFreshSignerLib', RequireFreshSigner__factory as any, txParams, 0, sequenceUtils.address) - const deployment = universalDeployer.getDeployment() +// const deployment = universalDeployer.getDeployment() - deployWalletContextCache.push({ - factory: deployment['WalletFactory'].address, - mainModule: deployment['MainModule'].address, - mainModuleUpgradable: deployment['MainModuleUpgradable'].address, - guestModule: deployment['GuestModule'].address, - sequenceUtils: deployment['SequenceUtils'].address, - libs: { - requireFreshSigner: deployment['RequireFreshSignerLib'].address - } - }) - })) +// deployWalletContextCache.push({ +// factory: deployment['WalletFactory'].address, +// mainModule: deployment['MainModule'].address, +// mainModuleUpgradable: deployment['MainModuleUpgradable'].address, +// guestModule: deployment['GuestModule'].address, +// sequenceUtils: deployment['SequenceUtils'].address, +// libs: { +// requireFreshSigner: deployment['RequireFreshSignerLib'].address +// } +// }) +// })) - return deployWalletContextCache[0] -} +// return deployWalletContextCache[0] +// } -// testWalletContext is determined by the `deployWalletContext` method above. We can use this -// across instances, but, we must ensure the contracts are deployed by the mock-wallet at least. -export const testWalletContext: WalletContext = { - factory: "0xf9D09D634Fb818b05149329C1dcCFAeA53639d96", - guestModule: "0x02390F3E6E5FD1C6786CB78FD3027C117a9955A7", - mainModule: "0xd01F11855bCcb95f88D7A48492F66410d4637313", - mainModuleUpgradable: "0x7EFE6cE415956c5f80C6530cC6cc81b4808F6118", - sequenceUtils: "0xd130B43062D875a4B7aF3f8fc036Bc6e9D3E1B3E" -} +// // testWalletContext is determined by the `deployWalletContext` method above. We can use this +// // across instances, but, we must ensure the contracts are deployed by the mock-wallet at least. +// export const testWalletContext: WalletContext = { +// factory: "0xf9D09D634Fb818b05149329C1dcCFAeA53639d96", +// guestModule: "0x02390F3E6E5FD1C6786CB78FD3027C117a9955A7", +// mainModule: "0xd01F11855bCcb95f88D7A48492F66410d4637313", +// mainModuleUpgradable: "0x7EFE6cE415956c5f80C6530cC6cc81b4808F6118", +// sequenceUtils: "0xd130B43062D875a4B7aF3f8fc036Bc6e9D3E1B3E" +// } diff --git a/packages/0xsequence/tests/browser/testutils/index.ts b/packages/0xsequence/tests/browser/testutils/index.ts index 7880169dd..63f7cc82a 100644 --- a/packages/0xsequence/tests/browser/testutils/index.ts +++ b/packages/0xsequence/tests/browser/testutils/index.ts @@ -1,3 +1,3 @@ export * from './accounts' -export * from './deploy-wallet-context' +// export * from './deploy-wallet-context' export * from './wallet' diff --git a/packages/0xsequence/tests/browser/wallet-provider/dapp.test.ts b/packages/0xsequence/tests/browser/wallet-provider/dapp.test.ts index 8a2bc0401..e51d7cdef 100644 --- a/packages/0xsequence/tests/browser/wallet-provider/dapp.test.ts +++ b/packages/0xsequence/tests/browser/wallet-provider/dapp.test.ts @@ -1,40 +1,42 @@ -import { test, assert } from '../../utils/assert' -import { ethers, TypedDataDomain, TypedDataField } from 'ethers' -import { Wallet, DefaultProviderConfig, isValidMessageSignature } from '@0xsequence/provider' -import { WalletContext } from '@0xsequence/network' -import { testAccounts, getEOAWallet, testWalletContext, sendETH } from '../testutils' -import { Transaction, TransactionRequest } from '@0xsequence/transactions' +import { commons, v2 } from '@0xsequence/core' +import { Wallet, DefaultProviderConfig } from '@0xsequence/provider' +import { context } from '@0xsequence/tests' import { configureLogger } from '@0xsequence/utils' +import { ethers, TypedDataDomain, TypedDataField } from 'ethers' +import { test, assert } from '../../utils/assert' +import { testAccounts, getEOAWallet, sendETH } from '../testutils' configureLogger({ logLevel: 'DEBUG', silence: false }) export const tests = async () => { - - // - // Deploy Sequence WalletContext (deterministic). We skip deployment - // as we rely on mock-wallet to deploy it. - // - const deployedWalletContext = testWalletContext - console.log('walletContext:', deployedWalletContext) - // // Setup // const providerConfig = { ...DefaultProviderConfig } providerConfig.walletAppURL = 'http://localhost:9999/mock-wallet/mock-wallet.test.html' providerConfig.networks = [{ - name: 'hardhat', rpcUrl: 'http://0.0.0.0:8545' + name: 'hardhat', rpcUrl: 'http://localhost:8545' }] - + + // + // Deploy Sequence WalletContext (deterministic). + // + const deployedWalletContext = await (async () => { + const provider = new ethers.providers.JsonRpcProvider('http://localhost:8545') + const signer = provider.getSigner() + return context.deploySequenceContexts(signer) + })() + console.log('walletContext:', deployedWalletContext) + const wallet = new Wallet('hardhat', providerConfig) // provider + signer, by default if a chainId is not specified it will direct // requests to the defaultChain const provider = wallet.getProvider()! - const signer = wallet.getSigner() + const signer = wallet.getSigner()! // clear it in case we're testing in browser session - wallet.disconnect() + await wallet.disconnect() await test('is disconnected / logged out', async () => { assert.false(wallet.isConnected(), 'is connected') @@ -51,7 +53,7 @@ export const tests = async () => { await test('connect', async () => { const { connected } = await wallet.connect({ keepWalletOpened: true, - redirectMode: true, + // redirectMode: true, }) assert.true(connected, 'is connected') }) @@ -64,11 +66,13 @@ export const tests = async () => { assert.true(wallet.isConnected(), 'is connected') }) - let walletContext: WalletContext + let walletContext: commons.context.VersionedContext await test('getWalletContext', async () => { walletContext = await wallet.getWalletContext() - assert.equal(walletContext.factory, deployedWalletContext.factory, 'wallet context factory') - assert.equal(walletContext.guestModule, deployedWalletContext.guestModule, 'wallet context guestModule') + assert.equal(walletContext[1].factory, deployedWalletContext[1].factory, 'wallet context factory') + assert.equal(walletContext[1].guestModule, deployedWalletContext[1].guestModule, 'wallet context guestModule') + assert.equal(walletContext[2].factory, deployedWalletContext[2].factory, 'wallet context factory') + assert.equal(walletContext[2].guestModule, deployedWalletContext[2].guestModule, 'wallet context guestModule') }) await test('getChainId', async () => { @@ -81,15 +85,10 @@ export const tests = async () => { assert.equal(networks.length, 2, '2 networks') assert.true(networks[0].isDefaultChain!, '1st network is DefaultChain') - assert.true(!networks[0].isAuthChain, '1st network is not AuthChain') assert.true(!networks[1].isDefaultChain, '1st network is not DefaultChain') - assert.true(networks[1].isAuthChain!, '2nd network is AuthChain') assert.true(networks[1].chainId === 31338, 'authChainId is correct') - const authNetwork = await wallet.getAuthNetwork() - assert.equal(networks[1].chainId, authNetwork.chainId, 'authNetwork matches chainId') - - const authProvider = wallet.getProvider(authNetwork)! + const authProvider = wallet.getProvider(31338)! assert.equal(await authProvider.getChainId(), 31338, 'authProvider chainId is 31338') assert.equal(await provider.getChainId(), 31337, 'provider chainId is 31337') @@ -97,40 +96,28 @@ export const tests = async () => { await test('getAccounts', async () => { const address = await wallet.getAddress() - assert.equal(address, ethers.utils.getAddress('0xa91Ab3C5390A408DDB4a322510A4290363efcEE9'), 'wallet address is correct') + assert.equal(address, ethers.utils.getAddress('0x0C90b76e8Ca332560f7909dBDB658623919aaA39'), 'wallet address is correct') }) await test('getWalletConfig', async () => { const allWalletConfigs = await wallet.getWalletConfig() - assert.equal(allWalletConfigs.length, 2, '2 wallet configs (one for each chain)') - - const config1 = allWalletConfigs[0] - assert.true(config1.chainId !== undefined, 'config1, chainId is set') - assert.true(config1.threshold === 1, 'config1, 1 threshold') - assert.true(config1.signers.length === 1, 'config1, 1 signer') - assert.true(config1.signers[0].address === '0x4e37E14f5d5AAC4DF1151C6E8DF78B7541680853', 'config1, signer address') - assert.true(config1.signers[0].weight === 1, 'config1, signer weight') - - const config2 = allWalletConfigs[0] - assert.true(config2.chainId !== undefined, 'config2, chainId is set') - assert.true(config2.threshold === 1, 'config2, 1 threshold') - assert.true(config2.signers.length === 1, 'config2, 1 signer') - assert.true(config2.signers[0].address === '0x4e37E14f5d5AAC4DF1151C6E8DF78B7541680853', 'config2, signer address') - assert.true(config2.signers[0].weight === 1, 'config2, signer weight') + + const config = allWalletConfigs as v2.config.WalletConfig + assert.equal(config.version, 2, 'wallet config version is correct') + assert.true(ethers.BigNumber.from(2).eq(config.threshold), 'config, 2 threshold') + assert.true(ethers.BigNumber.from(0).eq(config.checkpoint), 'config, 0 checkpoint') + assert.true(v2.config.isSignerLeaf(config.tree), 'config, isSignerLeaf') + assert.true((config.tree as v2.config.SignerLeaf).address === '0x4e37E14f5d5AAC4DF1151C6E8DF78B7541680853', 'config, signer address') + assert.true(ethers.BigNumber.from(2).eq((config.tree as v2.config.SignerLeaf).weight), 'config, signer weight') }) await test('getWalletState', async () => { - const allWalletStates = await signer.getWalletState() - assert.equal(allWalletStates.length, 2, '2 wallet states (one for each chain)') - - // we expect network order to be [defaultChain, authChain, ..], so chain 31337 will be at index 0 - const state1 = allWalletStates[0] - assert.true(state1.chainId === 31337, 'state1, chainId is 31337') - assert.true(state1.config!.threshold === 1, 'state1, threshold') - assert.true(state1.config!.signers.length === 1, 'state1, 1 signer') - assert.true(state1.address === await wallet.getAddress(), 'state1, address') - // assert.true(state1.deployed, 'state1, deployed') - // assert.true(state1.publishedLatest, 'state1, publishedLatest') + const state = await wallet.getWalletState() + + assert.true(state !== undefined, 'state is defined') + assert.true(ethers.BigNumber.from(0).eq(state.checkpoint), 'state, 0 checkpoint') + assert.true(state.fullyMigrated, 'state, fullyMigrated') + assert.true(state.version === 2, 'state, version') }) await test('multiple networks', async () => { @@ -187,10 +174,10 @@ export const tests = async () => { const sigs = await Promise.all([message, message2].map(async m => { // NOTE: below line is equivalent to `signer.signMessage(m)` call // const sig = await wallet.utils.signMessage(m) - const sig = await signer.signMessage(m) + const sig = await signer.signMessage(m, undefined, true) assert.equal( sig, - '0x00010001230f8b68557d982f26234c9c7ce4ff35a449392c1e7cbc9a1129268ce2acea40529252535b1caa300e30d53d5c24009cb6f2fafd0e132944016f9472c1a0cc8b1b02', + '0x000000000000000000000000e35a6b88704b08f944f37d0c709f8cb548594883000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002047a9a16280000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000050eb6e88efa415aeb63baca79db74c112bef2e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000004432c02a14000000000000000000000000eda2d9b473e7dcf61d18606c459e06e0635c3513d8738dddc5c63663e6da04fe5a8747677482036c1901527c8e92abc534dbd5d900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a0002000000000002dae61fe1d90658f8f4339bd58043b122929cd3f1faaeab38e4daa97b09471170464ebb81bb1957babce03c5fbd0bee815cc61de66d7edaff0d55a4bfbde016e11b02000000000000000000000000000000000000000000006492649264926492649264926492649264926492649264926492649264926492', 'signature match' ) return sig @@ -200,17 +187,6 @@ export const tests = async () => { // Verify the signature const isValid = await wallet.utils.isValidMessageSignature(address, message, sig, chainId) assert.true(isValid, 'signature is valid - 2') - - // Verify signature with other util - const isValid2 = await isValidMessageSignature(address, message, sig, provider) - assert.true(isValid2, 'signature is valid - 2b') - - // Recover the address / config from the signature - const walletConfig = await wallet.utils.recoverWalletConfigFromMessage(address, message, sig, chainId) - assert.true(walletConfig.address === address, 'recover address - 2') - - const singleSignerAddress = '0x4e37E14f5d5AAC4DF1151C6E8DF78B7541680853' // expected from mock-wallet owner - assert.true(singleSignerAddress === walletConfig.signers[0].address, 'owner address check') }) await test('signTypedData on defaultChain', async () => { @@ -237,42 +213,27 @@ export const tests = async () => { } const sig = await signer.signTypedData(domain, types, message) - assert.equal( - sig, - '0x00010001c25b59035ea662350e08f41b5087fc49a98b94936826b61a226f97e400c6ce290b8dfa09e3b0df82288fbc599d5b1a023a864bbd876bc67ec1f94c5f2fc4e6101b02', - 'signature match typed-data' - ) // Verify typed data const isValid = await wallet.utils.isValidTypedDataSignature(address, { domain, types, message }, sig, chainId) assert.true(isValid, 'signature is valid - 3') - - // Recover config / address - const walletConfig = await wallet.utils.recoverWalletConfigFromTypedData(address, { domain, types, message }, sig, chainId) - assert.true(walletConfig.address === address, 'recover address - 3') - - const singleSignerAddress = '0x4e37E14f5d5AAC4DF1151C6E8DF78B7541680853' // expected from mock-wallet owner - assert.true(singleSignerAddress === walletConfig.signers[0].address, 'owner address check') }) await test('signAuthMessage', async () => { - // NOTE: by definition, signAuthMessage will always be directed at the authChain network - const authNetwork = await wallet.getAuthNetwork() - const address = await wallet.getAddress() - const chainId = authNetwork.chainId - const authProvider = wallet.getProvider(authNetwork)! + const chainId = 31337 + const authProvider = wallet.getProvider(chainId)! - assert.equal(chainId, 31338, 'chainId is 31338 (authChain)') - assert.equal(await authProvider.getChainId(), 31338, 'authProvider chainId is 31338') - assert.equal(await authProvider.getChainId(), await authProvider.getSigner().getChainId(), 'authProvider signer chainId is 31338') + assert.equal(chainId, 31337, 'chainId is 31337 (authChain)') + assert.equal(await authProvider.getChainId(), 31337, 'authProvider chainId is 31337') + assert.equal(await authProvider.getChainId(), await authProvider.getSigner().getChainId(), 'authProvider signer chainId is 31337') // Sign the message const message = 'hihi' const sig = await signer.signMessage(message, chainId) assert.equal( sig, - '0x00010001bbbabd7be415ffbf6196f17072413bed8f9f59c530357eb479e2fbe7ea210f22428bbb18413f24fed2edc7d4e6c11d588e436a56a54497080c9434fdcfdbb8ed1b02', + '0x000000000000000000000000e35a6b88704b08f944f37d0c709f8cb548594883000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002047a9a16280000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000186a00000000000000000000000000050eb6e88efa415aeb63baca79db74c112bef2e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000004432c02a14000000000000000000000000eda2d9b473e7dcf61d18606c459e06e0635c3513d8738dddc5c63663e6da04fe5a8747677482036c1901527c8e92abc534dbd5d900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004a0002000000000002dae61fe1d90658f8f4339bd58043b122929cd3f1faaeab38e4daa97b09471170464ebb81bb1957babce03c5fbd0bee815cc61de66d7edaff0d55a4bfbde016e11b02000000000000000000000000000000000000000000006492649264926492649264926492649264926492649264926492649264926492', 'signAuthMessage, signature match' ) @@ -287,17 +248,6 @@ export const tests = async () => { // Verify the signature const isValid = await wallet.utils.isValidMessageSignature(address, message, sig, chainId) assert.true(isValid, 'signAuthMessage, signature is valid') - - // Verify signature with other util - const isValid2 = await isValidMessageSignature(address, message, sig, authProvider) - assert.true(isValid2, 'signAuthMessage, signature is valid') - - // Recover the address / config from the signature - const walletConfig = await wallet.utils.recoverWalletConfigFromMessage(address, message, sig, chainId) - assert.true(walletConfig.address === address, 'recover address') - - const singleSignerAddress = '0x4e37E14f5d5AAC4DF1151C6E8DF78B7541680853' // expected from mock-wallet owner - assert.true(singleSignerAddress === walletConfig.signers[0].address, 'owner address check') }) await test('getBalance', async () => { @@ -336,7 +286,7 @@ export const tests = async () => { const ethAmount = ethers.utils.parseEther('1.4242') - // NOTE: when a wallet is undeployed (counter-factual), and although the txn contents are to send from our + // NOTE: when a wallet is undeployed (counterfactual), and although the txn contents are to send from our // sequence wallet to the test account, the transaction by the Sequence Wallet instance will be sent `to` the // `GuestModule` smart contract address of the Sequence context `from` the Sequence Relayer (local) account. // @@ -354,7 +304,7 @@ export const tests = async () => { const beforeWalletDeployed = await wallet.isDeployed() // NOTE/TODO: gasPrice even if set will be set again by the LocalRelayer, we should allow it to be overridden - const tx: TransactionRequest = { + const tx: ethers.providers.TransactionRequest = { from: await walletAddress, to: toAddress, value: ethAmount, @@ -375,16 +325,20 @@ export const tests = async () => { if (beforeWalletDeployed) { assert.equal(txReceipt.to, await wallet.getAddress(), 'recipient is correct') } else { - assert.equal(txReceipt.to, walletContext.guestModule, 'recipient is correct') + assert.equal(txReceipt.to, walletContext[2].guestModule, 'recipient is correct') } // Ensure fromAddress sent their eth const walletBalanceAfter = await signer.getBalance() - assert.true(walletBalanceAfter.sub(walletBalanceBefore).mul(-1).eq(ethAmount), `wallet sent ${ethAmount} eth`) + const sent = walletBalanceAfter.sub(walletBalanceBefore).mul(-1) + console.log('BALANCE BEFOOOOORE', walletBalanceBefore.toString()) + + assert.true(sent.eq(ethAmount), `wallet sent ${sent} eth while expected ${ethAmount}`) // Ensure toAddress received their eth const toBalanceAfter = await provider.getBalance(toAddress) - assert.true(toBalanceAfter.sub(toBalanceBefore).eq(ethAmount), `toAddress received ${ethAmount} eth`) + const received = toBalanceAfter.sub(toBalanceBefore) + assert.true(received.eq(ethAmount), `toAddress received ${received} eth while expected ${ethAmount}`) // Extra checks if (opts.gasLimit) { @@ -407,11 +361,11 @@ export const tests = async () => { const ethAmount1 = ethers.utils.parseEther('1.234') const ethAmount2 = ethers.utils.parseEther('0.456') - const tx1: TransactionRequest = { + const tx1: ethers.providers.TransactionRequest = { to: testAccount.address, value: ethAmount1 } - const tx2: TransactionRequest = { + const tx2: ethers.providers.TransactionRequest = { to: testAccount.address, value: ethAmount2 } @@ -426,7 +380,9 @@ export const tests = async () => { await txnResp.wait() const toBalanceAfter = await provider.getBalance(testAccount.address) - assert.true(toBalanceAfter.sub(toBalanceBefore).mul(1).eq(ethAmount1.add(ethAmount2)), `wallet sent ${ethAmount1} + ${ethAmount2} eth`) + const sent = toBalanceAfter.sub(toBalanceBefore) + const expected = ethAmount1.add(ethAmount2) + assert.true(sent.eq(ethAmount1.add(ethAmount2)), `wallet sent ${sent} eth while expected ${expected} (${ethAmount1} + ${ethAmount2})`) }) await test('sendTransaction batch format 2', async () => { @@ -435,12 +391,12 @@ export const tests = async () => { const ethAmount1 = ethers.utils.parseEther('1.234') const ethAmount2 = ethers.utils.parseEther('0.456') - const tx1: TransactionRequest = { + const tx1: ethers.providers.TransactionRequest = { to: testAccount.address, value: ethAmount1 } - const tx2: TransactionRequest = { + const tx2: ethers.providers.TransactionRequest = { to: testAccount.address, value: ethAmount2 } @@ -451,7 +407,9 @@ export const tests = async () => { await txnResp.wait() const toBalanceAfter = await provider.getBalance(testAccount.address) - assert.true(toBalanceAfter.sub(toBalanceBefore).mul(1).eq(ethAmount1.add(ethAmount2)), `wallet sent ${ethAmount1} + ${ethAmount2} eth`) + const sent = toBalanceAfter.sub(toBalanceBefore) + const expected = ethAmount1.add(ethAmount2) + assert.true(sent.eq(ethAmount1.add(ethAmount2)), `wallet sent ${sent} eth while expected ${expected} (${ethAmount1} + ${ethAmount2})`) }) await test('sendTransaction batch format 3', async () => { @@ -460,31 +418,25 @@ export const tests = async () => { const ethAmount1 = ethers.utils.parseEther('1.234') const ethAmount2 = ethers.utils.parseEther('0.456') - const tx1: Transaction = { + const tx1: commons.transaction.Transaction = { to: testAccount.address, value: ethAmount1 - // data: '0x', - // gasLimit: '0x55555', - // delegateCall: false, - // revertOnError: false } - const tx2: Transaction = { + const tx2: commons.transaction.Transaction = { to: testAccount.address, - value: ethAmount2, - // data: '0x', - // gasLimit: '0x55555', - // delegateCall: false, - // revertOnError: false + value: ethAmount2 } const toBalanceBefore = await provider.getBalance(testAccount.address) - const txnResp = await signer.sendTransactionBatch([tx1, tx2]) + const txnResp = await signer.sendTransactionBatch([tx1, tx2]) await txnResp.wait() const toBalanceAfter = await provider.getBalance(testAccount.address) - assert.true(toBalanceAfter.sub(toBalanceBefore).mul(1).eq(ethAmount1.add(ethAmount2)), `wallet sent ${ethAmount1} + ${ethAmount2} eth`) + const sent = toBalanceAfter.sub(toBalanceBefore) + const expected = ethAmount1.add(ethAmount2) + assert.true(sent.eq(ethAmount1.add(ethAmount2)), `wallet sent ${sent} eth while expected ${expected} (${ethAmount1} + ${ethAmount2})`) }) await test('should reject a transaction response on sendTransactionBatch (at runtime)', async () => { @@ -500,14 +452,11 @@ export const tests = async () => { // NOTE: the account addresses are both chains have been seeded with the same private key // so we can have overlapping addresses and keys for ease of use duringtesting - // get provider of the 2nd chain (the authChain) + // get provider of the 2nd chain const provider2 = wallet.getProvider('hardhat2')! assert.equal(await provider2.getChainId(), 31338, 'provider is the 2nd chain') assert.equal(await provider2.getChainId(), await wallet.getProvider(31338)!.getChainId(), 'provider2 code path check') - const authProvider = await wallet.getAuthProvider() - assert.equal(await provider2.getChainId(), await authProvider.getChainId(), 'provider2 === authProvider') - const signer2 = provider2.getSigner() // confirm all account addresses are the same and correct @@ -587,37 +536,5 @@ export const tests = async () => { const toBalanceAfter = await provider2.getBalance(toAddress) assert.true(toBalanceAfter.sub(toBalanceBefore).eq(ethAmount), `toAddress received ${ethAmount} eth`) } - }) - + }) } - - -// TODO: send coins - -// TODO: send collectible - -// TODO: setup some failure states..? hmm, might be trickier, but maybe could have requestHandler do some faults/other.. - -// TODO: add auth helpers to @0xsequence/auth, and heplers in "commands" - -// -//-------- -// - -// import { sequence} from '@0xsequence' - -// const wallet = new sequence.Wallet() -// wallet.login() - -// wallet.sendETH() -// wallet.signMessage() - -// wallet.sendTransaction(...) - -// const tokens = new sequence.Tokens() - -// tokens.mintCoin(xx) -// tokens.mintCollectible() - -// wallet.sendTransaction(tokens.mintCoin(xx)) -// wallet.sendTransaction(tokens.mintCollectible(xx)) diff --git a/packages/0xsequence/tests/browser/wallet-provider/dapp2.test.ts b/packages/0xsequence/tests/browser/wallet-provider/dapp2.test.ts index bf4e12cf3..ce5e847f9 100644 --- a/packages/0xsequence/tests/browser/wallet-provider/dapp2.test.ts +++ b/packages/0xsequence/tests/browser/wallet-provider/dapp2.test.ts @@ -1,25 +1,31 @@ -import { test, assert } from '../../utils/assert' -import { TypedDataDomain, TypedDataField } from 'ethers' import { Wallet, DefaultProviderConfig } from '@0xsequence/provider' -import { configureLogger, packMessageData } from '@0xsequence/utils' -import { testAccounts, getEOAWallet, deployWalletContext, testWalletContext, sendETH } from '../testutils' +import { context } from '@0xsequence/tests' +import { configureLogger } from '@0xsequence/utils' +import { ethers, TypedDataDomain, TypedDataField } from 'ethers' +import { test, assert } from '../../utils/assert' configureLogger({ logLevel: 'DEBUG', silence: false }) export const tests = async () => { - // - // Deploy Sequence WalletContext (deterministic). We skip deployment - // as we rely on mock-wallet to deploy it. - // - const deployedWalletContext = testWalletContext - console.log('walletContext:', deployedWalletContext) - // // Setup // const providerConfig = { ...DefaultProviderConfig } providerConfig.walletAppURL = 'http://localhost:9999/mock-wallet/mock-wallet.test.html' + // + // Deploy Sequence WalletContext (deterministic). + // + const deployedWalletContext = await (async () => { + const provider1 = new ethers.providers.JsonRpcProvider('http://localhost:8545') + const provider2 = new ethers.providers.JsonRpcProvider('http://localhost:9545') + const signer1 = provider1.getSigner() + const signer2 = provider2.getSigner() + return Promise.all([context.deploySequenceContexts(signer1), context.deploySequenceContexts(signer2)]) + })() + + console.log('walletContext:', deployedWalletContext) + const wallet = new Wallet('hardhat2', providerConfig) // provider + signer, by default if a chainId is not specified it will direct @@ -60,23 +66,10 @@ export const tests = async () => { const networks = await wallet.getNetworks() console.log('=> networks', networks) - // There is exactly one default chain - assert.equal(networks.filter(network => network.isDefaultChain).length, 1, 'there is exactly one default chain') - - // There's exactly one auth chain - assert.equal(networks.filter(network => network.isAuthChain).length, 1, 'there is exactly one auth chain') - - // The default chain's chain ID is 31338 - assert.equal(networks.filter(network => network.isDefaultChain)[0].chainId, 31338, 'default chain id is 31338') - - // The non-default chain's chain ID is 31337 - assert.equal(networks.filter(network => !network.isDefaultChain)[0].chainId, 31337, 'non-default chain id is 31337') - - // The auth chain's chain ID is 31338 - assert.equal(networks.filter(network => network.isAuthChain)[0].chainId, 31338, 'auth chain id is 31338') - - // The non-auth chain's chain ID is 31337 - assert.equal(networks.filter(network => !network.isAuthChain)[0].chainId, 31337, 'non-auth chain id is 31337') + // There should be two chains, hardhat and hardhat2 + assert.equal(networks.length, 2, 'networks length is 2') + assert.equal(networks[0].chainId, 31337, 'chain id match') + assert.equal(networks[1].chainId, 31338, 'chain id match') }) await test('signMessage with our custom defaultChain', async () => { @@ -91,15 +84,6 @@ export const tests = async () => { // validate const isValid = await wallet.utils.isValidMessageSignature(await wallet.getAddress(), message, sig, await signer.getChainId()) assert.true(isValid, 'signMessage sig is valid') - - // recover - const walletConfig = await wallet.utils.recoverWalletConfigFromMessage( - await wallet.getAddress(), - message, - sig, - await signer.getChainId() - ) - assert.equal(walletConfig.address, await wallet.getAddress(), 'signMessage, recovered address ok') }) await test('signTypedData on defaultChain (in this case, hardhat2)', async () => { @@ -128,19 +112,12 @@ export const tests = async () => { const sig = await signer.signTypedData(domain, types, message) assert.equal( sig, - '0x00010001e52e6b56dffd0a295e86eb43e9648de569c44a1036f59030021e9f6e47b581022fe852153d5fa5aa65d243e98f901875ab0a3ad9ea7b3c97fa86ee4886259e331c02', + '0x000200000000000289cf4f28dad9e8062b2f4fdeb0463faa334dd7122ae4b50663e7b840f55f8cef03fd11063e67c4ba1b3266f28e53d647b55a641667f9adbe0adc241e8839bff61b02', 'signature match typed-data dapp' ) // Verify typed data const isValid = await wallet.utils.isValidTypedDataSignature(address, { domain, types, message }, sig, chainId) assert.true(isValid, 'signature is valid - 4') - - // Recover config / address - const walletConfig = await wallet.utils.recoverWalletConfigFromTypedData(address, { domain, types, message }, sig, chainId) - assert.true(walletConfig.address === address, 'recover address - 4') - - const singleSignerAddress = '0x4e37E14f5d5AAC4DF1151C6E8DF78B7541680853' // expected from mock-wallet owner - assert.true(singleSignerAddress === walletConfig.signers[0].address, 'owner address check') }) } diff --git a/packages/0xsequence/tests/browser/window-transport/dapp.test.ts b/packages/0xsequence/tests/browser/window-transport/dapp.test.ts index 1c82c7405..c91a696e6 100644 --- a/packages/0xsequence/tests/browser/window-transport/dapp.test.ts +++ b/packages/0xsequence/tests/browser/window-transport/dapp.test.ts @@ -1,13 +1,9 @@ -import { prefixEIP191Message, WindowMessageProvider } from '@0xsequence/provider' +import { isValidSignature, prefixEIP191Message, WindowMessageProvider } from '@0xsequence/provider' +import { context } from '@0xsequence/tests' +import { configureLogger, encodeMessageDigest, packMessageData } from '@0xsequence/utils' import { ethers } from 'ethers' import { test, assert } from '../../utils/assert' -import { isValidSignature, recoverConfig } from '@0xsequence/wallet' -import { addressOf } from '@0xsequence/config' -import { configureLogger, encodeMessageDigest, packMessageData } from '@0xsequence/utils' - -import { testWalletContext } from '../testutils' - configureLogger({ logLevel: 'DEBUG', silence: false }) const walletProvider = new WindowMessageProvider('http://localhost:9999/mock-wallet/mock-wallet.test.html') @@ -16,7 +12,12 @@ walletProvider.register() // ;(window as any).walletProvider = walletProvider export const tests = async () => { - + const testWalletContext = await (async () => { + const provider = new ethers.providers.JsonRpcProvider('http://localhost:8545') + const signer = provider.getSigner() + return context.deploySequenceContexts(signer) + })() + walletProvider.openWallet() await test('provider opened the wallet', async () => { @@ -32,7 +33,7 @@ export const tests = async () => { const chainId = await signer.getChainId() await test('getAddress', async () => { - assert.equal(address, ethers.utils.getAddress('0xa91Ab3C5390A408DDB4a322510A4290363efcEE9'), 'wallet address') + assert.equal(address, ethers.utils.getAddress('0x0C90b76e8Ca332560f7909dBDB658623919aaA39'), 'wallet address') }) await test('sending a json-rpc request', async () => { @@ -74,7 +75,7 @@ export const tests = async () => { const sig = await signer.signMessage(message) assert.equal( sig, - '0x00010001230f8b68557d982f26234c9c7ce4ff35a449392c1e7cbc9a1129268ce2acea40529252535b1caa300e30d53d5c24009cb6f2fafd0e132944016f9472c1a0cc8b1b02', + '0x0002000000000002dae61fe1d90658f8f4339bd58043b122929cd3f1faaeab38e4daa97b09471170464ebb81bb1957babce03c5fbd0bee815cc61de66d7edaff0d55a4bfbde016e11b02', 'signature match' ) @@ -82,42 +83,12 @@ export const tests = async () => { // Verify the message signature // const messageDigest = encodeMessageDigest(prefixEIP191Message(message)) - const isValid = await isValidSignature(address, messageDigest, sig, provider, testWalletContext, await signer.getChainId()) + const isValid = await isValidSignature(address, messageDigest, sig, provider, testWalletContext) assert.true(isValid, 'signature is valid - 5') // also compute the subDigest of the message, to be provided to the end-user // in order to recover the config properly, the subDigest + sig is required. const subDigest = packMessageData(address, chainId, messageDigest) - - - // - // Recover config / address - // - const walletConfig = await recoverConfig(subDigest, sig) - - const recoveredWalletAddress = addressOf(walletConfig, testWalletContext) - assert.true(recoveredWalletAddress === address, 'recover address - 5') - - const singleSignerAddress = '0x4e37E14f5d5AAC4DF1151C6E8DF78B7541680853' // expected from mock-wallet owner - assert.true(singleSignerAddress === walletConfig.signers[0].address, 'owner address check') - - - // NOTE: below is to verify and recover signature of an EOA account - - // const verifyOut = ethers.utils.verifyMessage(message, sig) - // assert.equal( - // verifyOut, - // address, - // 'verify address match' - // ) - - // const digest = ethers.utils.arrayify(ethers.utils.hashMessage(message)) - // const recoverOut = ethers.utils.recoverAddress(digest, sig) - // assert.equal( - // recoverOut, - // address, - // 'recovered address match' - // ) }) await test('sign EIP712 typed data and validate/recover', async () => { @@ -146,11 +117,6 @@ export const tests = async () => { // Sign the message // const sig = await provider.send('eth_signTypedData', [address, typedData]) - assert.equal( - sig, - '0x00010001c25b59035ea662350e08f41b5087fc49a98b94936826b61a226f97e400c6ce290b8dfa09e3b0df82288fbc599d5b1a023a864bbd876bc67ec1f94c5f2fc4e6101b02', - 'signature match typed-data' - ) // NOTE: verification of message below is identical to verifying a message with eth_sign, // the difference is we have to provide 'message' as the typedData digest format @@ -161,23 +127,11 @@ export const tests = async () => { const messageHash = ethers.utils._TypedDataEncoder.hash(typedData.domain, typedData.types, typedData.message) const messageDigest = ethers.utils.arrayify(messageHash) - const isValid = await isValidSignature(address, messageDigest, sig, provider, testWalletContext, await signer.getChainId()) + const isValid = await isValidSignature(address, messageDigest, sig, provider, testWalletContext) assert.true(isValid, 'signature is valid - 6') // also compute the subDigest of the message, to be provided to the end-user // in order to recover the config properly, the subDigest + sig is required. const subDigest = packMessageData(address, chainId, messageDigest) - - - // - // Recover config / address - // - const walletConfig = await recoverConfig(subDigest, sig) - - const recoveredWalletAddress = addressOf(walletConfig, testWalletContext) - assert.true(recoveredWalletAddress === address, 'recover address - 6') - - const singleSignerAddress = '0x4e37E14f5d5AAC4DF1151C6E8DF78B7541680853' // expected from mock-wallet owner - assert.true(singleSignerAddress === walletConfig.signers[0].address, 'owner address check') }) -} \ No newline at end of file +} diff --git a/packages/abi/src/wallet/erc5719.ts b/packages/abi/src/wallet/erc5719.ts new file mode 100644 index 000000000..6a195a8ec --- /dev/null +++ b/packages/abi/src/wallet/erc5719.ts @@ -0,0 +1,20 @@ + +export const abi = [ + { + "inputs": [ + { + "internalType": "bytes32", + "type": "bytes32" + } + ], + "name": "getAlternativeSignature", + "outputs": [ + { + "internalType": "string", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/packages/abi/src/wallet/erc6492.ts b/packages/abi/src/wallet/erc6492.ts new file mode 100644 index 000000000..69772f8b0 --- /dev/null +++ b/packages/abi/src/wallet/erc6492.ts @@ -0,0 +1,2 @@ + +export const abi = [{"inputs":[{"internalType":"bytes","name":"error","type":"bytes"}],"name":"ERC1271Revert","type":"error"},{"inputs":[{"internalType":"bytes","name":"error","type":"bytes"}],"name":"ERC6492DeployFailed","type":"error"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"},{"internalType":"bytes32","name":"_hash","type":"bytes32"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"isValidSig","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"},{"internalType":"bytes32","name":"_hash","type":"bytes32"},{"internalType":"bytes","name":"_signature","type":"bytes"},{"internalType":"bool","name":"allowSideEffects","type":"bool"},{"internalType":"bool","name":"deployAlreadyDeployed","type":"bool"}],"name":"isValidSigImpl","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"},{"internalType":"bytes32","name":"_hash","type":"bytes32"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"isValidSigNoThrow","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"},{"internalType":"bytes32","name":"_hash","type":"bytes32"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"isValidSigWithSideEffects","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"},{"internalType":"bytes32","name":"_hash","type":"bytes32"},{"internalType":"bytes","name":"_signature","type":"bytes"}],"name":"isValidSigWithSideEffectsNoThrow","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}] diff --git a/packages/abi/src/wallet/index.ts b/packages/abi/src/wallet/index.ts index 5792e2c93..cb9bdf867 100644 --- a/packages/abi/src/wallet/index.ts +++ b/packages/abi/src/wallet/index.ts @@ -1,4 +1,6 @@ +import * as erc5719 from './erc5719' import * as erc1271 from './erc1271' +import * as erc6492 from './erc6492' import * as factory from './factory' import * as mainModule from './mainModule' import * as mainModuleUpgradable from './mainModuleUpgradable' @@ -6,6 +8,8 @@ import * as sequenceUtils from './sequenceUtils' import * as requireFreshSigner from './libs/requireFreshSigners' export const walletContracts = { + erc6492, + erc5719, erc1271, factory, mainModule, diff --git a/packages/config/hardhat.config.js b/packages/account/hardhat.config.js similarity index 66% rename from packages/config/hardhat.config.js rename to packages/account/hardhat.config.js index eaca50531..9e73336b0 100644 --- a/packages/config/hardhat.config.js +++ b/packages/account/hardhat.config.js @@ -1,15 +1,12 @@ -/** - * @type import('hardhat/config').HardhatUserConfig - */ -module.exports = { - solidity: '0.7.6', +module.exports = { networks: { hardhat: { chainId: 31337, + port: 7146, accounts: { mnemonic: 'ripple axis someone ridge uniform wrist prosper there frog rate olympic knee' - } - } + }, + }, } } diff --git a/packages/account/hardhat2.config.js b/packages/account/hardhat2.config.js new file mode 100644 index 000000000..e984fc2e7 --- /dev/null +++ b/packages/account/hardhat2.config.js @@ -0,0 +1,11 @@ + +module.exports = { + networks: { + hardhat: { + chainId: 31338, + accounts: { + mnemonic: 'ripple axis someone ridge uniform wrist prosper there frog rate olympic knee' + } + } + } +} diff --git a/packages/account/package.json b/packages/account/package.json new file mode 100644 index 000000000..767b0c09b --- /dev/null +++ b/packages/account/package.json @@ -0,0 +1,39 @@ +{ + "name": "@0xsequence/account", + "version": "0.43.4", + "description": "tools for migrating sequence wallets to new versions", + "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/account", + "source": "src/index.ts", + "main": "dist/0xsequence-account.cjs.js", + "module": "dist/0xsequence-account.esm.js", + "author": "Horizon Blockchain Games", + "license": "Apache-2.0", + "scripts": { + "test": "pnpm test:concurrently 'pnpm test:run'", + "test:run": "pnpm test:file tests/**/*.spec.ts", + "test:file": "TS_NODE_PROJECT=../../tsconfig.test.json mocha -r ts-node/register --timeout 120000", + "test:concurrently": "concurrently -k --success first 'pnpm start:hardhat2 > /dev/null'", + "start:hardhat2": "hardhat node --hostname 0.0.0.0 --port 7048 --config ./hardhat2.config.js", + "test:coverage": "nyc pnpm test" + }, + "dependencies": { + "@0xsequence/core": "workspace:*", + "@0xsequence/migration": "workspace:*", + "@0xsequence/network": "workspace:*", + "@0xsequence/relayer": "workspace:*", + "@0xsequence/sessions": "workspace:*", + "@0xsequence/utils": "workspace:*", + "@0xsequence/wallet": "workspace:*", + "ethers": "^5.5.2" + }, + "devDependencies": { + "@0xsequence/signhub": "workspace:*", + "@0xsequence/tests": "workspace:*", + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "nyc": "^15.1.0" + }, + "files": [ + "src", + "dist" + ] +} diff --git a/packages/account/src/account.ts b/packages/account/src/account.ts new file mode 100644 index 000000000..511904811 --- /dev/null +++ b/packages/account/src/account.ts @@ -0,0 +1,878 @@ +import { commons, universal } from '@0xsequence/core' +import { migrator, defaults, version } from '@0xsequence/migration' +import { NetworkConfig } from '@0xsequence/network' +import { FeeOption, FeeQuote, isRelayer, Relayer, RpcRelayer } from '@0xsequence/relayer' +import { tracker } from '@0xsequence/sessions' +import { Orchestrator } from '@0xsequence/signhub' +import { encodeTypedDataDigest, getDefaultConnectionInfo } from '@0xsequence/utils' +import { Wallet } from '@0xsequence/wallet' +import { ethers, TypedDataDomain, TypedDataField } from 'ethers' + +export type AccountStatus = { + original: { + version: number + imageHash: string + context: commons.context.WalletContext + } + onChain: { + imageHash: string + config: commons.config.Config + version: number + deployed: boolean + } + fullyMigrated: boolean + signedMigrations: migrator.SignedMigration[] + version: number + presignedConfigurations: tracker.PresignedConfigLink[] + imageHash: string + config: commons.config.Config + checkpoint: ethers.BigNumberish + canOnchainValidate: boolean +} + +export type AccountOptions = { + // The only unique identifier for a wallet is the address + address: string + + // The config tracker keeps track of chained configs, + // counterfactual addresses and reverse lookups for configurations + // it must implement both the ConfigTracker and MigrationTracker + tracker: tracker.ConfigTracker & migrator.PresignedMigrationTracker + + // Versioned contexts contains the context information for each Sequence version + contexts: commons.context.VersionedContext + + // Optional list of migrations, if not provided, the default migrations will be used + // NOTICE: the last vestion is considered the "current" version for the account + migrations?: migrator.Migrations + + // Orchestrator manages signing messages and transactions + orchestrator: Orchestrator + + // Networks information and providers + networks: NetworkConfig[] +} + +export interface PreparedTransactions { + transactions: commons.transaction.SimulatedTransaction[] + flatDecorated: commons.transaction.Transaction[] + feeOptions: FeeOption[] + feeQuote?: FeeQuote +} + +class Chain0Reader implements commons.reader.Reader { + async isDeployed(_wallet: string): Promise { + return false + } + + async implementation(_wallet: string): Promise { + return undefined + } + + async imageHash(_wallet: string): Promise { + return undefined + } + + async nonce(_wallet: string, _space: ethers.BigNumberish): Promise { + return ethers.constants.Zero + } + + async isValidSignature(_wallet: string, _digest: ethers.utils.BytesLike, _signature: ethers.utils.BytesLike): Promise { + throw new Error('Method not supported.') + } +} + +export class Account { + public readonly address: string + + public readonly networks: NetworkConfig[] + public readonly tracker: tracker.ConfigTracker & migrator.PresignedMigrationTracker + public readonly contexts: commons.context.VersionedContext + + public readonly migrator: migrator.Migrator + public readonly migrations: migrator.Migrations + + private orchestrator: Orchestrator + + constructor(options: AccountOptions) { + this.address = ethers.utils.getAddress(options.address) + + this.contexts = options.contexts + this.tracker = options.tracker + this.networks = options.networks + this.orchestrator = options.orchestrator + + this.migrations = options.migrations || defaults.DefaultMigrations + this.migrator = new migrator.Migrator(options.tracker, this.migrations, this.contexts) + } + + static async new(options: { + config: commons.config.SimpleConfig + tracker: tracker.ConfigTracker & migrator.PresignedMigrationTracker + contexts: commons.context.VersionedContext + orchestrator: Orchestrator + networks: NetworkConfig[] + migrations?: migrator.Migrations + }): Promise { + const mig = new migrator.Migrator(options.tracker, options.migrations ?? defaults.DefaultMigrations, options.contexts) + + const lastMigration = mig.lastMigration() + const lastCoder = lastMigration.configCoder + + const config = lastCoder.fromSimple(options.config) + const imageHash = lastCoder.imageHashOf(config) + const context = options.contexts[lastMigration.version] + const address = commons.context.addressOf(context, imageHash) + + await options.tracker.saveCounterfactualWallet({ config, context: Object.values(options.contexts) }) + + return new Account({ + address, + tracker: options.tracker, + contexts: options.contexts, + networks: options.networks, + orchestrator: options.orchestrator, + migrations: options.migrations + }) + } + + getAddress(): Promise { + return Promise.resolve(this.address) + } + + get version(): number { + return this.migrator.lastMigration().version + } + + get coders(): { + signature: commons.signature.SignatureCoder + config: commons.config.ConfigCoder + } { + const lastMigration = this.migrator.lastMigration() + + return { + signature: lastMigration.signatureCoder, + config: lastMigration.configCoder + } + } + + network(chainId: ethers.BigNumberish): NetworkConfig { + const tcid = ethers.BigNumber.from(chainId) + const found = this.networks.find(n => tcid.eq(n.chainId)) + if (!found) throw new Error(`Network not found for chainId ${chainId}`) + return found + } + + provider(chainId: ethers.BigNumberish): ethers.providers.Provider { + const found = this.network(chainId) + if (!found.provider && !found.rpcUrl) throw new Error(`Provider not found for chainId ${chainId}`) + return ( + found.provider || + new ethers.providers.StaticJsonRpcProvider(getDefaultConnectionInfo(found.rpcUrl), { + name: '', + chainId: ethers.BigNumber.from(chainId).toNumber() + }) + ) + } + + reader(chainId: ethers.BigNumberish): commons.reader.Reader { + if (ethers.constants.Zero.eq(chainId)) return new Chain0Reader() + + // TODO: Networks should be able to provide a reader directly + // and we should default to the on-chain reader + return new commons.reader.OnChainReader(this.provider(chainId)) + } + + relayer(chainId: ethers.BigNumberish): Relayer { + const found = this.network(chainId) + if (!found.relayer) throw new Error(`Relayer not found for chainId ${chainId}`) + if (isRelayer(found.relayer)) return found.relayer + return new RpcRelayer(found.relayer) + } + + setOrchestrator(orchestrator: Orchestrator) { + this.orchestrator = orchestrator + } + + contextFor(version: number): commons.context.WalletContext { + const ctx = this.contexts[version] + if (!ctx) throw new Error(`Context not found for version ${version}`) + return ctx + } + + walletForStatus(chainId: ethers.BigNumberish, status: Pick & Pick): Wallet { + const coder = universal.coderFor(status.version) + return this.walletFor(chainId, this.contextFor(status.version), status.config, coder) + } + + walletFor( + chainId: ethers.BigNumberish, + context: commons.context.WalletContext, + config: commons.config.Config, + coders: typeof this.coders + ): Wallet { + const isNetworkZero = ethers.constants.Zero.eq(chainId) + return new Wallet({ + config, + context, + chainId, + coders, + relayer: isNetworkZero ? undefined : this.relayer(chainId), + address: this.address, + orchestrator: this.orchestrator, + reader: this.reader(chainId) + }) + } + + // Get the status of the account on a given network + // this does the following process: + // 1. Get the current on-chain status of the wallet (version + imageHash) + // 2. Get any pending migrations that have been signed by the wallet + // 3. Get any pending configuration updates that have been signed by the wallet + // 4. Fetch reverse lookups for both on-chain and pending configurations + async status(chainId: ethers.BigNumberish, longestPath: boolean = false): Promise { + const isDeployedPromise = this.reader(chainId).isDeployed(this.address) + + const counterfactualImageHashPromise = this.tracker + .imageHashOfCounterfactualWallet({ + wallet: this.address + }) + .then(r => { + if (!r) throw new Error(`Counterfactual imageHash not found for wallet ${this.address}`) + return r + }) + + const counterFactualVersionPromise = counterfactualImageHashPromise.then(r => { + return version.counterfactualVersion(this.address, r.imageHash, Object.values(this.contexts)) + }) + + const onChainVersionPromise = (async () => { + const isDeployed = await isDeployedPromise + if (!isDeployed) return counterFactualVersionPromise + + const implementation = await this.reader(chainId).implementation(this.address) + if (!implementation) throw new Error(`Implementation not found for wallet ${this.address}`) + + const versions = Object.values(this.contexts) + for (let i = 0; i < versions.length; i++) { + if (versions[i].mainModule === implementation || versions[i].mainModuleUpgradable === implementation) { + return versions[i].version + } + } + + throw new Error(`Version not found for implementation ${implementation}`) + })() + + const onChainImageHashPromise = (async () => { + const deployedImageHash = await this.reader(chainId).imageHash(this.address) + if (deployedImageHash) return deployedImageHash + const counterfactualImageHash = await counterfactualImageHashPromise + if (counterfactualImageHash) return counterfactualImageHash.imageHash + throw new Error(`On-chain imageHash not found for wallet ${this.address}`) + })() + + const onChainConfigPromise = (async () => { + const onChainImageHash = await onChainImageHashPromise + const onChainConfig = await this.tracker.configOfImageHash({ imageHash: onChainImageHash }) + if (onChainConfig) return onChainConfig + throw new Error(`On-chain config not found for imageHash ${onChainImageHash}`) + })() + + const onChainVersion = await onChainVersionPromise + const onChainImageHash = await onChainImageHashPromise + + let fromImageHash = onChainImageHash + let lastVersion = onChainVersion + let signedMigrations: migrator.SignedMigration[] = [] + + if (onChainVersion !== this.version) { + // We either need to use the presigned configuration updates, or we haven't performed + // any updates yet, so we can only use the on-chain imageHash as-is + const presignedMigrate = await this.migrator.getAllMigratePresignedTransaction({ + address: this.address, + fromImageHash: onChainImageHash, + fromVersion: onChainVersion, + chainId + }) + + // The migrator returns the original version and imageHash + // if no presigned migration is found, so no need to check here + fromImageHash = presignedMigrate.lastImageHash + lastVersion = presignedMigrate.lastVersion + + signedMigrations = presignedMigrate.signedMigrations + } + + const presigned = await this.tracker.loadPresignedConfiguration({ + wallet: this.address, + fromImageHash: fromImageHash, + longestPath + }) + + const imageHash = presigned && presigned.length > 0 ? presigned[presigned.length - 1].nextImageHash : fromImageHash + const config = await this.tracker.configOfImageHash({ imageHash }) + if (!config) { + throw new Error(`Config not found for imageHash ${imageHash}`) + } + + const isDeployed = await isDeployedPromise + const counterfactualImageHash = await counterfactualImageHashPromise + const checkpoint = universal.coderFor(lastVersion).config.checkpointOf(config as any) + + return { + original: { + ...counterfactualImageHash, + version: await counterFactualVersionPromise + }, + onChain: { + imageHash: onChainImageHash, + config: await onChainConfigPromise, + version: onChainVersion, + deployed: isDeployed + }, + fullyMigrated: lastVersion === this.version, + signedMigrations, + version: lastVersion, + presignedConfigurations: presigned, + imageHash, + config, + checkpoint, + canOnchainValidate: onChainVersion === this.version && isDeployed + } + } + + private mustBeFullyMigrated(status: AccountStatus) { + if (!status.fullyMigrated) { + throw new Error(`Wallet ${this.address} is not fully migrated`) + } + } + + async predecorateTransactions( + txs: commons.transaction.Transactionish, + status: AccountStatus, + chainId: ethers.BigNumberish + ): Promise { + // if onchain wallet config is not up to date + // then we should append an extra transaction that updates it + // to the latest "lazy" state + if (status.onChain.imageHash !== status.imageHash) { + const wallet = this.walletForStatus(chainId, status) + const updateConfig = await wallet.buildUpdateConfigurationTransaction(status.config) + return [Array.isArray(txs) ? txs : [txs], updateConfig.transactions].flat() + } + + return txs + } + + decorateTransactions( + bundle: commons.transaction.IntendedTransactionBundle, + status: AccountStatus + ): commons.transaction.IntendedTransactionBundle { + const bootstrapBundle = this.buildBootstrapTransactions(status, bundle.chainId) + if (bootstrapBundle.transactions.length === 0) { + return bundle + } + + return { + entrypoint: bootstrapBundle.entrypoint, + chainId: bundle.chainId, + intent: bundle.intent, + transactions: [ + ...bootstrapBundle.transactions, + { + to: bundle.entrypoint, + data: commons.transaction.encodeBundleExecData(bundle), + gasLimit: 0, + delegateCall: false, + revertOnError: true, + value: 0 + } + ] + } + } + + decorateSignature( + signature: T, + status: Partial> + ): Promise { + if (!status.presignedConfigurations || status.presignedConfigurations.length === 0) { + return new Promise(resolve => resolve(signature)) + } + + const coder = this.coders.signature + + const chain = status.presignedConfigurations.map(c => c.signature) + const chainedSignature = coder.chainSignatures(signature, chain) + return coder.trim(chainedSignature) + } + + async publishWitness(): Promise { + const digest = ethers.utils.keccak256(ethers.utils.toUtf8Bytes(`This is a Sequence account woo! ${Date.now()}`)) + const signature = await this.signDigest(digest, 0, false) + const decoded = this.coders.signature.decode(signature) + const signatures = this.coders.signature.signaturesOfDecoded(decoded) + return this.tracker.saveWitnesses({ wallet: this.address, digest, chainId: 0, signatures }) + } + + async signDigest( + digest: ethers.BytesLike, + chainId: ethers.BigNumberish, + decorate: boolean = true, + cantValidateBehavior: 'ignore' | 'eip6492' | 'throw' = 'ignore' + ): Promise { + // If we are signing a digest for chainId zero then we can never be fully migrated + // because Sequence v1 doesn't allow for signing a message on "all chains" + + // So we ignore the state on "chain zero" and instead use one of the states of the networks + // wallet-webapp should ensure the wallet is as migrated as possible, trying to mimic + // the behaviour of being migrated on all chains + + const chainRef = ethers.constants.Zero.eq(chainId) ? this.networks[0].chainId : chainId + const status = await this.status(chainRef) + + this.mustBeFullyMigrated(status) + + // Check if we can validate onchain and what to do if we can't + // revert early, since there is no point in signing a digest now + if (!status.canOnchainValidate && cantValidateBehavior === 'throw') { + throw new Error('Wallet cannot validate onchain') + } + + const wallet = this.walletForStatus(chainId, status) + const signature = await wallet.signDigest(digest) + + const decorated = decorate ? this.decorateSignature(signature, status) : signature + + // If the wallet can't validate onchain then we + // need to prefix the decorated signature with all deployments and migrations + // aka doing a bootstrap using EIP-6492 + if (!status.canOnchainValidate) { + switch (cantValidateBehavior) { + // NOTICE: We covered this case before signing the digest + // case 'throw': + // throw new Error('Wallet cannot validate on-chain') + case 'ignore': + return decorated + + case 'eip6492': + return this.buildEIP6492Signature(await decorated, status, chainId) + } + } + + return decorated + } + + private buildEIP6492Signature( + signature: string, + status: AccountStatus, + chainId: ethers.BigNumberish + ): string { + const bootstrapBundle = this.buildBootstrapTransactions(status, chainId) + if (bootstrapBundle.transactions.length === 0) { + throw new Error('Cannot build EIP-6492 signature without bootstrap transactions') + } + + const encoded = ethers.utils.defaultAbiCoder.encode( + ['address', 'bytes', 'bytes'], + [bootstrapBundle.entrypoint, commons.transaction.encodeBundleExecData(bootstrapBundle), signature] + ) + + return ethers.utils.solidityPack( + ['bytes', 'bytes32'], + [encoded, commons.EIP6492.EIP_6492_SUFFIX] + ) + } + + async editConfig(changes: { + add?: commons.config.SimpleSigner[] + remove?: string[] + threshold?: ethers.BigNumberish + }): Promise { + const currentConfig = await this.status(0).then(s => s.config) + const newConfig = this.coders.config.editConfig(currentConfig, { + ...changes, + checkpoint: this.coders.config.checkpointOf(currentConfig).add(1) + }) + + return this.updateConfig(newConfig) + } + + async updateConfig(config: commons.config.Config): Promise { + // config should be for the current version of the wallet + if (!this.coders.config.isWalletConfig(config)) { + throw new Error(`Invalid config for wallet ${this.address}`) + } + + const nextImageHash = this.coders.config.imageHashOf(config) + + // sign an update config struct + const updateStruct = this.coders.signature.hashSetImageHash(nextImageHash) + + // sign the update struct, using chain id 0 + const signature = await this.signDigest(updateStruct, 0, false) + + // save the presigned transaction to the sessions tracker + await this.tracker.savePresignedConfiguration({ + wallet: this.address, + nextConfig: config, + signature + }) + + // safety check, tracker should have a reverse lookup for the imageHash + // outside of the local cache + const reverseConfig = await this.tracker.configOfImageHash({ + imageHash: nextImageHash, + noCache: true + }) + + if (!reverseConfig || this.coders.config.imageHashOf(reverseConfig) !== nextImageHash) { + throw Error(`Reverse lookup failed for imageHash ${nextImageHash}`) + } + } + + /** + * This method is used to bootstrap the wallet on a given chain. + * this deploys the wallets and executes all the necessary transactions + * for that wallet to start working with the given version. + * + * This usually involves: (a) deploying the wallet, (b) executing migrations + * + * Notice: It should NOT explicitly include chained signatures. Unless internally used + * by any of the migrations. + * + */ + buildBootstrapTransactions(status: AccountStatus, chainId: ethers.BigNumberish): commons.transaction.IntendedTransactionBundle { + const transactions: commons.transaction.Transaction[] = [] + + // Add wallet deployment if needed + if (!status.onChain.deployed) { + // Wallet deployment will vary depending on the version + // so we need to use the context to get the correct deployment + const deployTransaction = Wallet.buildDeployTransaction(status.original.context, status.original.imageHash) + + transactions.push(...deployTransaction.transactions) + } + + // Get pending migrations + transactions.push( + ...status.signedMigrations.map(m => ({ + to: m.tx.entrypoint, + data: commons.transaction.encodeBundleExecData(m.tx), + value: 0, + gasLimit: 0, + revertOnError: true, + delegateCall: false + })) + ) + + // Build the transaction intent, if the transaction has migrations + // then we should use one of the intents of the migrations (anyone will do) + // if it doesn't, then the only intent we could use if the GuestModule one + // ... but this may fail if the relayer uses a different GuestModule + const id = + status.signedMigrations.length > 0 + ? status.signedMigrations[0].tx.intent.id + : commons.transaction.subdigestOfGuestModuleTransactions(this.contexts[this.version].guestModule, chainId, transactions) + + // Everything is encoded as a bundle + // using the GuestModule of the account version + const { guestModule } = this.contextFor(status.version) + return { entrypoint: guestModule, transactions, chainId, intent: { id, wallet: this.address } } + } + + async bootstrapTransactions( + chainId: ethers.BigNumberish, + prestatus?: AccountStatus + ): Promise> { + const status = prestatus || (await this.status(chainId)) + return this.buildBootstrapTransactions(status, chainId) + } + + async doBootstrap(chainId: ethers.BigNumberish, feeQuote?: FeeQuote, prestatus?: AccountStatus) { + const bootstrapTxs = await this.bootstrapTransactions(chainId, prestatus) + return this.relayer(chainId).relay({ ...bootstrapTxs, chainId }, feeQuote) + } + + signMessage( + message: ethers.BytesLike, + chainId: ethers.BigNumberish, + cantValidateBehavior: 'ignore' | 'eip6492' | 'throw' = 'ignore' + ): Promise { + return this.signDigest(ethers.utils.keccak256(message), chainId, true, cantValidateBehavior) + } + + async signTransactions( + txs: commons.transaction.Transactionish, + chainId: ethers.BigNumberish, + pstatus?: AccountStatus + ): Promise { + const status = pstatus || (await this.status(chainId)) + this.mustBeFullyMigrated(status) + + const wallet = this.walletForStatus(chainId, status) + const signed = await wallet.signTransactions(txs) + + return { + ...signed, + signature: await this.decorateSignature(signed.signature, status) + } + } + + async signMigrations( + chainId: ethers.BigNumberish, + editConfig: (prevConfig: commons.config.Config) => commons.config.Config + ): Promise { + const status = await this.status(chainId) + if (status.fullyMigrated) return false + + const wallet = this.walletForStatus(chainId, status) + const nextConfig = editConfig(wallet.config) + const signed = await this.migrator.signNextMigration(this.address, status.version, wallet, nextConfig) + if (!signed) return false + + // Make sure the tracker has a copy of the config + // before attempting to save the migration + // otherwise if this second step fails the tracker could end up + // with a migration to an unknown config + await this.tracker.saveWalletConfig({ config: nextConfig }) + const nextCoder = universal.coderFor(nextConfig.version).config + const nextImageHash = nextCoder.imageHashOf(nextConfig as any) + const reverseConfig = await this.tracker.configOfImageHash({ imageHash: nextImageHash, noCache: true }) + if (!reverseConfig || nextCoder.imageHashOf(reverseConfig as any) !== nextImageHash) { + throw Error(`Reverse lookup failed for imageHash ${nextImageHash}`) + } + + await this.tracker.saveMigration(this.address, signed, this.contexts) + + return true + } + + async signAllMigrations( + editConfig: (prevConfig: commons.config.Config) => commons.config.Config + ): Promise<{ signedMigrations: Array; failedChains: number[] }> { + const failedChains: number[] = [] + const signedMigrations = await Promise.all( + this.networks.map(async n => { + try { + // Signing migrations for each chain + return await this.signMigrations(n.chainId, editConfig) + } catch (error) { + console.warn(`Failed to sign migrations for chain ${n.chainId}`, error) + + // Adding failed chainId to the failedChains array + failedChains.push(n.chainId) + // Using null as a placeholder for failed chains + return null + } + }) + ) + + // Filter out null values to get only the successful signed migrations + const successfulSignedMigrations = signedMigrations.filter(migration => migration !== null) + + return { signedMigrations: successfulSignedMigrations, failedChains } + } + + async isMigratedAllChains(): Promise<{ migratedAllChains: boolean; failedChains: number[] }> { + const failedChains: number[] = [] + const statuses = await Promise.all( + this.networks.map(async n => { + try { + return await this.status(n.chainId) + } catch (error) { + failedChains.push(n.chainId) + + console.warn(`Failed to get status for chain ${n.chainId}`, error) + + // default to true for failed chains + return { fullyMigrated: true } + } + }) + ) + + const migratedAllChains = statuses.every(s => s.fullyMigrated) + return { migratedAllChains, failedChains } + } + + async sendSignedTransactions( + signedBundle: commons.transaction.IntendedTransactionBundle, + chainId: ethers.BigNumberish, + quote?: FeeQuote, + pstatus?: AccountStatus + ): Promise { + const status = pstatus || (await this.status(signedBundle.chainId)) + this.mustBeFullyMigrated(status) + + const decoratedBundle = this.decorateTransactions(signedBundle, status) + + return this.relayer(chainId).relay(decoratedBundle, quote) + } + + async fillGasLimits( + txs: commons.transaction.Transactionish, + chainId: ethers.BigNumberish, + status?: AccountStatus + ): Promise { + const wallet = this.walletForStatus(chainId, status || (await this.status(chainId))) + return wallet.fillGasLimits(txs) + } + + async gasRefundQuotes( + txs: commons.transaction.Transactionish, + chainId: ethers.BigNumberish, + stubSignatureOverrides: Map, + status?: AccountStatus + ): Promise<{ + options: FeeOption[] + quote?: FeeQuote + decorated: commons.transaction.IntendedTransactionBundle + }> { + const wstatus = status || (await this.status(chainId)) + const wallet = this.walletForStatus(chainId, wstatus) + + const predecorated = await this.predecorateTransactions(txs, wstatus, chainId) + const transactions = commons.transaction.fromTransactionish(this.address, predecorated) + + // We can't sign the transactions (because we don't want to bother the user) + // so we use the latest configuration to build a "stub" signature, the relayer + // knows to ignore the wallet signatures + const stubSignature = wallet.coders.config.buildStubSignature(wallet.config, stubSignatureOverrides) + + // Now we can decorate the transactions as always, but we need to manually build the signed bundle + const intentId = ethers.utils.hexlify(ethers.utils.randomBytes(32)) + const signedBundle: commons.transaction.SignedTransactionBundle = { + chainId, + intent: { + id: intentId, + wallet: this.address + }, + signature: stubSignature, + transactions, + entrypoint: this.address, + nonce: 0 // The relayer also ignored the nonce + } + + const decoratedBundle = this.decorateTransactions(signedBundle, wstatus) + const data = commons.transaction.encodeBundleExecData(decoratedBundle) + const res = await this.relayer(chainId).getFeeOptionsRaw(decoratedBundle.entrypoint, data) + return { ...res, decorated: decoratedBundle } + } + + async prepareTransactions(args: { + txs: commons.transaction.Transactionish + chainId: ethers.BigNumberish + stubSignatureOverrides: Map + }): Promise { + const status = await this.status(args.chainId) + + const transactions = await this.fillGasLimits(args.txs, args.chainId, status) + const gasRefundQuote = await this.gasRefundQuotes(transactions, args.chainId, args.stubSignatureOverrides, status) + const flatDecorated = commons.transaction.unwind(this.address, gasRefundQuote.decorated.transactions) + + return { + transactions, + flatDecorated, + feeOptions: gasRefundQuote.options, + feeQuote: gasRefundQuote.quote + } + } + + async sendTransaction( + txs: commons.transaction.Transactionish, + chainId: ethers.BigNumberish, + quote?: FeeQuote, + skipPreDecorate: boolean = false, + callback?: (signed: commons.transaction.SignedTransactionBundle) => void + ): Promise { + const status = await this.status(chainId) + const predecorated = skipPreDecorate ? txs : await this.predecorateTransactions(txs, status, chainId) + const signed = await this.signTransactions(predecorated, chainId) + if (callback) callback(signed) + return this.sendSignedTransactions(signed, chainId, quote) + } + + async signTypedData( + domain: TypedDataDomain, + types: Record>, + message: Record, + chainId: ethers.BigNumberish, + cantValidateBehavior: 'ignore' | 'eip6492' | 'throw' = 'ignore' + ): Promise { + const digest = encodeTypedDataDigest({ domain, types, message }) + return this.signDigest(digest, chainId, true, cantValidateBehavior) + } + + async getAllSigners(): Promise< + { + address: string + weight: number + network: number + flaggedForRemoval: boolean + }[] + > { + const allSigners: { + address: string + weight: number + network: number + flaggedForRemoval: boolean + }[] = [] + + // We need to get the signers for each status + await Promise.all( + this.networks.map(async network => { + const chainId = network.chainId + + // Getting the status with `longestPath` set to true will give us all the possible configurations + // between the current onChain config and the latest config, including the ones "flagged for removal" + const status = await this.status(chainId, true) + + const fullChain = [ + status.onChain.imageHash, + ...(status.onChain.version !== status.version + ? status.signedMigrations.map(m => universal.coderFor(m.toVersion).config.imageHashOf(m.toConfig as any)) + : []), + ...status.presignedConfigurations.map(update => update.nextImageHash) + ] + + return Promise.all( + fullChain.map(async (nextImageHash, iconf) => { + const isLast = iconf === fullChain.length - 1 + const config = await this.tracker.configOfImageHash({ imageHash: nextImageHash }) + + if (!config) { + console.warn(`AllSigners may be incomplete, config not found for imageHash ${nextImageHash}`) + return + } + + const coder = universal.genericCoderFor(config.version) + const signers = coder.config.signersOf(config) + + signers.forEach(signer => { + const exists = allSigners.find(s => s.address === signer.address && s.network === chainId) + + if (exists && isLast && exists.flaggedForRemoval) { + exists.flaggedForRemoval = false + return + } + + if (exists) return + + allSigners.push({ + address: signer.address, + weight: signer.weight, + network: chainId, + flaggedForRemoval: !isLast + }) + }) + }) + ) + }) + ) + + return allSigners + } +} + +export function isAccount(value: any): value is Account { + return value instanceof Account +} diff --git a/packages/account/src/index.ts b/packages/account/src/index.ts new file mode 100644 index 000000000..9b616a960 --- /dev/null +++ b/packages/account/src/index.ts @@ -0,0 +1,2 @@ + +export * from './account' diff --git a/packages/account/tests/account.spec.ts b/packages/account/tests/account.spec.ts new file mode 100644 index 000000000..1a84bf18b --- /dev/null +++ b/packages/account/tests/account.spec.ts @@ -0,0 +1,1124 @@ + +import hardhat from 'hardhat' +import * as chai from 'chai' +import * as utils from '@0xsequence/tests' + +import { ethers } from 'ethers' +import { Orchestrator } from '@0xsequence/signhub' +import { Account } from '../src/account' +import { migrator } from '@0xsequence/migration' +import { NetworkConfig } from '@0xsequence/network' +import { tracker, trackers } from '@0xsequence/sessions' +import { LocalRelayer, Relayer } from '@0xsequence/relayer' +import { commons, v1, v2 } from '@0xsequence/core' +import chaiAsPromised from 'chai-as-promised' +import { Wallet } from '@0xsequence/wallet' +import { encodeBundleExecData } from '@0xsequence/core/src/commons/transaction' + +const { expect } = chai.use(chaiAsPromised) + +const deterministic = false + +describe('Account', () => { + let provider1: ethers.providers.JsonRpcProvider + let provider2: ethers.providers.JsonRpcProvider + + let signer1: ethers.Signer + let signer2: ethers.Signer + + let contexts: commons.context.VersionedContext + let networks: NetworkConfig[] + + let tracker: tracker.ConfigTracker & migrator.PresignedMigrationTracker + + let defaultArgs: { + contexts: commons.context.VersionedContext + networks: NetworkConfig[] + tracker: tracker.ConfigTracker & migrator.PresignedMigrationTracker + } + + before(async () => { + provider1 = new ethers.providers.Web3Provider(hardhat.network.provider.send) + provider2 = new ethers.providers.JsonRpcProvider('http://127.0.0.1:7048') + + // TODO: Implement migrations on local config tracker + tracker = new trackers.local.LocalConfigTracker(provider1) as any + + networks = [{ + chainId: 31337, + name: 'hardhat', + provider: provider1, + rpcUrl: "", + relayer: new LocalRelayer(provider1.getSigner()) + }, { + chainId: 31338, + name: 'hardhat2', + provider: provider2, + rpcUrl: 'http://127.0.0.1:7048', + relayer: new LocalRelayer(provider2.getSigner()) + }] + + signer1 = provider1.getSigner() + signer2 = provider2.getSigner() + + contexts = await utils.context.deploySequenceContexts(signer1) + const context2 = await utils.context.deploySequenceContexts(signer2) + + expect(contexts).to.deep.equal(context2) + + defaultArgs = { + contexts, + networks, + tracker, + } + }) + + describe('New account', () => { + it('Should create a new account', async () => { + const signer = randomWallet('Should create a new account') + const config = { + threshold: 1, + checkpoint: Math.floor(now() / 1000), + signers: [{ address: signer.address, weight: 1 }] + } + + const account = await Account.new({ + ...defaultArgs, + config, + orchestrator: new Orchestrator([signer]), + }) + + expect(account).to.be.instanceOf(Account) + expect(account.address).to.not.be.undefined + + await account.sendTransaction([], networks[0].chainId) + + const status = await account.status(networks[0].chainId) + expect(status.fullyMigrated).to.be.true + expect(status.onChain.deployed).to.be.true + expect(status.onChain.version).to.equal(2) + }) + + it('Should send transactions on multiple networks', async () => { + const signer = randomWallet('Should send transactions on multiple networks') + const config = { + threshold: 1, + checkpoint: Math.floor(now() / 1000), + signers: [{ address: signer.address, weight: 1 }] + } + + const account = await Account.new({ + ...defaultArgs, + config, + orchestrator: new Orchestrator([signer]), + }) + + await account.sendTransaction([], networks[0].chainId) + await account.sendTransaction([], networks[1].chainId) + + const status1 = await account.status(networks[0].chainId) + const status2 = await account.status(networks[1].chainId) + + expect(status1.fullyMigrated).to.be.true + expect(status1.onChain.deployed).to.be.true + expect(status1.onChain.version).to.equal(2) + + expect(status2.fullyMigrated).to.be.true + expect(status2.onChain.deployed).to.be.true + expect(status2.onChain.version).to.equal(2) + }) + + it('Should create a new account with many signers', async () => { + const signers = new Array(24).fill(0).map(() => randomWallet('Should create a new account with many signers')) + const config = { + threshold: 3, + checkpoint: Math.floor(now() / 1000), + signers: signers.map((signer) => ({ + address: signer.address, weight: 1 + })) + } + + const rsigners = signers.sort(() => randomFraction('Should create a new account with many signers 2') - 0.5) + const account = await Account.new({ + ...defaultArgs, + config, + orchestrator: new Orchestrator(rsigners.slice(0, 4)), + }) + + await account.sendTransaction([], networks[0].chainId) + + const status = await account.status(networks[0].chainId) + expect(status.fullyMigrated).to.be.true + expect(status.onChain.deployed).to.be.true + expect(status.onChain.version).to.equal(2) + }) + + it('Should sign and validate a message', async () => { + const signer = randomWallet('Should sign and validate a message') + const config = { + threshold: 1, + checkpoint: Math.floor(now() / 1000), + signers: [{ address: signer.address, weight: 1 }] + } + + const account = await Account.new({ + ...defaultArgs, + config, + orchestrator: new Orchestrator([signer]), + }) + + await account.doBootstrap(networks[0].chainId) + + const msg = ethers.utils.toUtf8Bytes('Hello World') + const sig = await account.signMessage(msg, networks[0].chainId) + + const valid = await commons.EIP1271.isValidEIP1271Signature( + account.address, + ethers.utils.keccak256(msg), + sig, + networks[0].provider! + ) + + expect(valid).to.be.true + }) + + it('Should update account to new configuration', async () => { + const signer = randomWallet('Should update account to new configuration') + const simpleConfig1 = { + threshold: 1, + checkpoint: Math.floor(now() / 1000), + signers: [{ address: signer.address, weight: 1 }] + } + const config1 = v2.config.ConfigCoder.fromSimple(simpleConfig1) + + const account = await Account.new({ + ...defaultArgs, + config: simpleConfig1, + orchestrator: new Orchestrator([signer]), + }) + + const signer2a = randomWallet('Should update account to new configuration 2') + const signer2b = randomWallet('Should update account to new configuration 3') + + const simpleConfig2 = { + threshold: 4, + checkpoint: Math.floor(now() / 1000) + 1, + signers: [{ + address: signer2a.address, + weight: 2 + }, { + address: signer2b.address, + weight: 2 + }] + } + + const config2 = v2.config.ConfigCoder.fromSimple(simpleConfig2) + await account.updateConfig(config2) + + const status2 = await account.status(networks[0].chainId) + expect(status2.fullyMigrated).to.be.true + expect(status2.onChain.deployed).to.be.false + expect(status2.onChain.version).to.equal(2) + expect(status2.onChain.imageHash).to.deep.equal(v2.config.ConfigCoder.imageHashOf(config1)) + expect(status2.imageHash).to.deep.equal(v2.config.ConfigCoder.imageHashOf(config2)) + }) + + it('Should sign and validate a message without being deployed', async () => { + const signer = randomWallet('Should sign and validate a message without being deployed') + const config = { + threshold: 1, + checkpoint: Math.floor(now() / 1000), + signers: [{ address: signer.address, weight: 1 }] + } + + const account = await Account.new({ + ...defaultArgs, + config, + orchestrator: new Orchestrator([signer]), + }) + + const msg = ethers.utils.toUtf8Bytes('Hello World') + const sig = await account.signMessage(msg, networks[0].chainId, 'eip6492') + + const valid = await account.reader(networks[0].chainId).isValidSignature( + account.address, + ethers.utils.keccak256(msg), + sig + ) + + expect(valid).to.be.true + }) + + it('Should refuse to sign when not deployed', async () => { + const signer = randomWallet('Should refuse to sign when not deployed') + const config = { + threshold: 1, + checkpoint: Math.floor(now() / 1000), + signers: [{ address: signer.address, weight: 1 }] + } + + const account = await Account.new({ + ...defaultArgs, + config, + orchestrator: new Orchestrator([signer]), + }) + + const msg = ethers.utils.toUtf8Bytes('Hello World') + const sig = account.signMessage(msg, networks[0].chainId, 'throw') + + expect(sig).to.be.rejected + }) + + describe('After upgrading', () => { + let account: Account + + let signer1: ethers.Wallet + let signer2a: ethers.Wallet + let signer2b: ethers.Wallet + let signerIndex = 1 + + beforeEach(async () => { + signer1 = randomWallet(`After upgrading ${signerIndex++}`) + const simpleConfig1 = { + threshold: 1, + checkpoint: Math.floor(now() / 1000) + 1, + signers: [{ address: signer1.address, weight: 1 }] + } + + account = await Account.new({ + ...defaultArgs, + config: simpleConfig1, + orchestrator: new Orchestrator([signer1]), + }) + + signer2a = randomWallet(`After upgrading ${signerIndex++}`) + signer2b = randomWallet(`After upgrading ${signerIndex++}`) + + const simpleConfig2 = { + threshold: 4, + checkpoint: await account.status(0).then((s) => ethers.BigNumber.from(s.checkpoint).add(1)), + signers: [{ + address: signer2a.address, + weight: 2 + }, { + address: signer2b.address, + weight: 2 + }] + } + + const config2 = v2.config.ConfigCoder.fromSimple(simpleConfig2) + await account.updateConfig(config2) + account.setOrchestrator(new Orchestrator([signer2a, signer2b])) + }) + + it('Should send a transaction', async () => { + const tx = await account.sendTransaction([], networks[0].chainId) + expect(tx).to.not.be.undefined + + const status = await account.status(networks[0].chainId) + expect(status.fullyMigrated).to.be.true + expect(status.onChain.deployed).to.be.true + expect(status.onChain.imageHash).to.equal(status.imageHash) + }) + + it('Should sign a message', async () => { + const msg = ethers.utils.toUtf8Bytes('Hello World') + const sig = await account.signMessage(msg, networks[0].chainId) + + const canOnchainValidate = await account.status(networks[0].chainId).then((s) => s.canOnchainValidate) + expect(canOnchainValidate).to.be.false + await account.doBootstrap(networks[0].chainId) + + const valid = await commons.EIP1271.isValidEIP1271Signature( + account.address, + ethers.utils.keccak256(msg), + sig, + networks[0].provider! + ) + + expect(valid).to.be.true + }) + + it('Should fail to use old signer', async () => { + account.setOrchestrator(new Orchestrator([signer1])) + const tx = account.sendTransaction([], networks[0].chainId) + await expect(tx).to.be.rejected + }) + + it('Should send a transaction on a different network', async () => { + const tx = await account.sendTransaction([], networks[1].chainId) + expect(tx).to.not.be.undefined + + const status = await account.status(networks[1].chainId) + expect(status.fullyMigrated).to.be.true + expect(status.onChain.deployed).to.be.true + expect(status.onChain.imageHash).to.equal(status.imageHash) + }) + + describe('After reloading the account', () => { + beforeEach(async () => { + account = new Account({ + ...defaultArgs, + address: account.address, + orchestrator: new Orchestrator([signer2a, signer2b]) + }) + }) + + it('Should send a transaction', async () => { + const tx = await account.sendTransaction([], networks[0].chainId) + expect(tx).to.not.be.undefined + + const status = await account.status(networks[0].chainId) + expect(status.fullyMigrated).to.be.true + expect(status.onChain.deployed).to.be.true + expect(status.onChain.imageHash).to.equal(status.imageHash) + }) + + it('Should sign a message', async () => { + const msg = ethers.utils.toUtf8Bytes('Hello World') + const sig = await account.signMessage(msg, networks[0].chainId) + + const canOnchainValidate = await account.status(networks[0].chainId).then((s) => s.canOnchainValidate) + expect(canOnchainValidate).to.be.false + await account.doBootstrap(networks[0].chainId) + + const valid = await commons.EIP1271.isValidEIP1271Signature( + account.address, + ethers.utils.keccak256(msg), + sig, + networks[0].provider! + ) + + expect(valid).to.be.true + }) + }) + + describe('After updating the config again', () => { + let signer3a: ethers.Wallet + let signer3b: ethers.Wallet + let signer3c: ethers.Wallet + let signerIndex = 1 + + let config3: v2.config.WalletConfig + + beforeEach(async () => { + signer3a = randomWallet(`After updating the config again ${signerIndex++}`) + signer3b = randomWallet(`After updating the config again ${signerIndex++}`) + signer3c = randomWallet(`After updating the config again ${signerIndex++}`) + + const simpleConfig3 = { + threshold: 5, + checkpoint: await account.status(0).then((s) => ethers.BigNumber.from(s.checkpoint).add(1)), + signers: [{ + address: signer3a.address, + weight: 2 + }, { + address: signer3b.address, + weight: 2 + }, { + address: signer3c.address, + weight: 1 + }] + } + + config3 = v2.config.ConfigCoder.fromSimple(simpleConfig3) + + await account.updateConfig(config3) + account.setOrchestrator(new Orchestrator([signer3a, signer3b, signer3c])) + }) + + it('Should update account status', async () => { + const status = await account.status(networks[0].chainId) + expect(status.fullyMigrated).to.be.true + expect(status.onChain.deployed).to.be.false + expect(status.imageHash).to.equal(v2.config.ConfigCoder.imageHashOf(config3)) + expect(status.presignedConfigurations.length).to.equal(2) + }) + + it('Should send a transaction', async () => { + const tx = await account.sendTransaction([], networks[0].chainId) + expect(tx).to.not.be.undefined + + const status = await account.status(networks[0].chainId) + expect(status.fullyMigrated).to.be.true + expect(status.onChain.deployed).to.be.true + expect(status.onChain.imageHash).to.equal(status.imageHash) + }) + + it('Should sign a message', async () => { + const msg = ethers.utils.toUtf8Bytes('Hello World') + const sig = await account.signMessage(msg, networks[0].chainId) + + const canOnchainValidate = await account.status(networks[0].chainId).then((s) => s.canOnchainValidate) + expect(canOnchainValidate).to.be.false + await account.doBootstrap(networks[0].chainId) + + const status = await account.status(networks[0].chainId) + expect(status.onChain.imageHash).to.not.equal(status.imageHash) + + const valid = await commons.EIP1271.isValidEIP1271Signature( + account.address, + ethers.utils.keccak256(msg), + sig, + networks[0].provider! + ) + + expect(valid).to.be.true + }) + }) + + describe('After sending a transaction', () => { + beforeEach(async () => { + await account.sendTransaction([], networks[0].chainId) + }) + + it('Should send a transaction in a different network', async () => { + const tx = await account.sendTransaction([], networks[1].chainId) + expect(tx).to.not.be.undefined + + const status = await account.status(networks[1].chainId) + expect(status.fullyMigrated).to.be.true + expect(status.onChain.deployed).to.be.true + expect(status.onChain.imageHash).to.equal(status.imageHash) + }) + + it('Should send a second transaction', async () => { + const tx = await account.sendTransaction([], networks[0].chainId) + expect(tx).to.not.be.undefined + }) + + let signerIndex = 1 + it('Should update the configuration again', async () => { + const signer2a = randomWallet(`Should update the configuration again ${signerIndex++}`) + const signer2b = randomWallet(`Should update the configuration again ${signerIndex++}`) + const signer2c = randomWallet(`Should update the configuration again ${signerIndex++}`) + + const simpleConfig2 = { + threshold: 6, + checkpoint: await account.status(0).then((s) => ethers.BigNumber.from(s.checkpoint).add(1)), + signers: [{ + address: signer2a.address, + weight: 3 + }, { + address: signer2b.address, + weight: 3 + }, { + address: signer2c.address, + weight: 3 + }] + } + + const ogOnchainImageHash = await account.status(0).then((s) => s.onChain.imageHash) + const imageHash1 = await account.status(0).then((s) => s.imageHash) + + const config2 = v2.config.ConfigCoder.fromSimple(simpleConfig2) + await account.updateConfig(config2) + + const status1 = await account.status(networks[0].chainId) + const status2 = await account.status(networks[1].chainId) + + expect(status1.fullyMigrated).to.be.true + expect(status1.onChain.deployed).to.be.true + expect(status1.onChain.imageHash).to.equal(imageHash1) + expect(status1.imageHash).to.equal(v2.config.ConfigCoder.imageHashOf(config2)) + expect(status1.presignedConfigurations.length).to.equal(1) + + expect(status2.fullyMigrated).to.be.true + expect(status2.onChain.deployed).to.be.false + expect(status2.onChain.imageHash).to.equal(ogOnchainImageHash) + expect(status2.imageHash).to.equal(v2.config.ConfigCoder.imageHashOf(config2)) + expect(status2.presignedConfigurations.length).to.equal(2) + }) + }) + }) + }) + + describe('Migrated wallet', () => { + it('Should migrate undeployed account', async () => { + // Old account may be an address that's not even deployed + const signer1 = randomWallet('Should migrate undeployed account') + + const simpleConfig = { + threshold: 1, + checkpoint: 0, + signers: [{ + address: signer1.address, + weight: 1 + }] + } + + const config = v1.config.ConfigCoder.fromSimple(simpleConfig) + const configv2 = v2.config.ConfigCoder.fromSimple(simpleConfig) + + const imageHash = v1.config.ConfigCoder.imageHashOf(config) + const address = commons.context.addressOf(contexts[1], imageHash) + + // Sessions server MUST have information about the old wallet + // in production this is retrieved from SequenceUtils contract + await tracker.saveCounterfactualWallet({ config, context: [contexts[1]] }) + + // Importing the account should work! + const account = new Account({ ...defaultArgs, address, orchestrator: new Orchestrator([signer1]) }) + + const status = await account.status(0) + expect(status.fullyMigrated).to.be.false + expect(status.onChain.deployed).to.be.false + expect(status.onChain.imageHash).to.equal(imageHash) + expect(status.imageHash).to.equal(imageHash) + expect(status.version).to.equal(1) + + // Sending a transaction should fail (not fully migrated) + await expect(account.sendTransaction([], networks[0].chainId)).to.be.rejected + + // Should sign migration using the account + await account.signAllMigrations((c) => c) + + const status2 = await account.status(networks[0].chainId) + expect(status2.fullyMigrated).to.be.true + expect(status2.onChain.deployed).to.be.false + expect(status2.onChain.imageHash).to.equal(imageHash) + expect(status2.onChain.version).to.equal(1) + expect(status2.imageHash).to.equal(v2.config.ConfigCoder.imageHashOf(configv2)) + expect(status2.version).to.equal(2) + + // Send a transaction + const tx = await account.sendTransaction([], networks[0].chainId) + expect(tx).to.not.be.undefined + + const status3 = await account.status(networks[0].chainId) + expect(status3.fullyMigrated).to.be.true + expect(status3.onChain.deployed).to.be.true + expect(status3.onChain.imageHash).to.equal(v2.config.ConfigCoder.imageHashOf(configv2)) + expect(status3.onChain.version).to.equal(2) + expect(status3.imageHash).to.equal(v2.config.ConfigCoder.imageHashOf(configv2)) + expect(status3.version).to.equal(2) + + // Send another transaction on another chain + const tx2 = await account.sendTransaction([], networks[1].chainId) + expect(tx2).to.not.be.undefined + + const status4 = await account.status(networks[1].chainId) + expect(status4.fullyMigrated).to.be.true + expect(status4.onChain.deployed).to.be.true + expect(status4.onChain.imageHash).to.equal(v2.config.ConfigCoder.imageHashOf(configv2)) + expect(status4.onChain.version).to.equal(2) + expect(status4.imageHash).to.equal(v2.config.ConfigCoder.imageHashOf(configv2)) + expect(status4.version).to.equal(2) + }) + + it('Should migrate a half-deployed account', async () => { + // Old account created with 3 signers, and already deployed + // in one of the chains + const signer1 = randomWallet('Should migrate a half-deployed account') + const signer2 = randomWallet('Should migrate a half-deployed account 2') + const signer3 = randomWallet('Should migrate a half-deployed account 3') + + const simpleConfig = { + threshold: 2, + checkpoint: 0, + signers: [{ + address: signer1.address, + weight: 1 + }, { + address: signer2.address, + weight: 1 + }, { + address: signer3.address, + weight: 1 + }] + } + + const config = v1.config.ConfigCoder.fromSimple(simpleConfig) + const imageHash = v1.config.ConfigCoder.imageHashOf(config) + const address = commons.context.addressOf(contexts[1], imageHash) + + // Deploy the wallet on network 0 + const deployTx = Wallet.buildDeployTransaction(contexts[1], imageHash) + await (networks[0].relayer! as Relayer).relay({ + ...deployTx, + chainId: networks[0].chainId, + intent: { + id: '0x00', + wallet: address + } + }) + + // Feed all information to sequence-sessions + // (on prod this would be imported from SequenceUtils) + await tracker.saveCounterfactualWallet({ config, context: Object.values(contexts) }) + + // Importing the account should work! + const account = new Account({ + ...defaultArgs, + address, + orchestrator: new Orchestrator([signer1, signer3]) + }) + + // Status on network 0 should be deployed, network 1 not + // both should not be migrated, and use the original imageHash + const status1 = await account.status(networks[0].chainId) + expect(status1.fullyMigrated).to.be.false + expect(status1.onChain.deployed).to.be.true + expect(status1.onChain.imageHash).to.equal(imageHash) + expect(status1.onChain.version).to.equal(1) + expect(status1.imageHash).to.equal(imageHash) + expect(status1.version).to.equal(1) + + const status2 = await account.status(networks[1].chainId) + expect(status2.fullyMigrated).to.be.false + expect(status2.onChain.deployed).to.be.false + expect(status2.onChain.imageHash).to.equal(imageHash) + expect(status2.onChain.version).to.equal(1) + expect(status2.imageHash).to.equal(imageHash) + expect(status2.version).to.equal(1) + + // Signing transactions (on both networks) and signing messages should fail + await expect(account.sendTransaction([], networks[0].chainId)).to.be.rejected + await expect(account.sendTransaction([], networks[1].chainId)).to.be.rejected + await expect(account.signMessage('0x00', networks[0].chainId)).to.be.rejected + await expect(account.signMessage('0x00', networks[1].chainId)).to.be.rejected + + await account.signAllMigrations((c) => c) + + // Sign a transaction on network 0 and network 1, both should work + // and should take the wallet on-chain up to speed + const configv2 = v2.config.ConfigCoder.fromSimple(simpleConfig) + + const tx1 = await account.sendTransaction([], networks[0].chainId) + expect(tx1).to.not.be.undefined + + const status1b = await account.status(networks[0].chainId) + expect(status1b.fullyMigrated).to.be.true + expect(status1b.onChain.deployed).to.be.true + expect(status1b.onChain.imageHash).to.equal(v2.config.ConfigCoder.imageHashOf(configv2)) + expect(status1b.onChain.version).to.equal(2) + expect(status1b.imageHash).to.equal(v2.config.ConfigCoder.imageHashOf(configv2)) + expect(status1b.version).to.equal(2) + + const tx2 = await account.sendTransaction([], networks[1].chainId) + expect(tx2).to.not.be.undefined + + const status2b = await account.status(networks[1].chainId) + expect(status2b).to.be.deep.equal(status1b) + }) + + it('Should migrate an upgraded wallet', async () => { + const signer1 = randomWallet('Should migrate an upgraded wallet') + const signer2 = randomWallet('Should migrate an upgraded wallet 2') + const signer3 = randomWallet('Should migrate an upgraded wallet 3') + const signer4 = randomWallet('Should migrate an upgraded wallet 4') + + const simpleConfig1a = { + threshold: 3, + checkpoint: 0, + signers: [{ + address: signer1.address, + weight: 2 + }, { + address: signer2.address, + weight: 2 + }, { + address: signer3.address, + weight: 2 + }] + } + + const config1a = v1.config.ConfigCoder.fromSimple(simpleConfig1a) + const imageHash1a = v1.config.ConfigCoder.imageHashOf(config1a) + const address = commons.context.addressOf(contexts[1], imageHash1a) + + const simpleConfig1b = { + threshold: 3, + checkpoint: 0, + signers: [{ + address: signer1.address, + weight: 2 + }, { + address: signer2.address, + weight: 2 + }, { + address: signer4.address, + weight: 2 + }] + } + + const config1b = v1.config.ConfigCoder.fromSimple(simpleConfig1b) + const imageHash1b = v1.config.ConfigCoder.imageHashOf(config1b) + + // Update wallet to config 1b (on network 0) + const wallet = new Wallet({ + coders: { + signature: v1.signature.SignatureCoder, + config: v1.config.ConfigCoder + }, + context: contexts[1], + config: config1a, + chainId: networks[0].chainId, + address, + orchestrator: new Orchestrator([signer1, signer3]), + relayer: (networks[0].relayer as Relayer)!, + provider: networks[0].provider! + }) + + const utx = await wallet.buildUpdateConfigurationTransaction(config1b) + const signed = await wallet.signTransactionBundle(utx) + const decorated = await wallet.decorateTransactions(signed) + await (networks[0].relayer as Relayer).relay(decorated) + + // Importing the account should work! + const account = new Account({ + ...defaultArgs, + address, + orchestrator: new Orchestrator([signer1, signer3]) + }) + + // Feed the tracker with all the data + await tracker.saveCounterfactualWallet({ config: config1a, context: [contexts[1]] }) + await tracker.saveWalletConfig({ config: config1b }) + + // Status on network 0 should be deployed, network 1 not + // and the configuration on network 0 should be the B one + const status1 = await account.status(networks[0].chainId) + expect(status1.fullyMigrated).to.be.false + expect(status1.onChain.deployed).to.be.true + expect(status1.onChain.imageHash).to.equal(imageHash1b) + expect(status1.onChain.version).to.equal(1) + expect(status1.imageHash).to.equal(imageHash1b) + + const status2 = await account.status(networks[1].chainId) + expect(status2.fullyMigrated).to.be.false + expect(status2.onChain.deployed).to.be.false + expect(status2.onChain.imageHash).to.equal(imageHash1a) + expect(status2.onChain.version).to.equal(1) + expect(status2.imageHash).to.equal(imageHash1a) + + // Signing transactions (on both networks) and signing messages should fail + await expect(account.sendTransaction([], networks[0].chainId)).to.be.rejected + await expect(account.sendTransaction([], networks[1].chainId)).to.be.rejected + await expect(account.signMessage('0x00', networks[0].chainId)).to.be.rejected + await expect(account.signMessage('0x00', networks[1].chainId)).to.be.rejected + + // Sign all migrations should only have signers1 and 2 + // so the migration should only be available on network 1 (the one not updated) + await account.signAllMigrations((c) => c) + + const config2a = v2.config.ConfigCoder.fromSimple(simpleConfig1a) + const config2b = v2.config.ConfigCoder.fromSimple(simpleConfig1b) + const imageHash2a = v2.config.ConfigCoder.imageHashOf(config2a) + + const status1b = await account.status(networks[0].chainId) + expect(status1b.fullyMigrated).to.be.false + expect(status1b.onChain.deployed).to.be.true + expect(status1b.onChain.imageHash).to.equal(imageHash1b) + expect(status1b.onChain.version).to.equal(1) + expect(status1b.imageHash).to.equal(imageHash1b) + expect(status1b.version).to.equal(1) + + const status2b = await account.status(networks[1].chainId) + expect(status2b.fullyMigrated).to.be.true + expect(status2b.onChain.deployed).to.be.false + expect(status2b.onChain.imageHash).to.equal(imageHash1a) + expect(status2b.onChain.version).to.equal(1) + expect(status2b.imageHash).to.equal(imageHash2a) + expect(status2b.version).to.equal(2) + + // Sending a transaction should work for network 1 + // but fail for network 0, same with signing messages + await expect(account.sendTransaction([], networks[0].chainId)).to.be.rejected + await expect(account.sendTransaction([], networks[1].chainId)).to.be.fulfilled + + await expect(account.signMessage('0x00', networks[0].chainId)).to.be.rejected + await expect(account.signMessage('0x00', networks[1].chainId)).to.be.fulfilled + + // Signing another migration with signers1 and 2 should put both in sync + account.setOrchestrator(new Orchestrator([signer1, signer2])) + await account.signAllMigrations((c) => c) + + await expect(account.sendTransaction([], networks[0].chainId)).to.be.fulfilled + await expect(account.sendTransaction([], networks[1].chainId)).to.be.fulfilled + + await expect(account.signMessage('0x00', networks[0].chainId)).to.be.fulfilled + await expect(account.signMessage('0x00', networks[1].chainId)).to.be.fulfilled + + const status1c = await account.status(networks[0].chainId) + const status2c = await account.status(networks[1].chainId) + + expect(status1c.fullyMigrated).to.be.true + expect(status2c.fullyMigrated).to.be.true + + // Configs are still different! + expect(status1c.imageHash).to.not.equal(status2c.imageHash) + + const simpleConfig4 = { + threshold: 2, + checkpoint: 1, + signers: [{ + address: signer1.address, + weight: 1 + }, { + address: signer2.address, + weight: 1 + }, { + address: signer4.address, + weight: 1 + }] + } + + const config4 = v2.config.ConfigCoder.fromSimple(simpleConfig4) + + await account.updateConfig(config4) + + const status1d = await account.status(networks[0].chainId) + const status2d = await account.status(networks[1].chainId) + + // Configs are now the same! + expect(status1d.imageHash).to.be.equal(status2d.imageHash) + }) + + it('Should edit the configuration during the migration', async () => { + // Old account may be an address that's not even deployed + const signer1 = randomWallet('Should edit the configuration during the migration') + const signer2 = randomWallet('Should edit the configuration during the migration 2') + + const simpleConfig1 = { + threshold: 1, + checkpoint: 0, + signers: [{ + address: signer1.address, + weight: 1 + }] + } + + const simpleConfig2 = { + threshold: 1, + checkpoint: 0, + signers: [{ + address: signer2.address, + weight: 1 + }] + } + + const config = v1.config.ConfigCoder.fromSimple(simpleConfig1) + const configv2 = v2.config.ConfigCoder.fromSimple(simpleConfig2) + + const imageHash = v1.config.ConfigCoder.imageHashOf(config) + const address = commons.context.addressOf(contexts[1], imageHash) + + // Sessions server MUST have information about the old wallet + // in production this is retrieved from SequenceUtils contract + await tracker.saveCounterfactualWallet({ config, context: [contexts[1]] }) + + // Importing the account should work! + const orchestrator = new Orchestrator([signer1]) + const account = new Account({ ...defaultArgs, address, orchestrator: orchestrator }) + + const status = await account.status(0) + expect(status.fullyMigrated).to.be.false + expect(status.onChain.deployed).to.be.false + expect(status.onChain.imageHash).to.equal(imageHash) + expect(status.imageHash).to.equal(imageHash) + expect(status.version).to.equal(1) + + // Sending a transaction should fail (not fully migrated) + await expect(account.sendTransaction([], networks[0].chainId)).to.be.rejected + + // Should sign migration using the account + await account.signAllMigrations((c) => { + expect(v1.config.ConfigCoder.imageHashOf(c as any)).to.equal(v1.config.ConfigCoder.imageHashOf(config)) + return configv2 + }) + + const status2 = await account.status(networks[0].chainId) + expect(status2.fullyMigrated).to.be.true + expect(status2.onChain.deployed).to.be.false + expect(status2.onChain.imageHash).to.equal(imageHash) + expect(status2.onChain.version).to.equal(1) + expect(status2.imageHash).to.equal(v2.config.ConfigCoder.imageHashOf(configv2)) + expect(status2.version).to.equal(2) + + // Send a transaction + orchestrator.setSigners([signer2]) + const tx = await account.sendTransaction([], networks[0].chainId) + expect(tx).to.not.be.undefined + + const status3 = await account.status(networks[0].chainId) + expect(status3.fullyMigrated).to.be.true + expect(status3.onChain.deployed).to.be.true + expect(status3.onChain.imageHash).to.equal(v2.config.ConfigCoder.imageHashOf(configv2)) + expect(status3.onChain.version).to.equal(2) + expect(status3.imageHash).to.equal(v2.config.ConfigCoder.imageHashOf(configv2)) + expect(status3.version).to.equal(2) + + // Send another transaction on another chain + const tx2 = await account.sendTransaction([], networks[1].chainId) + expect(tx2).to.not.be.undefined + + const status4 = await account.status(networks[1].chainId) + expect(status4.fullyMigrated).to.be.true + expect(status4.onChain.deployed).to.be.true + expect(status4.onChain.imageHash).to.equal(v2.config.ConfigCoder.imageHashOf(configv2)) + expect(status4.onChain.version).to.equal(2) + expect(status4.imageHash).to.equal(v2.config.ConfigCoder.imageHashOf(configv2)) + expect(status4.version).to.equal(2) + }) + + context('Signing messages', async () => { + context('After migrating', async () => { + let account: Account + let imageHash: string + + beforeEach(async () => { + // Old account may be an address that's not even deployed + const signer1 = randomWallet( + 'Signing messages - After migrating' + + account?.address ?? '' // Append prev address to entropy to avoid collisions + ) + + const simpleConfig = { + threshold: 1, + checkpoint: 0, + signers: [{ + address: signer1.address, + weight: 1 + }] + } + + const config = v1.config.ConfigCoder.fromSimple(simpleConfig) + imageHash = v1.config.ConfigCoder.imageHashOf(config) + const address = commons.context.addressOf(contexts[1], imageHash) + + // Sessions server MUST have information about the old wallet + // in production this is retrieved from SequenceUtils contract + await tracker.saveCounterfactualWallet({ config, context: [contexts[1]] }) + + account = new Account({ ...defaultArgs, address, orchestrator: new Orchestrator([signer1]) }) + + // Should sign migration using the account + await account.signAllMigrations((c) => c) + }) + + it('Should validate a message signed by undeployed migrated wallet', async () => { + const msg = ethers.utils.toUtf8Bytes('I like that you are reading our tests') + const sig = await account.signMessage(msg, networks[0].chainId, 'eip6492') + + const valid = await account.reader(networks[0].chainId).isValidSignature( + account.address, + ethers.utils.keccak256(msg), + sig + ) + + expect(valid).to.be.true + }) + + it('Should reject a message signed by undeployed migrated wallet (if set the throw)', async () => { + const msg = ethers.utils.toUtf8Bytes('I do not know what to write here anymore') + const sig = account.signMessage(msg, networks[0].chainId, 'throw') + + await expect(sig).to.be.rejected + }) + + it('Should return an invalid signature by undeployed migrated wallet (if set to ignore)', async () => { + const msg = ethers.utils.toUtf8Bytes('Sending a hug') + const sig = await account.signMessage(msg, networks[0].chainId, 'ignore') + + const valid = await account.reader(networks[0].chainId).isValidSignature( + account.address, + ethers.utils.keccak256(msg), + sig + ) + + expect(valid).to.be.false + }) + + it('Should validate a message signed by deployed migrated wallet (deployed with v1)', async () => { + const deployTx = Wallet.buildDeployTransaction(contexts[1], imageHash) + await signer1.sendTransaction({ + to: deployTx.entrypoint, + data: encodeBundleExecData(deployTx), + }) + + expect(await networks[0].provider!.getCode(account.address).then((c) => ethers.utils.arrayify(c).length)) + .to.not.equal(0) + + const msg = ethers.utils.toUtf8Bytes('Everything seems to be working fine so far') + const sig = await account.signMessage(msg, networks[0].chainId, 'eip6492') + + const valid = await account.reader(networks[0].chainId).isValidSignature( + account.address, + ethers.utils.keccak256(msg), + sig + ) + + expect(valid).to.be.true + }) + + it('Should fail to sign a message signed by deployed migrated wallet (deployed with v1) if throw', async () => { + const deployTx = Wallet.buildDeployTransaction(contexts[1], imageHash) + await signer1.sendTransaction({ + to: deployTx.entrypoint, + data: encodeBundleExecData(deployTx), + }) + + expect(await networks[0].provider!.getCode(account.address).then((c) => ethers.utils.arrayify(c).length)) + .to.not.equal(0) + + const msg = ethers.utils.toUtf8Bytes('Everything seems to be working fine so far') + const sig = account.signMessage(msg, networks[0].chainId, 'throw') + expect(sig).to.be.rejected + }) + + it('Should return an invalid signature by deployed migrated wallet (deployed with v1) if ignore', async () => { + const deployTx = Wallet.buildDeployTransaction(contexts[1], imageHash) + await signer1.sendTransaction({ + to: deployTx.entrypoint, + data: encodeBundleExecData(deployTx), + }) + + expect(await networks[0].provider!.getCode(account.address).then((c) => ethers.utils.arrayify(c).length)) + .to.not.equal(0) + + const msg = ethers.utils.toUtf8Bytes('Everything seems to be working fine so far') + const sig = await account.signMessage(msg, networks[0].chainId, 'ignore') + const valid = await account.reader(networks[0].chainId).isValidSignature( + account.address, + ethers.utils.keccak256(msg), + sig + ) + + expect(valid).to.be.false + }) + }) + }) + }) +}) + +let nowCalls = 0 +function now(): number { + if (deterministic) { + return Date.parse('2023-02-14T00:00:00.000Z') + 1000 * nowCalls++ + } else { + return Date.now() + } +} + +function randomWallet(entropy: number | string): ethers.Wallet { + return new ethers.Wallet(randomBytes(32, entropy)) +} + +function randomFraction(entropy: number | string): number { + const bytes = randomBytes(7, entropy) + bytes[0] &= 0x1f + return bytes.reduce((sum, byte) => 256 * sum + byte) / Number.MAX_SAFE_INTEGER +} + +function randomBytes(length: number, entropy: number | string): Uint8Array { + if (deterministic) { + let bytes = '' + while (bytes.length < 2 * length) { + bytes += ethers.utils.id(`${bytes}${entropy}`).slice(2) + } + return ethers.utils.arrayify(`0x${bytes.slice(0, 2 * length)}`) + } else { + return ethers.utils.randomBytes(length) + } +} diff --git a/packages/auth/package.json b/packages/auth/package.json index a988b8ad2..fb39f2592 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -17,22 +17,26 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@0xsequence/abi": "^0.43.34", - "@0xsequence/api": "^0.43.34", - "@0xsequence/config": "^0.43.34", - "@0xsequence/ethauth": "^0.8.0", - "@0xsequence/indexer": "^0.43.34", - "@0xsequence/metadata": "^0.43.34", - "@0xsequence/network": "^0.43.34", - "@0xsequence/provider": "^0.43.34", - "@0xsequence/wallet": "^0.43.34", - "@0xsequence/utils": "^0.43.34" + "@0xsequence/abi": "workspace:*", + "@0xsequence/account": "workspace:*", + "@0xsequence/api": "workspace:*", + "@0xsequence/core": "workspace:*", + "@0xsequence/ethauth": "^0.8.1", + "@0xsequence/indexer": "workspace:*", + "@0xsequence/metadata": "workspace:*", + "@0xsequence/migration": "workspace:*", + "@0xsequence/network": "workspace:*", + "@0xsequence/sessions": "workspace:*", + "@0xsequence/signhub": "workspace:*", + "@0xsequence/wallet": "workspace:*", + "@0xsequence/utils": "workspace:*" }, "peerDependencies": { "ethers": ">=5.5 < 6" }, "devDependencies": { - "@0xsequence/wallet-contracts": "1.10.0", + "@0xsequence/tests": "workspace:*", + "@0xsequence/wallet-contracts": "^1.10.0", "concurrently": "^7.5.0", "ethers": "^5.7.2", "hardhat": "^2.12.2", diff --git a/packages/auth/src/authorization.ts b/packages/auth/src/authorization.ts index 7ff675bc3..eb954684c 100644 --- a/packages/auth/src/authorization.ts +++ b/packages/auth/src/authorization.ts @@ -1,8 +1,10 @@ import { ethers } from 'ethers' import { ETHAuth, Proof } from '@0xsequence/ethauth' -import { ETHAuthProof } from '@0xsequence/provider' -import { DEFAULT_SESSION_EXPIRATION } from './session' +import { ChainIdLike, toChainIdNumber } from '@0xsequence/network' +import { TypedData } from '@0xsequence/utils' import { Signer } from '@0xsequence/wallet' +import { DEFAULT_SESSION_EXPIRATION } from './session' +import { Account } from '@0xsequence/account' export interface AuthorizationOptions { // app name string, ie 'Skyweaver' @@ -15,11 +17,17 @@ export interface AuthorizationOptions { expiry?: number } +export interface ETHAuthProof { + // eip712 typed-data payload for ETHAuth domain as input + typedData: TypedData + + // signature encoded in an ETHAuth proof string + proofString: string +} + // signAuthorization will perform an EIP712 typed-data message signing of ETHAuth domain via the provided // Signer and authorization options. -export const signAuthorization = async (signer: Signer, options: AuthorizationOptions): Promise => { - const chainId = await signer.getChainId() - +export const signAuthorization = async (signer: Signer | Account, chainId: ChainIdLike, options: AuthorizationOptions): Promise => { const address = ethers.utils.getAddress(await signer.getAddress()) if (!address || address === '' || address === '0x') { throw ErrAccountIsRequired @@ -37,7 +45,14 @@ export const signAuthorization = async (signer: Signer, options: AuthorizationOp proof.setExpiryIn(options.expiry ? Math.max(options.expiry, 200) : DEFAULT_SESSION_EXPIRATION) const typedData = proof.messageTypedData() - proof.signature = await signer.signTypedData(typedData.domain, typedData.types, typedData.message, chainId) + + const chainIdNumber = toChainIdNumber(chainId) + + proof.signature = await (signer instanceof Account ? + // Account can sign EIP-6492 signatures, so it doesn't require deploying the wallet + signer.signTypedData(typedData.domain, typedData.types, typedData.message, chainIdNumber, 'eip6492') : + signer.signTypedData(typedData.domain, typedData.types, typedData.message, chainIdNumber) + ) const ethAuth = new ETHAuth() const proofString = await ethAuth.encodeProof(proof, true) diff --git a/packages/auth/src/index.ts b/packages/auth/src/index.ts index bee275c2f..af64af8df 100644 --- a/packages/auth/src/index.ts +++ b/packages/auth/src/index.ts @@ -1,3 +1,3 @@ export * from './authorization' +export * from './session' export * from './proof' -export * from './session' \ No newline at end of file diff --git a/packages/auth/src/proof.ts b/packages/auth/src/proof.ts index 676129278..62bf2b1cb 100644 --- a/packages/auth/src/proof.ts +++ b/packages/auth/src/proof.ts @@ -1,66 +1,20 @@ -import { ethers } from 'ethers' -import { Proof, ValidatorFunc, IsValidSignatureBytes32MagicValue } from '@0xsequence/ethauth' -import { sequenceContext, WalletContext } from '@0xsequence/network' -import { isValidSequenceUndeployedWalletSignature } from '@0xsequence/wallet' - -export const ValidateSequenceDeployedWalletProof: ValidatorFunc = async (provider: ethers.providers.JsonRpcProvider, chainId: number, proof: Proof): Promise<{ isValid: boolean, address?: string }> => { - if (!provider || provider === undefined || chainId === undefined) { - return { isValid: false } - } - - // Compute eip712 message digest from the proof claims - const digest = proof.messageDigest() - - // Early check to ensure the contract wallet has been deployed - const walletCode = await provider.getCode(proof.address) - if (walletCode === '0x' || walletCode.length <= 2) { - throw new Error('ValidateSequenceDeployedWalletProof failed. unable to fetch wallet contract code') - } - - // Call EIP-1271 IsValidSignature(bytes32, bytes) method on the deployed wallet. Note: for undeployed - // wallets, you will need to implement your own ValidatorFunc with the additional context. - const abi = [ 'function isValidSignature(bytes32, bytes) public view returns (bytes4)' ] - const contract = new ethers.Contract(proof.address, abi, provider) - - // hash the message digest as required by isValidSignature - const isValidSignature = await contract.isValidSignature(digest, ethers.utils.arrayify(proof.signature)) - - if (isValidSignature === IsValidSignatureBytes32MagicValue) { - return { isValid: true } - } else { - return { isValid: false } - } -} - -export const ValidateSequenceUndeployedWalletProof = (context?: WalletContext): ValidatorFunc => { +import { commons } from "@0xsequence/core" +import { Proof, ValidatorFunc } from "@0xsequence/ethauth" +import { tracker } from "@0xsequence/sessions" +import { ethers } from "ethers" + +export const ValidateSequenceWalletProof = ( + readerFor: (chainId: number) => commons.reader.Reader, + tracker: tracker.ConfigTracker, + context: commons.context.WalletContext +): ValidatorFunc => { return async ( - provider: ethers.providers.JsonRpcProvider, + _provider: ethers.providers.JsonRpcProvider, chainId: number, proof: Proof - ): Promise<{ isValid: boolean, address?: string }> => { - if (!provider || provider === undefined || chainId === undefined) { - return { isValid: false } - } - - // The contract must not be deployed - const walletCode = ethers.utils.arrayify(await provider.getCode(proof.address)) - if (walletCode.length !== 0) return { isValid: false } - - // Compute eip712 message digest from the proof claims - const message = proof.messageDigest() - - // hash the message digest as required by isValidSignature - const digest = ethers.utils.arrayify(ethers.utils.keccak256(message)) - - const isValid = await isValidSequenceUndeployedWalletSignature( - proof.address, - digest, - proof.signature, - context ? context : sequenceContext, - provider, - chainId - ) - - return { isValid: !!isValid } + ): Promise<{ isValid: boolean }> => { + const digest = proof.messageDigest() + const isValid = await readerFor(chainId).isValidSignature(proof.address, digest, proof.signature) + return { isValid } } } diff --git a/packages/auth/src/session.ts b/packages/auth/src/session.ts index bb598f1e8..ab083bb84 100644 --- a/packages/auth/src/session.ts +++ b/packages/auth/src/session.ts @@ -1,20 +1,15 @@ +import { NetworkConfig, ChainIdLike, findNetworkConfig } from '@0xsequence/network' +import { getDefaultConnectionInfo, jwtDecodeClaims } from '@0xsequence/utils' +import { Account } from '@0xsequence/account' +import { ethers } from 'ethers' +import { tracker, trackers } from '@0xsequence/sessions' +import { Orchestrator } from '@0xsequence/signhub' +import { migrator } from '@0xsequence/migration' +import { commons, v1 } from '@0xsequence/core' import { SequenceAPIClient } from '@0xsequence/api' -import { - ConfigFinder, - SequenceUtilsFinder, - WalletConfig, - decodeSignature, - editConfig, - genConfig, - isDecodedSigner -} from '@0xsequence/config' -import { ETHAuth, Proof } from '@0xsequence/ethauth' -import { Indexer, SequenceIndexerClient } from '@0xsequence/indexer' import { SequenceMetadataClient } from '@0xsequence/metadata' -import { ChainIdLike, NetworkConfig, WalletContext, findNetworkConfig, getAuthNetwork, JsonRpcProvider } from '@0xsequence/network' -import { jwtDecodeClaims } from '@0xsequence/utils' -import { Account } from '@0xsequence/wallet' -import { ethers, Signer as AbstractSigner } from 'ethers' +import { Indexer, SequenceIndexerClient } from '@0xsequence/indexer' +import { ETHAuth, Proof } from '@0xsequence/ethauth' export type SessionMeta = { // name of the app requesting the session, used with ETHAuth @@ -39,13 +34,27 @@ type ProofStringPromise = { expiration: number } -export interface SessionDump { - config: WalletConfig - context: WalletContext +export interface SessionDumpV1 { + config: Omit & { address?: string } + jwt?: SessionJWT + metadata: SessionMeta +} + +export interface SessionDumpV2 { + version: 2 + address: string jwt?: SessionJWT metadata: SessionMeta } +export function isSessionDumpV1(obj: any): obj is SessionDumpV1 { + return obj.config && obj.metadata && obj.version === undefined +} + +export function isSessionDumpV2(obj: any): obj is SessionDumpV2 { + return obj.version === 2 && obj.address && obj.metadata +} + // Default session expiration of ETHAuth token (1 week) export const DEFAULT_SESSION_EXPIRATION = 60 * 60 * 24 * 7 @@ -54,9 +63,22 @@ export const LONG_SESSION_EXPIRATION = 3e7 const EXPIRATION_JWT_MARGIN = 60 // seconds +// These chains are always validated for migrations +// if they are not available, the login will fail +export const CRITICAL_CHAINS = [1, 137] + +export type SessionSettings = { + contexts: commons.context.VersionedContext + sequenceApiUrl: string + sequenceApiChainId: ethers.BigNumberish + sequenceMetadataUrl: string + networks: NetworkConfig[] + tracker: tracker.ConfigTracker & migrator.PresignedMigrationTracker + orchestrator: Orchestrator +} + export class Session { _initialAuthRequest: Promise - _jwt: SessionJWTPromise | undefined // proof strings are indexed by account address and app name, see getProofStringKey() @@ -70,13 +92,12 @@ export class Session { constructor( public sequenceApiUrl: string, + public sequenceApiChainId: ethers.BigNumberish, public sequenceMetadataUrl: string, - private networks: NetworkConfig[], - public config: WalletConfig, - public context: WalletContext, + public networks: NetworkConfig[], + public contexts: commons.context.VersionedContext, public account: Account, public metadata: SessionMeta, - private readonly authProvider: ethers.providers.JsonRpcProvider, jwt?: SessionJWT ) { if (jwt) { @@ -103,10 +124,6 @@ export class Session { this.account = account } - setConfig(config: WalletConfig) { - this.config = config - } - async auth(maxTries: number = 5): Promise { const url = this.sequenceApiUrl if (!url) throw Error('No sequence api url') @@ -127,15 +144,10 @@ export class Session { return new SequenceAPIClient(url, jwtAuth) } - get isTestnetMode(): boolean | undefined { - if (!this.networks || this.networks.length === 0) return - return !!this.networks[0].testnet - } - async getAPIClient(tryAuth: boolean = true): Promise { if (!this.apiClient) { const url = this.sequenceApiUrl - if (!url) throw Error('No chaind url') + if (!url) throw Error('No sequence api url') const jwtAuth = (await this.getJWT(tryAuth)).token this.apiClient = new SequenceAPIClient(url, jwtAuth) @@ -171,9 +183,13 @@ export class Session { return this.indexerClients.get(network.chainId)! } + private now(): number { + return Math.floor(Date.now() / 1000) + } + private async getJWT(tryAuth: boolean): Promise { const url = this.sequenceApiUrl - if (!url) throw Error('No chaind url') + if (!url) throw Error('No sequence api url') // check if we already have or are waiting for a token if (this._jwt) { @@ -200,7 +216,7 @@ export class Session { .then(async proofString => { const api = new SequenceAPIClient(url) - const authResp = await api.getAuthToken({ ewtString: proofString, testnetMode: this.isTestnetMode }) + const authResp = await api.getAuthToken({ ewtString: proofString }) if (authResp?.status === true && authResp.jwtToken.length !== 0) { return authResp.jwtToken @@ -255,6 +271,7 @@ export class Session { const proof = new Proof({ address: this.account.address }) + proof.claims.app = this.name if (typeof window === 'object') { proof.claims.ogn = window.location.origin @@ -262,37 +279,34 @@ export class Session { proof.setExpiryIn(this.expiration) const ethAuth = new ETHAuth() - const configFinder = new SequenceUtilsFinder(this.authProvider) - const authWallet = this.account.authWallet() + const chainId = ethers.BigNumber.from(this.sequenceApiChainId) + const network = this.networks.find(n => chainId.eq(n.chainId)) + if (!network) throw Error('No network found') + ethAuth.chainId = chainId.toNumber() + // TODO: Modify ETHAuth so it can take a provider instead of a url + ethAuth.provider = new ethers.providers.StaticJsonRpcProvider(getDefaultConnectionInfo(network.rpcUrl), { + name: '', + chainId: chainId.toNumber() + }) + const expiration = this.now() + this.expiration - EXPIRATION_JWT_MARGIN const proofString = { - // Fetch latest config - // TODO: Should only search for latest config if necessary to be more efficient. - // Perhaps compare local config hash with on-chain hash before doing - // the search through the logs. Should do this accross sequence.js - proofString: configFinder - .findCurrentConfig({ - address: authWallet.wallet.address, - provider: this.authProvider, - context: authWallet.wallet.context, - knownConfigs: [authWallet.wallet.config] - }) - .then(val => { - if (!val.config) throw Error("Can't find latest config") - return authWallet.wallet - .useConfig(val.config!) - .sign(proof.messageDigest()) - .then(signature => { - const decodedSignature = decodeSignature(signature) - const totalWeight = decodedSignature.signers.filter(isDecodedSigner).reduce((totalWeight, signer) => totalWeight + signer.weight, 0) - if (totalWeight < decodedSignature.threshold) { - throw Error(`insufficient signing power, need ${decodedSignature.threshold}, have ${totalWeight}`) - } - - proof.signature = signature - return ethAuth.encodeProof(proof, true) - }) + proofString: Promise.resolve( + // NOTICE: TODO: Here we ask the account to sign the message + // using whatever configuration we have ON-CHAIN, this means + // that the account will still use the v1 wallet, even if the migration + // was signed. + // + // This works for Sequence webapp v1 -> v2 because all v1 configurations share the same formula + // (torus + guard), but if we ever decide to allow cross-device login, then it will not work, because + // those other signers may not be part of the configuration. + // + this.account.signDigest(proof.messageDigest(), this.sequenceApiChainId, true, 'eip6492') + ) + .then(s => { + proof.signature = s + return ethAuth.encodeProof(proof, true) }) .catch(reason => { this.proofStrings.delete(key) @@ -300,6 +314,7 @@ export class Session { }), expiration } + this.proofStrings.set(key, proofString) return proofString } @@ -311,7 +326,16 @@ export class Session { private async isProofStringValid(proofString: string): Promise { try { const ethAuth = new ETHAuth() - ethAuth.provider = this.authProvider + const chainId = ethers.BigNumber.from(this.sequenceApiChainId) + const network = this.networks.find(n => chainId.eq(n.chainId)) + if (!network) throw Error('No network found') + ethAuth.chainId = chainId.toNumber() + + // TODO: Modify ETHAuth so it can take a provider instead of a url + ethAuth.provider = new ethers.providers.StaticJsonRpcProvider(getDefaultConnectionInfo(network.rpcUrl), { + name: '', + chainId: chainId.toNumber() + }) await ethAuth.decodeProof(proofString) @@ -321,7 +345,7 @@ export class Session { } } - async dump(): Promise { + async dump(): Promise { let jwt: SessionJWT | undefined if (this._jwt) { try { @@ -331,184 +355,214 @@ export class Session { } return { - config: this.config, - context: this.context, + version: 2, + address: this.account.address, metadata: this.metadata, jwt } } - private now(): number { - return Math.floor(new Date().getTime() / 1000) - } - static async open(args: { - sequenceApiUrl: string - sequenceMetadataUrl: string - context: WalletContext - networks: NetworkConfig[] + settings: SessionSettings + addSigners: commons.config.SimpleSigner[] referenceSigner: string - signers: { signer: AbstractSigner | string; weight: ethers.BigNumberish }[] threshold: ethers.BigNumberish metadata: SessionMeta - deepSearch?: boolean - knownConfigs?: WalletConfig[] - noIndex?: boolean - configFinder?: ConfigFinder + selectWallet: (wallets: string[]) => Promise + editConfigOnMigration: (config: commons.config.Config) => commons.config.Config + onMigration?: (account: Account) => Promise }): Promise { - const { - sequenceApiUrl, - sequenceMetadataUrl, - context, - networks, - referenceSigner, - signers, - threshold, - deepSearch, - knownConfigs, - noIndex, - metadata - } = args - - const authProvider = getAuthProvider(networks) - const configFinder = args.configFinder ? args.configFinder : new SequenceUtilsFinder(authProvider) - - const solvedSigners = Promise.all( - signers.map(async s => ({ ...s, address: typeof s.signer === 'string' ? s.signer : await s.signer.getAddress() })) - ) + const { referenceSigner, threshold, metadata, addSigners, selectWallet, settings, editConfigOnMigration, onMigration } = args + const { sequenceApiUrl, sequenceApiChainId, sequenceMetadataUrl, contexts, networks, tracker, orchestrator } = settings + + const referenceChainId = sequenceApiChainId + if (!referenceChainId) throw Error('No reference chain found') - const fullSigners = signers.filter(s => typeof s.signer !== 'string').map(s => s.signer) + const foundWallets = await tracker.walletsOfSigner({ signer: referenceSigner }) + const selectedWallet = await selectWallet(foundWallets.map(w => w.wallet)) - const existingWallet = ( - await configFinder.findLastWalletOfInitialSigner({ - signer: referenceSigner, - context: context, - provider: authProvider, - requireIndex: deepSearch ? false : true + let account: Account + + if (selectedWallet) { + // existing account, lets update it + account = new Account({ + address: selectedWallet, + tracker, + networks, + contexts, + orchestrator }) - ).wallet - - if (existingWallet) { - // existing account - - // Find prev configuration - const config = ( - await configFinder.findCurrentConfig({ - address: existingWallet, - provider: authProvider, - context: context, - knownConfigs - }) - ).config - - if (!config) throw Error('Wallet config not found') - - // Load prev account - const account = new Account( - { - initialConfig: config, - networks: networks, - context: context - }, - ...fullSigners - ) - const session = new Session(sequenceApiUrl, sequenceMetadataUrl, networks, config, context, account, metadata, authProvider) + // Get the latest configuration of the wallet (on the reference chain) + // now this configuration should be of the latest version, so we can start + // manipulating it. + + // NOTICE: We are performing the wallet update on a single chain, assuming that + // all other networks have the same configuration. This is not always true. + if (addSigners.length > 0) { + // New wallets never need migrations + // (because we create them on the latest version) + let status = await account.status(referenceChainId) + + // If the wallet was created originally on v2, then we can skip + // the migration checks all together. + if (status.original.version !== status.version || account.version !== status.version) { + // Account may not have been migrated yet, so we need to check + // if it has been migrated and if not, migrate it (in all chains) + const { migratedAllChains: isFullyMigrated, failedChains } = await account.isMigratedAllChains() + + // Failed chains must not contain mainnet or polygon, otherwise we cannot proceed. + if (failedChains.some(c => CRITICAL_CHAINS.includes(c))) { + throw Error(`Failed to fetch account status on ${failedChains.join(', ')}`) + } - // Update wallet config on-chain on the authChain - const [newConfig] = await account.updateConfig( - editConfig(config, { threshold, set: await solvedSigners }), - noIndex ? false : true - ) + if (!isFullyMigrated) { + // This is an oportunity for whoever is opening the session to + // feed the orchestrator with more signers, so that the migration + // can be completed. + if (onMigration && !(await onMigration(account))) { + throw Error('Migration cancelled, cannot open session') + } - // Session is ready, lets update - session.setConfig(newConfig) - session.setAccount( - new Account( - { - initialConfig: newConfig, - networks: networks, - context: context - }, - ...fullSigners - ) - ) + const { failedChains } = await account.signAllMigrations(editConfigOnMigration) + if (failedChains.some(c => CRITICAL_CHAINS.includes(c))) { + throw Error(`Failed to sign migrations on ${failedChains.join(', ')}`) + } - if (sequenceApiUrl) { - // Fire JWT requests after updating config - session._initialAuthRequest = session.auth() - } else { - session._initialAuthRequest = Promise.reject('no sequence api url') - } + // If we are using a dedupped tracker we need to invalidate the cache + // otherwise we run the risk of not seeing the signed migrations reflected. + if (trackers.isDedupedTracker(tracker)) { + tracker.invalidateCache() + } - return session - } else { - // fresh account - const config = genConfig(threshold, await solvedSigners) - - const account = new Account( - { - initialConfig: config, - networks: networks, - context: context - }, - ...fullSigners - ) + let isFullyMigrated2: boolean + ;[isFullyMigrated2, status] = await Promise.all([ + account.isMigratedAllChains().then(r => r.migratedAllChains), + account.status(referenceChainId) + ]) - // send referenceSigner as "requireFreshSigners" - // this ensures the user doesn't end up with multiple accounts if there is a race condition during login + if (!isFullyMigrated2) throw Error('Failed to migrate account') + } + } - await account.publishConfig(noIndex ? false : true, [referenceSigner]) + // NOTICE: We only need to do this because the API will not be able to + // validate the v2 signature (if the account has an onchain version of 1) + // we could speed this up by sending the migration alongside the jwt request + // and letting the API validate it offchain. + if (status.onChain.version !== status.version) { + await account.doBootstrap(referenceChainId, undefined, status) + } - const session = new Session(sequenceApiUrl, sequenceMetadataUrl, networks, config, context, account, metadata, authProvider) + const prevConfig = status.config + const newConfig = account.coders.config.editConfig(prevConfig, { + add: addSigners, + checkpoint: account.coders.config.checkpointOf(prevConfig).add(1), + threshold + }) - if (sequenceApiUrl) { - // Fire JWT requests when opening session - session._initialAuthRequest = session.auth() - } else { - session._initialAuthRequest = Promise.reject('no sequence api url') + await account.updateConfig(newConfig) } + } else { + // fresh account + account = await Account.new({ + config: { threshold, checkpoint: 0, signers: addSigners }, + tracker, + contexts, + orchestrator, + networks + }) - return session + // sign a digest and send it to the tracker + // otherwise the tracker will not know about this account + await account.publishWitness() + + // safety check, the remove tracker should be able to find + // this account for the reference signer + const foundWallets = await tracker.walletsOfSigner({ signer: referenceSigner, noCache: true }) + if (!foundWallets.some(w => w.wallet === account.address)) { + throw Error('Account not found on tracker') + } + } + + const session = new Session(sequenceApiUrl, sequenceApiChainId, sequenceMetadataUrl, networks, contexts, account, metadata) + + if (sequenceApiUrl) { + // Fire JWT requests after updating config + session._initialAuthRequest = session.auth() + } else { + session._initialAuthRequest = Promise.reject('no sequence api url') } + + return session } - static load(args: { - sequenceApiUrl: string - sequenceMetadataUrl: string - dump: SessionDump - signers: AbstractSigner[] - networks: NetworkConfig[] - }): Session { - const { sequenceApiUrl, sequenceMetadataUrl, dump, signers, networks } = args + static async load(args: { + settings: SessionSettings + dump: SessionDumpV1 | SessionDumpV2 + editConfigOnMigration: (config: commons.config.Config) => commons.config.Config + onMigration?: (account: Account) => Promise + }): Promise { + const { dump, settings, editConfigOnMigration, onMigration } = args + const { sequenceApiUrl, sequenceApiChainId, sequenceMetadataUrl, contexts, networks, tracker, orchestrator } = settings + + let account: Account + + if (isSessionDumpV1(dump)) { + // Old configuration format used to also contain an "address" field + // but if it doesn't, it means that it was a "counterfactual" account + // not yet updated, so we need to compute the address + const oldAddress = + dump.config.address || + commons.context.addressOf(contexts[1], v1.config.ConfigCoder.imageHashOf({ ...dump.config, version: 1 })) + + account = new Account({ + address: oldAddress, + tracker, + networks, + contexts, + orchestrator + }) + + // TODO: This property may not hold if the user adds a new network + if (!(await account.isMigratedAllChains().then(r => r.migratedAllChains))) { + // This is an oportunity for whoever is opening the session to + // feed the orchestrator with more signers, so that the migration + // can be completed. + if (onMigration && !(await onMigration(account))) { + throw Error('Migration cancelled, cannot open session') + } + + console.log('Migrating account...') + await account.signAllMigrations(editConfigOnMigration) + if (!(await account.isMigratedAllChains().then(r => r.migratedAllChains))) throw Error('Failed to migrate account') + } + + // We may need to update the JWT if the account has been migrated + } else if (isSessionDumpV2(dump)) { + account = new Account({ + address: dump.address, + tracker, + networks, + contexts, + orchestrator + }) + } else { + throw Error('Invalid dump format') + } return new Session( sequenceApiUrl, + sequenceApiChainId, sequenceMetadataUrl, networks, - dump.config, - dump.context, - new Account( - { - initialConfig: dump.config, - context: dump.context, - networks: networks - }, - ...signers - ), + contexts, + account, dump.metadata, - getAuthProvider(networks), dump.jwt ) } } -function getAuthProvider(networks: NetworkConfig[]): ethers.providers.JsonRpcProvider { - const authChain = getAuthNetwork(networks) - if (!authChain) throw Error('Auth chain not found') - return authChain.provider ?? new JsonRpcProvider(authChain.rpcUrl!, { chainId: authChain.chainId, blockCache: true }) -} - function getJWTExpiration(jwt: string): number { return jwtDecodeClaims<{ exp: number }>(jwt).exp } diff --git a/packages/auth/tests/session.spec.ts b/packages/auth/tests/session.spec.ts index 08830ba89..366fec2ae 100644 --- a/packages/auth/tests/session.spec.ts +++ b/packages/auth/tests/session.spec.ts @@ -1,26 +1,26 @@ -import { deployWalletContext } from './utils/deploy-wallet-context' -import { delay, mockDate } from './utils' - -import { CallReceiverMock, HookCallerMock } from '@0xsequence/wallet-contracts' - +import { Account } from '@0xsequence/account' +import { commons, v1, v2 } from '@0xsequence/core' +import { ETHAuth } from '@0xsequence/ethauth' +import { migrator } from '@0xsequence/migration' +import { NetworkConfig } from '@0xsequence/network' import { LocalRelayer } from '@0xsequence/relayer' - -import { WalletContext, NetworkConfig } from '@0xsequence/network' -import { ethers, Signer as AbstractSigner } from 'ethers' - -import chaiAsPromised from 'chai-as-promised' +import { tracker, trackers } from '@0xsequence/sessions' +import { Orchestrator } from '@0xsequence/signhub' +import * as utils from '@0xsequence/tests' +import { CallReceiverMock, HookCallerMock } from '@0xsequence/wallet-contracts' import * as chai from 'chai' +import chaiAsPromised from 'chai-as-promised' +import { ethers, Signer as AbstractSigner } from 'ethers' +import * as mockServer from 'mockttp' +import { Session, SessionDumpV1, SessionSettings, ValidateSequenceWalletProof } from '../src' +import { delay, mockDate } from './utils' const CallReceiverMockArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/mocks/CallReceiverMock.sol/CallReceiverMock.json') const HookCallerMockArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/mocks/HookCallerMock.sol/HookCallerMock.json') const { expect } = chai.use(chaiAsPromised) -import { Session, ValidateSequenceDeployedWalletProof, ValidateSequenceUndeployedWalletProof } from '../src' -import { compareAddr, SequenceUtilsFinder } from '@0xsequence/config' - -import * as mockServer from 'mockttp' -import { ETHAuth } from '@0xsequence/ethauth' +const deterministic = false type EthereumInstance = { chainId?: number @@ -66,16 +66,21 @@ describe('Wallet integration', function () { let callReceiver: CallReceiverMock let hookCaller: HookCallerMock - let context: WalletContext + let contexts: commons.context.VersionedContext let networks: NetworkConfig[] + let tracker: tracker.ConfigTracker & migrator.PresignedMigrationTracker + let orchestrator: Orchestrator + let simpleSettings: SessionSettings + before(async () => { // Provider from hardhat without a server instance ethnode.providerUrl = `http://127.0.0.1:9546/` ethnode.provider = new ethers.providers.JsonRpcProvider(ethnode.providerUrl) + const chainId = (await ethnode.provider.getNetwork()).chainId ethnode.signer = ethnode.provider.getSigner() - ethnode.chainId = 31337 + ethnode.chainId = chainId // Deploy local relayer relayer = new LocalRelayer(ethnode.signer) @@ -83,30 +88,14 @@ describe('Wallet integration', function () { networks = [ { name: 'local', - chainId: ethnode.chainId, + chainId, provider: ethnode.provider, isDefaultChain: true, - isAuthChain: true, - relayer: relayer + relayer } ] as NetworkConfig[] - // Deploy Sequence env - const [factory, mainModule, mainModuleUpgradable, guestModule, sequenceUtils, requireFreshSigner] = await deployWalletContext( - ethnode.provider - ) - - // Create fixed context obj - context = { - factory: factory.address, - mainModule: mainModule.address, - mainModuleUpgradable: mainModuleUpgradable.address, - guestModule: guestModule.address, - sequenceUtils: sequenceUtils.address, - libs: { - requireFreshSigner: requireFreshSigner.address - } - } + contexts = await utils.context.deploySequenceContexts(ethnode.signer) // Deploy call receiver mock callReceiver = (await new ethers.ContractFactory( @@ -121,337 +110,476 @@ describe('Wallet integration', function () { HookCallerMockArtifact.bytecode, ethnode.signer ).deploy()) as HookCallerMock + + tracker = new trackers.local.LocalConfigTracker(ethnode.provider!) + orchestrator = new Orchestrator([]) + + simpleSettings = { + sequenceApiUrl: '', + sequenceApiChainId: chainId, + sequenceMetadataUrl: '', + contexts, + networks, + orchestrator, + tracker + } }) it('Should open a new session', async () => { - const referenceSigner = ethers.Wallet.createRandom() + const referenceSigner = randomWallet('Should open a new session') + orchestrator.setSigners([referenceSigner]) const session = await Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: networks, + settings: simpleSettings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: referenceSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async (ws) => { + expect(ws.length).to.equal(0) + return undefined + }, + editConfigOnMigration: (config) => config }) expect(session.account.address).to.not.equal(ethers.constants.AddressZero) - expect(session.config.address).to.be.undefined - expect(session.config.threshold).to.equal(1) - expect(session.config.signers.length).to.equal(1) - expect(session.config.signers[0].address).to.equal(referenceSigner.address) - expect(session.config.signers[0].weight).to.equal(1) - await session.account.sendTransaction({ to: referenceSigner.address }) + const status = await session.account.status(networks[0].chainId) + + expect(v2.config.isWalletConfig(status.config)).to.equal(true) + const configv2 = status.config as v2.config.WalletConfig + + expect(ethers.BigNumber.from(configv2.threshold)).to.deep.equal(ethers.BigNumber.from(1)) + expect(v2.config.isSignerLeaf(configv2.tree)).to.equal(true) + + const leaf = configv2.tree as v2.config.SignerLeaf + expect(leaf.address).to.equal(referenceSigner.address) + expect(ethers.BigNumber.from(leaf.weight)).to.deep.equal(ethers.BigNumber.from(1)) + + await session.account.sendTransaction({ to: referenceSigner.address }, networks[0].chainId) }) it('Should dump and load a session', async () => { - const referenceSigner = ethers.Wallet.createRandom() + const referenceSigner = randomWallet('Should dump and load a session') + orchestrator.setSigners([referenceSigner]) - let session = await Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: networks, + const session = await Session.open({ + settings: simpleSettings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: referenceSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async (ws) => { + expect(ws.length).to.equal(0) + return undefined + }, + editConfigOnMigration: (config) => config }) const dump = await session.dump() - session = Session.load({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - dump: dump, - signers: [referenceSigner], - networks: networks + const session2 = await Session.load({ + settings: simpleSettings, + dump, + editConfigOnMigration: (config) => config }) - await session.account.sendTransaction({ to: referenceSigner.address }) + await session.account.sendTransaction({ to: referenceSigner.address }, networks[0].chainId) + + expect(session.account.address).to.equal(session2.account.address) }) it('Should open an existing session', async () => { - const referenceSigner = ethers.Wallet.createRandom() + const referenceSigner = randomWallet('Should open an existing session') + orchestrator.setSigners([referenceSigner]) - const ogSession = await Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: networks, + const session = await Session.open({ + settings: simpleSettings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: referenceSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async (ws) => ws[0] ?? undefined, + editConfigOnMigration: (config) => config }) - const newSigner = ethers.Wallet.createRandom() - - const session = await Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: networks, + const newSigner = randomWallet('Should open an existing session 2') + const session2 = await Session.open({ + settings: simpleSettings, referenceSigner: referenceSigner.address, - signers: [ - { signer: referenceSigner, weight: 1 }, - { signer: newSigner, weight: 1 } - ], + addSigners: [{ address: newSigner.address, weight: 1 }], threshold: 2, metadata: { name: 'Test' - } + }, + selectWallet: async (ws) => { + expect(ws.length).to.equal(1) + return ws[0] + }, + editConfigOnMigration: (config) => config }) - const [ogSignerId, signerId] = compareAddr(referenceSigner.address, newSigner.address) === 1 ? [1, 0] : [0, 1] + const newConfig = await session2.account.status(networks[0].chainId).then((s) => s.config) as v2.config.WalletConfig - expect(session.account.address).to.equal(ogSession.account.address) - expect(session.config.threshold).to.equal(2) - expect(session.config.signers.length).to.equal(2) - expect(session.config.signers[ogSignerId].address).to.equal(referenceSigner.address) - expect(session.config.signers[ogSignerId].weight).to.equal(1) - expect(session.config.signers[signerId].address).to.equal(newSigner.address) - expect(session.config.signers[signerId].weight).to.equal(1) + expect(session2.account.address).to.equal(session.account.address) + expect(ethers.BigNumber.from(newConfig.threshold)).to.deep.equal(ethers.BigNumber.from(2)) + + const newSigners = v2.config.signersOf(newConfig.tree).map((s) => s.address) + expect(newSigners.length).to.equal(2) + expect(newSigners).to.include(newSigner.address) + expect(newSigners).to.include(referenceSigner.address) + expect(ethers.BigNumber.from((newConfig.tree as any).left.weight)).to.deep.equal(ethers.BigNumber.from(1)) + expect(ethers.BigNumber.from((newConfig.tree as any).right.weight)).to.deep.equal(ethers.BigNumber.from(1)) }) - it('Should open an existing session with one signer being an public address', async () => { - const referenceSigner = ethers.Wallet.createRandom() + it('Should create a new account if selectWallet returns undefined', async () => { + const referenceSigner = randomWallet('Should create a new account if selectWallet returns undefined') + orchestrator.setSigners([referenceSigner]) - const ogSession = await Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: networks, + const oldSession = await Session.open({ + settings: simpleSettings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: referenceSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) - const newSigner = ethers.Wallet.createRandom() - - const session = await Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: networks, + const newSigner = randomWallet('Should create a new account if selectWallet returns undefined 2') + const newSession = await Session.open({ + settings: simpleSettings, referenceSigner: referenceSigner.address, - signers: [ - { signer: referenceSigner, weight: 1 }, - { signer: newSigner.address, weight: 1 } - ], + addSigners: [{ address: referenceSigner.address, weight: 1, }, { address: newSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async (wallets) => { + expect(wallets.length).to.equal(1) + return undefined + }, + editConfigOnMigration: (config) => config }) - const [ogSignerId, signerId] = compareAddr(referenceSigner.address, newSigner.address) === 1 ? [1, 0] : [0, 1] - - expect(session.account.address).to.equal(ogSession.account.address) - expect(session.config.threshold).to.equal(1) - expect(session.config.signers.length).to.equal(2) - expect(session.config.signers[ogSignerId].address).to.equal(referenceSigner.address) - expect(session.config.signers[ogSignerId].weight).to.equal(1) - expect(session.config.signers[signerId].address).to.equal(newSigner.address) - expect(session.config.signers[signerId].weight).to.equal(1) + expect(newSession.account.address).to.not.equal(oldSession.account.address) }) - it('Should fail open an existing session with all signers being public addresses', async () => { - const referenceSigner = ethers.Wallet.createRandom() - - const newSigner = ethers.Wallet.createRandom() + it('Should select between two wallets using selectWallet', async () => { + const referenceSigner = randomWallet('Should select between two wallets using selectWallet') + orchestrator.setSigners([referenceSigner]) - const sessionPromise = Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: networks, + const oldSession1 = await Session.open({ + settings: simpleSettings, referenceSigner: referenceSigner.address, - signers: [ - { signer: referenceSigner.address, weight: 1 }, - { signer: newSigner.address, weight: 1 } - ], + addSigners: [{ address: referenceSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) - expect(sessionPromise).to.be.rejected - }) - - it('Should open session without index and using deepSearch', async () => { - const referenceSigner = ethers.Wallet.createRandom() - - const ogSession = await Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: networks, + const oldSession2 = await Session.open({ + settings: simpleSettings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], - threshold: 1, + addSigners: [{ address: referenceSigner.address, weight: 2 }], + threshold: 2, metadata: { name: 'Test' }, - noIndex: true + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) - const newSigner = ethers.Wallet.createRandom() - - const session = await Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: networks, + const newSigner = randomWallet('Should select between two wallets using selectWallet 2') + const newSession1 = await Session.open({ + settings: simpleSettings, referenceSigner: referenceSigner.address, - signers: [ - { signer: referenceSigner, weight: 1 }, - { signer: newSigner, weight: 1 } - ], - threshold: 2, + addSigners: [{ address: newSigner.address, weight: 1 }], + threshold: 1, metadata: { name: 'Test' }, - noIndex: true, - deepSearch: true + selectWallet: async (wallets) => { + expect(wallets.length).to.equal(2) + expect(wallets).to.include(oldSession1.account.address) + expect(wallets).to.include(oldSession2.account.address) + return oldSession1.account.address + }, + editConfigOnMigration: (config) => config }) - expect(ogSession.account.address).to.equal(session.account.address) - }) - - it('Should fail to open session without authChain', async () => { - const referenceSigner = ethers.Wallet.createRandom() + expect(newSession1.account.address).to.equal(oldSession1.account.address) - const sessionPromise = Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: [{ ...networks[0], isAuthChain: false }], + const newSession2 = await Session.open({ + settings: simpleSettings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: newSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async (wallets) => { + expect(wallets.length).to.equal(2) + expect(wallets).to.include(oldSession1.account.address) + expect(wallets).to.include(oldSession2.account.address) + return oldSession2.account.address + }, + editConfigOnMigration: (config) => config }) - expect(sessionPromise).to.be.rejected + expect(newSession2.account.address).to.equal(oldSession2.account.address) + + await newSession1.account.sendTransaction([], networks[0].chainId) + await newSession2.account.sendTransaction([], networks[0].chainId) }) - it('Should open a different session if noIndex and deepSearch are not equal', async () => { - const referenceSigner = ethers.Wallet.createRandom() + it('Should re-open a session after sending a transaction', async () => { + const referenceSigner = randomWallet('Should re-open a session after sending a transaction') + const signer1 = randomWallet('Should re-open a session after sending a transaction 2') + orchestrator.setSigners([referenceSigner, signer1]) - const ogSession = await Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: networks, + const session = await Session.open({ + settings: simpleSettings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], - threshold: 1, + addSigners: [{ + address: referenceSigner.address, weight: 1, + }, { + address: signer1.address, weight: 1 + }], + threshold: 2, metadata: { name: 'Test' }, - noIndex: true + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) - const newSigner = ethers.Wallet.createRandom() + await session.account.sendTransaction([], networks[0].chainId) - const session = await Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: networks, + const signer2 = randomWallet('Should re-open a session after sending a transaction 3') + + const newSession = await Session.open({ + settings: simpleSettings, referenceSigner: referenceSigner.address, - signers: [ - { signer: referenceSigner, weight: 1 }, - { signer: newSigner, weight: 1 } - ], + addSigners: [{ address: signer2.address, weight: 1 }], threshold: 2, metadata: { name: 'Test' }, - noIndex: true, - deepSearch: false + selectWallet: async (wallets) => { + expect(wallets.length).to.equal(1) + return wallets[0] + }, + editConfigOnMigration: (config) => config }) - expect(ogSession.account.address).to.not.equal(session.account.address) + expect(newSession.account.address).to.equal(session.account.address) + + await newSession.account.sendTransaction([], networks[0].chainId) }) - it('Should fail to open a session if using a non-fresh signer', async () => { - const referenceSigner = ethers.Wallet.createRandom() + describe('Migrate sessions', () => { + let ogAccount: Account + let referenceSigner: ethers.Wallet + let referenceSignerIndex = 1 + let v1SessionDump: SessionDumpV1 + + beforeEach(async () => { + // Create a wallet using v1 + referenceSigner = randomWallet(`Migrate sessions ${referenceSignerIndex++}`) + orchestrator.setSigners([referenceSigner]) + + ogAccount = await Account.new({ + config: { threshold: 1, checkpoint: 0, signers: [{ address: referenceSigner.address, weight: 1 }] }, + tracker, + contexts: { 1: contexts[1] }, + orchestrator, + networks, + migrations: { 0: { + version: 1, + configCoder: v1.config.ConfigCoder, + signatureCoder: v1.signature.SignatureCoder, + } as any} + }) - await Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: networks, - referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], - threshold: 1, - metadata: { - name: 'Test' + await ogAccount.publishWitness() + + v1SessionDump = { + config: { + threshold: 1, + signers: [{ address: referenceSigner.address, weight: 1 }], + }, + metadata: { + name: 'Test', + } } }) - const configFinder = new SequenceUtilsFinder(networks.find(n => n.isAuthChain)!.provider!) + it('Should open and migrate old session, without dump', async () => { + const newSigner = randomWallet('Should open and migrate old session, without dump') + orchestrator.setSigners([referenceSigner, newSigner]) - const session = Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: networks, - referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], - threshold: 1, - metadata: { - name: 'Test' - }, - configFinder: { - ...configFinder, - findLastWalletOfInitialSigner: async () => ({}) - } + const newSession = await Session.open({ + settings: simpleSettings, + referenceSigner: referenceSigner.address, + addSigners: [{ address: newSigner.address, weight: 1 }], + threshold: 1, + metadata: { + name: 'Test' + }, + selectWallet: async (wallets) => { + expect(wallets.length).to.equal(1) + return wallets[0] + }, + editConfigOnMigration: (config) => config + }) + + expect(newSession.account.address).to.equal(ogAccount.address) + const status = await newSession.account.status(networks[0].chainId) + expect(status.version).to.equal(2) + expect(status.fullyMigrated).to.be.true + + await newSession.account.sendTransaction([], networks[0].chainId) }) - await expect(session).to.be.rejectedWith() + it('Should open and migrate dump', async () => { + const newSession = await Session.load({ + settings: simpleSettings, + dump: v1SessionDump, + editConfigOnMigration: (config) => config + }) + + expect(newSession.account.address).to.equal(ogAccount.address) + + const status = await newSession.account.status(networks[0].chainId) + expect(status.version).to.equal(2) + expect(status.fullyMigrated).to.be.true + + await newSession.account.sendTransaction([], networks[0].chainId) + }) + + describe('After updating old wallet', () => { + let newSignerIndex = 1 + + beforeEach(async () => { + const status = await ogAccount.status(networks[0].chainId) + const wallet = ogAccount.walletForStatus(networks[0].chainId, status) + + const newSigner = randomWallet(`After updating old wallet ${newSignerIndex++}`) + orchestrator.setSigners([referenceSigner, newSigner]) + + const uptx = await wallet.buildUpdateConfigurationTransaction({ + threshold: 2, + signers: [ + { address: referenceSigner.address, weight: 1 }, + { address: newSigner.address, weight: 1 } + ] + } as v1.config.WalletConfig) + + const suptx = await wallet.signTransactionBundle(uptx) + await wallet.relayer?.relay(suptx) + + v1SessionDump = { + ...v1SessionDump, + config: { + ...v1SessionDump.config, + address: wallet.address + } + } + }) + + it('Should open and migrate old session', async () => { + const newSigner2 = randomWallet('Should open and migrate old session') + + const newSession = await Session.open({ + settings: simpleSettings, + referenceSigner: referenceSigner.address, + addSigners: [{ address: newSigner2.address, weight: 1 }], + threshold: 2, + metadata: { + name: 'Test' + }, + selectWallet: async (wallets) => { + expect(wallets.length).to.equal(1) + return wallets[0] + }, + editConfigOnMigration: (config) => config + }) + + expect(newSession.account.address).to.equal(ogAccount.address) + const status = await newSession.account.status(networks[0].chainId) + expect(status.version).to.equal(2) + expect(status.fullyMigrated).to.be.true + + orchestrator.setSigners([referenceSigner, newSigner2]) + await newSession.account.sendTransaction([], networks[0].chainId) + }) + + it('Should open and migrate dump', async () => { + const newSession = await Session.load({ + settings: simpleSettings, + dump: v1SessionDump, + editConfigOnMigration: (config) => config + }) + + expect(newSession.account.address).to.equal(ogAccount.address) + + const status = await newSession.account.status(networks[0].chainId) + expect(status.version).to.equal(2) + expect(status.fullyMigrated).to.be.true + + await newSession.account.sendTransaction([], networks[0].chainId) + }) + }) }) describe('JWT Auth', () => { let server: mockServer.Mockttp let fakeJwt: string + let fakeJwtIndex = 1 let proofAddress: string let delayMs: number = 0 - let totalCount: number = 0 let recoverCount: { [address: string]: number } = {} let alwaysFail: boolean = false const sequenceApiUrl = 'http://127.0.0.1:8099' + let settings: SessionSettings beforeEach(() => { - fakeJwt = ethers.utils.hexlify(ethers.utils.randomBytes(64)) + settings = { + ...simpleSettings, + sequenceApiUrl, + sequenceMetadataUrl: '' + } + + fakeJwt = ethers.utils.hexlify(randomBytes(64, `JWT Auth ${fakeJwtIndex++}`)) server = mockServer.getLocal() server.start(8099) server.forPost('/rpc/API/GetAuthToken').thenCallback(async request => { if (delayMs !== 0) await delay(delayMs) - const ethauth = new ETHAuth(ValidateSequenceUndeployedWalletProof(context), ValidateSequenceDeployedWalletProof) + const validator = ValidateSequenceWalletProof( + () => new commons.reader.OnChainReader(networks[0].provider!), + tracker, + contexts[2] + ) + + const ethauth = new ETHAuth(validator) ethauth.chainId = ethnode.chainId! ethauth.configJsonRpcProvider(ethnode.providerUrl!) @@ -500,19 +628,19 @@ describe('Wallet integration', function () { }) it('Should get JWT token', async () => { - const referenceSigner = ethers.Wallet.createRandom() + const referenceSigner = randomWallet('Should get JWT token') + orchestrator.setSigners([referenceSigner]) const session = await Session.open({ - sequenceApiUrl: sequenceApiUrl, - sequenceMetadataUrl: '', - context: context, - networks: networks, + settings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: referenceSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) await session.auth() @@ -522,37 +650,32 @@ describe('Wallet integration', function () { }) it('Should get JWT after updating session', async () => { - const referenceSigner = ethers.Wallet.createRandom() + const referenceSigner = randomWallet('Should get JWT after updating session') + orchestrator.setSigners([referenceSigner]) await Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: networks, + settings: simpleSettings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: referenceSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) - const newSigner = ethers.Wallet.createRandom() - + const newSigner = randomWallet('Should get JWT after updating session 2') const session = await Session.open({ - sequenceApiUrl: sequenceApiUrl, - sequenceMetadataUrl: '', - context: context, - networks: networks, + settings, referenceSigner: referenceSigner.address, - signers: [ - { signer: referenceSigner, weight: 1 }, - { signer: newSigner, weight: 1 } - ], - threshold: 2, + addSigners: [{ address: newSigner.address, weight: 1 }], + threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async (ws) => ws[0], + editConfigOnMigration: (config) => config }) await session.auth() @@ -564,19 +687,19 @@ describe('Wallet integration', function () { }) it('Should get JWT during first session creation', async () => { - const referenceSigner = ethers.Wallet.createRandom() + const referenceSigner = randomWallet('Should get JWT during first session creation') + orchestrator.setSigners([referenceSigner]) const session = await Session.open({ - sequenceApiUrl: sequenceApiUrl, - sequenceMetadataUrl: '', - context: context, - networks: networks, + settings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: referenceSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) await session._initialAuthRequest @@ -590,65 +713,65 @@ describe('Wallet integration', function () { it('Should get JWT during session opening', async () => { delayMs = 500 - const referenceSigner = ethers.Wallet.createRandom() + const referenceSigner = randomWallet('Should get JWT during session opening - 1') + orchestrator.setSigners([referenceSigner]) let session = await Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: networks, + settings: simpleSettings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: referenceSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) await expect(session._initialAuthRequest).to.be.rejected - const newSigner = ethers.Wallet.createRandom() + const newSigner = randomWallet('Should get JWT during session opening 2') + orchestrator.setSigners([referenceSigner, newSigner]) session = await Session.open({ - sequenceApiUrl: sequenceApiUrl, - sequenceMetadataUrl: '', - context: context, - networks: networks, + settings, referenceSigner: referenceSigner.address, - signers: [ - { signer: referenceSigner, weight: 1 }, - { signer: newSigner, weight: 1 } + addSigners: [ + { address: newSigner.address, weight: 1 } ], threshold: 2, metadata: { name: 'Test' - } + }, + selectWallet: async (ws) => { + expect(ws.length).to.equal(1) + return ws[0] + }, + editConfigOnMigration: (config) => config }) await session._initialAuthRequest expect(totalCount).to.equal(1) - // TODO: can't reproduce - // expect(recoverCount["error"]).to.equal(1) expect(recoverCount[session.account.address]).to.equal(1) expect(await session._jwt?.token).to.equal(fakeJwt) }) it('Should get API with lazy JWT during first session creation', async () => { - const referenceSigner = ethers.Wallet.createRandom() + const referenceSigner = randomWallet('Should get API with lazy JWT during first session creation') + orchestrator.setSigners([referenceSigner]) const session = await Session.open({ - sequenceApiUrl: sequenceApiUrl, - sequenceMetadataUrl: '', - context: context, - networks: networks, + settings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: referenceSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) const api = await session.getAPIClient() @@ -668,37 +791,36 @@ describe('Wallet integration', function () { it('Should get API with lazy JWT during session opening', async () => { delayMs = 500 - const referenceSigner = ethers.Wallet.createRandom() + const referenceSigner = randomWallet('Should get API with lazy JWT during session opening') + orchestrator.setSigners([referenceSigner]) await Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: networks, + settings: simpleSettings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: referenceSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) - const newSigner = ethers.Wallet.createRandom() + const newSigner = randomWallet('Should get API with lazy JWT during session opening 2') + orchestrator.setSigners([referenceSigner, newSigner]) const session = await Session.open({ - sequenceApiUrl: sequenceApiUrl, - sequenceMetadataUrl: '', - context: context, - networks: networks, + settings, referenceSigner: referenceSigner.address, - signers: [ - { signer: referenceSigner, weight: 1 }, - { signer: newSigner, weight: 1 } + addSigners: [ + { address: newSigner.address, weight: 1 } ], threshold: 2, metadata: { name: 'Test' - } + }, + selectWallet: async (ws) => ws[0], + editConfigOnMigration: (config) => config }) const api = await session.getAPIClient() @@ -717,19 +839,19 @@ describe('Wallet integration', function () { }) it('Should call callbacks on JWT token', async () => { - const referenceSigner = ethers.Wallet.createRandom() + const referenceSigner = randomWallet('Should call callbacks on JWT token') + orchestrator.setSigners([referenceSigner]) const session = await Session.open({ - sequenceApiUrl: sequenceApiUrl, - sequenceMetadataUrl: '', - context: context, - networks: networks, + settings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: referenceSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) let calledCallback = 0 @@ -743,37 +865,37 @@ describe('Wallet integration', function () { it('Should call callbacks on JWT token (on open only once)', async () => { delayMs = 500 - const referenceSigner = ethers.Wallet.createRandom() + const referenceSigner = randomWallet('Should call callbacks on JWT token (on open only once)') + orchestrator.setSigners([referenceSigner]) await Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: networks, + settings: simpleSettings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: referenceSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) - const newSigner = ethers.Wallet.createRandom() + const newSigner = randomWallet('Should call callbacks on JWT token (on open only once) 2') + orchestrator.setSigners([referenceSigner, newSigner]) const session = await Session.open({ - sequenceApiUrl: sequenceApiUrl, - sequenceMetadataUrl: '', - context: context, - networks: networks, + settings, referenceSigner: referenceSigner.address, - signers: [ - { signer: referenceSigner, weight: 1 }, - { signer: newSigner, weight: 1 } + addSigners: [ + { address: referenceSigner.address, weight: 1 }, + { address: newSigner.address, weight: 1 } ], threshold: 2, metadata: { name: 'Test' - } + }, + selectWallet: async (ws) => ws[0], + editConfigOnMigration: (config) => config }) let calledCallback = 0 @@ -786,19 +908,19 @@ describe('Wallet integration', function () { it('Should retry 5 times retrieving the JWT token', async () => { delayMs = 1000 - const referenceSigner = ethers.Wallet.createRandom() + const referenceSigner = randomWallet('Should retry 5 times retrieving the JWT token') + orchestrator.setSigners([referenceSigner]) const session = await Session.open({ - sequenceApiUrl: sequenceApiUrl, - sequenceMetadataUrl: '', - context: context, - networks: networks, + settings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: referenceSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) alwaysFail = true @@ -809,51 +931,47 @@ describe('Wallet integration', function () { it('Should get API with JWT already present', async () => { delayMs = 500 - const referenceSigner = ethers.Wallet.createRandom() + + const referenceSigner = randomWallet('Should get API with JWT already present') + orchestrator.setSigners([referenceSigner]) await Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: networks, + settings: simpleSettings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: referenceSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) - const newSigner = ethers.Wallet.createRandom() + const newSigner = randomWallet('Should get API with JWT already present 2') + orchestrator.setSigners([referenceSigner, newSigner]) const session = await Session.open({ - sequenceApiUrl: sequenceApiUrl, - sequenceMetadataUrl: '', - context: context, - networks: networks, + settings, referenceSigner: referenceSigner.address, - signers: [ - { signer: referenceSigner, weight: 1 }, - { signer: newSigner, weight: 1 } + addSigners: [ + { address: newSigner.address, weight: 1 } ], threshold: 2, metadata: { name: 'Test' - } + }, + selectWallet: async (ws) => ws[0], + editConfigOnMigration: (config) => config }) await session._initialAuthRequest - const totalCountBefore = totalCount // This should use the already existing JWT const api = await session.getAPIClient() expect(totalCount).to.equal(totalCountBefore) - // TODO: can't reproduce - // expect(recoverCount["error"]).to.equal(1) expect(recoverCount[session.account.address]).to.equal(1) - expect(await session._jwt?.token).to.equal(fakeJwt) server.forPost('/rpc/API/FriendList').thenCallback(async request => { @@ -865,21 +983,21 @@ describe('Wallet integration', function () { }) it('Should fail to get API with false tryAuth and no JWT', async () => { - const referenceSigner = ethers.Wallet.createRandom() + const referenceSigner = randomWallet('Should fail to get API with false tryAuth and no JWT') + orchestrator.setSigners([referenceSigner]) alwaysFail = true const session = await Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: networks, + settings: simpleSettings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: referenceSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) await expect(session._initialAuthRequest).to.be.rejected @@ -895,19 +1013,19 @@ describe('Wallet integration', function () { }) it('Should fail to get API without api url', async () => { - const referenceSigner = ethers.Wallet.createRandom() + const referenceSigner = randomWallet('Should fail to get API without api url') + orchestrator.setSigners([referenceSigner]) const session = await Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: networks, + settings: simpleSettings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: referenceSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) const apiPromise = session.getAPIClient() @@ -919,19 +1037,19 @@ describe('Wallet integration', function () { }) it('Should fail to get JWT with no api configured', async () => { - const referenceSigner = ethers.Wallet.createRandom() + const referenceSigner = randomWallet('Should fail to get JWT with no api configured') + orchestrator.setSigners([referenceSigner]) const session = await Session.open({ - sequenceApiUrl: '', - sequenceMetadataUrl: '', - context: context, - networks: networks, + settings: simpleSettings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: referenceSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) await expect(session.auth()).to.be.rejected @@ -941,25 +1059,25 @@ describe('Wallet integration', function () { }) it('Should reuse outstanding JWT requests', async () => { - const referenceSigner = new CountingSigner(ethers.Wallet.createRandom()) + const referenceSigner = new CountingSigner(randomWallet('Should reuse outstanding JWT requests')) + orchestrator.setSigners([referenceSigner]) alwaysFail = true const session = await Session.open({ - sequenceApiUrl: sequenceApiUrl, - sequenceMetadataUrl: '', - context, - networks: networks, + settings, referenceSigner: await referenceSigner.getAddress(), - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: await referenceSigner.getAddress(), weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) - // 2 signatures are made to publish signers - expect(referenceSigner.signingRequests).to.equal(2) + // 1 signing request is made to publish signers + expect(referenceSigner.signingRequests).to.equal(1) const signingRequestsBefore = referenceSigner.signingRequests @@ -980,25 +1098,25 @@ describe('Wallet integration', function () { }) it('Should reuse existing proof signatures', async () => { - const referenceSigner = new CountingSigner(ethers.Wallet.createRandom()) + const referenceSigner = new CountingSigner(randomWallet('Should reuse existing proof signatures')) + orchestrator.setSigners([referenceSigner]) alwaysFail = true const session = await Session.open({ - sequenceApiUrl: sequenceApiUrl, - sequenceMetadataUrl: '', - context, - networks: networks, + settings, referenceSigner: await referenceSigner.getAddress(), - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: await referenceSigner.getAddress(), weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) - // 2 signatures are made to publish signers - expect(referenceSigner.signingRequests).to.equal(2) + // 1 signing request is made to publish signers + expect(referenceSigner.signingRequests).to.equal(1) const signingRequestsBefore = referenceSigner.signingRequests @@ -1016,19 +1134,19 @@ describe('Wallet integration', function () { }) it('Should neither re-authenticate nor retry if request succeeds', async () => { - const referenceSigner = new CountingSigner(ethers.Wallet.createRandom()) + const referenceSigner = new CountingSigner(randomWallet('Should neither re-authenticate nor retry if request succeeds')) + orchestrator.setSigners([referenceSigner]) const session = await Session.open({ - sequenceApiUrl: sequenceApiUrl, - sequenceMetadataUrl: '', - context, - networks: networks, + settings, referenceSigner: await referenceSigner.getAddress(), - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: await referenceSigner.getAddress(), weight: 1 }], threshold: 1, metadata: { name: 'Test' - } + }, + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) await session._initialAuthRequest @@ -1066,20 +1184,20 @@ describe('Wallet integration', function () { const baseTime = 1613579057 setDate(baseTime) - const referenceSigner = ethers.Wallet.createRandom() + const referenceSigner = randomWallet('Should request a new JWT after expiration') + orchestrator.setSigners([referenceSigner]) const session = await Session.open({ - sequenceApiUrl: sequenceApiUrl, - sequenceMetadataUrl: '', - context: context, - networks: networks, + settings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: referenceSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test', expiration: 240 - } + }, + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) await session._initialAuthRequest @@ -1092,7 +1210,7 @@ describe('Wallet integration', function () { const newBaseTime = baseTime + 60 * 60 setDate(newBaseTime) - fakeJwt = ethers.utils.hexlify(ethers.utils.randomBytes(96)) + fakeJwt = ethers.utils.hexlify(randomBytes(96, 'Should request a new JWT after expiration 2')) await session.getAPIClient() @@ -1105,20 +1223,20 @@ describe('Wallet integration', function () { const baseTime = 1613579057 setDate(baseTime) - const referenceSigner = ethers.Wallet.createRandom() + const referenceSigner = randomWallet('Should force min expiration time') + orchestrator.setSigners([referenceSigner]) const session = await Session.open({ - sequenceApiUrl: sequenceApiUrl, - sequenceMetadataUrl: '', - context: context, - networks: networks, + settings, referenceSigner: referenceSigner.address, - signers: [{ signer: referenceSigner, weight: 1 }], + addSigners: [{ address: referenceSigner.address, weight: 1 }], threshold: 1, metadata: { name: 'Test', expiration: 1 - } + }, + selectWallet: async () => undefined, + editConfigOnMigration: (config) => config }) await session._initialAuthRequest @@ -1130,3 +1248,19 @@ describe('Wallet integration', function () { }) }) }) + +function randomWallet(entropy: number | string): ethers.Wallet { + return new ethers.Wallet(randomBytes(32, entropy)) +} + +function randomBytes(length: number, entropy: number | string): Uint8Array { + if (deterministic) { + let bytes = '' + while (bytes.length < 2 * length) { + bytes += ethers.utils.id(`${bytes}${entropy}`).slice(2) + } + return ethers.utils.arrayify(`0x${bytes.slice(0, 2 * length)}`) + } else { + return ethers.utils.randomBytes(length) + } +} diff --git a/packages/auth/tests/utils/deploy-wallet-context.ts b/packages/auth/tests/utils/deploy-wallet-context.ts deleted file mode 100644 index 503b82aeb..000000000 --- a/packages/auth/tests/utils/deploy-wallet-context.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { ethers } from 'ethers' - -import { - Factory, - GuestModule, - MainModule, - MainModuleUpgradable, - SequenceUtils, - RequireFreshSigner -} from '@0xsequence/wallet-contracts' - -const FactoryArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/Factory.sol/Factory.json') -const GuestModuleArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/modules/GuestModule.sol/GuestModule.json') -const MainModuleArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/modules/MainModule.sol/MainModule.json') -const MainModuleUpgradableArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/modules/MainModuleUpgradable.sol/MainModuleUpgradable.json') -const SequenceUtilsArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/modules/utils/SequenceUtils.sol/SequenceUtils.json') -const RequireFreshSignerArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/modules/utils/libs/RequireFreshSigner.sol/RequireFreshSigner.json') - -export async function deployWalletContext( - provider: ethers.providers.Provider -): Promise<[Factory, MainModule, MainModuleUpgradable, GuestModule, SequenceUtils, RequireFreshSigner]> { - const factory = (await new ethers.ContractFactory( - FactoryArtifact.abi, - FactoryArtifact.bytecode, - (provider as any).getSigner() - ).deploy()) as unknown as Factory - - const mainModule = (await new ethers.ContractFactory( - MainModuleArtifact.abi, - MainModuleArtifact.bytecode, - (provider as any).getSigner() - ).deploy(factory.address)) as unknown as MainModule - - const mainModuleUpgradable = (await new ethers.ContractFactory( - MainModuleUpgradableArtifact.abi, - MainModuleUpgradableArtifact.bytecode, - (provider as any).getSigner() - ).deploy()) as unknown as MainModuleUpgradable - - const guestModule = (await new ethers.ContractFactory( - GuestModuleArtifact.abi, - GuestModuleArtifact.bytecode, - (provider as any).getSigner() - ).deploy()) as unknown as GuestModule - - const sequenceUtils = (await new ethers.ContractFactory( - SequenceUtilsArtifact.abi, - SequenceUtilsArtifact.bytecode, - (provider as any).getSigner() - ).deploy(factory.address, mainModule.address)) as unknown as SequenceUtils - - const requireFreshSigner = (await new ethers.ContractFactory( - RequireFreshSignerArtifact.abi, - RequireFreshSignerArtifact.bytecode, - (provider as any).getSigner() - ).deploy(sequenceUtils.address)) as unknown as RequireFreshSigner - - return [factory, mainModule, mainModuleUpgradable, guestModule, sequenceUtils, requireFreshSigner] -} diff --git a/packages/config/CHANGELOG.md b/packages/config/CHANGELOG.md deleted file mode 100644 index d9b3fea3e..000000000 --- a/packages/config/CHANGELOG.md +++ /dev/null @@ -1,1750 +0,0 @@ -# @0xsequence/config - -## 0.43.34 - -### Patch Changes - -- auth: no jwt for indexer -- Updated dependencies - - @0xsequence/abi@0.43.34 - - @0xsequence/multicall@0.43.34 - - @0xsequence/network@0.43.34 - - @0xsequence/utils@0.43.34 - -## 0.43.33 - -### Patch Changes - -- Adding onConnectOptionsChange handler to WalletRequestHandler -- Updated dependencies - - @0xsequence/abi@0.43.33 - - @0xsequence/multicall@0.43.33 - - @0xsequence/network@0.43.33 - - @0xsequence/utils@0.43.33 - -## 0.43.32 - -### Patch Changes - -- add Base Goerli network -- Updated dependencies - - @0xsequence/abi@0.43.32 - - @0xsequence/multicall@0.43.32 - - @0xsequence/network@0.43.32 - - @0xsequence/utils@0.43.32 - -## 0.43.31 - -### Patch Changes - -- remove AuxDataProvider, add promptSignInConnect -- Updated dependencies - - @0xsequence/abi@0.43.31 - - @0xsequence/multicall@0.43.31 - - @0xsequence/network@0.43.31 - - @0xsequence/utils@0.43.31 - -## 0.43.30 - -### Patch Changes - -- add arbitrum goerli testnet -- Updated dependencies - - @0xsequence/abi@0.43.30 - - @0xsequence/multicall@0.43.30 - - @0xsequence/network@0.43.30 - - @0xsequence/utils@0.43.30 - -## 0.43.29 - -### Patch Changes - -- provider: check availability of window object -- Updated dependencies - - @0xsequence/abi@0.43.29 - - @0xsequence/multicall@0.43.29 - - @0xsequence/network@0.43.29 - - @0xsequence/utils@0.43.29 - -## 0.43.28 - -### Patch Changes - -- update api bindings -- Updated dependencies - - @0xsequence/abi@0.43.28 - - @0xsequence/multicall@0.43.28 - - @0xsequence/network@0.43.28 - - @0xsequence/utils@0.43.28 - -## 0.43.27 - -### Patch Changes - -- Add rpc is sequence method -- Updated dependencies - - @0xsequence/abi@0.43.27 - - @0xsequence/multicall@0.43.27 - - @0xsequence/network@0.43.27 - - @0xsequence/utils@0.43.27 - -## 0.43.26 - -### Patch Changes - -- add zkevm url to enum -- Updated dependencies - - @0xsequence/abi@0.43.26 - - @0xsequence/multicall@0.43.26 - - @0xsequence/network@0.43.26 - - @0xsequence/utils@0.43.26 - -## 0.43.25 - -### Patch Changes - -- added polygon zkevm to mainnet networks -- Updated dependencies - - @0xsequence/abi@0.43.25 - - @0xsequence/multicall@0.43.25 - - @0xsequence/network@0.43.25 - - @0xsequence/utils@0.43.25 - -## 0.43.24 - -### Patch Changes - -- name change from zkevm to polygon-zkevm -- Updated dependencies - - @0xsequence/abi@0.43.24 - - @0xsequence/multicall@0.43.24 - - @0xsequence/network@0.43.24 - - @0xsequence/utils@0.43.24 - -## 0.43.23 - -### Patch Changes - -- update zkEVM name to Polygon zkEVM -- Updated dependencies - - @0xsequence/abi@0.43.23 - - @0xsequence/multicall@0.43.23 - - @0xsequence/network@0.43.23 - - @0xsequence/utils@0.43.23 - -## 0.43.22 - -### Patch Changes - -- add zkevm chain -- Updated dependencies - - @0xsequence/abi@0.43.22 - - @0xsequence/multicall@0.43.22 - - @0xsequence/network@0.43.22 - - @0xsequence/utils@0.43.22 - -## 0.43.21 - -### Patch Changes - -- api: update client bindings -- Updated dependencies - - @0xsequence/abi@0.43.21 - - @0xsequence/multicall@0.43.21 - - @0xsequence/network@0.43.21 - - @0xsequence/utils@0.43.21 - -## 0.43.20 - -### Patch Changes - -- indexer: update bindings -- Updated dependencies - - @0xsequence/abi@0.43.20 - - @0xsequence/multicall@0.43.20 - - @0xsequence/network@0.43.20 - - @0xsequence/utils@0.43.20 - -## 0.43.19 - -### Patch Changes - -- session proof update -- Updated dependencies - - @0xsequence/abi@0.43.19 - - @0xsequence/multicall@0.43.19 - - @0xsequence/network@0.43.19 - - @0xsequence/utils@0.43.19 - -## 0.43.18 - -### Patch Changes - -- rpc client global check, hardening -- Updated dependencies - - @0xsequence/abi@0.43.18 - - @0xsequence/multicall@0.43.18 - - @0xsequence/network@0.43.18 - - @0xsequence/utils@0.43.18 - -## 0.43.17 - -### Patch Changes - -- rpc clients, check of 'global' is defined -- Updated dependencies - - @0xsequence/abi@0.43.17 - - @0xsequence/multicall@0.43.17 - - @0xsequence/network@0.43.17 - - @0xsequence/utils@0.43.17 - -## 0.43.16 - -### Patch Changes - -- ethers peerDep to v5, update rpc client global use -- Updated dependencies - - @0xsequence/abi@0.43.16 - - @0xsequence/multicall@0.43.16 - - @0xsequence/network@0.43.16 - - @0xsequence/utils@0.43.16 - -## 0.43.15 - -### Patch Changes - -- - provider: expand receiver type on some util methods -- Updated dependencies - - @0xsequence/abi@0.43.15 - - @0xsequence/multicall@0.43.15 - - @0xsequence/network@0.43.15 - - @0xsequence/utils@0.43.15 - -## 0.43.14 - -### Patch Changes - -- bump -- Updated dependencies - - @0xsequence/abi@0.43.14 - - @0xsequence/multicall@0.43.14 - - @0xsequence/network@0.43.14 - - @0xsequence/utils@0.43.14 - -## 0.43.13 - -### Patch Changes - -- update rpc bindings -- Updated dependencies - - @0xsequence/abi@0.43.13 - - @0xsequence/multicall@0.43.13 - - @0xsequence/network@0.43.13 - - @0xsequence/utils@0.43.13 - -## 0.43.12 - -### Patch Changes - -- provider: single wallet init, and add new unregisterWallet() method -- Updated dependencies - - @0xsequence/abi@0.43.12 - - @0xsequence/multicall@0.43.12 - - @0xsequence/network@0.43.12 - - @0xsequence/utils@0.43.12 - -## 0.43.11 - -### Patch Changes - -- fix lockfiles -- re-add mocha type deleter -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.43.11 - - @0xsequence/multicall@0.43.11 - - @0xsequence/network@0.43.11 - - @0xsequence/utils@0.43.11 - -## 0.43.10 - -### Patch Changes - -- various improvements -- Updated dependencies - - @0xsequence/abi@0.43.10 - - @0xsequence/multicall@0.43.10 - - @0xsequence/network@0.43.10 - - @0xsequence/utils@0.43.10 - -## 0.43.9 - -### Patch Changes - -- update deps -- Updated dependencies - - @0xsequence/abi@0.43.9 - - @0xsequence/multicall@0.43.9 - - @0xsequence/network@0.43.9 - - @0xsequence/utils@0.43.9 - -## 0.43.8 - -### Patch Changes - -- network: JsonRpcProvider with caching -- Updated dependencies - - @0xsequence/abi@0.43.8 - - @0xsequence/multicall@0.43.8 - - @0xsequence/network@0.43.8 - - @0xsequence/utils@0.43.8 - -## 0.43.7 - -### Patch Changes - -- provider: fix wallet network init -- Updated dependencies - - @0xsequence/abi@0.43.7 - - @0xsequence/multicall@0.43.7 - - @0xsequence/network@0.43.7 - - @0xsequence/utils@0.43.7 - -## 0.43.6 - -### Patch Changes - -- metadatata: update rpc bindings -- Updated dependencies - - @0xsequence/abi@0.43.6 - - @0xsequence/multicall@0.43.6 - - @0xsequence/network@0.43.6 - - @0xsequence/utils@0.43.6 - -## 0.43.5 - -### Patch Changes - -- provider: do not set default network for connect messages -- provider: forward missing error message -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.43.5 - - @0xsequence/multicall@0.43.5 - - @0xsequence/network@0.43.5 - - @0xsequence/utils@0.43.5 - -## 0.43.4 - -### Patch Changes - -- no-change version bump to fix incorrectly tagged snapshot build -- Updated dependencies - - @0xsequence/abi@0.43.4 - - @0xsequence/multicall@0.43.4 - - @0xsequence/network@0.43.4 - - @0xsequence/utils@0.43.4 - -## 0.43.3 - -### Patch Changes - -- metadata: update bindings -- Updated dependencies - - @0xsequence/abi@0.43.3 - - @0xsequence/multicall@0.43.3 - - @0xsequence/network@0.43.3 - - @0xsequence/utils@0.43.3 - -## 0.43.2 - -### Patch Changes - -- provider: implement connectUnchecked -- Updated dependencies - - @0xsequence/abi@0.43.2 - - @0xsequence/multicall@0.43.2 - - @0xsequence/network@0.43.2 - - @0xsequence/utils@0.43.2 - -## 0.43.1 - -### Patch Changes - -- update to latest ethauth dep -- Updated dependencies - - @0xsequence/abi@0.43.1 - - @0xsequence/multicall@0.43.1 - - @0xsequence/network@0.43.1 - - @0xsequence/utils@0.43.1 - -## 0.43.0 - -### Minor Changes - -- move ethers to a peer dependency - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.43.0 - - @0xsequence/multicall@0.43.0 - - @0xsequence/network@0.43.0 - - @0xsequence/utils@0.43.0 - -## 0.42.10 - -### Patch Changes - -- add auxDataProvider -- Updated dependencies - - @0xsequence/abi@0.42.10 - - @0xsequence/multicall@0.42.10 - - @0xsequence/network@0.42.10 - - @0xsequence/utils@0.42.10 - -## 0.42.9 - -### Patch Changes - -- provider: add eip-191 exceptions -- Updated dependencies - - @0xsequence/abi@0.42.9 - - @0xsequence/multicall@0.42.9 - - @0xsequence/network@0.42.9 - - @0xsequence/utils@0.42.9 - -## 0.42.8 - -### Patch Changes - -- provider: skip setting intent origin if we're unity plugin -- Updated dependencies - - @0xsequence/abi@0.42.8 - - @0xsequence/multicall@0.42.8 - - @0xsequence/network@0.42.8 - - @0xsequence/utils@0.42.8 - -## 0.42.7 - -### Patch Changes - -- Add sign in options to connection settings -- Updated dependencies - - @0xsequence/abi@0.42.7 - - @0xsequence/multicall@0.42.7 - - @0xsequence/network@0.42.7 - - @0xsequence/utils@0.42.7 - -## 0.42.6 - -### Patch Changes - -- api bindings update -- Updated dependencies - - @0xsequence/abi@0.42.6 - - @0xsequence/multicall@0.42.6 - - @0xsequence/network@0.42.6 - - @0xsequence/utils@0.42.6 - -## 0.42.5 - -### Patch Changes - -- relayer: don't treat missing receipt as hard failure -- Updated dependencies - - @0xsequence/abi@0.42.5 - - @0xsequence/multicall@0.42.5 - - @0xsequence/network@0.42.5 - - @0xsequence/utils@0.42.5 - -## 0.42.4 - -### Patch Changes - -- provider: add custom app protocol to connect options -- Updated dependencies - - @0xsequence/abi@0.42.4 - - @0xsequence/multicall@0.42.4 - - @0xsequence/network@0.42.4 - - @0xsequence/utils@0.42.4 - -## 0.42.3 - -### Patch Changes - -- update api bindings -- Updated dependencies - - @0xsequence/abi@0.42.3 - - @0xsequence/multicall@0.42.3 - - @0xsequence/network@0.42.3 - - @0xsequence/utils@0.42.3 - -## 0.42.2 - -### Patch Changes - -- disable rinkeby network -- Updated dependencies - - @0xsequence/abi@0.42.2 - - @0xsequence/multicall@0.42.2 - - @0xsequence/network@0.42.2 - - @0xsequence/utils@0.42.2 - -## 0.42.1 - -### Patch Changes - -- wallet: optional waitForReceipt parameter -- Updated dependencies - - @0xsequence/abi@0.42.1 - - @0xsequence/multicall@0.42.1 - - @0xsequence/network@0.42.1 - - @0xsequence/utils@0.42.1 - -## 0.42.0 - -### Minor Changes - -- relayer: estimateGasLimits -> simulate -- add simulator package - -### Patch Changes - -- transactions: fix flattenAuxTransactions -- provider: only filter nullish values -- provider: re-map transaction 'gas' back to 'gasLimit' -- Updated dependencies -- Updated dependencies -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.42.0 - - @0xsequence/multicall@0.42.0 - - @0xsequence/network@0.42.0 - - @0xsequence/utils@0.42.0 - -## 0.41.3 - -### Patch Changes - -- api bindings update -- Updated dependencies - - @0xsequence/abi@0.41.3 - - @0xsequence/multicall@0.41.3 - - @0xsequence/network@0.41.3 - - @0xsequence/utils@0.41.3 - -## 0.41.2 - -### Patch Changes - -- api bindings update -- Updated dependencies - - @0xsequence/abi@0.41.2 - - @0xsequence/multicall@0.41.2 - - @0xsequence/network@0.41.2 - - @0xsequence/utils@0.41.2 - -## 0.41.1 - -### Patch Changes - -- update default networks -- Updated dependencies - - @0xsequence/abi@0.41.1 - - @0xsequence/multicall@0.41.1 - - @0xsequence/network@0.41.1 - - @0xsequence/utils@0.41.1 - -## 0.41.0 - -### Minor Changes - -- relayer: fix Relayer.wait() interface - - The interface for calling Relayer.wait() has changed. Instead of a single optional ill-defined timeout/delay parameter, there are three optional parameters, in order: - - - timeout: the maximum time to wait for the transaction receipt - - delay: the polling interval, i.e. the time to wait between requests - - maxFails: the maximum number of hard failures to tolerate before giving up - - Please update your codebase accordingly. - -- relayer: add optional waitForReceipt parameter to Relayer.relay - - The behaviour of Relayer.relay() was not well-defined with respect to whether or not it waited for a receipt. - This change allows the caller to specify whether to wait or not, with the default behaviour being to wait. - -### Patch Changes - -- relayer: wait receipt retry logic -- fix wrapped object error -- provider: forward delegateCall and revertOnError transaction fields -- Updated dependencies -- Updated dependencies -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.41.0 - - @0xsequence/multicall@0.41.0 - - @0xsequence/network@0.41.0 - - @0xsequence/utils@0.41.0 - -## 0.40.6 - -### Patch Changes - -- add arbitrum-nova chain -- Updated dependencies - - @0xsequence/abi@0.40.6 - - @0xsequence/multicall@0.40.6 - - @0xsequence/network@0.40.6 - - @0xsequence/utils@0.40.6 - -## 0.40.5 - -### Patch Changes - -- api: update bindings -- Updated dependencies - - @0xsequence/abi@0.40.5 - - @0xsequence/multicall@0.40.5 - - @0xsequence/network@0.40.5 - - @0xsequence/utils@0.40.5 - -## 0.40.4 - -### Patch Changes - -- add unreal transport -- Updated dependencies - - @0xsequence/abi@0.40.4 - - @0xsequence/multicall@0.40.4 - - @0xsequence/network@0.40.4 - - @0xsequence/utils@0.40.4 - -## 0.40.3 - -### Patch Changes - -- provider: fix MessageToSign message type -- Updated dependencies - - @0xsequence/abi@0.40.3 - - @0xsequence/multicall@0.40.3 - - @0xsequence/network@0.40.3 - - @0xsequence/utils@0.40.3 - -## 0.40.2 - -### Patch Changes - -- Wallet provider, loadSession method -- Updated dependencies - - @0xsequence/abi@0.40.2 - - @0xsequence/multicall@0.40.2 - - @0xsequence/network@0.40.2 - - @0xsequence/utils@0.40.2 - -## 0.40.1 - -### Patch Changes - -- export sequence.initWallet and sequence.getWallet -- Updated dependencies - - @0xsequence/abi@0.40.1 - - @0xsequence/multicall@0.40.1 - - @0xsequence/network@0.40.1 - - @0xsequence/utils@0.40.1 - -## 0.40.0 - -### Minor Changes - -- add sequence.initWallet(network, config) and sequence.getWallet() helper methods - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.40.0 - - @0xsequence/multicall@0.40.0 - - @0xsequence/network@0.40.0 - - @0xsequence/utils@0.40.0 - -## 0.39.6 - -### Patch Changes - -- indexer: update client bindings -- Updated dependencies - - @0xsequence/abi@0.39.6 - - @0xsequence/multicall@0.39.6 - - @0xsequence/network@0.39.6 - - @0xsequence/utils@0.39.6 - -## 0.39.5 - -### Patch Changes - -- provider: fix networkRpcUrl config option -- Updated dependencies - - @0xsequence/abi@0.39.5 - - @0xsequence/multicall@0.39.5 - - @0xsequence/network@0.39.5 - - @0xsequence/utils@0.39.5 - -## 0.39.4 - -### Patch Changes - -- api: update client bindings -- Updated dependencies - - @0xsequence/abi@0.39.4 - - @0xsequence/multicall@0.39.4 - - @0xsequence/network@0.39.4 - - @0xsequence/utils@0.39.4 - -## 0.39.3 - -### Patch Changes - -- add request method on Web3Provider -- Updated dependencies - - @0xsequence/abi@0.39.3 - - @0xsequence/multicall@0.39.3 - - @0xsequence/network@0.39.3 - - @0xsequence/utils@0.39.3 - -## 0.39.2 - -### Patch Changes - -- update umd name -- Updated dependencies - - @0xsequence/abi@0.39.2 - - @0xsequence/multicall@0.39.2 - - @0xsequence/network@0.39.2 - - @0xsequence/utils@0.39.2 - -## 0.39.1 - -### Patch Changes - -- add Aurora network -- add origin info for accountsChanged event to handle it per dapp -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.39.1 - - @0xsequence/multicall@0.39.1 - - @0xsequence/network@0.39.1 - - @0xsequence/utils@0.39.1 - -## 0.39.0 - -### Minor Changes - -- abstract window.localStorage to interface type - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.39.0 - - @0xsequence/multicall@0.39.0 - - @0xsequence/network@0.39.0 - - @0xsequence/utils@0.39.0 - -## 0.38.2 - -### Patch Changes - -- provider: add Settings.defaultPurchaseAmount -- Updated dependencies - - @0xsequence/abi@0.38.2 - - @0xsequence/multicall@0.38.2 - - @0xsequence/network@0.38.2 - - @0xsequence/utils@0.38.2 - -## 0.38.1 - -### Patch Changes - -- update api and metadata rpc bindings -- Updated dependencies - - @0xsequence/abi@0.38.1 - - @0xsequence/multicall@0.38.1 - - @0xsequence/network@0.38.1 - - @0xsequence/utils@0.38.1 - -## 0.38.0 - -### Minor Changes - -- api: update bindings, change TokenPrice interface -- bridge: remove @0xsequence/bridge package -- api: update bindings, rename ContractCallArg to TupleComponent - -### Patch Changes - -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.38.0 - - @0xsequence/multicall@0.38.0 - - @0xsequence/network@0.38.0 - - @0xsequence/utils@0.38.0 - -## 0.37.1 - -### Patch Changes - -- Add back sortNetworks - Removing sorting was a breaking change for dapps on older versions which directly integrate sequence. -- Updated dependencies - - @0xsequence/abi@0.37.1 - - @0xsequence/multicall@0.37.1 - - @0xsequence/network@0.37.1 - - @0xsequence/utils@0.37.1 - -## 0.37.0 - -### Minor Changes - -- network related fixes and improvements -- api: bindings: exchange rate lookups - -### Patch Changes - -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.37.0 - - @0xsequence/multicall@0.37.0 - - @0xsequence/network@0.37.0 - - @0xsequence/utils@0.37.0 - -## 0.36.13 - -### Patch Changes - -- api: update bindings with new price endpoints -- Updated dependencies - - @0xsequence/abi@0.36.13 - - @0xsequence/multicall@0.36.13 - - @0xsequence/network@0.36.13 - - @0xsequence/utils@0.36.13 - -## 0.36.12 - -### Patch Changes - -- wallet: skip remote signers if not needed -- auth: check that signature meets threshold before requesting auth token -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.36.12 - - @0xsequence/multicall@0.36.12 - - @0xsequence/network@0.36.12 - - @0xsequence/utils@0.36.12 - -## 0.36.11 - -### Patch Changes - -- Prefix EIP191 message on wallet-request-handler -- Updated dependencies - - @0xsequence/abi@0.36.11 - - @0xsequence/multicall@0.36.11 - - @0xsequence/network@0.36.11 - - @0xsequence/utils@0.36.11 - -## 0.36.10 - -### Patch Changes - -- support bannerUrl on connect -- Updated dependencies - - @0xsequence/abi@0.36.10 - - @0xsequence/multicall@0.36.10 - - @0xsequence/network@0.36.10 - - @0xsequence/utils@0.36.10 - -## 0.36.9 - -### Patch Changes - -- minor dev xp improvements -- Updated dependencies - - @0xsequence/abi@0.36.9 - - @0xsequence/multicall@0.36.9 - - @0xsequence/network@0.36.9 - - @0xsequence/utils@0.36.9 - -## 0.36.8 - -### Patch Changes - -- more connect options (theme, payment providers, funding currencies) -- Updated dependencies - - @0xsequence/abi@0.36.8 - - @0xsequence/multicall@0.36.8 - - @0xsequence/network@0.36.8 - - @0xsequence/utils@0.36.8 - -## 0.36.7 - -### Patch Changes - -- fix missing break -- Updated dependencies - - @0xsequence/abi@0.36.7 - - @0xsequence/multicall@0.36.7 - - @0xsequence/network@0.36.7 - - @0xsequence/utils@0.36.7 - -## 0.36.6 - -### Patch Changes - -- wallet_switchEthereumChain support -- Updated dependencies - - @0xsequence/abi@0.36.6 - - @0xsequence/multicall@0.36.6 - - @0xsequence/network@0.36.6 - - @0xsequence/utils@0.36.6 - -## 0.36.5 - -### Patch Changes - -- auth: bump ethauth to 0.7.0 - network, wallet: don't assume position of auth network in list - api/indexer/metadata: trim trailing slash on hostname, and add endpoint urls - relayer: Allow to specify local relayer transaction parameters like gas price or gas limit -- Updated dependencies - - @0xsequence/abi@0.36.5 - - @0xsequence/multicall@0.36.5 - - @0xsequence/network@0.36.5 - - @0xsequence/utils@0.36.5 - -## 0.36.4 - -### Patch Changes - -- Updating list of chain ids to include other ethereum compatible chains -- Updated dependencies - - @0xsequence/abi@0.36.4 - - @0xsequence/multicall@0.36.4 - - @0xsequence/network@0.36.4 - - @0xsequence/utils@0.36.4 - -## 0.36.3 - -### Patch Changes - -- provider: pass connect options to prompter methods -- Updated dependencies - - @0xsequence/abi@0.36.3 - - @0xsequence/multicall@0.36.3 - - @0xsequence/network@0.36.3 - - @0xsequence/utils@0.36.3 - -## 0.36.2 - -### Patch Changes - -- transactions: Setting target to 0x0 when empty to during SequenceTxAbiEncode -- Updated dependencies - - @0xsequence/abi@0.36.2 - - @0xsequence/multicall@0.36.2 - - @0xsequence/network@0.36.2 - - @0xsequence/utils@0.36.2 - -## 0.36.1 - -### Patch Changes - -- metadata: update client with more fields -- Updated dependencies - - @0xsequence/abi@0.36.1 - - @0xsequence/multicall@0.36.1 - - @0xsequence/network@0.36.1 - - @0xsequence/utils@0.36.1 - -## 0.36.0 - -### Minor Changes - -- relayer, wallet: fee quote support - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.36.0 - - @0xsequence/multicall@0.36.0 - - @0xsequence/network@0.36.0 - - @0xsequence/utils@0.36.0 - -## 0.35.12 - -### Patch Changes - -- provider: rename wallet.commands to wallet.utils -- Updated dependencies - - @0xsequence/abi@0.35.12 - - @0xsequence/multicall@0.35.12 - - @0xsequence/network@0.35.12 - - @0xsequence/utils@0.35.12 - -## 0.35.11 - -### Patch Changes - -- provider/utils: smoother message validation -- Updated dependencies - - @0xsequence/abi@0.35.11 - - @0xsequence/multicall@0.35.11 - - @0xsequence/network@0.35.11 - - @0xsequence/utils@0.35.11 - -## 0.35.10 - -### Patch Changes - -- upgrade deps -- Updated dependencies - - @0xsequence/abi@0.35.10 - - @0xsequence/multicall@0.35.10 - - @0xsequence/network@0.35.10 - - @0xsequence/utils@0.35.10 - -## 0.35.9 - -### Patch Changes - -- provider: window-transport override event handlers with new wallet instance -- Updated dependencies - - @0xsequence/abi@0.35.9 - - @0xsequence/multicall@0.35.9 - - @0xsequence/network@0.35.9 - - @0xsequence/utils@0.35.9 - -## 0.35.8 - -### Patch Changes - -- provider: async wallet sign in improvements -- Updated dependencies - - @0xsequence/abi@0.35.8 - - @0xsequence/multicall@0.35.8 - - @0xsequence/network@0.35.8 - - @0xsequence/utils@0.35.8 - -## 0.35.7 - -### Patch Changes - -- config: cache wallet configs -- Updated dependencies - - @0xsequence/abi@0.35.7 - - @0xsequence/multicall@0.35.7 - - @0xsequence/network@0.35.7 - - @0xsequence/utils@0.35.7 - -## 0.35.6 - -### Patch Changes - -- provider: support async signin of wallet request handler -- Updated dependencies - - @0xsequence/abi@0.35.6 - - @0xsequence/multicall@0.35.6 - - @0xsequence/network@0.35.6 - - @0xsequence/utils@0.35.6 - -## 0.35.5 - -### Patch Changes - -- wallet: skip threshold check during fee estimation -- Updated dependencies - - @0xsequence/abi@0.35.5 - - @0xsequence/network@0.35.5 - - @0xsequence/utils@0.35.5 - -## 0.35.4 - -### Patch Changes - -- - browser extension mode, center window -- Updated dependencies - - @0xsequence/abi@0.35.4 - - @0xsequence/network@0.35.4 - - @0xsequence/utils@0.35.4 - -## 0.35.3 - -### Patch Changes - -- - update window position when in browser extension mode -- Updated dependencies - - @0xsequence/abi@0.35.3 - - @0xsequence/network@0.35.3 - - @0xsequence/utils@0.35.3 - -## 0.35.2 - -### Patch Changes - -- - provider: WindowMessageHandler accept optional windowHref -- Updated dependencies - - @0xsequence/abi@0.35.2 - - @0xsequence/network@0.35.2 - - @0xsequence/utils@0.35.2 - -## 0.35.1 - -### Patch Changes - -- wallet: update config on undeployed too -- Updated dependencies - - @0xsequence/abi@0.35.1 - - @0xsequence/network@0.35.1 - - @0xsequence/utils@0.35.1 - -## 0.35.0 - -### Minor Changes - -- - config: add buildStubSignature - - provider: add checks to signing cases for wallet deployment and config statuses - - provider: add prompt for wallet deployment - - relayer: add BaseRelayer.prependWalletDeploy - - relayer: add Relayer.feeOptions - - relayer: account for wallet deployment in fee estimation - - transactions: add fromTransactionish - - wallet: add Account.prependConfigUpdate - - wallet: add Account.getFeeOptions - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.35.0 - - @0xsequence/network@0.35.0 - - @0xsequence/utils@0.35.0 - -## 0.34.0 - -### Minor Changes - -- - upgrade deps - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.34.0 - - @0xsequence/network@0.34.0 - - @0xsequence/utils@0.34.0 - -## 0.31.0 - -### Minor Changes - -- - upgrading to ethers v5.5 - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.31.0 - - @0xsequence/network@0.31.0 - - @0xsequence/utils@0.31.0 - -## 0.30.0 - -### Minor Changes - -- - upgrade most deps - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.30.0 - - @0xsequence/network@0.30.0 - - @0xsequence/utils@0.30.0 - -## 0.29.8 - -### Patch Changes - -- update api -- Updated dependencies [undefined] - - @0xsequence/abi@0.29.8 - - @0xsequence/network@0.29.8 - - @0xsequence/utils@0.29.8 - -## 0.29.6 - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/network@0.29.6 - -## 0.29.5 - -### Patch Changes - -- auth: pass testnetMode flag depending on network - -## 0.29.0 - -### Minor Changes - -- major architectural changes in Sequence design - - - only one API instance, API is no longer a per-chain service - - separate per-chain indexer service, API no longer handles indexing - - single contract metadata service, API no longer serves metadata - - chaind package has been removed, indexer and metadata packages have been added - - stronger typing with new explicit ChainId type - - multicall fixes and improvements - - forbid "wait" transactions in sendTransactionBatch calls - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/network@0.29.0 - - @0xsequence/abi@0.29.0 - - @0xsequence/utils@0.29.0 - -## 0.28.0 - -### Minor Changes - -- extension provider - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.28.0 - - @0xsequence/network@0.28.0 - - @0xsequence/utils@0.28.0 - -## 0.27.0 - -### Minor Changes - -- Add requireFreshSigner lib to sessions - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.27.0 - - @0xsequence/network@0.27.0 - - @0xsequence/utils@0.27.0 - -## 0.25.1 - -### Patch Changes - -- Fix build typescrypt issue -- Updated dependencies [undefined] - - @0xsequence/abi@0.25.1 - - @0xsequence/network@0.25.1 - - @0xsequence/utils@0.25.1 - -## 0.25.0 - -### Minor Changes - -- 10c8af8: Add estimator package - Fix multicall few calls bug - -### Patch Changes - -- Updated dependencies [10c8af8] - - @0xsequence/abi@0.25.0 - - @0xsequence/network@0.25.0 - - @0xsequence/utils@0.25.0 - -## 0.23.0 - -### Minor Changes - -- - relayer: offer variety of gas fee options from the relayer service" - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.23.0 - - @0xsequence/network@0.23.0 - - @0xsequence/utils@0.23.0 - -## 0.22.2 - -### Patch Changes - -- e1c109e: Fix authProof on expired sessions -- Updated dependencies [e1c109e] - - @0xsequence/abi@0.22.2 - - @0xsequence/network@0.22.2 - - @0xsequence/utils@0.22.2 - -## 0.22.1 - -### Patch Changes - -- transport session cache -- Updated dependencies [undefined] - - @0xsequence/abi@0.22.1 - - @0xsequence/network@0.22.1 - - @0xsequence/utils@0.22.1 - -## 0.22.0 - -### Minor Changes - -- e667b65: Expose all relayer options on networks - -### Patch Changes - -- Updated dependencies [e667b65] - - @0xsequence/abi@0.22.0 - - @0xsequence/network@0.22.0 - - @0xsequence/utils@0.22.0 - -## 0.21.5 - -### Patch Changes - -- Give priority to metaTxnId returned by relayer -- Updated dependencies [undefined] - - @0xsequence/abi@0.21.5 - - @0xsequence/network@0.21.5 - - @0xsequence/utils@0.21.5 - -## 0.21.4 - -### Patch Changes - -- Add has enough signers method -- Updated dependencies [undefined] - - @0xsequence/abi@0.21.4 - - @0xsequence/network@0.21.4 - - @0xsequence/utils@0.21.4 - -## 0.21.3 - -### Patch Changes - -- add window session cache -- Updated dependencies [undefined] - - @0xsequence/abi@0.21.3 - - @0xsequence/network@0.21.3 - - @0xsequence/utils@0.21.3 - -## 0.21.2 - -### Patch Changes - -- exception handlind in relayer -- Updated dependencies [undefined] - - @0xsequence/abi@0.21.2 - - @0xsequence/network@0.21.2 - - @0xsequence/utils@0.21.2 - -## 0.21.0 - -### Minor Changes - -- - fix gas estimation on wallets with large number of signers - - update to session handling and wallet config construction upon auth - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.21.0 - - @0xsequence/network@0.21.0 - - @0xsequence/utils@0.21.0 - -## 0.19.3 - -### Patch Changes - -- jwtAuth visibility, package version sync -- Updated dependencies [undefined] - - @0xsequence/abi@0.19.3 - - @0xsequence/network@0.19.3 - - @0xsequence/utils@0.19.3 - -## 0.19.2 - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.19.2 - -## 0.19.0 - -### Minor Changes - -- - provider, improve dapp / wallet transport io - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.19.0 - - @0xsequence/network@0.19.0 - - @0xsequence/utils@0.19.0 - -## 0.18.0 - -### Minor Changes - -- relayer improvements and pending transaction handling - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.18.0 - - @0xsequence/network@0.18.0 - - @0xsequence/utils@0.18.0 - -## 0.16.0 - -### Minor Changes - -- relayer as its own service separate from chaind - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.16.0 - - @0xsequence/network@0.16.0 - - @0xsequence/utils@0.16.0 - -## 0.15.1 - -### Patch Changes - -- update api clients -- Updated dependencies [undefined] - - @0xsequence/abi@0.15.1 - - @0xsequence/network@0.15.1 - - @0xsequence/utils@0.15.1 - -## 0.14.3 - -### Patch Changes - -- Fix 0xSequence relayer dependencies -- Updated dependencies [undefined] - - @0xsequence/abi@0.14.3 - - @0xsequence/network@0.14.3 - - @0xsequence/utils@0.14.3 - -## 0.14.2 - -### Patch Changes - -- Add debug logs to rpc-relayer -- Updated dependencies [undefined] - - @0xsequence/abi@0.14.2 - - @0xsequence/network@0.14.2 - - @0xsequence/utils@0.14.2 - -## 0.14.0 - -### Minor Changes - -- update sequence utils finder which includes optimization - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.14.0 - - @0xsequence/network@0.14.0 - - @0xsequence/utils@0.14.0 - -## 0.13.0 - -### Minor Changes - -- Update SequenceUtils deployed contract - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.13.0 - - @0xsequence/network@0.13.0 - - @0xsequence/utils@0.13.0 - -## 0.12.1 - -### Patch Changes - -- npm bump -- Updated dependencies [undefined] - - @0xsequence/abi@0.12.1 - - @0xsequence/network@0.12.1 - -## 0.12.0 - -### Minor Changes - -- provider: improvements to window transport - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.12.0 - - @0xsequence/network@0.12.0 - -## 0.11.4 - -### Patch Changes - -- update api client -- Updated dependencies [undefined] - - @0xsequence/abi@0.11.4 - - @0xsequence/network@0.11.4 - -## 0.11.3 - -### Patch Changes - -- improve openWindow state options handling -- Updated dependencies [undefined] - - @0xsequence/abi@0.11.3 - - @0xsequence/network@0.11.3 - -## 0.11.2 - -### Patch Changes - -- Fix multicall proxy scopes -- Updated dependencies [undefined] - - @0xsequence/abi@0.11.2 - - @0xsequence/network@0.11.2 - -## 0.11.1 - -### Patch Changes - -- Add support for dynamic and nested signatures -- Updated dependencies [undefined] - - @0xsequence/abi@0.11.1 - - @0xsequence/network@0.11.1 - -## 0.11.0 - -### Minor Changes - -- Update wallet context to 1.7 contracts - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.11.0 - - @0xsequence/network@0.11.0 - -## 0.10.9 - -### Patch Changes - -- add support for public addresses as signers in session.open -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.9 - - @0xsequence/network@0.10.9 - -## 0.10.8 - -### Patch Changes - -- Multicall production configuration -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.8 - - @0xsequence/network@0.10.8 - -## 0.10.7 - -### Patch Changes - -- allow provider transport to force disconnect -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.7 - - @0xsequence/network@0.10.7 - -## 0.10.6 - -### Patch Changes - -- - fix getWalletState method -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.6 - - @0xsequence/network@0.10.6 - -## 0.10.5 - -### Patch Changes - -- update relayer gas refund options -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.5 - - @0xsequence/network@0.10.5 - -## 0.10.4 - -### Patch Changes - -- Update api proto -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.4 - - @0xsequence/network@0.10.4 - -## 0.10.3 - -### Patch Changes - -- Fix loading config cross-chain -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.3 - - @0xsequence/network@0.10.3 - -## 0.10.2 - -### Patch Changes - -- - message digest fix -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.2 - - @0xsequence/network@0.10.2 - -## 0.10.1 - -### Patch Changes - -- upgrade deps -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.1 - - @0xsequence/network@0.10.1 - -## 0.10.0 - -### Minor Changes - -- Deployed new contracts with ERC1271 signer support - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.0 - - @0xsequence/network@0.10.0 - -## 0.9.6 - -### Patch Changes - -- Update ABIs for latest sequence contracts -- Updated dependencies [undefined] - - @0xsequence/network@0.9.6 - - @0xsequence/abi@0.9.6 - -## 0.9.5 - -### Patch Changes - -- Implemented session class -- Updated dependencies [undefined] - - @0xsequence/network@0.9.5 - -## 0.9.3 - -### Patch Changes - -- - minor improvements -- Updated dependencies [undefined] - - @0xsequence/network@0.9.3 - -## 0.9.1 - -### Patch Changes - -- - patch bump -- Updated dependencies [undefined] - - @0xsequence/network@0.9.1 - -## 0.9.0 - -### Minor Changes - -- - provider transport hardening - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/network@0.9.0 - -## 0.8.5 - -### Patch Changes - -- - use latest wallet-contracts -- Updated dependencies [undefined] - - @0xsequence/network@0.8.5 - -## 0.8.4 - -### Patch Changes - -- - minor improvements, name updates and comments -- Updated dependencies [undefined] - - @0xsequence/network@0.8.4 - -## 0.8.3 - -### Patch Changes - -- - refinements - - - normalize signer address in config - - - provider: getWalletState() method to WalletProvider - -- Updated dependencies [undefined] - - @0xsequence/network@0.8.3 - -## 0.8.2 - -### Patch Changes - -- - field rename and ethauth dependency bump -- Updated dependencies [undefined] - - @0xsequence/network@0.8.2 - -## 0.8.1 - -### Patch Changes - -- - variety of optimizations -- Updated dependencies [undefined] - - @0xsequence/network@0.8.1 - -## 0.8.0 - -### Minor Changes - -- - changeset fix - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/network@0.8.0 - -## 0.7.0 - -### Patch Changes - -- 6f11ed7: sequence.js, init release -- Updated dependencies [6f11ed7] - - @0xsequence/network@0.7.0 diff --git a/packages/config/README.md b/packages/config/README.md deleted file mode 100644 index 0c7ecc784..000000000 --- a/packages/config/README.md +++ /dev/null @@ -1,4 +0,0 @@ -@0xsequence/config -================== - -See [0xsequence project page](https://github.com/0xsequence/sequence.js). diff --git a/packages/config/package.json b/packages/config/package.json deleted file mode 100644 index 5bfe1bcfa..000000000 --- a/packages/config/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "@0xsequence/config", - "version": "0.43.34", - "description": "config sub-package for Sequence", - "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/config", - "source": "src/index.ts", - "main": "dist/0xsequence-config.cjs.js", - "module": "dist/0xsequence-config.esm.js", - "author": "Horizon Blockchain Games", - "license": "Apache-2.0", - "scripts": { - "test": "pnpm test:concurrently 'pnpm test:run'", - "test:run": "pnpm test:file tests/**/*.spec.ts", - "test:file": "NODE_OPTIONS='--loader tsx' mocha --timeout 30000", - "test:concurrently": "concurrently -k --success first 'pnpm start:hardhat_b > /dev/null' 'pnpm start:hardhat_a > /dev/null' ", - "start:hardhat_a": "pnpm hardhat node --port 7547", - "start:hardhat_b": "pnpm hardhat node --port 7548", - "typecheck": "tsc --noEmit" - }, - "dependencies": { - "@0xsequence/abi": "^0.43.34", - "@0xsequence/multicall": "^0.43.34", - "@0xsequence/network": "^0.43.34", - "@0xsequence/utils": "^0.43.34" - }, - "peerDependencies": { - "ethers": ">=5.5 < 6" - }, - "devDependencies": { - "@0xsequence/wallet-contracts": "1.10.0", - "ethers": "^5.7.2" - }, - "files": [ - "src", - "dist" - ] -} diff --git a/packages/config/src/bytecode.ts b/packages/config/src/bytecode.ts deleted file mode 100644 index 3c714c850..000000000 --- a/packages/config/src/bytecode.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* tslint:disable */ - -/** - Minimal upgradeable proxy implementation, delegates all calls to the address - defined by the storage slot matching the wallet address. - - Inspired by EIP-1167 Implementation (https://eips.ethereum.org/EIPS/eip-1167) - - deployed code: - - 0x00 0x36 0x36 CALLDATASIZE cds - 0x01 0x3d 0x3d RETURNDATASIZE 0 cds - 0x02 0x3d 0x3d RETURNDATASIZE 0 0 cds - 0x03 0x37 0x37 CALLDATACOPY - 0x04 0x3d 0x3d RETURNDATASIZE 0 - 0x05 0x3d 0x3d RETURNDATASIZE 0 0 - 0x06 0x3d 0x3d RETURNDATASIZE 0 0 0 - 0x07 0x36 0x36 CALLDATASIZE cds 0 0 0 - 0x08 0x3d 0x3d RETURNDATASIZE 0 cds 0 0 0 - 0x09 0x30 0x30 ADDRESS addr 0 cds 0 0 0 - 0x0A 0x54 0x54 SLOAD imp 0 cds 0 0 0 - 0x0B 0x5a 0x5a GAS gas imp 0 cds 0 0 0 - 0x0C 0xf4 0xf4 DELEGATECALL suc 0 - 0x0D 0x3d 0x3d RETURNDATASIZE rds suc 0 - 0x0E 0x82 0x82 DUP3 0 rds suc 0 - 0x0F 0x80 0x80 DUP1 0 0 rds suc 0 - 0x10 0x3e 0x3e RETURNDATACOPY suc 0 - 0x11 0x90 0x90 SWAP1 0 suc - 0x12 0x3d 0x3d RETURNDATASIZE rds 0 suc - 0x13 0x91 0x91 SWAP2 suc 0 rds - 0x14 0x60 0x18 0x6018 PUSH1 0x18 suc 0 rds - /-- 0x16 0x57 0x57 JUMPI 0 rds - | 0x17 0xfd 0xfd REVERT - \-> 0x18 0x5b 0x5b JUMPDEST 0 rds - 0x19 0xf3 0xf3 RETURN - - flat deployed code: 0x363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3 - - deploy function: - - 0x00 0x60 0x3a 0x603a PUSH1 0x3a - 0x02 0x60 0x0e 0x600e PUSH1 0x0e 0x3a - 0x04 0x3d 0x3d RETURNDATASIZE 0 0x0e 0x3a - 0x05 0x39 0x39 CODECOPY - 0x06 0x60 0x1a 0x601a PUSH1 0x1a - 0x08 0x80 0x80 DUP1 0x1a 0x1a - 0x09 0x51 0x51 MLOAD imp 0x1a - 0x0A 0x30 0x30 ADDRESS addr imp 0x1a - 0x0B 0x55 0x55 SSTORE 0x1a - 0x0C 0x3d 0x3d RETURNDATASIZE 0 0x1a - 0x0D 0xf3 0xf3 RETURN - [...deployed code] - - flat deploy function: 0x603a600e3d39601a805130553df3363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3 -*/ - -export const WalletContractBytecode = '0x603a600e3d39601a805130553df3363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3' diff --git a/packages/config/src/cache.ts b/packages/config/src/cache.ts deleted file mode 100644 index 1eb3779e4..000000000 --- a/packages/config/src/cache.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { WalletConfig } from './config' - -const maxCachedConfigs = 10 - -const listKey = '@sequence.config.imageHashes' -const configKey = (imageHash: string) => `@sequence.config.${imageHash}` - -let storage: { - getItem(key: string): string | null - setItem(key: string, value: string): void - removeItem(key: string): void -} - -try { - storage = localStorage -} catch { - const map: Map = new Map() - storage = { - getItem: key => map.get(key) ?? null, - setItem: (key, value) => map.set(key, value), - removeItem: key => map.delete(key) - } -} - -export function getCachedConfig(imageHash: string): WalletConfig | undefined { - const config = JSON.parse(storage.getItem(configKey(imageHash)) ?? 'null') - if (config) { - pushImageHash(imageHash) - return config - } else { - return - } -} - -export function cacheConfig(imageHash: string, config: WalletConfig) { - storage.setItem(configKey(imageHash), JSON.stringify(config)) - pushImageHash(imageHash) -} - -function pushImageHash(imageHash: string) { - let imageHashes: string[] = JSON.parse(storage.getItem(listKey) ?? '[]') - imageHashes = imageHashes.filter(hash => hash !== imageHash) - imageHashes.push(imageHash) - while (imageHashes.length > maxCachedConfigs) { - storage.removeItem(configKey(imageHashes.shift()!)) - } - storage.setItem(listKey, JSON.stringify(imageHashes)) -} diff --git a/packages/config/src/config.ts b/packages/config/src/config.ts deleted file mode 100644 index c9a8f3415..000000000 --- a/packages/config/src/config.ts +++ /dev/null @@ -1,181 +0,0 @@ -import { ethers, Signer as AbstractSigner } from 'ethers' -import { WalletContext } from '@0xsequence/network' -import { WalletContractBytecode } from './bytecode' -import { cacheConfig } from './cache' - -// WalletConfig is the configuration of key signers that can access -// and control the wallet -export interface WalletConfig { - threshold: number - signers: { - weight: number - address: string - }[] - - address?: string - chainId?: number -} - -export interface WalletState { - context: WalletContext - config?: WalletConfig - - // the wallet address - address: string - - // the chainId of the network - chainId: number - - // whether the wallet has been ever deployed - deployed: boolean - - // the imageHash of the `config` WalletConfig - imageHash: string - - // the last imageHash of a WalletConfig, stored on-chain - lastImageHash?: string - - // whether the WalletConfig object itself has been published to logs - published?: boolean -} - -// TODO: createWalletConfig and genConfig are very similar, lets update + remove one -export const createWalletConfig = async (threshold: number, signers: { weight: number, signer: string | AbstractSigner }[]): Promise => { - const config: WalletConfig = { - threshold, - signers: [] - } - signers.forEach(async s => { - config.signers.push({ - weight: s.weight, - address: AbstractSigner.isSigner(s.signer) ? await s.signer.getAddress() : s.signer, - }) - }) - if (!isUsableConfig(config)) { - throw new Error('wallet config is not usable') - } - return config -} - -// isUsableConfig checks if a the sum of the owners in the configuration meets the necessary threshold to sign a transaction -// a wallet that has a non-usable configuration is not able to perform any transactions, and can be considered as destroyed -export const isUsableConfig = (config: WalletConfig): boolean => { - const sum = config.signers.reduce((p, c) => ethers.BigNumber.from(c.weight).add(p), ethers.constants.Zero) - return sum.gte(ethers.BigNumber.from(config.threshold)) -} - - -export const isValidConfigSigners = (config: WalletConfig, signers: string[]): boolean => { - if (signers.length === 0) return true - const a = config.signers.map(s => ethers.utils.getAddress(s.address)) - const b = signers.map(s => ethers.utils.getAddress(s)) - let valid = true - b.forEach(s => { - if (!a.includes(s)) valid = false - }) - return valid -} - -export const addressOf = (salt: WalletConfig | string, context: WalletContext, ignoreAddress: boolean = false): string => { - if (typeof salt === 'string') { - const codeHash = ethers.utils.keccak256( - ethers.utils.solidityPack(['bytes', 'bytes32'], [WalletContractBytecode, ethers.utils.hexZeroPad(context.mainModule, 32)]) - ) - - const hash = ethers.utils.keccak256( - ethers.utils.solidityPack(['bytes1', 'address', 'bytes32', 'bytes32'], ['0xff', context.factory, salt, codeHash]) - ) - - return ethers.utils.getAddress(ethers.utils.hexDataSlice(hash, 12)) - } - - if (salt.address && !ignoreAddress) return salt.address - return addressOf(imageHash(salt), context) -} - -export const imageHash = (config: WalletConfig): string => { - config = sortConfig(config) - - const imageHash = config.signers.reduce( - (imageHash, signer) => ethers.utils.keccak256( - ethers.utils.defaultAbiCoder.encode( - ['bytes32', 'uint8', 'address'], - [imageHash, signer.weight, signer.address] - ) - ), - ethers.utils.solidityPack(['uint256'], [config.threshold]) - ) - - cacheConfig(imageHash, config) - - return imageHash -} - -// sortConfig normalizes the list of signer addreses in a WalletConfig -export const sortConfig = (config: WalletConfig): WalletConfig => { - config.signers.sort((a, b) => compareAddr(a.address, b.address)) - - // normalize - config.signers.forEach(s => s.address = ethers.utils.getAddress(s.address)) - if (config.address) config.address = ethers.utils.getAddress(config.address) - - // ensure no duplicate signers in the config - const signers = config.signers.map(s => s.address) - const signerDupes = signers.filter((c, i) => signers.indexOf(c) !== i) - if (signerDupes.length > 0) { - throw new Error('invalid wallet config: duplicate signer addresses detected in the config, ${signerDupes}') - } - - return config -} - -export const isConfigEqual = (a: WalletConfig, b: WalletConfig): boolean => { - return imageHash(a) === imageHash(b) -} - -export const compareAddr = (a: string, b: string): number => { - const bigA = ethers.BigNumber.from(a) - const bigB = ethers.BigNumber.from(b) - - if (bigA.lt(bigB)) { - return -1 - } else if (bigA.eq(bigB)) { - return 0 - } else { - return 1 - } -} - -export function editConfig(config: WalletConfig, args: { - threshold?: ethers.BigNumberish, - set?: { weight: ethers.BigNumberish, address: string }[], - del?: { address: string }[] -}): WalletConfig { - const normSigner = (s: { weight: ethers.BigNumberish, address: string }) => ({ weight: ethers.BigNumber.from(s.weight).toNumber(), address: ethers.utils.getAddress(s.address) }) - - const normSrcSigners = config.signers.map(normSigner) - - const normSetSigners = args.set ? args.set.map(normSigner) : [] - const normDelAddress = args.del ? args.del.map((a) => ethers.utils.getAddress(a.address)) : [] - - const normSetAddress = normSetSigners.map((s) => s.address) - - const newSigners = normSrcSigners - .filter((s) => normDelAddress.indexOf(s.address) === -1 && normSetAddress.indexOf(s.address) === -1) - .concat(...normSetSigners) - - return sortConfig({ - address: config.address, - threshold: args.threshold ? ethers.BigNumber.from(args.threshold).toNumber() : config.threshold, - signers: newSigners - }) -} - -// TODO: very similar to createWalletConfig, but doesn't allow an AbstractSigner object -// TODO: lets also check isUsableConfig before returning it -export function genConfig(threshold: ethers.BigNumberish, signers: { weight: ethers.BigNumberish, address: string }[]): WalletConfig { - return sortConfig({ - threshold: ethers.BigNumber.from(threshold).toNumber(), - signers: signers.map((s) => ({ weight: ethers.BigNumber.from(s.weight).toNumber(), address: ethers.utils.getAddress(s.address) })) - }) -} diff --git a/packages/config/src/finder/config-finder.ts b/packages/config/src/finder/config-finder.ts deleted file mode 100644 index 57f983be9..000000000 --- a/packages/config/src/finder/config-finder.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { WalletContext } from '@0xsequence/network' -import { ethers } from 'ethers' -import { WalletConfig } from '..' - -export abstract class ConfigFinder { - findCurrentConfig: (args: { - address: string - provider: ethers.providers.Provider - context: WalletContext - knownConfigs?: WalletConfig[] - }) => Promise<{ config: WalletConfig | undefined }> - findLastWalletOfInitialSigner: (args: { - signer: string - provider: ethers.providers.Provider - context: WalletContext - }) => Promise<{ wallet?: string | undefined }> -} diff --git a/packages/config/src/finder/index.ts b/packages/config/src/finder/index.ts deleted file mode 100644 index a2223b503..000000000 --- a/packages/config/src/finder/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { ConfigFinder } from './config-finder' -export { SequenceUtilsFinder } from './sequence-utils-finder' diff --git a/packages/config/src/finder/sequence-utils-finder.ts b/packages/config/src/finder/sequence-utils-finder.ts deleted file mode 100644 index 08c3117b0..000000000 --- a/packages/config/src/finder/sequence-utils-finder.ts +++ /dev/null @@ -1,254 +0,0 @@ -import { Contract, ethers } from 'ethers' -import { addressOf, imageHash, WalletConfig } from '..' -import { getCachedConfig } from '../cache' -import { ConfigFinder } from './config-finder' -import { walletContracts } from '@0xsequence/abi' -import { WalletContext } from '@0xsequence/network' -import { logger } from '@0xsequence/utils' - -export class SequenceUtilsFinder implements ConfigFinder { - constructor(public authProvider: ethers.providers.Provider) {} - - findCurrentConfig = async (args: { - address: string - provider: ethers.providers.Provider - context: WalletContext - knownConfigs?: WalletConfig[] - ignoreIndex?: boolean - requireIndex?: boolean - skipCache?: boolean - }): Promise<{ config: WalletConfig | undefined }> => { - const { provider, context, ignoreIndex, requireIndex, skipCache } = args - const address = ethers.utils.getAddress(args.address) - - logger.info(`[findCurrentConfig] address:${address}, ignoreIndex:${ignoreIndex}, requireIndex:${requireIndex}`) - - if (requireIndex && ignoreIndex) throw Error(`findCurrentConfig: can't ignore index and require index`) - - const chainIdPromise = provider.getNetwork() - const knownConfigs = args.knownConfigs ? args.knownConfigs : [] - - // Get imageHash of wallet - const { imageHash, config } = await this.findCurrentImageHash(context, provider, address, knownConfigs, skipCache) - if (imageHash === undefined) return { config: undefined } - - // Get config for that imageHash - const found = await this.findConfigForImageHash( - context, - imageHash, - config ? [config, ...knownConfigs] : knownConfigs, - skipCache - ) - const chainId = (await chainIdPromise).chainId - - return { - config: found ? { ...found, chainId, address } : undefined - } - } - - findLastWalletOfInitialSigner = async (args: { - signer: string - provider: ethers.providers.Provider - context: WalletContext - ignoreIndex?: boolean - requireIndex?: boolean - }): Promise<{ wallet: string | undefined }> => { - const { signer, context, ignoreIndex, requireIndex } = args - - logger.info(`[findLastWalletOfInitialSigner] signer:${signer}`) - - if (requireIndex && ignoreIndex) throw Error(`findCurrentConfig: can't ignore index and require index`) - - const authContract = new Contract(context.sequenceUtils!, walletContracts.sequenceUtils.abi, this.authProvider) - const logBlockHeight = ignoreIndex ? 0 : (await authContract.lastSignerUpdate(signer)).toNumber() - if (requireIndex && logBlockHeight === 0) return { wallet: undefined } - const filter = authContract.filters.RequiredSigner(null, signer) - const lastLog = await this.findLatestLog(this.authProvider, { - ...filter, - fromBlock: logBlockHeight, - toBlock: logBlockHeight !== 0 ? logBlockHeight : 'latest' - }) - if (lastLog === undefined) { - logger.warn('publishConfig: wallet config last log not found') - return { wallet: undefined } - } - const event = authContract.interface.decodeEventLog('RequiredSigner', lastLog.data, lastLog.topics) - return { wallet: event._wallet } - } - - findConfigForImageHash = async ( - context: WalletContext, - image: string, - knownConfigs: WalletConfig[] = [], - skipCache: boolean = false - ): Promise => { - // Lookup config in known configurations - const found = knownConfigs.find(kc => imageHash(kc) === image) - if (found) return found - - // Lookup config in cached configurations - if (!skipCache) { - const cached = getCachedConfig(image) - if (cached) { - return cached - } - } - - logger.info(`[findConfigForImageHash] image:${image}`) - - // Load index for last imageHash update - const authContract = new Contract(context.sequenceUtils!, walletContracts.sequenceUtils.abi, this.authProvider) - const imageHashHeight = (await authContract.lastImageHashUpdate(image)).toNumber() as number - - // Get requireConfig with imageHash info - const filter = authContract.filters.RequiredConfig(undefined, image) - const lastLog = await this.findLatestLog(this.authProvider, { - ...filter, - fromBlock: imageHashHeight, - toBlock: imageHashHeight !== 0 ? imageHashHeight : 'latest' - }) - - // If there is no log, and no knownConfig... - // the config is not found - if (lastLog === undefined) return undefined - - const event = authContract.interface.decodeEventLog('RequiredConfig', lastLog.data, lastLog.topics) - const signers = ethers.utils.defaultAbiCoder.decode( - [ - `tuple( - uint256 weight, - address signer - )[]` - ], - event._signers - )[0] - - const config = { - threshold: ethers.BigNumber.from(event._threshold).toNumber(), - signers: signers.map((s: any) => ({ - address: s.signer, - weight: ethers.BigNumber.from(s.weight).toNumber() - })) - } - - // Cache this config - imageHash(config) - - return config - } - - findCurrentImageHash = async ( - context: WalletContext, - provider: ethers.providers.Provider, - address: string, - knownConfigs: WalletConfig[] = [], - skipCache?: boolean - ): Promise<{ imageHash?: string; config?: WalletConfig }> => { - logger.info(`[findCurrentImageHash] address:${address}`) - - const walletContract = new Contract(address, walletContracts.mainModuleUpgradable.abi, provider) - const currentImageHash = (await walletContract.functions.imageHash.call([]).catch(() => [])) as string[] - - // Wallet is not counterfactual and has a defined imageHash - if (currentImageHash[0] !== undefined) { - return { - imageHash: currentImageHash[0], - config: skipCache ? undefined : getCachedConfig(currentImageHash[0]) - } - } - - // Wallet is in counter-factual mode - // Lookup config in known configurations - const normalizedAddress = ethers.utils.getAddress(address) - const found = knownConfigs.find(kc => addressOf(kc, context, true) === normalizedAddress) - if (found) return { imageHash: imageHash(found), config: found } - - // Call wallet index - const authContract = new Contract(context.sequenceUtils!, walletContracts.sequenceUtils.abi, this.authProvider) - const knownImageHash = (await authContract.knownImageHashes(address)) as string - - if (knownImageHash !== ethers.constants.HashZero) { - if (addressOf(knownImageHash, context) !== address) throw Error('findCurrentImageHash: inconsistent RequireUtils results') - return { imageHash: knownImageHash } - } - - // Get known image hash from raw logs, as last resort - const filter = authContract.filters.RequiredConfig(address) - const log = await this.findFirstLog(this.authProvider, filter) - - if (log !== undefined) { - const event = authContract.interface.decodeEventLog('RequiredConfig', log.data, log.topics) - const signers = ethers.utils.defaultAbiCoder.decode( - [ - `tuple( - uint256 weight, - address signer - )[]` - ], - event._signers - )[0] - - const config = { - threshold: ethers.BigNumber.from(event._threshold).toNumber(), - signers: signers.map((s: any) => ({ - address: s.signer, - weight: ethers.BigNumber.from(s.weight).toNumber() - })) - } - - const gotImageHash = imageHash(config) - if (addressOf(gotImageHash, context) === address) { - return { imageHash: gotImageHash, config } - } - } - - // Counter-factual imageHash not found - return {} - } - - private findLatestLog = async ( - provider: ethers.providers.Provider, - filter: ethers.providers.Filter - ): Promise => { - const toBlock = filter.toBlock === 'latest' ? await provider.getBlockNumber() : (filter.toBlock as number) - const fromBlock = filter.fromBlock as number - - if (fromBlock === 0) { - logger.warn(`findLatestLog: expensive getLogs query fromBlock 0 toBlock ${toBlock}`) - } - - try { - const logs = await provider.getLogs({ ...filter, toBlock: toBlock }) - return logs.length === 0 ? undefined : logs[logs.length - 1] - } catch (e) { - // TODO Don't assume all errors are bad - const pivot = Math.floor((toBlock - fromBlock) / 2 + fromBlock) - const nhalf = await this.findLatestLog(provider, { ...filter, fromBlock: pivot, toBlock: toBlock }) - if (nhalf !== undefined) return nhalf - return this.findLatestLog(provider, { ...filter, fromBlock: fromBlock, toBlock: pivot }) - } - } - - private findFirstLog = async ( - provider: ethers.providers.Provider, - filter: ethers.providers.Filter - ): Promise => { - const toBlock = filter.toBlock === 'latest' || !filter.toBlock ? await provider.getBlockNumber() : (filter.toBlock as number) - const fromBlock = filter.fromBlock ? (filter.fromBlock as number) : 0 - - if (fromBlock === 0) { - logger.warn(`findFirstLog: expensive getLogs query fromBlock 0 toBlock ${toBlock}`) - } - - try { - const logs = await provider.getLogs({ ...filter, fromBlock, toBlock }) - return logs.length === 0 ? undefined : logs[0] - } catch (e) { - // TODO Don't assume all errors are bad - const pivot = Math.floor((toBlock - fromBlock) / 2 + fromBlock) - const nhalf = await this.findFirstLog(provider, { ...filter, fromBlock, toBlock: pivot }) - if (nhalf !== undefined) return nhalf - return this.findFirstLog(provider, { ...filter, fromBlock: pivot, toBlock }) - } - } -} diff --git a/packages/config/src/index.ts b/packages/config/src/index.ts deleted file mode 100644 index 551583a5f..000000000 --- a/packages/config/src/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './bytecode' -export * from './config' -export * from './finder' -export * from './signature' diff --git a/packages/config/src/signature.ts b/packages/config/src/signature.ts deleted file mode 100644 index e540f304c..000000000 --- a/packages/config/src/signature.ts +++ /dev/null @@ -1,384 +0,0 @@ -import * as multicall from '@0xsequence/multicall' -import { BytesLike, ethers } from 'ethers' -import { WalletConfig } from "." - -export type DecodedSignature = { - threshold: number - signers: DecodedSignaturePart[] -} - -export type DecodedSignaturePart = DecodedAddressPart | DecodedEOASigner | DecodedEOASplitSigner | DecodedFullSigner - -export type DecodedAddressPart = { - weight: number - address: string -} - -export type DecodedEOASigner = { - weight: number - signature: ethers.BytesLike -} - -export type DecodedEOASplitSigner = { - weight: number - r: string - s: string - v: number - t: number -} - -export type DecodedFullSigner = { - weight: number - address: string - signature: ethers.BytesLike -} - -export function isDecodedAddress(cand: DecodedSignaturePart): cand is DecodedAddressPart { - const c = cand as any; return c.address !== undefined && !isDecodedSigner(cand) -} - -export function isDecodedSigner(cand: DecodedSignaturePart): cand is DecodedEOASigner | DecodedEOASplitSigner | DecodedFullSigner { - return isDecodedEOASigner(cand) || isDecodedEOASplitSigner(cand) || isDecodedFullSigner(cand) -} - -export function isDecodedEOASigner(cand: DecodedSignaturePart): cand is DecodedEOASigner { - const c = cand as any - - return ( - c.signature !== undefined && - c.address === undefined - ) -} - -export function isDecodedEOASplitSigner(cand: DecodedSignaturePart): cand is DecodedEOASplitSigner { - const c = cand as any - - return ( - c.r !== undefined && - c.s !== undefined && - c.v !== undefined && - c.t !== undefined - ) -} - -export function isDecodedFullSigner(cand: DecodedSignaturePart): cand is DecodedFullSigner { - const c = cand as any - - return ( - c.address !== undefined && - c.signature !== undefined - ) -} - -export enum SignatureType { - EOA = 0, - Address = 1, - Full = 2 -} - - -export const decodeSignature = (signature: string | DecodedSignature): DecodedSignature => { - if (typeof signature !== 'string') return signature - - const auxsig = signature.replace('0x', '') - - const threshold = ethers.BigNumber.from(`0x${auxsig.slice(0, 4)}`).toNumber() - - const signers: DecodedSignaturePart[] = [] - - for (let rindex = 4; rindex < auxsig.length; ) { - const signatureType = ethers.BigNumber.from(auxsig.slice(rindex, rindex + 2)).toNumber() as SignatureType - rindex += 2 - - const weight = ethers.BigNumber.from(`0x${auxsig.slice(rindex, rindex + 2)}`).toNumber() - rindex += 2 - - switch (signatureType) { - case SignatureType.Address: - const addr = ethers.utils.getAddress(auxsig.slice(rindex, rindex + 40)) - rindex += 40 - - signers.push({ - weight: weight, - address: addr - }) - break; - - case SignatureType.EOA: - const sig = ethers.utils.arrayify(`0x${auxsig.slice(rindex, rindex + 132)}`) - rindex += 132 - - const split = ethers.utils.splitSignature(sig.slice(0, 65)) - const r = split.r - const s = split.s - const v = split.v - - const t = ethers.BigNumber.from(sig[sig.length - 1]).toNumber() - - signers.push({ - weight: weight, - signature: sig, - r: r, - s: s, - v: v, - t: t - }) - - break - - case SignatureType.Full: - const address = ethers.utils.getAddress(auxsig.slice(rindex, rindex + 40)) - rindex += 40 - - const size = ethers.BigNumber.from(`0x${auxsig.slice(rindex, rindex + 4)}`).mul(2).toNumber() - rindex += 4 - - const signature = ethers.utils.arrayify(`0x${auxsig.slice(rindex, rindex + size)}`) - rindex += size - - signers.push({ - weight: weight, - address: address, - signature: signature - }) - break - - default: - throw Error('Signature type not supported') - } - } - - return { - threshold: threshold, - signers: signers - } -} - -const SIG_TYPE_EIP712 = 1 -const SIG_TYPE_ETH_SIGN = 2 -const SIG_TYPE_WALLET_BYTES32 = 3 - -export const splitDecodedEOASigner = (sig: DecodedEOASigner): DecodedEOASplitSigner => { - const signature = ethers.utils.arrayify(sig.signature) - const split = ethers.utils.splitSignature(signature.slice(0, 65)) - const t = ethers.BigNumber.from(signature[signature.length - 1]).toNumber() - - return { - ...sig, - ...split, - t: t - } -} - -export const recoverEOASigner = (digest: BytesLike, sig: DecodedEOASigner | DecodedEOASplitSigner) => { - const signature = isDecodedEOASplitSigner(sig) ? sig : splitDecodedEOASigner(sig) - - switch (signature.t) { - case SIG_TYPE_EIP712: - return ethers.utils.recoverAddress(digest, { - r: signature.r, - s: signature.s, - v: signature.v - }) - case SIG_TYPE_ETH_SIGN: - const subDigest = ethers.utils.keccak256( - ethers.utils.solidityPack( - ['string', 'bytes32'], - ['\x19Ethereum Signed Message:\n32', digest] - ) - ) - - return ethers.utils.recoverAddress(subDigest, { - r: signature.r, - s: signature.s, - v: signature.v - }) - default: - throw new Error('Unknown signature') - } -} - -export const joinSignatures = (...signatures: Array): DecodedSignature => { - const parts = signatures.map((s) => typeof s === 'string' ? decodeSignature(s) : s) - return parts.reduce((p, c) => joinTwoSignatures(p, c)) -} - -export const joinTwoSignatures = (a: DecodedSignature, b: DecodedSignature): DecodedSignature => { - return { threshold: a.threshold, signers: a.signers.map((s, i) => isDecodedAddress(s) ? b.signers[i] : s) } -} - -export const encodeSignature = (sig: DecodedSignature | string): string => { - if (typeof sig === 'string') return encodeSignature(decodeSignature(sig)) - - const accountBytes = sig.signers.map(s => { - if (isDecodedAddress(s)) { - return ethers.utils.solidityPack( - ['uint8', 'uint8', 'address'], - [SignatureType.Address, s.weight, s.address] - ) - } - - if (isDecodedEOASplitSigner(s)) { - return ethers.utils.solidityPack( - ['uint8', 'uint8', 'bytes32', 'bytes32', 'uint8', 'uint8'], - [SignatureType.EOA, s.weight, s.r, s.s, s.v, s.t] - ) - } - - if (isDecodedFullSigner(s)) { - const signatureSize = ethers.utils.arrayify(s.signature).length - return ethers.utils.solidityPack( - ['uint8', 'uint8', 'address', 'uint16', 'bytes'], - [SignatureType.Full, s.weight, s.address, signatureSize, s.signature] - ) - } - - if (isDecodedEOASigner(s)) { - return ethers.utils.solidityPack( - ['uint8', 'uint8', 'bytes'], - [SignatureType.EOA, s.weight, s.signature] - ) - } - - throw Error('Unkwnown signature part type') - }) - - return ethers.utils.solidityPack(['uint16', ...Array(accountBytes.length).fill('bytes')], [sig.threshold, ...accountBytes]) -} - -export function signerOf(part: DecodedSignaturePart, digest: BytesLike): string { - if (isDecodedAddress(part)) { - return part.address - } - - if (isDecodedFullSigner(part)) { - return part.address - } - - if (isDecodedEOASplitSigner(part) || isDecodedEOASigner(part)) { - return recoverEOASigner(digest, part) - } - - throw Error('Unkwnown signature part type') -} - -export function mutateSignature(sig: DecodedSignature, config: WalletConfig, digest: BytesLike): DecodedSignature { - const allSigners = sig.signers.map((s) => signerOf(s, digest)) - - return { - threshold: config.threshold, - signers: config.signers.map((s) => { - const found = allSigners.indexOf(s.address) - if (found !== -1) { - const part = sig.signers[found] - return { ...part, weight: s.weight } - } - - return { - weight: s.weight, - address: s.address - } - }) - } -} - -export async function buildStubSignature( - provider: ethers.providers.Provider, - config: WalletConfig -): Promise { - const multicallProvider = new multicall.providers.MulticallProvider(provider) - - // Pre-load if signers are EOAs or not - const signers = await Promise.all( - config.signers.map(async (s, i) => { - return { - ...s, - index: i, - isEOA: ethers.utils.arrayify((await multicallProvider.getCode(s.address))).length === 0 - } - }) - ) - - // Sort signers by weight - // and prepare them for selection - let sortedSigners: { - weight: number, - index: number, - address: string, - isEOA: boolean, - willSign?: boolean - }[] = signers.sort((a, b) => a.weight - b.weight) - - // Keep track of the total signing power - let totalWeight = 0 - - // First pick non-eoa signers - sortedSigners = sortedSigners.map((s) => { - if (totalWeight >= config.threshold || s.isEOA) return s - - totalWeight += s.weight - return { ...s, willSign: true } - }) - - // If we still haven't reached threshold - // start picking non-EOA signers - if (totalWeight < config.threshold) { - sortedSigners = sortedSigners.map((s) => { - if (s.willSign || totalWeight >= config.threshold) return s - - totalWeight += s.weight - return { ...s, willSign: true } - }) - } - - // Stub signature part - // pre-determined signature, tailored for worse-case scenario in gas costs - const stubSig = ethers.utils.arrayify("0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a01b02") - - // Re-sort signers by original index - const finalSigners = sortedSigners.sort((a, b) => a.index - b.index) - - // Map final signers to signature parts - return { - threshold: config.threshold, - signers: finalSigners.map((s) => { - // If wallet shouldn't sign - // just return address part - if (!s.willSign) { - return { - address: s.address, - weight: s.weight, - } as DecodedAddressPart - } - - // If wallet is EOA return signature - // part is with stubSign - if (s.isEOA) { - return { - weight: s.weight, - signature: stubSig, - } as DecodedEOASigner - } - - // If wallet is a contract - // build a stub nested signature - return { - weight: s.weight, - address: s.address, - signature: encodeSignature({ - threshold: 1, - signers: [ - { - address: ethers.Wallet.createRandom().address, - weight: 1, - }, - { - weight: 1, - signature: stubSig - } - ] - }) + ethers.utils.hexlify(SIG_TYPE_WALLET_BYTES32).substring(2) - } - }) - } -} diff --git a/packages/config/tests/sequence-utils-finder.spec.ts b/packages/config/tests/sequence-utils-finder.spec.ts deleted file mode 100644 index a57bb2497..000000000 --- a/packages/config/tests/sequence-utils-finder.spec.ts +++ /dev/null @@ -1,564 +0,0 @@ -import { deployWalletContext } from './utils/deploy-wallet-context' - -import { CallReceiverMock, HookCallerMock } from '@0xsequence/wallet-contracts' - -import { LocalRelayer } from '@0xsequence/relayer' -import { Wallet } from '@0xsequence/wallet' -import { WalletContext } from '@0xsequence/network' -import { ethers, Signer as AbstractSigner } from 'ethers' - -import chaiAsPromised from 'chai-as-promised' -import * as chai from 'chai' -import { imageHash, SequenceUtilsFinder, sortConfig, WalletConfig } from '../src' -import { getCachedConfig } from '../src/cache' - -const CallReceiverMockArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/mocks/CallReceiverMock.sol/CallReceiverMock.json') -const HookCallerMockArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/mocks/HookCallerMock.sol/HookCallerMock.json') - -const { expect } = chai.use(chaiAsPromised) - - -type EthereumInstance = { - chainId: number - providerUrl: string - provider: ethers.providers.JsonRpcProvider - signer: AbstractSigner - relayer?: LocalRelayer - callReceiver?: CallReceiverMock - hookCaller?: HookCallerMock -} - -describe('Wallet integration', function () { - let ethnode: EthereumInstance[] = [] - - let authChain: EthereumInstance - let mainChain: EthereumInstance - - let context: WalletContext - - before(async () => { - const nodeA = "http://127.0.0.1:7547/" - const providerA = new ethers.providers.JsonRpcProvider(nodeA) - const signerA = providerA.getSigner() - - const nodeB = "http://127.0.0.1:7548/" - const providerB = new ethers.providers.JsonRpcProvider(nodeB) - const signerB = providerB.getSigner() - - // Create network instances - ethnode = [{ - providerUrl: nodeA, - chainId: 31337, - provider: providerA, - signer: signerA - }, { - providerUrl: nodeB, - chainId: 31337, - provider: providerB, - signer: signerB - }] - - authChain = ethnode[0] - mainChain = ethnode[1] - - // Deploy local relayer - await Promise.all(ethnode.map(async (en) => { - en.relayer = new LocalRelayer(en.signer) - - // Deploy Sequence env - const [ - factory, - mainModule, - mainModuleUpgradable, - guestModule, - sequenceUtils, - requireFreshSigner - ] = await deployWalletContext(en.provider) - - if (context) { - expect(context.factory).to.equal(factory.address) - expect(context.mainModule).to.equal(mainModule.address) - expect(context.mainModuleUpgradable).to.equal(mainModuleUpgradable.address) - expect(context.guestModule).to.equal(guestModule.address) - expect(context.sequenceUtils).to.equal(sequenceUtils.address) - } else { - // Create fixed context obj - context = { - factory: factory.address, - mainModule: mainModule.address, - mainModuleUpgradable: mainModuleUpgradable.address, - guestModule: guestModule.address, - sequenceUtils: sequenceUtils.address, - libs: { - requireFreshSigner: requireFreshSigner.address - } - } - } - - // Deploy call receiver mock - en.callReceiver = (await new ethers.ContractFactory( - CallReceiverMockArtifact.abi, - CallReceiverMockArtifact.bytecode, - en.signer - ).deploy()) as CallReceiverMock - - // Deploy hook caller mock - en.hookCaller = (await new ethers.ContractFactory( - HookCallerMockArtifact.abi, - HookCallerMockArtifact.bytecode, - en.signer - ).deploy()) as HookCallerMock - })) - }) - - describe('Retrieve configuration', () => { - it('Find counterfactual wallet using known configs', async () => { - const wallet = await Wallet.singleOwner(ethers.Wallet.createRandom(), context) - const finder = new SequenceUtilsFinder(authChain.provider) - - const found = await finder.findCurrentConfig( - { address: wallet.address, - provider: mainChain.provider, - context: wallet.context, - knownConfigs: [ - (await Wallet.singleOwner(ethers.Wallet.createRandom())).config, - wallet.config, - (await Wallet.singleOwner(ethers.Wallet.createRandom())).config, - (await Wallet.singleOwner(ethers.Wallet.createRandom())).config - ] - } - ) - - expect(imageHash(found.config!)).to.equal(imageHash(wallet.config)) - }) - it('Fail to find counterfactual wallet without known configs', async () => { - const wallet = await Wallet.singleOwner(ethers.Wallet.createRandom(), context) - const finder = new SequenceUtilsFinder(authChain.provider) - - const found = await finder.findCurrentConfig( - { address: wallet.address, - provider: mainChain.provider, - context: wallet.context, - knownConfigs: [ - (await Wallet.singleOwner(ethers.Wallet.createRandom())).config, - (await Wallet.singleOwner(ethers.Wallet.createRandom())).config, - (await Wallet.singleOwner(ethers.Wallet.createRandom())).config - ] - } - ) - - expect(found.config).to.be.undefined - }) - it('Find counterfactual wallet after deployment', async () => { - const wallet = new Wallet({ context, config: { threshold: 2, signers: [{ weight: 3, address: ethers.Wallet.createRandom().address }, { weight: 1, address: ethers.Wallet.createRandom().address }] }}) - const finder = new SequenceUtilsFinder(authChain.provider) - - await mainChain.relayer!.deployWallet(wallet.config, wallet.context) - - const found = await finder.findCurrentConfig( - { address: wallet.address, - provider: mainChain.provider, - context: wallet.context, - knownConfigs: [ - wallet.config, - (await Wallet.singleOwner(ethers.Wallet.createRandom())).config, - (await Wallet.singleOwner(ethers.Wallet.createRandom())).config, - (await Wallet.singleOwner(ethers.Wallet.createRandom())).config - ] - } - ) - - expect(imageHash(found.config!)).to.equal(imageHash(wallet.config)) - }) - it('Find counterfactual wallet after deployment on authChain', async () => { - const wallet = new Wallet({ context, config: { threshold: 2, signers: [{ weight: 3, address: ethers.Wallet.createRandom().address }, { weight: 1, address: ethers.Wallet.createRandom().address }] }}) - const finder = new SequenceUtilsFinder(authChain.provider) - - await authChain.relayer!.deployWallet(wallet.config, wallet.context) - - const found = await finder.findCurrentConfig( - { address: wallet.address, - provider: mainChain.provider, - context: wallet.context, - knownConfigs: [ - wallet.config, - (await Wallet.singleOwner(ethers.Wallet.createRandom())).config, - (await Wallet.singleOwner(ethers.Wallet.createRandom())).config, - (await Wallet.singleOwner(ethers.Wallet.createRandom())).config - ] - } - ) - - expect(imageHash(found.config!)).to.equal(imageHash(wallet.config)) - }) - it('Find counterfactual wallet after deployment and update on authChain (indexed)', async () => { - const signer1 = ethers.Wallet.createRandom() - const signer2 = ethers.Wallet.createRandom() - let wallet = new Wallet({ context, config: { threshold: 2, signers: [{ weight: 3, address: signer1.address }, { weight: 1, address: signer2.address }] }}, signer1, signer2) - const finder = new SequenceUtilsFinder(authChain.provider) - - const newConfig = { threshold: 1, signers: [{ weight: 2, address: ethers.Wallet.createRandom().address }] } - wallet = wallet.connect(authChain.provider, authChain.relayer) - await wallet.publishConfig(true) - await wallet.updateConfig(newConfig, undefined, true, true) - - const found = await finder.findCurrentConfig( - { address: wallet.address, - provider: mainChain.provider, - context: wallet.context, - knownConfigs: [ - (await Wallet.singleOwner(ethers.Wallet.createRandom())).config, - (await Wallet.singleOwner(ethers.Wallet.createRandom())).config, - (await Wallet.singleOwner(ethers.Wallet.createRandom())).config - ] - } - ) - - expect(found).to.not.be.undefined - expect(imageHash(found.config!)).to.equal(imageHash(wallet.config)) - }) - it('Find counterfactual wallet after deployment and update on authChain (not-indexed)', async () => { - const signer1 = ethers.Wallet.createRandom() - const signer2 = ethers.Wallet.createRandom() - let wallet = new Wallet({ context, config: { threshold: 2, signers: [{ weight: 3, address: signer1.address }, { weight: 1, address: signer2.address }] }}, signer1, signer2) - const finder = new SequenceUtilsFinder(authChain.provider) - - const newConfig = { threshold: 1, signers: [{ weight: 2, address: ethers.Wallet.createRandom().address }] } - wallet = wallet.connect(authChain.provider, authChain.relayer) - await wallet.publishConfig(false) - await wallet.updateConfig(newConfig, undefined, true, false) - - const found = await finder.findCurrentConfig( - { address: wallet.address, - provider: mainChain.provider, - context: wallet.context, - knownConfigs: [ - (await Wallet.singleOwner(ethers.Wallet.createRandom())).config, - (await Wallet.singleOwner(ethers.Wallet.createRandom())).config, - (await Wallet.singleOwner(ethers.Wallet.createRandom())).config - ] - } - ) - - expect(found?.config).to.not.be.undefined - expect(imageHash(found.config!)).to.equal(imageHash(wallet.config)) - }) - it('Fail to find counterfactual wallet after deployment and update on authChain if og config is not published', async () => { - const signer1 = ethers.Wallet.createRandom() - const signer2 = ethers.Wallet.createRandom() - let wallet = new Wallet({ context, config: { threshold: 2, signers: [{ weight: 3, address: signer1.address }, { weight: 1, address: signer2.address }] }}, signer1, signer2) - const finder = new SequenceUtilsFinder(authChain.provider) - - const newConfig = { threshold: 1, signers: [{ weight: 2, address: ethers.Wallet.createRandom().address }] } - wallet = wallet.connect(authChain.provider, authChain.relayer) - await wallet.updateConfig(newConfig, undefined, true, false) - - const found = await finder.findCurrentConfig( - { address: wallet.address, - provider: mainChain.provider, - context: wallet.context, - knownConfigs: [ - (await Wallet.singleOwner(ethers.Wallet.createRandom())).config, - (await Wallet.singleOwner(ethers.Wallet.createRandom())).config, - (await Wallet.singleOwner(ethers.Wallet.createRandom())).config - ] - } - ) - - expect(found.config).to.be.undefined - }) - it('Find wallet configuration after update on both chains (indexed)', async () => { - const signer1 = ethers.Wallet.createRandom() - const signer2 = ethers.Wallet.createRandom() - - let wallet = new Wallet({ context, config: { threshold: 2, signers: [{ weight: 3, address: signer1.address }, { weight: 1, address: signer2.address }] }}, signer1, signer2) - - const finder = new SequenceUtilsFinder(authChain.provider) - - await authChain.relayer!.deployWallet(wallet.config, wallet.context) - await mainChain.relayer!.deployWallet(wallet.config, wallet.context) - - const newConfig = { threshold: 1, signers: [{ weight: 2, address: ethers.Wallet.createRandom().address }] } - - await wallet.connect(mainChain.provider, mainChain.relayer).updateConfig(newConfig) - wallet = wallet.connect(authChain.provider, authChain.relayer) - await wallet.updateConfig(newConfig, undefined, true, true) - - const found = await finder.findCurrentConfig( - { - address: wallet.address, - provider: mainChain.provider, - context: wallet.context - } - ) - - expect(imageHash(found.config!)).to.equal(imageHash(newConfig)) - }) - it('Find wallet configuration after update on both chains (not-indexed)', async () => { - const signer1 = ethers.Wallet.createRandom() - const signer2 = ethers.Wallet.createRandom() - - let wallet = new Wallet({ context, config: { threshold: 2, signers: [{ weight: 3, address: signer1.address }, { weight: 1, address: signer2.address }] }}, signer1, signer2) - - const finder = new SequenceUtilsFinder(authChain.provider) - - await authChain.relayer!.deployWallet(wallet.config, wallet.context) - await mainChain.relayer!.deployWallet(wallet.config, wallet.context) - - const newConfig = { threshold: 1, signers: [{ weight: 2, address: ethers.Wallet.createRandom().address }] } - - await wallet.connect(mainChain.provider, mainChain.relayer).updateConfig(newConfig) - wallet = wallet.connect(authChain.provider, authChain.relayer) - await wallet.updateConfig(newConfig, undefined, true, false) - - const found = await finder.findCurrentConfig( - { - address: wallet.address, - provider: mainChain.provider, - context: wallet.context - } - ) - - expect(imageHash(found.config!)).to.equal(imageHash(newConfig)) - }) - it('Fail to find wallet configuration after update on both chains (not-published)', async () => { - const signer1 = ethers.Wallet.createRandom() - const signer2 = ethers.Wallet.createRandom() - - let wallet = new Wallet({ context, config: { threshold: 2, signers: [{ weight: 3, address: signer1.address }, { weight: 1, address: signer2.address }] }}, signer1, signer2) - - const finder = new SequenceUtilsFinder(authChain.provider) - - await authChain.relayer!.deployWallet(wallet.config, wallet.context) - await mainChain.relayer!.deployWallet(wallet.config, wallet.context) - - const newConfig = { threshold: 1, signers: [{ weight: 2, address: ethers.Wallet.createRandom().address }] } - - await wallet.connect(mainChain.provider, mainChain.relayer).updateConfig(newConfig) - wallet = wallet.connect(authChain.provider, authChain.relayer) - await wallet.updateConfig(newConfig) - - const found = await finder.findCurrentConfig( - { - address: wallet.address, - provider: mainChain.provider, - context: wallet.context, - skipCache: true - } - ) - - expect(found.config).to.be.undefined - }) - it('Find wallet configuration after asymetric update on both chains (indexed)', async () => { - const signer1 = ethers.Wallet.createRandom() - const signer2 = ethers.Wallet.createRandom() - - let wallet = new Wallet({ context, config: { threshold: 2, signers: [{ weight: 3, address: signer1.address }, { weight: 1, address: signer2.address }] }}, signer1, signer2) - - const finder = new SequenceUtilsFinder(authChain.provider) - - const newConfigA = { threshold: 1, signers: [{ weight: 2, address: signer1.address }] } - const newConfigB = { threshold: 1, signers: [{ weight: 2, address: signer2.address }] } - - // Update wallet on mainChain to newConfigA - await wallet.connect(mainChain.provider, mainChain.relayer).updateConfig(newConfigA) - - // Update wallet on authChain to newConfigB - wallet = wallet.connect(authChain.provider, authChain.relayer) - await wallet.publishConfig(true) - await wallet.updateConfig(newConfigA, undefined, true, true) - - const updatedWallet = new Wallet({ context, config: { address: wallet.address, ...newConfigA } }, signer1).connect(authChain.provider, authChain.relayer) - await updatedWallet.updateConfig(newConfigB, undefined, true, true) - - // Get config on mainChain - const found = await finder.findCurrentConfig( - { - address: wallet.address, - provider: mainChain.provider, - context: wallet.context - } - ) - - expect(imageHash(found.config!)).to.equal(imageHash(newConfigA)) - }) - it('Find wallet configuration after asymetric update on both chains (not-indexed)', async () => { - const signer1 = ethers.Wallet.createRandom() - const signer2 = ethers.Wallet.createRandom() - - let wallet = new Wallet({ context, config: { threshold: 2, signers: [{ weight: 3, address: signer1.address }, { weight: 1, address: signer2.address }] }}, signer1, signer2) - - const finder = new SequenceUtilsFinder(authChain.provider) - - const newConfigA = { threshold: 1, signers: [{ weight: 2, address: signer1.address }] } - const newConfigB = { threshold: 1, signers: [{ weight: 2, address: signer2.address }] } - - // Update wallet on mainChain to newConfigA - await wallet.connect(mainChain.provider, mainChain.relayer).updateConfig(newConfigA) - - // Update wallet on authChain to newConfigB - wallet = wallet.connect(authChain.provider, authChain.relayer) - await wallet.publishConfig(true) - await wallet.updateConfig(newConfigA, undefined, true, true) - - const updatedWallet = new Wallet({ context, config: { address: wallet.address, ...newConfigA } }, signer1).connect(authChain.provider, authChain.relayer) - await updatedWallet.updateConfig(newConfigB, undefined, true, true) - - // Get config on mainChain - const found = await finder.findCurrentConfig( - { - address: wallet.address, - provider: mainChain.provider, - context: wallet.context - } - ) - - expect(imageHash(found.config!)).to.equal(imageHash(newConfigA)) - }) - it('Find wallet configuration after asymetric update on both chains (not published, from known configs)', async () => { - const signer1 = ethers.Wallet.createRandom() - const signer2 = ethers.Wallet.createRandom() - - let wallet = new Wallet({ context, config: { threshold: 2, signers: [{ weight: 3, address: signer1.address }, { weight: 1, address: signer2.address }] }}, signer1, signer2) - - const finder = new SequenceUtilsFinder(authChain.provider) - - const newConfigA = { threshold: 1, signers: [{ weight: 2, address: signer1.address }] } - const newConfigB = { threshold: 1, signers: [{ weight: 2, address: signer2.address }] } - - // Update wallet on mainChain to newConfigA - await wallet.connect(mainChain.provider, mainChain.relayer).updateConfig(newConfigA) - - // Update wallet on authChain to newConfigB - wallet = wallet.connect(authChain.provider, authChain.relayer) - await wallet.publishConfig(true) - await wallet.updateConfig(newConfigA) - - const updatedWallet = new Wallet({ context, config: { address: wallet.address, ...newConfigA } }, signer1).connect(authChain.provider, authChain.relayer) - await updatedWallet.updateConfig(newConfigB, undefined, true, true) - - // Get config on mainChain - const found = await finder.findCurrentConfig( - { - address: wallet.address, - provider: mainChain.provider, - context: wallet.context, - knownConfigs: [ - newConfigA, - newConfigA, - { threshold: 2, signers: [{ weight: 3, address: signer1.address }, { weight: 1, address: signer2.address }] } - ] - } - ) - - expect(imageHash(found.config!)).to.equal(imageHash(newConfigA)) - }) - it('Fail to find wallet configuration after asymetric update on both chains if config is not published', async () => { - const signer1 = ethers.Wallet.createRandom() - const signer2 = ethers.Wallet.createRandom() - - let wallet = new Wallet({ context, config: { threshold: 2, signers: [{ weight: 3, address: signer1.address }, { weight: 1, address: signer2.address }] }}, signer1, signer2) - - const finder = new SequenceUtilsFinder(authChain.provider) - - const newConfigA = { threshold: 1, signers: [{ weight: 2, address: signer1.address }] } - const newConfigB = { threshold: 1, signers: [{ weight: 2, address: signer2.address }] } - - // Update wallet on mainChain to newConfigA - await wallet.connect(mainChain.provider, mainChain.relayer).updateConfig(newConfigA) - - // Update wallet on authChain to newConfigB - wallet = wallet.connect(authChain.provider, authChain.relayer) - await wallet.publishConfig(true) - await wallet.updateConfig(newConfigA) - - const updatedWallet = new Wallet({ context, config: { address: wallet.address, ...newConfigA } }, signer1).connect(authChain.provider, authChain.relayer) - await updatedWallet.updateConfig(newConfigB, undefined, true, true) - - // Get config on mainChain - const found = await finder.findCurrentConfig( - { - address: wallet.address, - provider: mainChain.provider, - context: wallet.context, - skipCache: true - } - ) - - expect(found.config).to.be.undefined - }) - it('Cache wallet configuration after instantiation or update', async () => { - // non-caching version of imageHash for testing - const imageHash = (config: WalletConfig): string => { - return sortConfig(config).signers.reduce( - (imageHash, signer) => ethers.utils.keccak256( - ethers.utils.defaultAbiCoder.encode( - ['bytes32', 'uint8', 'address'], - [imageHash, signer.weight, signer.address] - ) - ), - ethers.utils.solidityPack(['uint256'], [config.threshold]) - ) - } - - const signer1 = ethers.Wallet.createRandom() - const signer2 = ethers.Wallet.createRandom() - const signer3 = ethers.Wallet.createRandom() - - const initialConfig = { - threshold: 1, - signers: [ - { weight: 1, address: signer1.address } - ] - } - - const nextConfig = { - threshold: 2, - signers: [ - { weight: 1, address: signer1.address }, - { weight: 1, address: signer2.address } - ] - } - - const finalConfig = { - threshold: 3, - signers: [ - { weight: 1, address: signer1.address }, - { weight: 1, address: signer2.address }, - { weight: 1, address: signer3.address } - ] - } - - // none of these configs are cached - expect(getCachedConfig(imageHash(initialConfig))).to.be.undefined - expect(getCachedConfig(imageHash(nextConfig))).to.be.undefined - expect(getCachedConfig(imageHash(finalConfig))).to.be.undefined - - // create wallet with initialConfig - let wallet = new Wallet({ context, config: initialConfig }, signer1) - wallet = wallet.connect(authChain.provider, authChain.relayer) - - // only initialConfig should be cached - expect(getCachedConfig(imageHash(initialConfig))).to.not.be.undefined - expect(getCachedConfig(imageHash(nextConfig))).to.be.undefined - expect(getCachedConfig(imageHash(finalConfig))).to.be.undefined - - // update config to nextConfig - await wallet.publishConfig(true) - await wallet.updateConfig(nextConfig) - - // now nextConfig should also be cached - expect(getCachedConfig(imageHash(initialConfig))).to.not.be.undefined - expect(getCachedConfig(imageHash(nextConfig))).to.not.be.undefined - expect(getCachedConfig(imageHash(finalConfig))).to.be.undefined - - // Wallet.useConfig should also cache - wallet = wallet.useConfig(finalConfig) - - // finally finalConfig should be cached - expect(getCachedConfig(imageHash(initialConfig))).to.not.be.undefined - expect(getCachedConfig(imageHash(nextConfig))).to.not.be.undefined - expect(getCachedConfig(imageHash(finalConfig))).to.not.be.undefined - }) - }) -}) diff --git a/packages/config/tests/signature.spec.ts b/packages/config/tests/signature.spec.ts deleted file mode 100644 index 46ab48d35..000000000 --- a/packages/config/tests/signature.spec.ts +++ /dev/null @@ -1,296 +0,0 @@ -import hardhat from 'hardhat' -import chaiAsPromised from 'chai-as-promised' -import * as chai from 'chai' - -import { deployWalletContext } from './utils/deploy-wallet-context' - -import { - CallReceiverMock, - HookCallerMock, - CallReceiverMock__factory, - HookCallerMock__factory -} from '@0xsequence/wallet-contracts' - -import { LocalRelayer } from '@0xsequence/relayer' -import { Wallet } from '@0xsequence/wallet' -import { WalletContext } from '@0xsequence/network' -import { ethers, Signer as AbstractSigner, providers } from 'ethers' - -import { - buildStubSignature, decodeSignature, isDecodedEOASigner, mutateSignature, WalletConfig, - encodeSignature, isDecodedAddress, isDecodedFullSigner -} from '../src' - -import { encodeData } from '@0xsequence/wallet/tests/utils' -import { encodeNonce } from '@0xsequence/transactions' - -const { expect } = chai.use(chaiAsPromised) - -type EthereumInstance = { - chainId: number - provider: providers.JsonRpcProvider - signer: AbstractSigner - relayer: LocalRelayer - callReceiver: CallReceiverMock - hookCaller: HookCallerMock -} - -describe('Signature tools', function () { - let chain: EthereumInstance = {} as any - - let context: WalletContext - - before(async () => { - const providerA = new ethers.providers.Web3Provider(hardhat.network.provider.send) - const signerA = providerA.getSigner() - - chain = { - chainId: 31337, - provider: providerA, - signer: signerA - } as any - - // Deploy local relayer - chain.relayer = new LocalRelayer(chain.signer) - - // Deploy Sequence env - const [ - factory, - mainModule, - mainModuleUpgradable, - guestModule, - sequenceUtils, - requireFreshSigner - ] = await deployWalletContext(chain.provider) - - if (context) { - expect(context.factory).to.equal(factory.address) - expect(context.mainModule).to.equal(mainModule.address) - expect(context.mainModuleUpgradable).to.equal(mainModuleUpgradable.address) - expect(context.guestModule).to.equal(guestModule.address) - expect(context.sequenceUtils).to.equal(sequenceUtils.address) - } else { - // Create fixed context obj - context = { - factory: factory.address, - mainModule: mainModule.address, - mainModuleUpgradable: mainModuleUpgradable.address, - guestModule: guestModule.address, - sequenceUtils: sequenceUtils.address, - libs: { - requireFreshSigner: requireFreshSigner.address - } - } - } - - // Deploy call receiver mock - chain.callReceiver = await (new CallReceiverMock__factory()).connect(chain.signer).deploy() - - // Deploy hook caller mock - chain.hookCaller = await (new HookCallerMock__factory()).connect(chain.signer).deploy() - }) - - describe("Change signature configuration", () => { - [{ - name: "same as old configuration", - getNewConfig: ((config: WalletConfig) => { - return config - }), - }, { - name: "with an extra signer", - getNewConfig: ((config: WalletConfig) => { - return { threshold: config.threshold, signers: [ - ...config.signers, - { - address: ethers.Wallet.createRandom().address, - weight: 2 - } - ]} - }), - }, { - name: "with one less signer", - getNewConfig: ((config: WalletConfig) => { - return { threshold: config.threshold, signers: [ - config.signers[0] - ]} - }), - }, { - name: "with higher weights", - getNewConfig: ((config: WalletConfig) => { - return { threshold: config.threshold, signers: config.signers.map((s) => { - return { ...s, weight: 4 } - })} - }), - }, { - name: "with lower weights", - getNewConfig: ((config: WalletConfig) => { - return { threshold: config.threshold, signers: config.signers.map((s) => { - return { ...s, weight: 4 } - })} - }), - }, { - name: "with lower threshold", - getNewConfig: ((config: WalletConfig) => { - return { threshold: 1, signers: config.signers } - }), - }, { - name: "with higher threshold", - getNewConfig: ((config: WalletConfig) => { - return { threshold: 1, signers: config.signers } - }), - }].map((c) => it(`Should mutate signature after ${c.name}`, async () => { - const signer1 = ethers.Wallet.createRandom() - const signer2 = ethers.Wallet.createRandom() - - const config: WalletConfig = { - threshold: 2, - signers: [{ - address: signer1.address, - weight: 2 - }, { - address: signer2.address, - weight: 3 - }] - } - - const newConfig = c.getNewConfig(config) - - const wallet = new Wallet({ config, context }, signer1, signer2).connect(chain.provider, chain.relayer) - const data = ethers.utils.hexlify(ethers.utils.randomBytes(32)) - - const transaction = { - gasLimit: '121000', - to: chain.callReceiver.address, - value: 0, - data: await encodeData(chain.callReceiver, "testCall", 1, data), - delegateCall: false, - revertOnError: true, - nonce: encodeNonce(Date.now(), 0) - } - - const ogSig = await wallet.signTransactions(transaction) - const subDigest = await wallet.subDigest(ogSig.digest) - - const oldSignature = decodeSignature(await ogSig.signature) - - await wallet.updateConfig(newConfig, undefined, false) - - const newSig = mutateSignature(oldSignature, newConfig, subDigest) - await wallet.sendSignedTransactions({ ...ogSig, signature: newSig }) - - expect(await chain.callReceiver.lastValB()).to.equal(data) - })) - }) - - describe("Should generate stub signatures", async () => { - it("Should generate stub signature for a simple wallet", async () => { - const signer1 = ethers.Wallet.createRandom() - const signer2 = ethers.Wallet.createRandom() - - const config: WalletConfig = { - threshold: 2, - signers: [{ - address: signer1.address, - weight: 2 - }, { - address: signer2.address, - weight: 3 - }] - } - - const stub = await buildStubSignature(chain.provider, config) - expect(stub.signers.length).to.equal(2) - expect(stub.threshold).to.equal(2) - expect(stub.signers.find((s) => isDecodedAddress(s))!.weight).to.equal(3) - expect(stub.signers.find((s) => isDecodedEOASigner(s))!.weight).to.equal(2) - }) - - it("Should generate stub signature with 4 signers with lower weight", async () => { - const signer1 = ethers.Wallet.createRandom() - const signer2 = ethers.Wallet.createRandom() - const signer3 = ethers.Wallet.createRandom() - const signer4 = ethers.Wallet.createRandom() - const signer5 = ethers.Wallet.createRandom() - const signer6 = ethers.Wallet.createRandom() - - const config: WalletConfig = { - threshold: 5, - signers: [{ - address: signer1.address, - weight: 1 - }, { - address: signer2.address, - weight: 1 - }, { - address: signer3.address, - weight: 15 - }, { - address: signer4.address, - weight: 1 - }, { - address: signer5.address, - weight: 3 - }, { - address: signer6.address, - weight: 2 - }] - } - - const stub = await buildStubSignature(chain.provider, config) - - expect(stub.signers.length).to.equal(6) - expect(stub.threshold).to.equal(5) - expect(stub.signers.filter((s) => isDecodedAddress(s)).length).to.equal(2) - expect(stub.signers.filter((s) => isDecodedEOASigner(s)).length).to.equal(4) - expect(stub.signers.filter((s) => isDecodedAddress(s)).map((s: any) => s.address)).to.deep.equal([signer3.address, signer5.address]) - expect(stub.signers.filter((s) => isDecodedEOASigner(s)).map((s) => s.weight)).to.deep.equal([1, 1, 1, 2]) - }) - - it("Should generate signature with nested configuration", async () => { - const signer1 = ethers.Wallet.createRandom().address - const signer2 = ethers.Wallet.createRandom().address - const signer3 = context.guestModule! - - const config: WalletConfig = { - threshold: 16, - signers: [{ - address: signer1, - weight: 1 - }, { - address: signer2, - weight: 2 - }, { - address: signer3, - weight: 15 - }] - } - - const stub = await buildStubSignature(chain.provider, config) - - const stubSig = ethers.utils.arrayify("0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a01b02") - - expect(stub.signers.length).to.equal(3) - expect(stub.threshold).to.equal(16) - expect(stub.signers.filter((s) => isDecodedAddress(s)).length).to.equal(1) - expect(stub.signers.filter((s) => isDecodedEOASigner(s)).length).to.equal(1) - expect(stub.signers.filter((s) => isDecodedFullSigner(s)).length).to.equal(1) - expect((stub.signers.find((s) => isDecodedFullSigner(s)) as any).address).to.equal(signer3) - expect(stub.signers.find((s) => isDecodedAddress(s))!.weight).to.equal(2) - expect((stub.signers.find((s) => isDecodedFullSigner(s)) as any).signature.length).to.equal( - (encodeSignature({ - threshold: 1, - signers: [ - { - address: ethers.Wallet.createRandom().address, - weight: 1, - }, - { - weight: 1, - signature: stubSig - } - ] - }) + '03').length - ) - }) - }) -}) diff --git a/packages/config/tests/utils/deploy-wallet-context.ts b/packages/config/tests/utils/deploy-wallet-context.ts deleted file mode 100644 index 24818efd5..000000000 --- a/packages/config/tests/utils/deploy-wallet-context.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { ethers, providers } from 'ethers' - -import { - Factory, - GuestModule, - MainModule, - MainModuleUpgradable, - SequenceUtils, - RequireFreshSigner -} from '@0xsequence/wallet-contracts' - -const FactoryArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/Factory.sol/Factory.json') -const GuestModuleArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/modules/GuestModule.sol/GuestModule.json') -const MainModuleArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/modules/MainModule.sol/MainModule.json') -const MainModuleUpgradableArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/modules/MainModuleUpgradable.sol/MainModuleUpgradable.json') -const SequenceUtilsArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/modules/utils/SequenceUtils.sol/SequenceUtils.json') -const RequireFreshSignerArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/modules/utils/libs/RequireFreshSigner.sol/RequireFreshSigner.json') - -export async function deployWalletContext( - provider: providers.Provider -): Promise<[Factory, MainModule, MainModuleUpgradable, GuestModule, SequenceUtils, RequireFreshSigner]> { - const factory = (await new ethers.ContractFactory( - FactoryArtifact.abi, - FactoryArtifact.bytecode, - (provider as any).getSigner() - ).deploy()) as unknown as Factory - - const mainModule = (await new ethers.ContractFactory( - MainModuleArtifact.abi, - MainModuleArtifact.bytecode, - (provider as any).getSigner() - ).deploy(factory.address)) as unknown as MainModule - - const mainModuleUpgradable = (await new ethers.ContractFactory( - MainModuleUpgradableArtifact.abi, - MainModuleUpgradableArtifact.bytecode, - (provider as any).getSigner() - ).deploy()) as unknown as MainModuleUpgradable - - const guestModule = (await new ethers.ContractFactory( - GuestModuleArtifact.abi, - GuestModuleArtifact.bytecode, - (provider as any).getSigner() - ).deploy()) as unknown as GuestModule - - const sequenceUtils = (await new ethers.ContractFactory( - SequenceUtilsArtifact.abi, - SequenceUtilsArtifact.bytecode, - (provider as any).getSigner() - ).deploy(factory.address, mainModule.address)) as unknown as SequenceUtils - - const requireFreshSigner = (await new ethers.ContractFactory( - RequireFreshSignerArtifact.abi, - RequireFreshSignerArtifact.bytecode, - (provider as any).getSigner() - ).deploy(sequenceUtils.address)) as unknown as RequireFreshSigner - - return [factory, mainModule, mainModuleUpgradable, guestModule, sequenceUtils, requireFreshSigner] -} diff --git a/packages/config/tests/utils/index.ts b/packages/config/tests/utils/index.ts deleted file mode 100644 index 8c4c6f999..000000000 --- a/packages/config/tests/utils/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -export function delay(time: number): Promise { - return new Promise(solve => setTimeout(solve, time)) -} - -/** - * @param {Date} expected The date to which we want to freeze time - * @returns {Function} Call to remove Date mocking - */ -export const mockDate = (expected: Date): (() => void) => { - const _Date = Date - - // If any Date or number is passed to the constructor - // use that instead of our mocked date - function MockDate(mockOverride?: Date | number) { - return new _Date(mockOverride || expected) - } - - MockDate.UTC = _Date.UTC - MockDate.parse = _Date.parse - MockDate.now = () => expected.getTime() - // Give our mock Date has the same prototype as Date - // Some libraries rely on this to identify Date objects - MockDate.prototype = _Date.prototype - - // Our mock is not a full implementation of Date - // Types will not match but it's good enough for our tests - global.Date = MockDate as any - - // Callback function to remove the Date mock - return () => { - global.Date = _Date - } -} diff --git a/packages/core/package.json b/packages/core/package.json new file mode 100644 index 000000000..66daad0d7 --- /dev/null +++ b/packages/core/package.json @@ -0,0 +1,30 @@ +{ + "name": "@0xsequence/core", + "version": "0.43.7", + "description": "core primitives for interacting with the sequence wallet contracts", + "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/core", + "source": "src/index.ts", + "main": "dist/0xsequence-core.cjs.js", + "module": "dist/0xsequence-core.esm.js", + "author": "Horizon Blockchain Games", + "license": "Apache-2.0", + "scripts": { + "test": "pnpm test:file tests/**/*.spec.ts", + "test:file": "TS_NODE_PROJECT=../../tsconfig.test.json mocha -r ts-node/register --timeout 30000", + "test:coverage": "nyc yarn test" + }, + "peerDependencies": { + "ethers": ">=5.5" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "nyc": "^15.1.0" + }, + "files": [ + "src", + "dist" + ], + "dependencies": { + "@0xsequence/abi": "workspace:*" + } +} diff --git a/packages/core/src/commons/config.ts b/packages/core/src/commons/config.ts new file mode 100644 index 000000000..ae01ed9e1 --- /dev/null +++ b/packages/core/src/commons/config.ts @@ -0,0 +1,67 @@ + +import { ethers } from 'ethers' +import { WalletContext } from './context' +import * as transaction from './transaction' + +export type Config = { + version: number +} + +export type SimpleSigner = { address: string, weight: ethers.BigNumberish } + +export type SimpleConfig = { + threshold: ethers.BigNumberish, + checkpoint: ethers.BigNumberish, + signers: SimpleSigner[] +} + +export interface ConfigCoder { + imageHashOf: (config: T) => string + hasSubdigest: (config: T, subdigest: string) => boolean + + isWalletConfig: (config: Config) => config is T + + checkpointOf: (config: T) => ethers.BigNumber + + fromSimple: (config: SimpleConfig) => T + + signersOf: (config: T) => { address: string, weight: number }[] + + toJSON: (config: T) => string + fromJSON: (json: string) => T + + isComplete: (config: T) => boolean + + editConfig: (config: T, action: { + add?: SimpleSigner[], + remove?: string[], + threshold?: ethers.BigNumberish, + checkpoint?: ethers.BigNumberish + }) => T + + buildStubSignature: ( + config: T, + overrides: Map + ) => string + + // isValid: (config: T) => boolean + + // TODO: This may not be the best place for this + // maybe it could go in the migration classes? + update: { + isKindUsed: boolean, + + buildTransaction: ( + address: string, + config: T, + context: WalletContext, + kind?: 'first' | 'later' | undefined + ) => transaction.TransactionBundle + + decodeTransaction: (tx: transaction.TransactionBundle) => { + address: string, + newImageHash: string, + kind: 'first' | 'later' | undefined + } + } +} diff --git a/packages/core/src/commons/context.ts b/packages/core/src/commons/context.ts new file mode 100644 index 000000000..135ff1249 --- /dev/null +++ b/packages/core/src/commons/context.ts @@ -0,0 +1,100 @@ +import { ethers } from "ethers" +import { allVersions } from ".." + +export type WalletContext = { + version: number, + factory: string, + mainModule: string, + mainModuleUpgradable: string, + guestModule: string, + + walletCreationCode: string, +} + +export function addressOf(context: WalletContext, imageHash: ethers.BytesLike) { + const codeHash = ethers.utils.keccak256( + ethers.utils.solidityPack( + ['bytes', 'bytes32'], + [context.walletCreationCode, ethers.utils.hexZeroPad(context.mainModule, 32)] + ) + ) + + const hash = ethers.utils.keccak256( + ethers.utils.solidityPack( + ['bytes1', 'address', 'bytes32', 'bytes32'], + ['0xff', context.factory, imageHash, codeHash] + ) + ) + + return ethers.utils.getAddress(ethers.utils.hexDataSlice(hash, 12)) +} + +export async function isValidCounterfactual( + wallet: string, + digest: ethers.BytesLike, + signature: ethers.BytesLike, + chainId: ethers.BigNumberish, + provider: ethers.providers.Provider, + contexts: { [key: number]: WalletContext } +) { + // We don't know the version of the signature + // so we need to try all of them + const res = await Promise.all(allVersions.map(async (version) => { + try { + const decoded = version.signature.SignatureCoder.decode(ethers.utils.hexlify(signature)) + + const recovered1 = await version.signature.SignatureCoder.recover(decoded as any, { + address: wallet, + digest: ethers.utils.hexlify(digest), + chainId, + }, provider) + + const imageHash = version.config.ConfigCoder.imageHashOf(recovered1.config as any) + const counterfactualAddress = addressOf(contexts[version.version], imageHash) + + if (counterfactualAddress.toLowerCase() === wallet.toLowerCase()) { + return true + } + + // chainId=0 means no chainId, so the signature is valid for all chains + // we need to check that case too + const recovered2 = await version.signature.SignatureCoder.recover(decoded as any, { + address: wallet, + digest: ethers.utils.hexlify(digest), + chainId, + }, provider) + + const imageHash2 = version.config.ConfigCoder.imageHashOf(recovered2.config as any) + const counterfactualAddress2 = addressOf(contexts[version.version], imageHash2) + + return counterfactualAddress2.toLowerCase() === wallet.toLowerCase() + } catch {} + + // We most likely failed to decode the signature + return false + })) + + return res.some((r) => r) +} + +export type VersionedContext = { [key: number]: WalletContext } + +export function isValidVersionedContext(contexts: VersionedContext): boolean { + // number of keys is the number of versions + const versions = Object.keys(contexts).length + + // check that all versions exist and are valid + for (let i = 1; i <= versions; i++) { + const context = contexts[i] + if (!context || context.version !== i) { + return false + } + } + + return true +} + +export function latestContext(contexts: VersionedContext): WalletContext { + const versions = Object.keys(contexts).length + return contexts[versions] +} diff --git a/packages/core/src/commons/index.ts b/packages/core/src/commons/index.ts new file mode 100644 index 000000000..6d109b51e --- /dev/null +++ b/packages/core/src/commons/index.ts @@ -0,0 +1,11 @@ + +export * as config from './config' +export * as signature from './signature' +export * as context from './context' +export * as signer from './signer' +export * as EIP1271 from './validateEIP1271' +export * as transaction from './transaction' +export * as reader from './reader' +export * as EIP6492 from './validateEIP6492' + +export * from './orchestrator' diff --git a/packages/core/src/commons/orchestrator.ts b/packages/core/src/commons/orchestrator.ts new file mode 100644 index 000000000..70949b885 --- /dev/null +++ b/packages/core/src/commons/orchestrator.ts @@ -0,0 +1,32 @@ +import { ethers } from "ethers" +import { commons } from ".." +import { Config } from "./config" + +/** + * Request metadata, used to by the wallet to pass additional information to the + * orchestrator. + */ +export type WalletSignRequestMetadata = { + address: string, + digest: ethers.utils.BytesLike, + chainId: ethers.BigNumberish, + + config: Config, + + parts?: Map, + + // TODO: We can add a "percentage" field to the orchestrator to indicate + // how close are we to the threshold. This can be used to display + // a progress bar or something similar. + + message?: ethers.utils.BytesLike + transactions?: commons.transaction.Transaction[] + + // This is used only when a Sequence wallet is nested in another Sequence wallet + // it contains the original metadata of the parent wallet. + parent?: WalletSignRequestMetadata +} + +export function isWalletSignRequestMetadata(obj: any): obj is WalletSignRequestMetadata { + return obj && obj.address && obj.digest && obj.chainId !== undefined && obj.config +} diff --git a/packages/core/src/commons/reader.ts b/packages/core/src/commons/reader.ts new file mode 100644 index 000000000..4c7627ce9 --- /dev/null +++ b/packages/core/src/commons/reader.ts @@ -0,0 +1,108 @@ +import { walletContracts } from "@0xsequence/abi" +import { ethers } from "ethers" +import { commons } from ".." +import { validateEIP6492Offchain } from "./validateEIP6492" + +/** + * Provides stateful information about the wallet. + */ +export interface Reader { + isDeployed(wallet: string): Promise + implementation(wallet: string): Promise + imageHash(wallet: string): Promise + nonce(wallet: string, space: ethers.BigNumberish): Promise + isValidSignature( + wallet: string, + digest: ethers.BytesLike, + signature: ethers.BytesLike + ): Promise +} + +/** + * The OnChainReader class fetches on-chain data from a wallet. + * It is used to understand the "real" state of the wallet contract on-chain. + */ + export class OnChainReader implements Reader { + // Simple cache to avoid re-fetching the same data + private isDeployedCache: Set = new Set() + + constructor( + public readonly provider: ethers.providers.Provider, + public readonly contexts?: { [key: number]: commons.context.WalletContext } + ) {} + + private module(address: string) { + return new ethers.Contract( + address, + [ + ...walletContracts.mainModuleUpgradable.abi, + ...walletContracts.mainModule.abi, + ...walletContracts.erc1271.abi + ], + this.provider + ) + } + + async isDeployed(wallet: string): Promise { + // This is safe to cache because the wallet cannot be undeployed once deployed + if (this.isDeployedCache.has(wallet)) { + return true + } + + const code = await this.provider.getCode(wallet).then((c) => ethers.utils.arrayify(c)) + const isDeployed = code.length !== 0 + if (isDeployed) { + this.isDeployedCache.add(wallet) + } + + return isDeployed + } + + async implementation(wallet: string): Promise { + const position = ethers.utils.defaultAbiCoder.encode(['address'], [wallet]) + const val = await this.provider.getStorageAt(wallet, position).then((c) => ethers.utils.arrayify(c)) + + if (val.length === 20) { + return ethers.utils.getAddress(ethers.utils.hexlify(val)) + } + + if (val.length === 32) { + return ethers.utils.defaultAbiCoder.decode(['address'], val)[0] + } + + return undefined + } + + async imageHash(wallet: string): Promise { + try { + const imageHash = await this.module(wallet).imageHash() + return imageHash + } catch {} + + return undefined + } + + async nonce(wallet: string, space: ethers.BigNumberish = 0): Promise { + try { + const nonce = await this.module(wallet).readNonce(space) + return nonce + } catch (e) { + if (!(await this.isDeployed(wallet))) { + return 0 + } + + throw e + } + } + + // We use the EIP-6492 validator contract to check the signature + // this means that if the wallet is not deployed, then the signature + // must be prefixed with a transaction that deploys the wallet + async isValidSignature( + wallet: string, + digest: ethers.BytesLike, + signature: ethers.BytesLike + ): Promise { + return validateEIP6492Offchain(this.provider, wallet, digest, signature) + } +} diff --git a/packages/core/src/commons/signature.ts b/packages/core/src/commons/signature.ts new file mode 100644 index 000000000..e42d8542c --- /dev/null +++ b/packages/core/src/commons/signature.ts @@ -0,0 +1,88 @@ + +import { ethers } from 'ethers' +import * as config from './config' + +export type SignaturePart = { + signature: string, + isDynamic: boolean +} + +export type Signature = { + version: number, + config: T, + subdigest: string, + payload?: SignedPayload +} + +export type UnrecoveredSignature = { + version: number +} + +export type SignedPayload = { + message?: ethers.BytesLike, + digest: string, + chainId: ethers.BigNumberish, + address: string +} + +export interface SignatureCoder< + Y extends config.Config = config.Config, + T extends Signature = Signature, + Z extends UnrecoveredSignature = UnrecoveredSignature +> { + decode: (data: string) => Z, + encode: (data: T | Z | ethers.BytesLike) => string, + + trim: (data: string) => Promise, + + recover: ( + data: Z, + payload: SignedPayload, + provider: ethers.providers.Provider + ) => Promise + + supportsNoChainId: boolean + + encodeSigners: ( + config: Y, + signatures: Map, + subdigests: string[], + chainId: ethers.BigNumberish + ) => { + encoded: string, + weight: ethers.BigNumber + } + + hasEnoughSigningPower: ( + config: Y, + signatures: Map + ) => boolean + + chainSignatures: ( + main: T | Z | ethers.BytesLike, + suffixes: (T | Z | ethers.BytesLike)[] + ) => string + + hashSetImageHash: ( + imageHash: string + ) => string + + signaturesOf: ( + config: Y, + ) => { address: string, signature: string }[] + + signaturesOfDecoded: ( + decoded: Z + ) => string[] +} + +export function subdigestOf(payload: SignedPayload) { + return ethers.utils.solidityKeccak256( + ['bytes', 'uint256', 'address', 'bytes32'], + ['0x1901', payload.chainId, payload.address, payload.digest] + ) +} + +export function isSignedPayload(payload: any): payload is SignedPayload { + return payload.digest !== undefined && payload.chainId !== undefined && payload.address !== undefined +} diff --git a/packages/core/src/commons/signer.ts b/packages/core/src/commons/signer.ts new file mode 100644 index 000000000..7ac2ca710 --- /dev/null +++ b/packages/core/src/commons/signer.ts @@ -0,0 +1,76 @@ +import { ethers } from "ethers" +import { isValidEIP1271Signature } from "./validateEIP1271" + +export enum SigType { + EIP712 = 1, + ETH_SIGN = 2, + WALLET_BYTES32 = 3 +} + +export function canRecover(signature: ethers.BytesLike) { + const bytes = ethers.utils.arrayify(signature) + const type = bytes[bytes.length - 1] + + return type === SigType.EIP712 || type === SigType.ETH_SIGN +} + +export function recoverSigner(digest: ethers.BytesLike, signature: ethers.BytesLike) { + const bytes = ethers.utils.arrayify(signature) + const digestBytes = ethers.utils.arrayify(digest) + + // type is last byte + const type = bytes[bytes.length - 1] + + // Split r:s:v + const r = ethers.utils.hexlify(bytes.slice(0, 32)) + const s = ethers.utils.hexlify(bytes.slice(32, 64)) + const v = ethers.BigNumber.from(bytes.slice(64, 65)).toNumber() + + const splitSignature = { r, s, v } + + if (type === SigType.EIP712) { + return ethers.utils.recoverAddress(digestBytes, splitSignature) + } + + if (type === SigType.ETH_SIGN) { + return ethers.utils.recoverAddress(ethers.utils.hashMessage(digestBytes), splitSignature) + } + + throw new Error(`Unsupported signature type: ${type}`) +} + +export function isValidSignature( + address: string, + digest: ethers.BytesLike, + signature: ethers.BytesLike, + provider: ethers.providers.Provider +) { + const bytes = ethers.utils.arrayify(signature) + + // type is last byte + const type = bytes[bytes.length - 1] + + if (type === SigType.EIP712 || type === SigType.ETH_SIGN) { + return address === recoverSigner(digest, signature) + } + + if (type === SigType.WALLET_BYTES32) { + return isValidEIP1271Signature(address, ethers.utils.hexlify(digest), bytes.slice(0, -1), provider) + } + + throw new Error(`Unsupported signature type: ${type}`) +} + +export function tryRecoverSigner( + digest: ethers.BytesLike, + signature: ethers.BytesLike +): string | undefined { + const bytes = ethers.utils.arrayify(signature) + if (bytes.length !== 66) return undefined + + try { + return recoverSigner(digest, bytes) + } catch {} + + return undefined +} diff --git a/packages/core/src/commons/transaction.ts b/packages/core/src/commons/transaction.ts new file mode 100644 index 000000000..086e78972 --- /dev/null +++ b/packages/core/src/commons/transaction.ts @@ -0,0 +1,316 @@ +import { BigNumberish, BytesLike, ethers } from "ethers" +import { subdigestOf } from "./signature" +import { walletContracts } from "@0xsequence/abi" + +export interface Transaction { + to: string + value?: BigNumberish + data?: BytesLike + gasLimit?: BigNumberish + delegateCall?: boolean + revertOnError?: boolean +} + +export interface SimulatedTransaction extends Transaction { + succeeded: boolean + executed: boolean + gasUsed: number + gasLimit: number + result?: string + reason?: string +} + +export interface TransactionEncoded { + delegateCall: boolean + revertOnError: boolean + gasLimit: BigNumberish + target: string + value: BigNumberish + data: BytesLike +} + +export type Transactionish = ethers.providers.TransactionRequest | ethers.providers.TransactionRequest[] | Transaction | Transaction[] + +export interface TransactionResponse extends ethers.providers.TransactionResponse { + receipt?: R +} + +export type TransactionBundle = { + entrypoint: string, + transactions: Transaction[], + nonce?: BigNumberish +} + +export type IntendedTransactionBundle = TransactionBundle & { + chainId: BigNumberish, + intent: { + id: string, + wallet: string + } +} + +export type SignedTransactionBundle = IntendedTransactionBundle & { + signature: string, + nonce: BigNumberish +} + +export type RelayReadyTransactionBundle = SignedTransactionBundle | IntendedTransactionBundle + +export const MetaTransactionsType = `tuple( + bool delegateCall, + bool revertOnError, + uint256 gasLimit, + address target, + uint256 value, + bytes data +)[]` + +export function intendTransactionBundle( + bundle: TransactionBundle, + wallet: string, + chainId: BigNumberish, + id: string +): IntendedTransactionBundle { + return { + ...bundle, + chainId, + intent: { id: id, wallet } + } +} + +export function intendedTransactionID(bundle: IntendedTransactionBundle) { + return ethers.utils.keccak256( + ethers.utils.defaultAbiCoder.encode( + ['address', 'uint256', 'bytes32'], + [bundle.intent.wallet, bundle.chainId, bundle.intent.id] + ) + ) +} + +export function unpackMetaTransactionsData(data: BytesLike): [ethers.BigNumber, TransactionEncoded[]] { + const res = ethers.utils.defaultAbiCoder.decode(['uint256', MetaTransactionsType], data) + if (res.length !== 2 || !res[0] || !res[1]) throw new Error('Invalid meta transaction data') + return [res[0], res[1]] +} + +export function packMetaTransactionsData(nonce: ethers.BigNumberish, txs: Transaction[]): string { + return ethers.utils.defaultAbiCoder.encode(['uint256', MetaTransactionsType], [nonce, sequenceTxAbiEncode(txs)]) +} + +export function digestOfTransactions(nonce: BigNumberish, txs: Transaction[]) { + return ethers.utils.keccak256(packMetaTransactionsData(nonce, txs)) +} + +export function subdigestOfTransactions(address: string, chainId: BigNumberish, nonce: ethers.BigNumberish, txs: Transaction[]): string { + return subdigestOf({ address, chainId, digest: digestOfTransactions(nonce, txs) }) +} + +export function subdigestOfGuestModuleTransactions(guestModule: string, chainId: BigNumberish, txs: Transaction[]): string { + return subdigestOf({ + address: guestModule, + chainId, + digest: ethers.utils.keccak256( + ethers.utils.defaultAbiCoder.encode( + ['string', MetaTransactionsType], + ['guest:', sequenceTxAbiEncode(txs)] + ) + ) + }) +} + +export function toSequenceTransactions( + wallet: string, + txs: (Transaction | ethers.providers.TransactionRequest)[] +): { nonce?: ethers.BigNumberish, transaction: Transaction }[] { + return txs.map(tx => toSequenceTransaction(wallet, tx)) +} + +export function toSequenceTransaction( + wallet: string, + tx: ethers.providers.TransactionRequest +): { nonce?: ethers.BigNumberish, transaction: Transaction } { + if (tx.to) { + return { + nonce: tx.nonce, + transaction: { + delegateCall: false, + revertOnError: false, + gasLimit: tx.gasLimit || 0, + to: tx.to, + value: tx.value || 0, + data: tx.data || '0x', + } + } + } else { + const walletInterface = new ethers.utils.Interface(walletContracts.mainModule.abi) + const data = walletInterface.encodeFunctionData(walletInterface.getFunction('createContract'), [tx.data]) + + return { + nonce: tx.nonce, + transaction: { + delegateCall: false, + revertOnError: false, + gasLimit: tx.gasLimit, + to: wallet, + value: tx.value || 0, + data: data + } + } + } +} + +export function isSequenceTransaction(tx: any): tx is Transaction { + return tx.delegateCall !== undefined || tx.revertOnError !== undefined +} + +export function hasSequenceTransactions(txs: any[]): txs is Transaction[] { + return txs.every(isSequenceTransaction) +} + +// TODO: We may be able to remove this if we make Transaction === TransactionEncoded +export function sequenceTxAbiEncode(txs: Transaction[]): TransactionEncoded[] { + return txs.map(t => ({ + delegateCall: t.delegateCall === true, + revertOnError: t.revertOnError === true, + gasLimit: t.gasLimit !== undefined ? t.gasLimit : ethers.constants.Zero, + target: t.to ?? ethers.constants.AddressZero, + value: t.value !== undefined ? t.value : ethers.constants.Zero, + data: t.data !== undefined ? t.data : [] + })) +} + +export function fromTxAbiEncode(txs: TransactionEncoded[]): Transaction[] { + return txs.map(t => ({ + delegateCall: t.delegateCall, + revertOnError: t.revertOnError, + gasLimit: t.gasLimit, + to: t.target, + value: t.value, + data: t.data + })) +} + +// export function appendNonce(txs: Transaction[], nonce: BigNumberish): Transaction[] { +// return txs.map((t: Transaction) => ({ ...t, nonce })) +// } + +export function encodeNonce(space: BigNumberish, nonce: BigNumberish): ethers.BigNumber { + const bspace = ethers.BigNumber.from(space) + const bnonce = ethers.BigNumber.from(nonce) + + const shl = ethers.constants.Two.pow(ethers.BigNumber.from(96)) + + if (!bnonce.div(shl).eq(ethers.constants.Zero)) { + throw new Error('Space already encoded') + } + + return bnonce.add(bspace.mul(shl)) +} + +export function decodeNonce(nonce: BigNumberish): [ethers.BigNumber, ethers.BigNumber] { + const bnonce = ethers.BigNumber.from(nonce) + const shr = ethers.constants.Two.pow(ethers.BigNumber.from(96)) + + return [bnonce.div(shr), bnonce.mod(shr)] +} + +export function fromTransactionish( + wallet: string, + transaction: Transactionish +): Transaction[] { + if (Array.isArray(transaction)) { + if (hasSequenceTransactions(transaction)) { + return transaction + } else { + const stx = toSequenceTransactions(wallet, transaction) + return stx.map(t => t.transaction) + } + } else if (isSequenceTransaction(transaction)) { + return [transaction] + } else { + return [toSequenceTransaction(wallet, transaction).transaction] + } +} + +export function isTransactionBundle(cand: any): cand is TransactionBundle { + return ( + cand !== undefined && + cand.entrypoint !== undefined && + cand.chainId !== undefined && + cand.transactions !== undefined && + cand.nonce !== undefined && + cand.intent !== undefined && + cand.intent.id !== undefined && + cand.intent.wallet !== undefined && + Array.isArray(cand.transactions) && + (cand).transactions.reduce((p, c) => p && isSequenceTransaction(c), true) + ) +} + +export function isSignedTransactionBundle(cand: any): cand is SignedTransactionBundle { + return ( + cand !== undefined && + cand.signature !== undefined && + cand.signature !== '' && + isTransactionBundle(cand) + ) +} + +export function encodeBundleExecData(bundle: TransactionBundle): string { + const walletInterface = new ethers.utils.Interface(walletContracts.mainModule.abi) + return walletInterface.encodeFunctionData(walletInterface.getFunction('execute'), + isSignedTransactionBundle(bundle) ? [ + // Signed transaction bundle has all 3 parameters + sequenceTxAbiEncode(bundle.transactions), + bundle.nonce, + bundle.signature + ] : [ + // Unsigned bundle may be a GuestModule call, so signature and nonce are missing + sequenceTxAbiEncode(bundle.transactions), + 0, + [] + ] + ) +} + +// TODO: Use Sequence ABI package +export const selfExecuteSelector = '0x61c2926c' +export const selfExecuteAbi = `tuple( + bool delegateCall, + bool revertOnError, + uint256 gasLimit, + address target, + uint256 value, + bytes data +)[]` + +// Splits Sequence batch transactions into individual parts +export const unwind = (wallet: string, transactions: Transaction[]): Transaction[] => { + const unwound: Transaction[] = [] + + const walletInterface = new ethers.utils.Interface(walletContracts.mainModule.abi) + + for (const tx of transactions) { + const txData = ethers.utils.arrayify(tx.data || '0x') + + if (tx.to === wallet && ethers.utils.hexlify(txData.slice(0, 4)) === selfExecuteSelector) { + // Decode as selfExecute call + const data = txData.slice(4) + const decoded = ethers.utils.defaultAbiCoder.decode([selfExecuteAbi], data)[0] + unwound.push(...decoded.map((d: TransactionEncoded) => ({ ...d, to: d.target }))) + } else { + try { + const innerTransactions = walletInterface.decodeFunctionData('execute', txData)[0] + const unwoundTransactions = unwind( + wallet, + innerTransactions.map((tx: TransactionEncoded) => ({ ...tx, to: tx.target })) + ) + unwound.push(...unwoundTransactions) + } catch { + unwound.push(tx) + } + } + } + + return unwound +} diff --git a/packages/core/src/commons/validateEIP1271.ts b/packages/core/src/commons/validateEIP1271.ts new file mode 100644 index 000000000..58d25f32e --- /dev/null +++ b/packages/core/src/commons/validateEIP1271.ts @@ -0,0 +1,32 @@ + +import { ethers } from "ethers" + +const EIP1271_MAGIC_VALUE = "0x1626ba7e" + +const EIP1271_ABI = [{ + "inputs": [{ + "internalType": "bytes32", + "type": "bytes32" + }, { + "internalType": "bytes", + "type": "bytes" + }], + "name": "isValidSignature", + "outputs": [{ + "internalType": "bytes4", + "type": "bytes4" + }], + "stateMutability": "view", + "type": "function" +}] + +export async function isValidEIP1271Signature( + address: string, + digest: string, + signature: ethers.BytesLike, + provider: ethers.providers.Provider +): Promise { + const contract = new ethers.Contract(address, EIP1271_ABI, provider) + const result = await contract.isValidSignature(digest, signature) + return result === EIP1271_MAGIC_VALUE +} diff --git a/packages/core/src/commons/validateEIP6492.ts b/packages/core/src/commons/validateEIP6492.ts new file mode 100644 index 000000000..db0d50cab --- /dev/null +++ b/packages/core/src/commons/validateEIP6492.ts @@ -0,0 +1,193 @@ +import { ethers } from "ethers"; + +/* Source of Offchain EIP-6492 validation: + +// SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.18; + + +// As per ERC-1271 +interface IERC1271Wallet { + function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4 magicValue); +} + +error ERC1271Revert(bytes error); +error ERC6492DeployFailed(bytes error); + +contract UniversalSigValidator { + bytes32 private constant ERC6492_DETECTION_SUFFIX = 0x6492649264926492649264926492649264926492649264926492649264926492; + bytes4 private constant ERC1271_SUCCESS = 0x1626ba7e; + + function isValidSigImpl( + address _signer, + bytes32 _hash, + bytes calldata _signature, + bool allowSideEffects, + bool deployAlreadyDeployed + ) public returns (bool) { + uint contractCodeLen = address(_signer).code.length; + bytes memory sigToValidate; + // The order here is striclty defined in https://eips.ethereum.org/EIPS/eip-6492 + // - ERC-6492 suffix check and verification first, while being permissive in case the contract is already deployed; if the contract is deployed we will check the sig against the deployed version, this allows 6492 signatures to still be validated while taking into account potential key rotation + // - ERC-1271 verification if there's contract code + // - finally, ecrecover + bool isCounterfactual = bytes32(_signature[_signature.length-32:_signature.length]) == ERC6492_DETECTION_SUFFIX; + if (isCounterfactual) { + address create2Factory; + bytes memory factoryCalldata; + (create2Factory, factoryCalldata, sigToValidate) = abi.decode(_signature[0:_signature.length-32], (address, bytes, bytes)); + + if (contractCodeLen == 0 || deployAlreadyDeployed) { + (bool success, bytes memory err) = create2Factory.call(factoryCalldata); + if (!success) revert ERC6492DeployFailed(err); + } + } else { + sigToValidate = _signature; + } + + // Try ERC-1271 verification + if (isCounterfactual || contractCodeLen > 0) { + try IERC1271Wallet(_signer).isValidSignature(_hash, sigToValidate) returns (bytes4 magicValue) { + bool isValid = magicValue == ERC1271_SUCCESS; + + // EXPERIMENTAL: This is not part of the EIP-6492 spec *yet* + // but it may be useful to retry the call making the factory call + // even if the wallet is already deployed, in case the wallet + // needs to perform some sort of migration or onchain key rotation + if (!isValid && !deployAlreadyDeployed && contractCodeLen > 0) { + return isValidSigImpl(_signer, _hash, _signature, allowSideEffects, true); + } + + if (contractCodeLen == 0 && isCounterfactual && !allowSideEffects) { + // if the call had side effects we need to return the + // result using a `revert` (to undo the state changes) + assembly { + mstore(0, isValid) + revert(31, 1) + } + } + + return isValid; + } catch (bytes memory err) { + // EXPERIMENTAL: This is not part of the EIP-6492 spec *yet* + // but it may be useful to retry the call making the factory call + // even if the wallet is already deployed, in case the wallet + // needs to perform some sort of migration or onchain key rotation + if (!deployAlreadyDeployed && contractCodeLen > 0) { + return isValidSigImpl(_signer, _hash, _signature, allowSideEffects, true); + } + + revert ERC1271Revert(err); + } + } + + // ecrecover verification + require(_signature.length == 65, 'SignatureValidator#recoverSigner: invalid signature length'); + bytes32 r = bytes32(_signature[0:32]); + bytes32 s = bytes32(_signature[32:64]); + uint8 v = uint8(_signature[64]); + + if (v != 27 && v != 28) { + revert('SignatureValidator: invalid signature v value'); + } + + return ecrecover(_hash, v, r, s) == _signer; + } + + function isValidSigWithSideEffects( + address _signer, + bytes32 _hash, + bytes calldata _signature + ) external returns (bool) { + return this.isValidSigImpl(_signer, _hash, _signature, true, false); + } + + function isValidSig( + address _signer, + bytes32 _hash, + bytes calldata _signature + ) external returns (bool) { + try this.isValidSigImpl(_signer, _hash, _signature, false, false) returns (bool isValid) { + return isValid; + } catch (bytes memory error) { + // in order to avoid side effects from the contract getting deployed, the entire call will revert with a single byte result + uint len = error.length; + if (len == 1) { + return error[0] == 0x01; + // all other errors are simply forwarded, but in custom formats so that nothing else can revert with a single byte in the call + } else { + assembly { revert(error, len) } + } + } + } + + // NOTICE: These functions aren't part of the standard + // they are helpers that behave like the above functions + // but they don't revert on failure, instead they return false + + function isValidSigNoThrow( + address _signer, + bytes32 _hash, + bytes calldata _signature + ) external returns (bool) { + try this.isValidSigImpl(_signer, _hash, _signature, false, false) returns (bool isValid) { + return isValid; + } catch (bytes memory error) { + // in order to avoid side effects from the contract getting deployed, the entire call will revert with a single byte result + uint len = error.length; + if (len == 1) { + return error[0] == 0x01; + // all other errors are simply forwarded, but in custom formats so that nothing else can revert with a single byte in the call + } else { + // Ignore all other errors and return false + return false; + } + } + } + + function isValidSigWithSideEffectsNoThrow( + address _signer, + bytes32 _hash, + bytes calldata _signature + ) external returns (bool) { + try this.isValidSigImpl(_signer, _hash, _signature, true, false) returns (bool isValid) { + return isValid; + } catch (bytes memory error) { + // Ignore all errors and return false + return false; + } + } +} + +// this is a helper so we can perform validation in a single eth_call without pre-deploying a singleton +contract ValidateSigOffchain { + constructor (address _signer, bytes32 _hash, bytes memory _signature) { + UniversalSigValidator validator = new UniversalSigValidator(); + bool isValidSig = validator.isValidSigWithSideEffects(_signer, _hash, _signature); + assembly { + mstore(0, isValidSig) + return(31, 1) + } + } +} +*/ + +export const EIP_6492_OFFCHAIN_DEPLOY_CODE = "0x608060405234801561001057600080fd5b5060405161124a38038061124a83398101604081905261002f91610124565b600060405161003d906100dd565b604051809103906000f080158015610059573d6000803e3d6000fd5b5090506000816001600160a01b0316638f0684308686866040518463ffffffff1660e01b815260040161008e939291906101fb565b6020604051808303816000875af11580156100ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100d19190610244565b9050806000526001601ff35b610fdc8061026e83390190565b634e487b7160e01b600052604160045260246000fd5b60005b8381101561011b578181015183820152602001610103565b50506000910152565b60008060006060848603121561013957600080fd5b83516001600160a01b038116811461015057600080fd5b6020850151604086015191945092506001600160401b038082111561017457600080fd5b818601915086601f83011261018857600080fd5b81518181111561019a5761019a6100ea565b604051601f8201601f19908116603f011681019083821181831017156101c2576101c26100ea565b816040528281528960208487010111156101db57600080fd5b6101ec836020830160208801610100565b80955050505050509250925092565b60018060a01b0384168152826020820152606060408201526000825180606084015261022e816080850160208701610100565b601f01601f191691909101608001949350505050565b60006020828403121561025657600080fd5b8151801515811461026657600080fd5b939250505056fe608060405234801561001057600080fd5b50610fbc806100206000396000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c806376be4cea1161005057806376be4cea146100a65780638f068430146100b957806398ef1ed8146100cc57600080fd5b80631c6453271461006c5780633d787b6314610093575b600080fd5b61007f61007a366004610ad4565b6100df565b604051901515815260200160405180910390f35b61007f6100a1366004610ad4565b61023d565b61007f6100b4366004610b3e565b61031e565b61007f6100c7366004610ad4565b6108e1565b61007f6100da366004610ad4565b61096e565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea9061012890889088908890889088908190600401610bc3565b6020604051808303816000875af1925050508015610181575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261017e91810190610c45565b60015b610232573d8080156101af576040519150601f19603f3d011682016040523d82523d6000602084013e6101b4565b606091505b508051600181900361022757816000815181106101d3576101d3610c69565b6020910101517fff00000000000000000000000000000000000000000000000000000000000000167f0100000000000000000000000000000000000000000000000000000000000000149250610235915050565b600092505050610235565b90505b949350505050565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea906102879088908890889088906001908990600401610bc3565b6020604051808303816000875af19250505080156102e0575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526102dd91810190610c45565b60015b610232573d80801561030e576040519150601f19603f3d011682016040523d82523d6000602084013e610313565b606091505b506000915050610235565b600073ffffffffffffffffffffffffffffffffffffffff87163b6060827f64926492649264926492649264926492649264926492649264926492649264928888610369602082610c98565b610375928b9290610cd8565b61037e91610d02565b1490508015610484576000606089828a610399602082610c98565b926103a693929190610cd8565b8101906103b39190610e18565b955090925090508415806103c45750865b1561047d576000808373ffffffffffffffffffffffffffffffffffffffff16836040516103f19190610eb2565b6000604051808303816000865af19150503d806000811461042e576040519150601f19603f3d011682016040523d82523d6000602084013e610433565b606091505b50915091508161047a57806040517f9d0d6e2d0000000000000000000000000000000000000000000000000000000081526004016104719190610f18565b60405180910390fd5b50505b50506104be565b87878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509294505050505b80806104ca5750600083115b156106bb576040517f1626ba7e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8b1690631626ba7e90610523908c908690600401610f2b565b602060405180830381865afa92505050801561057a575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261057791810190610f44565b60015b61060f573d8080156105a8576040519150601f19603f3d011682016040523d82523d6000602084013e6105ad565b606091505b50851580156105bc5750600084115b156105db576105d08b8b8b8b8b600161031e565b9450505050506108d7565b806040517f6f2a95990000000000000000000000000000000000000000000000000000000081526004016104719190610f18565b7fffffffff0000000000000000000000000000000000000000000000000000000081167f1626ba7e000000000000000000000000000000000000000000000000000000001480158161065f575086155b801561066b5750600085115b1561068b5761067f8c8c8c8c8c600161031e565b955050505050506108d7565b841580156106965750825b80156106a0575087155b156106af57806000526001601ffd5b94506108d79350505050565b6041871461074b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f5369676e617475726556616c696461746f72237265636f7665725369676e657260448201527f3a20696e76616c6964207369676e6174757265206c656e6774680000000000006064820152608401610471565b600061075a6020828a8c610cd8565b61076391610d02565b90506000610775604060208b8d610cd8565b61077e91610d02565b905060008a8a604081811061079557610795610c69565b919091013560f81c915050601b81148015906107b557508060ff16601c14155b15610842576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f5369676e617475726556616c696461746f723a20696e76616c6964207369676e60448201527f617475726520762076616c7565000000000000000000000000000000000000006064820152608401610471565b6040805160008152602081018083528e905260ff831691810191909152606081018490526080810183905273ffffffffffffffffffffffffffffffffffffffff8e169060019060a0016020604051602081039080840390855afa1580156108ad573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff161496505050505050505b9695505050505050565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea9061092b9088908890889088906001908990600401610bc3565b6020604051808303816000875af115801561094a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102329190610c45565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea906109b790889088908890889088908190600401610bc3565b6020604051808303816000875af1925050508015610a10575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252610a0d91810190610c45565b60015b610232573d808015610a3e576040519150601f19603f3d011682016040523d82523d6000602084013e610a43565b606091505b5080516001819003610a6257816000815181106101d3576101d3610c69565b8082fd5b73ffffffffffffffffffffffffffffffffffffffff81168114610a8857600080fd5b50565b60008083601f840112610a9d57600080fd5b50813567ffffffffffffffff811115610ab557600080fd5b602083019150836020828501011115610acd57600080fd5b9250929050565b60008060008060608587031215610aea57600080fd5b8435610af581610a66565b935060208501359250604085013567ffffffffffffffff811115610b1857600080fd5b610b2487828801610a8b565b95989497509550505050565b8015158114610a8857600080fd5b60008060008060008060a08789031215610b5757600080fd5b8635610b6281610a66565b955060208701359450604087013567ffffffffffffffff811115610b8557600080fd5b610b9189828a01610a8b565b9095509350506060870135610ba581610b30565b91506080870135610bb581610b30565b809150509295509295509295565b73ffffffffffffffffffffffffffffffffffffffff8716815285602082015260a060408201528360a0820152838560c0830137600060c085830181019190915292151560608201529015156080820152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016909101019392505050565b600060208284031215610c5757600080fd5b8151610c6281610b30565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b81810381811115610cd2577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b92915050565b60008085851115610ce857600080fd5b83861115610cf557600080fd5b5050820193919092039150565b80356020831015610cd2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602084900360031b1b1692915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f830112610d7e57600080fd5b813567ffffffffffffffff80821115610d9957610d99610d3e565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715610ddf57610ddf610d3e565b81604052838152866020858801011115610df857600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080600060608486031215610e2d57600080fd5b8335610e3881610a66565b9250602084013567ffffffffffffffff80821115610e5557600080fd5b610e6187838801610d6d565b93506040860135915080821115610e7757600080fd5b50610e8486828701610d6d565b9150509250925092565b60005b83811015610ea9578181015183820152602001610e91565b50506000910152565b60008251610ec4818460208701610e8e565b9190910192915050565b60008151808452610ee6816020860160208601610e8e565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610c626020830184610ece565b8281526040602082015260006102356040830184610ece565b600060208284031215610f5657600080fd5b81517fffffffff0000000000000000000000000000000000000000000000000000000081168114610c6257600080fdfea26469706673582212201a72aed4b15ffb05b6502997a9bb655992e06590bd26b336dfbb153d7ff6f34b64736f6c63430008120033" +export const EIP_6492_SUFFIX = "0x6492649264926492649264926492649264926492649264926492649264926492" + +// TODO: This is a length payload, we can lower the load by deploying +// the contract on some of the popular chains, and calling the contract +// if the provider is one of those chains +export async function validateEIP6492Offchain( + provider: ethers.providers.Provider, + signer: string, + hash: ethers.utils.BytesLike, + signature: ethers.utils.BytesLike +): Promise { + return '0x01' === await provider.call({ + data: ethers.utils.concat([ + EIP_6492_OFFCHAIN_DEPLOY_CODE, + (new ethers.utils.AbiCoder()).encode(['address', 'bytes32', 'bytes'], [signer, hash, signature]) + ]) + }) +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts new file mode 100644 index 000000000..6a9eef9db --- /dev/null +++ b/packages/core/src/index.ts @@ -0,0 +1,10 @@ + +export * as v1 from './v1' +export * as v2 from "./v2" +export * as commons from "./commons" +export * as universal from './universal' + +import * as v1 from './v1' +import * as v2 from "./v2" + +export const allVersions = [v1, v2] diff --git a/packages/core/src/universal/index.ts b/packages/core/src/universal/index.ts new file mode 100644 index 000000000..9d7862706 --- /dev/null +++ b/packages/core/src/universal/index.ts @@ -0,0 +1,27 @@ + + +import { commons, v1, v2 } from ".." + +export const ALL_CODERS = [ + { config: v1.config.ConfigCoder, signature: v1.signature.SignatureCoder }, + { config: v2.config.ConfigCoder, signature: v2.signature.SignatureCoder } +] + +export function coderFor(version: number) { + const index = version - 1 + if (index < 0 || index >= ALL_CODERS.length) { + throw new Error(`No coder for version: ${version}`) + } + + return ALL_CODERS[index] +} + +/** + * Same as `coderFor` but returns `generic` coders without versioned types. + */ +export function genericCoderFor(version: number): { + config: commons.config.ConfigCoder, + signature: commons.signature.SignatureCoder +} { + return coderFor(version) +} diff --git a/packages/core/src/v1/config.ts b/packages/core/src/v1/config.ts new file mode 100644 index 000000000..f2d0013df --- /dev/null +++ b/packages/core/src/v1/config.ts @@ -0,0 +1,231 @@ + +import { ethers } from 'ethers' +import { walletContracts } from '@0xsequence/abi' +import { commons } from '..' +import { encodeSigners } from './signature' + + +export type AddressMember = { + weight: ethers.BigNumberish, + address: string, + signature?: string +} + +export type WalletConfig = commons.config.Config & { + threshold: ethers.BigNumberish, + signers: AddressMember[] +} + +export const ConfigCoder: commons.config.ConfigCoder = { + isWalletConfig: (config: commons.config.Config): config is WalletConfig => { + return ( + config.version === 1 && + (config as WalletConfig).threshold !== undefined && + (config as WalletConfig).signers !== undefined + ) + }, + + imageHashOf: (config: WalletConfig): string => { + return config.signers.reduce( + (imageHash, signer) => ethers.utils.keccak256( + ethers.utils.defaultAbiCoder.encode( + ['bytes32', 'uint8', 'address'], + [imageHash, signer.weight, signer.address] + ) + ), + ethers.utils.solidityPack(['uint256'], [config.threshold]) + ) + }, + + hasSubdigest: (_walletConfig: WalletConfig, _subdigest: string): boolean => { + // v1 does not support explicit subdigests + return false + }, + + isComplete: (_config: WalletConfig): boolean => { + // v1 does not support incomplete configs + return true + }, + + checkpointOf: (_config: WalletConfig): ethers.BigNumber => { + return ethers.BigNumber.from(0) + }, + + signersOf: (config: WalletConfig): { address: string, weight: number }[] => { + return config.signers.map((s) => ({ address: s.address, weight: ethers.BigNumber.from(s.weight).toNumber() })) + }, + + fromSimple: (config: { + threshold: ethers.BigNumberish + checkpoint: ethers.BigNumberish + signers: { address: string; weight: ethers.BigNumberish} [] + }): WalletConfig => { + if (!ethers.constants.Zero.eq(config.checkpoint)) { + throw new Error('v1 wallet config does not support checkpoint') + } + + return { + version: 1, + threshold: config.threshold, + signers: config.signers + } + }, + + update: { + isKindUsed: true, + + buildTransaction: ( + wallet: string, + config: WalletConfig, + context: commons.context.WalletContext, + kind?: 'first' | 'later' | undefined + ): commons.transaction.TransactionBundle => { + const module = new ethers.utils.Interface([ + ...walletContracts.mainModule.abi, + ...walletContracts.mainModuleUpgradable.abi + ]) + + const transactions: commons.transaction.Transaction[] = [] + + if (!kind || kind === 'first') { + transactions.push({ + to: wallet, + data: module.encodeFunctionData(module.getFunction('updateImplementation'), [ + context.mainModuleUpgradable + ]), + gasLimit: 0, + delegateCall: false, + revertOnError: true, + value: 0 + }) + } + + transactions.push({ + to: wallet, + data: module.encodeFunctionData(module.getFunction('updateImageHash'), [ + ConfigCoder.imageHashOf(config) + ]), + gasLimit: 0, + delegateCall: false, + revertOnError: true, + value: 0 + }) + + return { + entrypoint: wallet, + transactions + } + }, + decodeTransaction: function (tx: commons.transaction.TransactionBundle): { address: string; newImageHash: string; kind: "first" | "later" | undefined} { + throw new Error("Function not implemented.") + } + }, + + toJSON: function (config: WalletConfig): string { + const plainMembers = config.signers.map((signer) => { + return { + weight: ethers.BigNumber.from(signer.weight).toString(), + address: signer.address + } + }) + + return JSON.stringify({ + version: config.version, + threshold: ethers.BigNumber.from(config.threshold).toString(), + signers: plainMembers + }) + }, + + fromJSON: function (json: string): WalletConfig { + const parsed = JSON.parse(json) + + const signers = parsed.signers.map((signer: any) => { + return { + weight: ethers.BigNumber.from(signer.weight), + address: signer.address + } + }) + + return { + version: parsed.version, + threshold: ethers.BigNumber.from(parsed.threshold), + signers + } + }, + + editConfig: function ( + config: WalletConfig, + action: { + add?: commons.config.SimpleSigner[]; + remove?: string[]; + threshold?: ethers.BigNumberish, + checkpoint?: ethers.BigNumberish + } + ): WalletConfig { + const newSigners = config.signers.slice() + + if (action.checkpoint && !ethers.constants.Zero.eq(action.checkpoint)) { + throw new Error('v1 wallet config does not support checkpoint') + } + + if (action.add) { + for (const signer of action.add) { + if (newSigners.find((s) => s.address === signer.address)) { + continue + } + + newSigners.push({ + weight: signer.weight, + address: signer.address + }) + } + } + + if (action.remove) { + for (const address of action.remove) { + const index = newSigners.findIndex((signer) => signer.address === address) + if (index >= 0) { + newSigners.splice(index, 1) + } + } + } + + return { + version: config.version, + threshold: action.threshold ?? config.threshold, + signers: newSigners + } + }, + + buildStubSignature: function ( + config: WalletConfig, + overrides: Map + ) { + const parts = new Map() + + for (const [signer, signature] of overrides.entries()) { + parts.set(signer, { signature, isDynamic: true }) + + const { encoded, weight } = encodeSigners(config, parts, [], 0) + + if (weight.gte(config.threshold)) { + return encoded + } + } + + const signers = config.signers + + for (const { address } of signers.sort(({ weight: a }, { weight: b }) => ethers.BigNumber.from(a).sub(b).toNumber())) { + const signature = '0x4e82f02f388a12b5f9d29eaf2452dd040c0ee5804b4e504b4dd64e396c6c781f2c7624195acba242dd825bfd25a290912e3c230841fd55c9a734c4de8d9899451b02' + parts.set(address, { signature, isDynamic: false }) + + const { encoded, weight } = encodeSigners(config, parts, [], 0) + + if (weight.gte(config.threshold)) { + return encoded + } + } + + return encodeSigners(config, parts, [], 0).encoded + } +} diff --git a/packages/core/src/v1/index.ts b/packages/core/src/v1/index.ts new file mode 100644 index 000000000..c0cfe342b --- /dev/null +++ b/packages/core/src/v1/index.ts @@ -0,0 +1,5 @@ + +export * as config from './config' +export * as signature from './signature' + +export const version = 1 diff --git a/packages/core/src/v1/signature.ts b/packages/core/src/v1/signature.ts new file mode 100644 index 000000000..df73f7647 --- /dev/null +++ b/packages/core/src/v1/signature.ts @@ -0,0 +1,282 @@ + +import { ethers } from 'ethers' +import * as base from '../commons/signature' +import { AddressMember, WalletConfig } from './config' +import { isValidSignature, recoverSigner } from "../commons/signer" + +export enum SignaturePartType { + EOASignature = 0, + Address = 1, + DynamicSignature = 2 +} + +export type Signature = base.Signature + +export type UnrecoveredSignatureMember = { + unrecovered: true, + weight: ethers.BigNumberish, + signature: string, + address?: string, + isDynamic: boolean +} + +export type UnrecoveredMember = AddressMember | UnrecoveredSignatureMember + +export type UnrecoveredSignature = base.UnrecoveredSignature & { + threshold: ethers.BigNumberish, + signers: UnrecoveredMember[] +} + +export function isAddressMember(member: any): member is AddressMember { + return ( + (member as AddressMember).address !== undefined && + !isUnrecoveredSignatureMember(member) + ) +} + +export function isUnrecoveredSignatureMember(member: any): member is UnrecoveredSignatureMember { + return ( + (member as UnrecoveredSignatureMember).signature !== undefined && + (member as UnrecoveredSignatureMember).weight !== undefined && + (member as UnrecoveredSignatureMember).isDynamic !== undefined + ) +} + +export function isUnrecoveredSignature(signature: Signature | UnrecoveredSignature): signature is UnrecoveredSignature { + return ( + (signature as UnrecoveredSignature).threshold !== undefined && + (signature as UnrecoveredSignature).signers !== undefined + ) +} + +export function decodeSignature(signature: ethers.BytesLike): UnrecoveredSignature { + const bytes = ethers.utils.arrayify(signature) + + const threshold = bytes[0] << 8 | bytes[1] + const signers: UnrecoveredMember[] = [] + + for (let i = 2; i < bytes.length;) { + const type = bytes[i++] + const weight = bytes[i++] + + switch (type) { + case SignaturePartType.EOASignature: + signers.push({ + unrecovered: true, + weight, + signature: ethers.utils.hexlify(bytes.slice(i, i + 66)), + isDynamic: false + }) + i += 66 + break + + case SignaturePartType.Address: + signers.push({ + weight, + address: ethers.utils.getAddress(ethers.utils.hexlify(bytes.slice(i, i + 20))) + }) + i += 20 + break + + case SignaturePartType.DynamicSignature: + const address = ethers.utils.getAddress(ethers.utils.hexlify(bytes.slice(i, i + 20))) + i += 20 + + const size = bytes[i] << 8 | bytes[i + 1] + i += 2 + + signers.push({ + unrecovered: true, + weight, + signature: ethers.utils.hexlify(bytes.slice(i, i + size)), + address, + isDynamic: true + }) + i += size + break + + default: + throw new Error(`Unknown signature part type: ${type}`) + } + } + + return { version: 1, threshold, signers } +} + +export function encodeSignature(signature: Signature | UnrecoveredSignature | ethers.BytesLike): string { + if (ethers.utils.isBytesLike(signature)) return ethers.utils.hexlify(signature) + + const { signers, threshold } = isUnrecoveredSignature(signature) ? signature : signature.config + + const encodedSigners = signers.map((s) => { + if (isAddressMember(s)) { + return ethers.utils.solidityPack( + ['uint8', 'uint8', 'address'], + [SignaturePartType.Address, s.weight, s.address] + ) + } + + if (s.isDynamic) { + const bytes = ethers.utils.arrayify(s.signature) + return ethers.utils.solidityPack( + ['uint8', 'uint8', 'address', 'uint16', 'bytes'], + [SignaturePartType.DynamicSignature, s.weight, s.address, bytes.length, bytes] + ) + } + + return ethers.utils.solidityPack( + ['uint8', 'uint8', 'bytes'], + [SignaturePartType.EOASignature, s.weight, s.signature] + ) + }) + + return ethers.utils.solidityPack( + ['uint16', ...new Array(encodedSigners.length).fill('bytes')], + [threshold, ...encodedSigners] + ) +} + +export async function recoverSignature( + data: UnrecoveredSignature, + payload: base.SignedPayload, + provider: ethers.providers.Provider +): Promise { + const subdigest = base.subdigestOf(payload) + const signers = await Promise.all(data.signers.map(async (s) => { + if (isAddressMember(s)) { + return s + } + + if (s.isDynamic) { + if (!s.address) throw new Error('Dynamic signature part must have address') + if (!isValidSignature(s.address, subdigest, s.signature, provider)) { + throw new Error(`Invalid dynamic signature part ${s.address}`) + } + + return { address: s.address, weight: s.weight, signature: s.signature } + } else { + const address = recoverSigner(subdigest, s.signature) + return { address, weight: s.weight, signature: s.signature } + } + })) + + return { + version: 1, + payload, + subdigest, + config: { + version: 1, + threshold: data.threshold, + signers + } + } +} + +export function encodeSigners( + config: WalletConfig, + signatures: Map, + subdigests: string[], + _: ethers.BigNumberish +): { encoded: string, weight: ethers.BigNumber } { + if (subdigests.length !== 0) { + throw new Error('Explicit subdigests not supported on v1') + } + + let weight = ethers.BigNumber.from(0) + const parts = config.signers.map((s) => { + if (!signatures.has(s.address)) { + return s + } + + const signature = signatures.get(s.address)! + const bytes = ethers.utils.arrayify(signature.signature) + + weight = weight.add(s.weight) + + if (signature.isDynamic || bytes.length !== 66) { + return { + ...s, + isDynamic: true, + signature: signature.signature, + address: s.address + } + } + + return { + ...s, + isDynamic: false, + signature: signature.signature + } + }) + + const encoded = encodeSignature({ version: 1, threshold: config.threshold, signers: parts }) + return { encoded, weight } +} + +export const SignatureCoder: base.SignatureCoder< + WalletConfig, + Signature, + UnrecoveredSignature +> = { + decode: (data: string): UnrecoveredSignature => { + return decodeSignature(data) + }, + + encode: (data: Signature | UnrecoveredSignature | ethers.BytesLike): string => { + return encodeSignature(data) + }, + + trim: async (data: string): Promise => { + return data + }, + + supportsNoChainId: true, + + recover: ( + data: UnrecoveredSignature, + payload: base.SignedPayload, + provider: ethers.providers.Provider + ): Promise => { + return recoverSignature(data, payload, provider) + }, + + encodeSigners: ( + config: WalletConfig, + signatures: Map, + subdigests: string[], + chainId: ethers.BigNumberish + ): { + encoded: string + weight: ethers.BigNumber + } => { + return encodeSigners(config, signatures, subdigests, chainId) + }, + + hasEnoughSigningPower: (config: WalletConfig, signatures: Map): boolean => { + const { weight } = SignatureCoder.encodeSigners(config, signatures, [], 0) + return weight.gte(config.threshold) + }, + + chainSignatures: ( + _main: Signature | UnrecoveredSignature | ethers.BytesLike, + _suffix: (Signature | UnrecoveredSignature | ethers.BytesLike)[] + ): string => { + throw new Error('Signature chaining not supported on v1') + }, + + hashSetImageHash: function (_imageHash: string): string { + throw new Error('Image hash not supported on v1') + }, + + signaturesOf(config: WalletConfig): { address: string; signature: string} [] { + return config.signers + .filter((s) => s.signature !== undefined) + .map((s) => ({ address: s.address, signature: s.signature! })) + }, + + signaturesOfDecoded: function (data: UnrecoveredSignature): string[] { + return data.signers + .map((s) => s.signature) + .filter((s) => s !== undefined) as string[] + } +} diff --git a/packages/core/src/v2/chained.ts b/packages/core/src/v2/chained.ts new file mode 100644 index 000000000..e3f17a97d --- /dev/null +++ b/packages/core/src/v2/chained.ts @@ -0,0 +1,26 @@ +import { ethers } from "ethers" + +// = keccak256("SetImageHash(bytes32 imageHash)") +export const SetImageHashPrefix = '0x8713a7c4465f6fbee2b6e9d6646d1d9f83fec929edfc4baf661f3c865bdd04d1' + +export function hashSetImageHash(imageHash: string): string { + return ethers.utils.keccak256(messageSetImageHash(imageHash)) +} + +export function messageSetImageHash(imageHash: string) { + return ethers.utils.solidityPack( + ['bytes32', 'bytes32'], + [SetImageHashPrefix, imageHash] + ) +} + +export function decodeMessageSetImageHash(message: ethers.BytesLike): string | undefined { + const arr = ethers.utils.arrayify(message) + if (arr.length !== 64) return undefined + if (ethers.utils.hexlify(arr.slice(0, 32)) !== SetImageHashPrefix) return undefined + return ethers.utils.hexlify(arr.slice(32, 64)) +} + +export function isMessageSetImageHash(message: ethers.BytesLike): boolean { + return decodeMessageSetImageHash(message) !== undefined +} diff --git a/packages/core/src/v2/config.ts b/packages/core/src/v2/config.ts new file mode 100644 index 000000000..987f07590 --- /dev/null +++ b/packages/core/src/v2/config.ts @@ -0,0 +1,658 @@ + +import { ethers } from "ethers" +import { walletContracts } from "@0xsequence/abi" +import { commons } from ".." +import { encodeSigners } from "./signature" + +// +// Tree typings - leaves +// + +export type SignerLeaf = { + address: string, + weight: ethers.BigNumberish, + signature?: string +} + +export type SubdigestLeaf = { + subdigest: string +} + +export type NestedLeaf = { + tree: Topology, + weight: ethers.BigNumberish, + threshold: ethers.BigNumberish +} + +// This is an unknown node +// it means the tree has a branch +// but we don't know what the content +export type NodeLeaf = { + nodeHash: string +} + +export type Leaf = SignerLeaf | SubdigestLeaf | NestedLeaf | NodeLeaf + +export function isSignerLeaf(leaf: any): leaf is SignerLeaf { + return ( + (leaf as SignerLeaf).address !== undefined && + (leaf as SignerLeaf).weight !== undefined + ) +} + +export function isSubdigestLeaf(leaf: any): leaf is SubdigestLeaf { + return (leaf as SubdigestLeaf).subdigest !== undefined && (leaf as SignerLeaf).address === undefined +} + +export function topologyToJSON(tree: Topology): string { + if (isNode(tree)) { + return JSON.stringify({ + left: topologyToJSON(tree.left), + right: topologyToJSON(tree.right) + }) + } + + if (isNestedLeaf(tree)) { + return JSON.stringify({ + weight: ethers.BigNumber.from(tree.weight).toString(), + threshold: ethers.BigNumber.from(tree.threshold).toString(), + tree: topologyToJSON(tree.tree) + }) + } + + if (isSignerLeaf(tree)) { + return JSON.stringify({ + address: tree.address, + weight: ethers.BigNumber.from(tree.weight).toString() + }) + } + + return JSON.stringify(tree) +} + +export function topologyFromJSON(json: string | Object): Topology { + const parsed = typeof json === 'string' ? JSON.parse(json) : json + + if (parsed.left !== undefined && parsed.right !== undefined) { + return { + left: topologyFromJSON(parsed.left), + right: topologyFromJSON(parsed.right) + } + } + + if (parsed.weight !== undefined && parsed.threshold !== undefined && parsed.tree !== undefined) { + return { + weight: ethers.BigNumber.from(parsed.weight), + threshold: ethers.BigNumber.from(parsed.threshold), + tree: topologyFromJSON(parsed.tree) + } + } + + if (parsed.address !== undefined && parsed.weight !== undefined) { + return { + address: parsed.address, + weight: ethers.BigNumber.from(parsed.weight) + } + } + + return parsed +} + +export function isNestedLeaf(leaf: any): leaf is NestedLeaf { + return ( + (leaf as NestedLeaf).tree !== undefined && + (leaf as NestedLeaf).weight !== undefined && + (leaf as NestedLeaf).threshold !== undefined + ) +} + +export function isNodeLeaf(leaf: any): leaf is NodeLeaf { + return (leaf as NodeLeaf).nodeHash !== undefined +} + +export function isLeaf(leaf: any): leaf is Leaf { + return isSignerLeaf(leaf) || isSubdigestLeaf(leaf) || isNestedLeaf(leaf) || isNodeLeaf(leaf) +} + +// +// Tree typings - nodes +// + +export type Node = { + left: Node | Leaf, + right: Node | Leaf +} + +export type Topology = Node | Leaf + +export function isNode(node: any): node is Node { + return ( + (node as Node).left !== undefined && + (node as Node).right !== undefined + ) +} + +export function isTopology(topology: any): topology is Topology { + return isNode(topology) || isLeaf(topology) +} + +export function encodeSignerLeaf(leaf: SignerLeaf): string { + return ethers.utils.solidityPack( + ['uint96', 'address'], + [leaf.weight, leaf.address] + ) +} + +export function decodeSignerLeaf(encoded: string): SignerLeaf { + const bytes = ethers.utils.arrayify(encoded); + + if (bytes.length !== 32) { + throw new Error('Invalid encoded string length'); + } + + const weight = ethers.BigNumber.from(bytes.slice(0, 12)); + const address = ethers.utils.getAddress(ethers.utils.hexlify(bytes.slice(12))); + + return { weight, address } +} + +export function isEncodedSignerLeaf(encoded: string): boolean { + const bytes = ethers.utils.arrayify(encoded) + + if (bytes.length !== 32) { + return false + } + + const prefix = bytes.slice(0, 11) + return prefix.every((byte) => byte === 0) +} + +export function hashNode(node: Node | Leaf): string { + if (isSignerLeaf(node)) { + return encodeSignerLeaf(node) + } + + if (isSubdigestLeaf(node)) { + return ethers.utils.solidityKeccak256( + ['string', 'bytes32'], + ['Sequence static digest:\n', node.subdigest] + ) + } + + if (isNestedLeaf(node)) { + const nested = hashNode(node.tree) + return ethers.utils.solidityKeccak256( + ['string', 'bytes32', 'uint256', 'uint256'], + ['Sequence nested config:\n', nested, node.threshold, node.weight] + ) + } + + if (isNodeLeaf(node)) { + return node.nodeHash + } + + return ethers.utils.solidityKeccak256( + ['bytes32', 'bytes32'], + [hashNode(node.left), hashNode(node.right)] + ) +} + +export function leftFace(topology: Topology): Topology[] { + const stack: Topology[] = [] + + let prev = topology + while (!isLeaf(prev)) { + stack.unshift(prev.right) + prev = prev.left + } + + stack.unshift(prev) + + return stack +} + +// +// Wallet config types +// + +export type WalletConfig = commons.config.Config & { + threshold: ethers.BigNumberish, + checkpoint: ethers.BigNumberish, + tree: Topology +} + +export function isWalletConfig(config: any): config is WalletConfig { + return ( + (config as WalletConfig).threshold !== undefined && + (config as WalletConfig).checkpoint !== undefined && + (config as WalletConfig).tree !== undefined && + (config as WalletConfig).version !== undefined && + (config as WalletConfig).version === 2 + ) +} + +export function imageHash(config: WalletConfig): string { + return ethers.utils.solidityKeccak256( + ['bytes32', 'uint256'], + [ + ethers.utils.solidityKeccak256( + ['bytes32', 'uint256'], + [ + hashNode(config.tree), + config.threshold + ] + ), + config.checkpoint + ] + ) +} + +// +// Simple wallet config types +// (used for building and reading merkle configs) +// +// dev: `members` is a flat representation of the tree +// it keeps relevant structure like 'nested trees' but +// it ignores the tree structure +// +// + +export type SimpleNestedMember = { + threshold: ethers.BigNumberish, + weight: ethers.BigNumberish, + members: SimpleConfigMember[] +} + +export type SimpleConfigMember = SubdigestLeaf | SignerLeaf | SimpleNestedMember + +export type SimpleWalletConfig = { + threshold: ethers.BigNumberish, + checkpoint: ethers.BigNumberish, + members: SimpleConfigMember[] +} + +export function isSimpleNestedMember(member: any): member is SimpleNestedMember { + return ( + (member as SimpleNestedMember).threshold !== undefined && + (member as SimpleNestedMember).weight !== undefined && + (member as SimpleNestedMember).members !== undefined + ) +} + +export function topologyToMembers(tree: Topology): SimpleConfigMember[] { + if (isSignerLeaf(tree) || isSubdigestLeaf(tree)) { + return [tree] + } + + if (isNestedLeaf(tree)) { + return [{ + threshold: tree.threshold, + weight: tree.weight, + members: topologyToMembers(tree.tree) + }] + } + + if (isNodeLeaf(tree)) { + // we don't know the content of this node + // so we omit it + return [] + } + + return [ + ...topologyToMembers(tree.left), + ...topologyToMembers(tree.right) + ] +} + +export function hasUnknownNodes(tree: Topology): boolean { + if (isNodeLeaf(tree)) { + return true + } + + if (isNode(tree)) { + return hasUnknownNodes(tree.left) || hasUnknownNodes(tree.right) + } + + return false +} + +export function toSimpleWalletConfig(config: WalletConfig): SimpleWalletConfig { + return { + threshold: config.threshold, + checkpoint: config.checkpoint, + members: topologyToMembers(config.tree) + } +} + +export type TopologyBuilder = (members: SimpleConfigMember[]) => Topology + +const membersAsTopologies = (members: SimpleConfigMember[], builder: TopologyBuilder): Topology[] => { + return members.map((member) => { + if (isSimpleNestedMember(member)) { + return { + tree: builder(member.members), + threshold: member.threshold, + weight: member.weight + } + } + + return member + }) +} + +export function legacyTopologyBuilder(members: SimpleConfigMember[]): Topology { + if (members.length === 0) { + throw new Error('Empty members array') + } + + const asTopologies = membersAsTopologies(members, legacyTopologyBuilder) + return asTopologies.reduce((acc, member) => { + return { + left: acc, + right: member + } + }) +} + +export function merkleTopologyBuilder(members: SimpleConfigMember[]): Topology { + if (members.length === 0) { + throw new Error('Empty members array') + } + + const leaves = membersAsTopologies(members, merkleTopologyBuilder) + for (let s = leaves.length; s > 1; s = s / 2) { + for (let i = 0; i < s / 2; i++) { + const j1 = i * 2 + const j2 = j1 + 1 + + if (j2 >= s) { + leaves[i] = leaves[j1] + } else { + leaves[i] = { + left: leaves[j1], + right: leaves[j2] + } + } + } + } + + return leaves[0] +} + +export function optimized2SignersTopologyBuilder(members: SimpleConfigMember[]): Topology { + if (members.length > 8) { + return merkleTopologyBuilder(members) + } + + return legacyTopologyBuilder(members) +} + +export function toWalletConfig( + simpleWalletConfig: SimpleWalletConfig, + builder: TopologyBuilder = optimized2SignersTopologyBuilder +): WalletConfig { + + return { + version: 2, + threshold: simpleWalletConfig.threshold, + checkpoint: simpleWalletConfig.checkpoint, + tree: builder(simpleWalletConfig.members) + } +} + +export function hasSubdigest(tree: Topology, subdigest: string): boolean { + if (isSubdigestLeaf(tree)) { + return tree.subdigest === subdigest + } + + if (isNode(tree)) { + return hasSubdigest(tree.left, subdigest) || hasSubdigest(tree.right, subdigest) + } + + return false +} + +export function signersOf(tree: Topology): { address: string, weight: number }[] { + const stack: Topology[] = [tree] + const signers = new Set<{ address: string, weight: number }>() + + while (stack.length > 0) { + const node = stack.pop() + + if (isNestedLeaf(node)) { + stack.push(node.tree) + } else if (isNode(node)) { + stack.push(node.left) + stack.push(node.right) + } else if (isSignerLeaf(node)) { + signers.add({ address: node.address, weight: ethers.BigNumber.from(node.weight).toNumber() }) + } + } + + return Array.from(signers) +} + +export function isComplete(tree: Topology): boolean { + if (isNode(tree)) { + return isComplete(tree.left) && isComplete(tree.right) + } + + return !isNodeLeaf(tree) +} + +export const ConfigCoder: commons.config.ConfigCoder = { + isWalletConfig: (config: commons.config.Config): config is WalletConfig => { + return ( + config.version === 2 && + (config as WalletConfig).threshold !== undefined && + (config as WalletConfig).tree !== undefined + ) + }, + + imageHashOf: (config: WalletConfig): string => { + return imageHash(config) + }, + + hasSubdigest: (config: WalletConfig, subdigest: string): boolean => { + return hasSubdigest(config.tree, subdigest) + }, + + checkpointOf: (config: WalletConfig): ethers.BigNumber => { + return ethers.BigNumber.from(config.checkpoint) + }, + + signersOf: (config: WalletConfig): { address: string, weight: number }[] => { + return signersOf(config.tree) + }, + + fromSimple: (config: { + threshold: ethers.BigNumberish + checkpoint: ethers.BigNumberish + signers: { address: string; weight: ethers.BigNumberish} [] + }): WalletConfig => { + return toWalletConfig({ + threshold: config.threshold, + checkpoint: config.checkpoint, + members: config.signers.map((signer) => { + return { + address: signer.address, + weight: signer.weight + } + }) + }) + }, + + isComplete: (config: WalletConfig): boolean => { + return isComplete(config.tree) + }, + + // isValid = (config: WalletConfig): boolean {} + /** + * + * Notice: context and kind are ignored because v2 + * doesn't need to manually update the implementation before + * a configuration update, it's automatically done by the contract. + * + */ + update: { + isKindUsed: true, + + buildTransaction: ( + wallet: string, + config: WalletConfig, + _context: commons.context.WalletContext, + _kind?: 'first' | 'later' | undefined + ): commons.transaction.TransactionBundle => { + const module = new ethers.utils.Interface(walletContracts.mainModuleUpgradable.abi) + + return { + entrypoint: wallet, + transactions: [{ + to: wallet, + data: module.encodeFunctionData(module.getFunction('updateImageHash'), [ + ConfigCoder.imageHashOf(config) + ]), + gasLimit: 0, + delegateCall: false, + revertOnError: true, + value: 0 + }] + } + }, + decodeTransaction: function (tx: commons.transaction.TransactionBundle): { address: string; newImageHash: string; kind: "first" | "later" | undefined} { + const module = new ethers.utils.Interface(walletContracts.mainModuleUpgradable.abi) + + if (tx.transactions.length !== 1) { + throw new Error('Invalid transaction bundle, expected 1 transaction') + } + + const data = tx.transactions[0].data + if (!data) { + throw new Error('Invalid transaction bundle, expected data') + } + + const decoded = module.decodeFunctionData(module.getFunction('updateImageHash'), data) + if (!decoded) { + throw new Error('Invalid transaction bundle, expected valid data') + } + + if (tx.transactions[0].to !== tx.entrypoint) { + throw new Error('Invalid transaction bundle, expected to be sent to entrypoint') + } + + if (tx.transactions[0].delegateCall) { + throw new Error('Invalid transaction bundle, expected not to be a delegateCall') + } + + if (!tx.transactions[0].revertOnError) { + throw new Error('Invalid transaction bundle, expected revertOnError') + } + + if (!ethers.constants.Zero.eq(tx.transactions[0]?.value ?? 0)) { + throw new Error('Invalid transaction bundle, expected value to be 0') + } + + if (!ethers.constants.Zero.eq(tx.transactions[0]?.gasLimit ?? 0)) { + throw new Error('Invalid transaction bundle, expected value to be 0') + } + + return { + address: tx.entrypoint, + newImageHash: decoded[0], + kind: undefined + } + } + }, + + toJSON: function (config: WalletConfig): string { + return JSON.stringify({ + version: config.version, + threshold: ethers.BigNumber.from(config.threshold).toString(), + checkpoint: ethers.BigNumber.from(config.checkpoint).toString(), + tree: topologyToJSON(config.tree) + }) + }, + + fromJSON: function (json: string): WalletConfig { + const config = JSON.parse(json) + return { + version: config.version, + threshold: ethers.BigNumber.from(config.threshold), + checkpoint: ethers.BigNumber.from(config.checkpoint), + tree: topologyFromJSON(config.tree) + } + }, + + editConfig: function ( + config: WalletConfig, + action: { + add?: commons.config.SimpleSigner[]; + remove?: string[]; + threshold?: ethers.BigNumberish, + checkpoint?: ethers.BigNumberish + } + ): WalletConfig { + const members = topologyToMembers(config.tree) + + if (action.add) { + for (const signer of action.add) { + if (members.find((s) => isSignerLeaf(s) && s.address === signer.address)) { + continue + } + + members.push({ + address: signer.address, + weight: signer.weight + }) + } + } + + if (action.remove) { + for (const address of action.remove) { + const index = members.findIndex((s) => isSignerLeaf(s) && s.address === address) + if (index >= 0) { + members.splice(index, 1) + } + } + } + + return { + version: config.version, + threshold: action.threshold ?? config.threshold, + checkpoint: action.checkpoint ?? config.checkpoint, + tree: optimized2SignersTopologyBuilder(members) + } + }, + + buildStubSignature: function ( + config: WalletConfig, + overrides: Map + ) { + const parts = new Map() + + for (const [signer, signature] of overrides.entries()) { + parts.set(signer, { signature, isDynamic: true }) + + const { encoded, weight } = encodeSigners(config, parts, [], 0) + + if (weight.gte(config.threshold)) { + return encoded + } + } + + const signers = signersOf(config.tree) + + for (const { address } of signers.sort(({ weight: a }, { weight: b }) => a - b)) { + const signature = '0x4e82f02f388a12b5f9d29eaf2452dd040c0ee5804b4e504b4dd64e396c6c781f2c7624195acba242dd825bfd25a290912e3c230841fd55c9a734c4de8d9899451b02' + parts.set(address, { signature, isDynamic: false }) + + const { encoded, weight } = encodeSigners(config, parts, [], 0) + + if (weight.gte(config.threshold)) { + return encoded + } + } + + return encodeSigners(config, parts, [], 0).encoded + } +} diff --git a/packages/core/src/v2/context.ts b/packages/core/src/v2/context.ts new file mode 100644 index 000000000..3498989c7 --- /dev/null +++ b/packages/core/src/v2/context.ts @@ -0,0 +1,6 @@ + +import { WalletContext as BaseContext } from '../commons/context' + +export type WalletContext = BaseContext & { + version: 2 +} diff --git a/packages/core/src/v2/index.ts b/packages/core/src/v2/index.ts new file mode 100644 index 000000000..1a939a732 --- /dev/null +++ b/packages/core/src/v2/index.ts @@ -0,0 +1,15 @@ + +export * as config from "./config" +export * as signature from "./signature" +export * as context from './context' +export * as chained from './chained' + +import { ConfigCoder } from "./config" +import { SignatureCoder } from "./signature" + +export const coders = { + config: ConfigCoder, + signature: SignatureCoder, +} + +export const version = 2 diff --git a/packages/core/src/v2/signature.ts b/packages/core/src/v2/signature.ts new file mode 100644 index 000000000..48cbf1c96 --- /dev/null +++ b/packages/core/src/v2/signature.ts @@ -0,0 +1,1014 @@ + +import { BigNumberish, ethers } from "ethers" +import { isValidSignature, recoverSigner, tryRecoverSigner } from "../commons/signer" +import { hashNode, isNestedLeaf, isNode, isNodeLeaf, isSignerLeaf, isSubdigestLeaf, Leaf, WalletConfig, SignerLeaf, Topology, imageHash, NodeLeaf, decodeSignerLeaf, isEncodedSignerLeaf } from "./config" +import * as base from '../commons/signature' +import { hashSetImageHash } from "./chained" + +export enum SignatureType { + Legacy = 0, + Dynamic = 1, + NoChainIdDynamic = 2, + Chained = 3 +} + +export enum SignaturePartType { + Signature = 0, + Address = 1, + DynamicSignature = 2, + Node = 3, + Branch = 4, + Subdigest = 5, + Nested = 6 +} + +export const SignaturePartTypeLength = 66 + +export type SignatureLeaf = SignerLeaf & { + signature: string, + isDynamic: boolean +} + +export type UnrecoveredSignatureLeaf = Omit & Pick, 'address'> & { + unrecovered: true +} + +export type UnrecoveredNestedLeaf = { + tree: UnrecoveredTopology, + weight: BigNumberish, + threshold: BigNumberish +} + +export type UnrecoveredLeaf = UnrecoveredNestedLeaf | UnrecoveredSignatureLeaf | Leaf + +export type UnrecoveredNode = { + left: UnrecoveredNode | UnrecoveredLeaf, + right: UnrecoveredNode | UnrecoveredLeaf +} + +export type UnrecoveredTopology = UnrecoveredNode | UnrecoveredLeaf + +export function isUnrecoveredNode(node: UnrecoveredTopology): node is UnrecoveredNode { + return (node as UnrecoveredNode).left !== undefined && (node as UnrecoveredNode).right !== undefined +} + +export function isUnrecoveredNestedLeaf(leaf: UnrecoveredTopology): leaf is UnrecoveredNestedLeaf { + return (leaf as UnrecoveredNestedLeaf).tree !== undefined +} + +export function isUnrecoveredSignatureLeaf(leaf: UnrecoveredTopology): leaf is UnrecoveredSignatureLeaf { + return ( + (leaf as UnrecoveredSignatureLeaf).unrecovered && + (leaf as UnrecoveredSignatureLeaf).signature !== undefined && + (leaf as UnrecoveredSignatureLeaf).isDynamic !== undefined + ) +} + +export function decodeSignatureTree(body: ethers.BytesLike): UnrecoveredTopology { + let arr = ethers.utils.arrayify(body) + + let pointer: undefined | (Omit & Pick, 'right'>) + + const append = (prevPointer: typeof pointer, node: UnrecoveredNode | UnrecoveredLeaf): typeof pointer => { + if (!prevPointer) { + return { + left: node + } + } + + if (!prevPointer.right) { + return { + left: prevPointer.left, + right: node + } + } + + return { + left: prevPointer as Required, + right: node + } + } + + while (arr.length > 0) { + const type = arr[0] as SignaturePartType + arr = arr.slice(1) + + switch (type) { + case SignaturePartType.Signature: { + const weight = arr[0] + const signature = ethers.utils.hexlify(arr.slice(1, SignaturePartTypeLength + 1)) + + pointer = append(pointer, { + signature, + weight, + unrecovered: true, + isDynamic: false + }) + arr = arr.slice(SignaturePartTypeLength + 1) + + } break + + case SignaturePartType.Address: { + const weight = arr[0] + const address = ethers.utils.getAddress(ethers.utils.hexlify(arr.slice(1, 21))) + + pointer = append(pointer, { + address, + weight, + }) + arr = arr.slice(21) + + } break + + case SignaturePartType.DynamicSignature: { + const weight = arr[0] + const address = ethers.utils.getAddress(ethers.utils.hexlify(arr.slice(1, 21))) + const size = arr[21] << 16 | arr[22] << 8 | arr[23] + const signature = ethers.utils.hexlify(arr.slice(24, 24 + size)) + + pointer = append(pointer, { + address, + signature, + weight, + unrecovered: true, + isDynamic: true + }) + arr = arr.slice(24 + size) + + } break + + case SignaturePartType.Node: { + const nodeHash = ethers.utils.hexlify(arr.slice(0, 32)) + + pointer = append(pointer, { nodeHash }) + arr = arr.slice(32) + + } break + + case SignaturePartType.Branch: { + const size = arr[0] << 16 | arr[1] << 8 | arr[2] + const branch = decodeSignatureTree(arr.slice(3, 3 + size)) + + pointer = append(pointer, branch) + arr = arr.slice(3 + size) + + } break + + case SignaturePartType.Subdigest: { + const subdigest = ethers.utils.hexlify(arr.slice(0, 32)) + + pointer = append(pointer, { subdigest }) + arr = arr.slice(32) + + } break + + case SignaturePartType.Nested: { + const weight = arr[0] + const threshold = arr[1] << 8 | arr[2] + const size = arr[3] << 16 | arr[4] << 8 | arr[5] + + const tree = decodeSignatureTree(arr.slice(6, 6 + size)) + + pointer = append(pointer, { + weight, + threshold, + tree + }) + arr = arr.slice(6 + size) + + } break + + default: + throw new Error(`Unknown signature part type: ${type}: ${ethers.utils.hexlify(arr)}`) + } + } + + if (!pointer) { + throw new Error('Empty signature tree') + } + + if (pointer.right) { + return pointer as Required + } + + return pointer.left +} + +export class InvalidSignatureLeafError extends Error { + constructor(public leaf: UnrecoveredLeaf) { + super(`Invalid signature leaf: ${JSON.stringify(leaf)}`) + } +} + +export async function recoverTopology( + unrecovered: UnrecoveredTopology, + subdigest: string, + provider: ethers.providers.Provider +): Promise { + if (isUnrecoveredNode(unrecovered)) { + const [left, right] = await Promise.all([ + recoverTopology(unrecovered.left, subdigest, provider), + recoverTopology(unrecovered.right, subdigest, provider) + ]) + + return { left, right } + } + + if (isUnrecoveredNestedLeaf(unrecovered)) { + return { + weight: unrecovered.weight, + threshold: unrecovered.threshold, + tree: await recoverTopology(unrecovered.tree, subdigest, provider) + } + } + + if (isUnrecoveredSignatureLeaf(unrecovered)) { + if (unrecovered.isDynamic) { + if (!unrecovered.address) { + throw new Error('Dynamic signature leaf without address') + } + + const isValid = await isValidSignature(unrecovered.address, subdigest, unrecovered.signature, provider) + if (!isValid) { + throw new InvalidSignatureLeafError(unrecovered) + } + + return { + weight: unrecovered.weight, + address: unrecovered.address!, + signature: unrecovered.signature, + subdigest + } + } else { + return { + weight: unrecovered.weight, + address: recoverSigner(subdigest, unrecovered.signature), + signature: unrecovered.signature, + subdigest + } + } + } + + return unrecovered +} + +// TODO: It should be possible to re-use encodeSignatureTree +// and avoid duplicating this logic +export const partEncoder = { + concat: (a: ethers.BytesLike, b: ethers.BytesLike) => { + return ethers.utils.solidityPack(['bytes', 'bytes'], [a, b]) + }, + node: (nodeHash: ethers.BytesLike): string => { + return ethers.utils.solidityPack( + ['uint8', 'bytes32'], + [SignaturePartType.Node, nodeHash] + ) + }, + branch: (tree: ethers.BytesLike): string => { + const arr = ethers.utils.arrayify(tree) + return ethers.utils.solidityPack( + ['uint8', 'uint24', 'bytes'], + [SignaturePartType.Branch, arr.length, arr] + ) + }, + nested: (weight: ethers.BigNumberish, threshold: ethers.BigNumberish, tree: ethers.BytesLike): string => { + const arr = ethers.utils.arrayify(tree) + return ethers.utils.solidityPack( + ['uint8', 'uint8', 'uint16', 'uint24', 'bytes'], + [SignaturePartType.Nested, weight, threshold, arr.length, arr] + ) + }, + subdigest: (subdigest: ethers.BytesLike): string => { + return ethers.utils.solidityPack( + ['uint8', 'bytes32'], + [SignaturePartType.Subdigest, subdigest] + ) + }, + signature: (weight: ethers.BigNumberish, signature: ethers.BytesLike): string => { + return ethers.utils.solidityPack( + ['uint8', 'uint8', 'bytes'], + [SignaturePartType.Signature, weight, signature] + ) + }, + dynamicSignature: (weight: ethers.BigNumberish, address: ethers.BytesLike, signature: ethers.BytesLike): string => { + const arrSignature = ethers.utils.arrayify(signature) + return ethers.utils.solidityPack( + ['uint8', 'uint8', 'address', 'uint24', 'bytes'], + [SignaturePartType.DynamicSignature, weight, address, arrSignature.length, arrSignature] + ) + }, + address: (weight: ethers.BigNumberish, address: ethers.BytesLike): string => { + return ethers.utils.solidityPack( + ['uint8', 'uint8', 'address'], + [SignaturePartType.Address, weight, address] + ) + } +} + +export type EncodingOptions = { + forceDynamicEncoding?: boolean, + disableTrim?: boolean +} + +export function encodeSigners( + config: WalletConfig, + parts: Map, + subdigests: string[], + chainId: ethers.BigNumberish, + options: EncodingOptions = {} +): { + encoded: string, + weight: ethers.BigNumber +} { + const tree = encodeTree(config.tree, parts, subdigests, options) + + if (ethers.BigNumber.from(chainId).isZero()) { + return { + encoded: ethers.utils.solidityPack( + ['uint8', 'uint16', 'uint32', 'bytes'], + [SignatureType.NoChainIdDynamic, config.threshold, config.checkpoint, tree.encoded] + ), + weight: tree.weight + } + } + + if (ethers.BigNumber.from(config.threshold).gt(255)) { + return { + encoded: ethers.utils.solidityPack( + ['uint8', 'uint16', 'uint32', 'bytes'], + [SignatureType.Dynamic, config.threshold, config.checkpoint, tree.encoded] + ), + weight: tree.weight + } + } + + return { + encoded: ethers.utils.solidityPack( + ['uint8', 'uint8', 'uint32', 'bytes'], + [SignatureType.Legacy, config.threshold, config.checkpoint, tree.encoded] + ), + weight: tree.weight + } +} + +export function encodeTree( + topology: Topology, + parts: Map, + subdigests: string[], + options: EncodingOptions = {} +): { + encoded: string, + weight: ethers.BigNumber +} { + const trim = !options.disableTrim + + if (isNode(topology)) { + const left = encodeTree(topology.left, parts, subdigests) + const right = encodeTree(topology.right, parts, subdigests) + + const isLeftSigner = isSignerLeaf(topology.left) + const isRightSigner = isSignerLeaf(topology.right) + + if (trim && left.weight.eq(0) && right.weight.eq(0) && !isLeftSigner && !isRightSigner) { + return { + // We don't need to include anything for this node + // just the hash will be enough + encoded: partEncoder.node(hashNode(topology)), + weight: ethers.constants.Zero + } + } + + if (trim && right.weight.eq(0) && !isRightSigner) { + return { + // The right node doesn't have any weight + // but we still need to include the left node encoded + encoded: partEncoder.concat( + left.encoded, + partEncoder.node(hashNode(topology.right)) + ), + weight: left.weight + } + } + + if (trim && left.weight.eq(0) && !isLeftSigner) { + return { + // The left node doesn't have any weight + // we can just append its hash, but for the right node + // we need to create a new "branch" + encoded: partEncoder.concat( + partEncoder.node(hashNode(topology.left)), + partEncoder.branch(right.encoded) + ), + weight: right.weight + } + } + + return { + // Both nodes have weight, we need to include both + // the right one must be a branch + encoded: partEncoder.concat( + left.encoded, + partEncoder.branch(right.encoded) + ), + weight: left.weight.add(right.weight) + } + } + + if (isNestedLeaf(topology)) { + const tree = encodeTree(topology.tree, parts, subdigests) + + if (trim && tree.weight.eq(0)) { + return { + encoded: partEncoder.node(hashNode(topology)), + weight: ethers.constants.Zero + } + } + + return { + encoded: partEncoder.nested(topology.weight, topology.threshold, tree.encoded), + weight: tree.weight + } + } + + if (isNodeLeaf(topology)) { + return { + encoded: partEncoder.node(hashNode(topology)), + weight: ethers.constants.Zero + } + } + + if (isSubdigestLeaf(topology)) { + const include = subdigests.includes(topology.subdigest) + return { + encoded: partEncoder.node(hashNode(topology)), + weight: include ? ethers.constants.MaxUint256 : ethers.constants.Zero + } + } + + if (isSignerLeaf(topology)) { + const include = parts.has(topology.address) + + if (include) { + const part = parts.get(topology.address)! + const signature = part.signature + + if (options.forceDynamicEncoding || part.isDynamic) { + return { + encoded: partEncoder.dynamicSignature(topology.weight, topology.address, signature), + weight: ethers.BigNumber.from(topology.weight) + } + } else { + return { + encoded: partEncoder.signature(topology.weight, signature), + weight: ethers.BigNumber.from(topology.weight) + } + } + + } else { + return { + encoded: partEncoder.address(topology.weight, topology.address), + weight: ethers.constants.Zero + } + } + } + + throw new Error(`Invalid topology - unknown error: ${JSON.stringify(topology)}`) +} + +export type UnrecoveredConfig = { + tree: UnrecoveredTopology, + threshold: ethers.BigNumberish, + checkpoint: ethers.BigNumberish +} + +export type UnrecoveredSignature = base.UnrecoveredSignature & { + type: SignatureType, + decoded: UnrecoveredConfig +} + +export type Signature = base.Signature & { + type: SignatureType +} + +export type UnrecoveredChainedSignature = UnrecoveredSignature & { + suffix: (UnrecoveredSignature | UnrecoveredChainedSignature)[] +} + +export type ChainedSignature = Signature & { + suffix: (Signature | ChainedSignature)[] +} + +export function deepestConfigOfSignature(signature: Signature | ChainedSignature): WalletConfig { + return isChainedSignature(signature) ? deepestConfigOfSignature(signature.suffix[signature.suffix.length - 1]) : signature.config +} + +export function isUnrecoveredSignature(sig: any): sig is UnrecoveredSignature { + return ( + sig.type !== undefined && + sig.decoded !== undefined && + sig.version !== undefined && + sig.version === 2 + ) +} + +export function isUnrecoveredChainedSignature(sig: any): sig is UnrecoveredChainedSignature { + return sig.suffix !== undefined && Array.isArray(sig.suffix) && sig.suffix.every(isUnrecoveredSignature) +} + +export function isSignature(sig: any): sig is Signature { + return ( + sig.type !== undefined && + sig.config !== undefined && + sig.digest !== undefined && + sig.version !== undefined && + sig.version === 2 + ) +} + +export function isChainedSignature(sig: any): sig is ChainedSignature { + return sig.chain !== undefined && Array.isArray(sig.chain) && sig.chain.every(isSignature) +} + +export function decodeSignature(signature: ethers.BytesLike): UnrecoveredSignature | UnrecoveredChainedSignature { + const bytes = ethers.utils.arrayify(signature) + const type = bytes[0] + + switch (type) { + case SignatureType.Legacy: + return { version: 2, type: SignatureType.Legacy, decoded: decodeSignatureBody(bytes) } + + case SignatureType.Dynamic: + return { version: 2, type: SignatureType.Dynamic, decoded: decodeSignatureBody(bytes.slice(1)) } + + case SignatureType.NoChainIdDynamic: + return { version: 2, type: SignatureType.NoChainIdDynamic, decoded: decodeSignatureBody(bytes.slice(1)) } + + case SignatureType.Chained: + return decodeChainedSignature(bytes) + + default: + throw new Error(`Invalid signature type: ${type}`) + } +} + +export function decodeSignatureBody(signature: ethers.BytesLike): UnrecoveredConfig { + const bytes = ethers.utils.arrayify(signature) + + const threshold = bytes[0] << 8 | bytes[1] + const checkpoint = bytes[2] << 24 | bytes[3] << 16 | bytes[4] << 8 | bytes[5] + + const tree = decodeSignatureTree(bytes.slice(6)) + + return { threshold, checkpoint, tree } +} + +export function decodeChainedSignature(signature: ethers.BytesLike): UnrecoveredChainedSignature { + const arr = ethers.utils.arrayify(signature) + const type = arr[0] + + if (type !== SignatureType.Chained) { + throw new Error(`Expected chained signature type: ${type}`) + } + + const chain: (UnrecoveredSignature | UnrecoveredChainedSignature)[] = [] + let index = 1 + + while (index < arr.length) { + const size = arr[index] << 16 | arr[index + 1] << 8 | arr[index + 2] + index += 3 + + const sig = decodeSignature(arr.slice(index, index + size)) + chain.push(sig) + + index += size + } + + const main = chain[0] + if (isUnrecoveredChainedSignature(main)) { + throw new Error(`Expected first link of chained signature to be a simple signature (not chained)`) + } + + const suffix = chain.slice(1) + + return { ...main, suffix } +} + +export function setImageHashStruct(imageHash: string) { + return ethers.utils.solidityPack( + ['bytes32', 'bytes32'], + [ethers.utils.solidityKeccak256(['string'], ['SetImageHash(bytes32 imageHash)']), imageHash] + ) +} + +export async function recoverSignature( + signature: UnrecoveredSignature | UnrecoveredChainedSignature, + payload: base.SignedPayload | { subdigest: string }, + provider: ethers.providers.Provider +): Promise { + const signedPayload = (payload as { subdigest: string}).subdigest === undefined ? payload as base.SignedPayload : undefined + + const isNoChainId = signature.type === SignatureType.NoChainIdDynamic + if (isNoChainId && signedPayload) { + signedPayload.chainId = 0 + } + + const subdigest = signedPayload ? base.subdigestOf(signedPayload) : (payload as { subdigest: string }).subdigest + + if (!isUnrecoveredChainedSignature(signature)) { + const tree = await recoverTopology(signature.decoded.tree, subdigest, provider) + return { version: 2, type: signature.type, subdigest, config: { version: 2, ...signature.decoded, tree } } + } + + if (!base.isSignedPayload(signedPayload)) { + throw new Error(`Chained signature recovery requires detailed signed payload, subdigest is not enough`) + } + + const result: (Signature | ChainedSignature)[] = [] + let mutatedPayload = signedPayload + + // Recover the chain of signatures + // NOTICE: Remove the suffix from the "first" siganture + // otherwise we recurse infinitely + for (const sig of [{ ...signature, suffix: undefined }, ...signature.suffix]) { + const recovered = await recoverSignature(sig, mutatedPayload, provider) + result.unshift(recovered) + + const nextMessage = setImageHashStruct( + imageHash(deepestConfigOfSignature(recovered)) + ) + + mutatedPayload = { + ...mutatedPayload, + message: nextMessage, + digest: ethers.utils.keccak256(nextMessage) + } + } + + const main = result[0] + const suffix = result.slice(1) + + return { ...main, suffix } +} + +export function encodeChain(main: ethers.BytesLike, suffix: ethers.BytesLike[]): string { + const allSignatures = [main, ...(suffix || [])] + const encodedMap = allSignatures.map((s) => ethers.utils.arrayify(encodeSignature(s))) + + const body = ethers.utils.solidityPack( + encodedMap.map(() => ['uint24', 'bytes']).flat(), + encodedMap.map((s) => [s.length, s]).flat() + ) + + return ethers.utils.solidityPack( + ['uint8', 'bytes'], + [SignatureType.Chained, body] + ) +} + +export function encodeSignature( + decoded: UnrecoveredChainedSignature | ChainedSignature | UnrecoveredSignature | Signature | ethers.BytesLike, +): string { + if (ethers.utils.isBytesLike(decoded)) return ethers.utils.hexlify(decoded) + + if (isUnrecoveredChainedSignature(decoded) || isChainedSignature(decoded)) { + return encodeChain( + encodeSignature(decoded), + (decoded.suffix || []).map(encodeSignature) + ) + } + + const body = isUnrecoveredSignature(decoded) ? decoded.decoded : decoded.config + + switch (decoded.type) { + case SignatureType.Legacy: + if (ethers.BigNumber.from(body.threshold).gt(255)) { + throw new Error(`Legacy signature threshold is too large: ${body.threshold} (max 255)`) + } + + return encodeSignatureBody(body) + + case SignatureType.NoChainIdDynamic: + case SignatureType.Dynamic: + return ethers.utils.solidityPack( + ['uint8', 'bytes'], + [decoded.type, encodeSignatureBody(body)] + ) + + + case SignatureType.Chained: + throw new Error(`Unreachable code: Chained signature should be handled above`) + + default: + throw new Error(`Invalid signature type: ${decoded.type}`) + } +} + +export function encodeSignatureBody(decoded: WalletConfig | UnrecoveredConfig): string { + return ethers.utils.solidityPack( + ['uint16', 'uint32', 'bytes'], + [decoded.threshold, decoded.checkpoint, encodeSignatureTree(decoded.tree)] + ) +} + +export function encodeSignatureTree(tree: UnrecoveredTopology | Topology): string { + if (isNode(tree) || isUnrecoveredNode(tree)) { + const encodedRight = ethers.utils.arrayify(encodeSignatureTree(tree.right)) + const encodedLeft = ethers.utils.arrayify(encodeSignatureTree(tree.left)) + const isBranching = isNode(tree.right) || isUnrecoveredNode(tree.right) + + if (isBranching) { + return ethers.utils.solidityPack( + ['bytes', 'uint8', 'uint24', 'bytes'], + [encodedLeft, SignaturePartType.Branch, encodedRight.length, encodedRight] + ) + } else { + return ethers.utils.solidityPack( + ['bytes', 'bytes'], + [encodedLeft, encodedRight] + ) + } + } + + if (isNestedLeaf(tree) || isUnrecoveredNestedLeaf(tree)) { + const nested = ethers.utils.arrayify(encodeSignatureTree(tree.tree)) + + return ethers.utils.solidityPack( + ['uint8', 'uint8', 'uint16', 'uint24', 'bytes'], + [SignaturePartType.Nested, tree.weight, tree.threshold, nested.length, nested] + ) + } + + if (isUnrecoveredSignatureLeaf(tree) || (isSignerLeaf(tree) && tree.signature !== undefined)) { + const signature = ethers.utils.arrayify(tree.signature!) + + if ((tree as { isDynamic?: boolean }).isDynamic || signature.length !== SignaturePartTypeLength) { + if (!tree.address) throw new Error(`Dynamic signature leaf must have address`) + return ethers.utils.solidityPack( + ['uint8', 'uint8', 'address', 'uint24', 'bytes'], + [SignaturePartType.DynamicSignature, tree.weight, tree.address, signature.length, signature] + ) + } else { + return ethers.utils.solidityPack( + ['uint8', 'uint8', 'bytes'], + [SignaturePartType.Signature, tree.weight, signature] + ) + } + } + + if (isSignerLeaf(tree)) { + return ethers.utils.solidityPack( + ['uint8', 'uint8', 'address'], + [SignaturePartType.Address, tree.weight, tree.address] + ) + } + + if (isNodeLeaf(tree)) { + return ethers.utils.solidityPack( + ['uint8', 'bytes32'], + [SignaturePartType.Node, tree.nodeHash] + ) + } + + if (isSubdigestLeaf(tree)) { + return ethers.utils.solidityPack( + ['uint8', 'bytes32'], + [SignaturePartType.Subdigest, tree.subdigest] + ) + } + + throw new Error(`Unknown signature tree type: ${tree}`) +} + +export function signaturesOf(topology: Topology): { address: string, signature: string }[] { + if (isNode(topology)) { + return [...signaturesOf(topology.left), ...signaturesOf(topology.right)] + } + + if (isNestedLeaf(topology)) { + return signaturesOf(topology.tree) + } + + if (isSignerLeaf(topology) && topology.signature) { + return [{ address: topology.address, signature: topology.signature }] + } + + return [] +} + +export function signaturesOfDecoded(utopology: UnrecoveredTopology): string[] { + if (isUnrecoveredNode(utopology)) { + return [...signaturesOfDecoded(utopology.left), ...signaturesOfDecoded(utopology.right)] + } + + if (isUnrecoveredNestedLeaf(utopology)) { + return signaturesOfDecoded(utopology.tree) + } + + if (isUnrecoveredSignatureLeaf(utopology)) { + return [utopology.signature] + } + + return [] +} + +export function subdigestsOfDecoded(utopology: UnrecoveredTopology): string[] { + if (isUnrecoveredNode(utopology)) { + return [...subdigestsOfDecoded(utopology.left), ...subdigestsOfDecoded(utopology.right)] + } + + if (isUnrecoveredNestedLeaf(utopology)) { + return subdigestsOfDecoded(utopology.tree) + } + + if (isSubdigestLeaf(utopology)) { + return [utopology.subdigest] + } + + return [] +} + +export async function trimSignature(signature: string | UnrecoveredSignature): Promise { + const decoded = typeof signature === 'string' ? decodeSignature(signature) : signature + + if (isUnrecoveredChainedSignature(decoded)) { + // We need to trim every suffix AND the main signature + const trimmed = await Promise.all([ + trimSignature({ ...decoded, suffix: undefined } as UnrecoveredSignature), + ...decoded.suffix.map((s) => trimSignature(s)) + ]) + + return encodeChain(trimmed[0], trimmed.slice(1)) + } + + const { trimmed } = await trimUnrecoveredTree(decoded.decoded.tree) + return encodeSignature({ ...decoded, decoded: { ...decoded.decoded, tree: trimmed }}) +} + +export async function trimUnrecoveredTree(tree: UnrecoveredTopology, trimStaticDigest: boolean = true): Promise<{ + weight: number, + trimmed: UnrecoveredTopology +}> { + if (isUnrecoveredNode(tree)) { + const [left, right] = await Promise.all([ + trimUnrecoveredTree(tree.left), + trimUnrecoveredTree(tree.right) + ]) + + if (left.weight === 0 && right.weight === 0) { + try { + // If both weights are 0 then it means we don't have any signatures yet + // because of that, we should be able to "recover" the tree with any subdigest + // and still get the valid node hash (there shouldn't be any signatures to verify) + const recovered = await recoverTopology(tree, ethers.constants.HashZero, undefined as any) + + return { + weight: 0, + trimmed: { + nodeHash: hashNode(recovered) + } as NodeLeaf + } + } catch { + // If something fails it's more likely because some signatures have sneaked in + // in that case we should keep this node + } + } else { + return { + weight: left.weight + right.weight, + trimmed: { + left: left.trimmed, + right: right.trimmed + } as UnrecoveredNode + } + } + } + + if (isUnrecoveredNestedLeaf(tree)) { + const trimmed = await trimUnrecoveredTree(tree.tree) + + if (trimmed.weight === 0) { + try { + // If the nested leaf is empty, we can recover it with any subdigest + // and still get the valid node hash (there shouldn't be any signatures to verify) + const recovered = await recoverTopology(tree, ethers.constants.HashZero, undefined as any) + + return { + weight: 0, + trimmed: { + nodeHash: hashNode(recovered) + } as NodeLeaf + } + } catch { + // If something fails it's more likely because some signatures have sneaked in + // in that case we should keep this node + } + } + + return { + weight: trimmed.weight, + trimmed: { + weight: tree.weight, + threshold: tree.threshold, + tree: trimmed.trimmed + } as UnrecoveredNestedLeaf + } + } + + // Hash nodes can be encoded as signer leaves if they have a weight below + // 256, most likely the are signer leaves wrongly encoded + if (isNodeLeaf(tree) && isEncodedSignerLeaf(tree.nodeHash)) { + return { + weight: 0, + trimmed: { + ...decodeSignerLeaf(tree.nodeHash), + } as SignerLeaf + } + } + + if (isUnrecoveredSignatureLeaf(tree) || (isSignerLeaf(tree) && tree.signature !== undefined)) { + return { + weight: ethers.BigNumber.from(tree.weight).toNumber(), + trimmed: tree + } + } + + if (!trimStaticDigest && isSubdigestLeaf(tree)) { + return { + weight: +Infinity, + trimmed: tree + } + } + + return { + weight: 0, + trimmed: tree + } +} + +export const SignatureCoder: base.SignatureCoder< + WalletConfig, + Signature, + UnrecoveredChainedSignature | UnrecoveredSignature +> = { + decode: (data: string): UnrecoveredSignature => { + return decodeSignature(data) + }, + + encode: (data: Signature | UnrecoveredSignature): string => { + return encodeSignature(data) + }, + + trim: (data: string): Promise => { + return trimSignature(data) + }, + + supportsNoChainId: true, + + recover: ( + data: UnrecoveredSignature | UnrecoveredChainedSignature, + payload: base.SignedPayload, + provider: ethers.providers.Provider + ): Promise => { + return recoverSignature(data, payload, provider) + }, + + encodeSigners: ( + config: WalletConfig, + signatures: Map, + subdigests: string[], + chainId: ethers.BigNumberish + ): { + encoded: string + weight: ethers.BigNumber + } => { + return encodeSigners(config, signatures, subdigests, chainId) + }, + + hasEnoughSigningPower: (config: WalletConfig, signatures: Map): boolean => { + const { weight } = SignatureCoder.encodeSigners(config, signatures, [], 0) + return weight.gte(config.threshold) + }, + + chainSignatures: ( + main: Signature | UnrecoveredSignature | UnrecoveredChainedSignature | ethers.BytesLike, + suffix: (Signature | UnrecoveredSignature | UnrecoveredChainedSignature | ethers.BytesLike)[] + ): string => { + // Notice: v2 expects suffix to be reversed + // that being: from signed to current imageHash + const reversed = suffix.reverse() + const mraw = ethers.utils.isBytesLike(main) ? main : encodeSignature(main) + const sraw = reversed.map(s => (ethers.utils.isBytesLike(s) ? s : encodeSignature(s))) + return encodeChain(mraw, sraw) + }, + + hashSetImageHash: function (imageHash: string): string { + return hashSetImageHash(imageHash) + }, + + signaturesOf(config: WalletConfig): { address: string, signature: string }[] { + return signaturesOf(config.tree) + }, + + signaturesOfDecoded: function (data: UnrecoveredSignature): string[] { + return signaturesOfDecoded(data.decoded.tree) + } +} diff --git a/packages/core/tests/v2/config.spec.ts b/packages/core/tests/v2/config.spec.ts new file mode 100644 index 000000000..cad3cb8ee --- /dev/null +++ b/packages/core/tests/v2/config.spec.ts @@ -0,0 +1,503 @@ + +import { expect } from 'chai' +import { config } from '../../src/v2' + +const sampleTree1: config.Topology = { + left: { + address: '0x07ab71Fe97F9122a2dBE3797aa441623f5a59DB1', + weight: 2, + }, + right: { + left: { + left: { + subdigest: '0xb374baf809e388014912ca7020c8ef51ad68591db3f010f9e35a77c15d4d6bed', + }, + right: { + subdigest: '0x787c83a19321fc70f8653f8faa39cce60bf26cac51c25df1b0634eb7ddbe0c60' + } + }, + right: { + address: '0xdafea492d9c6733ae3d56b7ed1adb60692c98bc5', + weight: 1, + } + } +} + +const sampleTree2: config.Topology = { + left: { + left: { + left: { + subdigest: '0x0000000000000000000000000000000000000000000000000000000000000000' + }, + right: { + subdigest: '0x0000000000000000000000000000000000000000000000000000000000000001' + } + }, + right: { + left: { + subdigest: '0x0000000000000000000000000000000000000000000000000000000000000002' + }, + right: { + subdigest: '0x0000000000000000000000000000000000000000000000000000000000000003' + } + } + }, + right: { + left: { + left: { + subdigest: '0x0000000000000000000000000000000000000000000000000000000000000004' + }, + right: { + subdigest: '0x0000000000000000000000000000000000000000000000000000000000000005' + } + }, + right: { + left: { + subdigest: '0x0000000000000000000000000000000000000000000000000000000000000006' + }, + right: { + subdigest: '0x0000000000000000000000000000000000000000000000000000000000000007' + } + } + }, +} + +const sampleTree3: config.Topology = { + left: { + tree: { + left: { + address: '0x07ab71Fe97F9122a2dBE3797aa441623f5a59DB1', + weight: 2, + }, + right: { + left: { + subdigest: '0x0000000000000000000000000000000000000000000000000000000000000006', + }, + right: { + subdigest: '0x787c83a19321fc70f8653f8faa39cce60bf26cac51c25df10000000000000000', + } + } + }, + weight: 90, + threshold: 2, + }, + right: { + left: { + left: { + subdigest: '0xb374baf809e388014912ca7020c8ef51ad68591db3f010f9e35a77c15d4d6bed', + }, + right: { + subdigest: '0x787c83a19321fc70f8653f8faa39cce60bf26cac51c25df1b0634eb7ddbe0c60' + } + }, + right: { + address: '0xdafea492d9c6733ae3d56b7ed1adb60692c98bc5', + weight: 1, + } + } +} + +describe('v2 config utils', () => { + describe('Detect different leaves', () => { + it('Should detect signer leaf', () => { + const leaf: config.Leaf = { + address: '0x07ab71Fe97F9122a2dBE3797aa441623f5a59DB1', + weight: 2, + } + + expect(config.isLeaf(leaf)).to.be.true + expect(config.isSignerLeaf(leaf)).to.be.true + expect(config.isTopology(leaf)).to.be.true + expect(config.isNode(leaf)).to.be.false + expect(config.isSubdigestLeaf(leaf)).to.be.false + expect(config.isNestedLeaf(leaf)).to.be.false + }) + + it('Should detect subdigest leaf', () => { + const leaf: config.Leaf = { + subdigest: '0x787c83a19321fc70f8653f8faa39cce60bf26cac51c25df1b0634eb7ddbe0c60' + } + + expect(config.isLeaf(leaf)).to.be.true + expect(config.isSubdigestLeaf(leaf)).to.be.true + expect(config.isTopology(leaf)).to.be.true + expect(config.isNode(leaf)).to.be.false + expect(config.isSignerLeaf(leaf)).to.be.false + expect(config.isNestedLeaf(leaf)).to.be.false + }) + + it('Should detect nested leaf', () => { + const leaf: config.Leaf = { + tree: sampleTree1, + weight: 90, + threshold: 2, + } + + expect(config.isLeaf(leaf)).to.be.true + expect(config.isNestedLeaf(leaf)).to.be.true + expect(config.isTopology(leaf)).to.be.true + expect(config.isNode(leaf)).to.be.false + expect(config.isSignerLeaf(leaf)).to.be.false + expect(config.isSubdigestLeaf(leaf)).to.be.false + }) + + it('Should detect node', () => { + expect(config.isTopology(sampleTree1)).to.be.true + expect(config.isNode(sampleTree1)).to.be.true + expect(config.isLeaf(sampleTree1)).to.be.false + expect(config.isNestedLeaf(sampleTree1)).to.be.false + expect(config.isSignerLeaf(sampleTree1)).to.be.false + expect(config.isSubdigestLeaf(sampleTree1)).to.be.false + }) + }) + + describe('Hash leaves', () => { + it('Hash signer leaf', () => { + const hash = config.hashNode({ + address: '0x07ab71Fe97F9122a2dBE3797aa441623f5a59DB1', + weight: 129, + }) + + expect(hash).to.equal(`0x00000000000000000000008107ab71fe97f9122a2dbe3797aa441623f5a59db1`) + }) + + it('Hash subdigest', () => { + const hash = config.hashNode({ + subdigest: '0xb38b3da0ef56c3094675167fed4a263c3346b325dddb6e56a3eb9a10ed7539ed', + }) + + expect(hash).to.equal(`0x7cf15e50f6d44f71912ca6575b7fd911a5c6f19d0195692c7d35a102ad5ae98b`) + }) + + it('Hash nested leaf', () => { + + + const hash = config.hashNode({ + tree: sampleTree1, + weight: 90, + threshold: 211, + }) + + expect(hash).to.equal(`0x6cca65d12b31379a7b429e43443969524821e57d2c6a7fafae8e30bd31a5295b`) + }) + + it('Hash node', () => { + const tree = { + left: { + address: '0x07ab71Fe97F9122a2dBE3797aa441623f5a59DB1', + weight: 129 + }, + right: { + subdigest: '0x787c83a19321fc70f8653f8faa39cce60bf26cac51c25df1b0634eb7ddbe0c60' + } + } + + const hash = config.hashNode(tree) + expect(hash).to.equal(`0x47dcfac6c5622054a0ac762baa1a5eb10705484ea1e000869bbc11a093bec97e`) + }) + }) + + it('Read left face of tree', () => { + const leftFace1 = config.leftFace(sampleTree1) + expect(leftFace1.length).to.equal(2) + expect(leftFace1[0]).to.deep.equal(sampleTree1['left']) + expect(leftFace1[1]).to.deep.equal(sampleTree1['right']) + + const leftFace2 = config.leftFace(sampleTree2) + expect(leftFace2.length).to.equal(4) + expect(leftFace2[0]).to.deep.equal(sampleTree2['left']['left']['left']) + expect(leftFace2[1]).to.deep.equal(sampleTree2['left']['left']['right']) + expect(leftFace2[2]).to.deep.equal(sampleTree2['left']['right']) + expect(leftFace2[3]).to.deep.equal(sampleTree2['right']) + + const leftFace3 = config.leftFace(sampleTree3) + expect(leftFace3.length).to.equal(2) + expect(leftFace3[0]).to.deep.equal(sampleTree3['left']) + expect(leftFace3[1]).to.deep.equal(sampleTree3['right']) + }) + + describe('Simplify configurations', () => { + it('Should simplify configuration', () => { + const simplifiedConfig1 = config.toSimpleWalletConfig({ + version: 2, + tree: sampleTree1, + threshold: 11, + checkpoint: 999999 + }) + + expect(simplifiedConfig1).to.deep.equal({ + checkpoint: 999999, + threshold: 11, + members: [ + sampleTree1['left'], + sampleTree1['right']['left']['left'], + sampleTree1['right']['left']['right'], + sampleTree1['right']['right'] + ] + }) + + const simplifiedConfig2 = config.toSimpleWalletConfig({ + version: 2, + tree: sampleTree2, + threshold: 1, + checkpoint: 2 + }) + + expect(simplifiedConfig2).to.deep.equal({ + checkpoint: 2, + threshold: 1, + members: [ + sampleTree2['left']['left']['left'], + sampleTree2['left']['left']['right'], + sampleTree2['left']['right']['left'], + sampleTree2['left']['right']['right'], + sampleTree2['right']['left']['left'], + sampleTree2['right']['left']['right'], + sampleTree2['right']['right']['left'], + sampleTree2['right']['right']['right'] + ] + }) + + const simplifiedConfig3 = config.toSimpleWalletConfig({ + version: 2, + tree: sampleTree3, + threshold: 2, + checkpoint: 3 + }) + + expect(simplifiedConfig3).to.deep.equal({ + checkpoint: 3, + threshold: 2, + members: [ + { + threshold: sampleTree3['left']['threshold'], + weight: sampleTree3['left']['weight'], + members: [ + sampleTree3['left']['tree']['left'], + sampleTree3['left']['tree']['right']['left'], + sampleTree3['left']['tree']['right']['right'] + ] + }, + sampleTree3['right']['left']['left'], + sampleTree3['right']['left']['right'], + sampleTree3['right']['right'] + ] + }) + }) + }) + + describe('Build configurations', async () => { + it('Build legacy configuration', () => { + const legacyConfig1 = config.toWalletConfig({ + members: [ + sampleTree1['left'], + sampleTree1['right']['left']['left'], + sampleTree1['right']['left']['right'], + sampleTree1['right']['right'] + ], + threshold: 11, + checkpoint: 999999 + }, config.legacyTopologyBuilder) + + expect(legacyConfig1).to.deep.equal({ + version: 2, + checkpoint: 999999, + threshold: 11, + tree: { + left: { + left: { + left: sampleTree1['left'], + right: sampleTree1['right']['left']['left'] + }, + right: sampleTree1['right']['left']['right'] + }, + right: sampleTree1['right']['right'] + } + }) + + const legacyConfig2 = config.toWalletConfig({ + members: [ + sampleTree2['left']['left']['left'], + sampleTree2['left']['left']['right'], + sampleTree2['left']['right']['left'], + sampleTree2['left']['right']['right'], + sampleTree2['right']['left']['left'], + sampleTree2['right']['left']['right'], + sampleTree2['right']['right']['left'], + sampleTree2['right']['right']['right'] + ], + threshold: 1, + checkpoint: 2 + }) + + expect(legacyConfig2).to.deep.equal({ + version: 2, + checkpoint: 2, + threshold: 1, + tree: { + left: { + left: { + left: { + left: { + left: { + left: { + left: sampleTree2['left']['left']['left'], + right: sampleTree2['left']['left']['right'] + }, + right: sampleTree2['left']['right']['left'] + }, + right: sampleTree2['left']['right']['right'] + }, + right: sampleTree2['right']['left']['left'] + }, + right: sampleTree2['right']['left']['right'] + }, + right: sampleTree2['right']['right']['left'] + }, + right: sampleTree2['right']['right']['right'] + } + }) + + const legacyConfig3 = config.toWalletConfig({ + members: [ + { + threshold: sampleTree3['left']['threshold'], + weight: sampleTree3['left']['weight'], + members: [ + sampleTree3['left']['tree']['left'], + sampleTree3['left']['tree']['right']['left'], + sampleTree3['left']['tree']['right']['right'] + ] + }, + sampleTree3['right']['left']['left'], + sampleTree3['right']['left']['right'], + sampleTree3['right']['right'] + ], + threshold: 2, + checkpoint: 3 + }) + + expect(legacyConfig3).to.deep.equal({ + version: 2, + checkpoint: 3, + threshold: 2, + tree: { + left: { + left: { + left: { + weight: sampleTree3['left']['weight'], + threshold: sampleTree3['left']['threshold'], + tree: { + left: { + left: sampleTree3['left']['tree']['left'], + right: sampleTree3['left']['tree']['right']['left'] + }, + right: sampleTree3['left']['tree']['right']['right'] + } + }, + right: sampleTree3['right']['left']['left'] + }, + right: sampleTree3['right']['left']['right'] + }, + right: sampleTree3['right']['right'] + }, + }) + }) + + it('Build merkle configuration', () => { + const merkleConfig1 = config.toWalletConfig({ + members: [ + sampleTree1['left'], + sampleTree1['right']['left']['left'], + sampleTree1['right']['left']['right'], + sampleTree1['right']['right'] + ], + threshold: 11, + checkpoint: 999999 + }, config.merkleTopologyBuilder) + + expect(merkleConfig1).to.deep.equal({ + version: 2, + checkpoint: 999999, + threshold: 11, + tree: { + left: { + left: sampleTree1['left'], + right: sampleTree1['right']['left']['left'], + }, + right: { + left: sampleTree1['right']['left']['right'], + right: sampleTree1['right']['right'] + }, + } + }) + + const merkleConfig2 = config.toWalletConfig({ + members: [ + sampleTree2['left']['left']['left'], + sampleTree2['left']['left']['right'], + sampleTree2['left']['right']['left'], + sampleTree2['left']['right']['right'], + sampleTree2['right']['left']['left'], + sampleTree2['right']['left']['right'], + sampleTree2['right']['right']['left'], + sampleTree2['right']['right']['right'] + ], + threshold: 1, + checkpoint: 2 + }, config.merkleTopologyBuilder) + + expect(merkleConfig2).to.deep.equal({ + version: 2, + checkpoint: 2, + threshold: 1, + tree: sampleTree2 + }) + + const merkleConfig3 = config.toWalletConfig({ + members: [ + { + threshold: sampleTree3['left']['threshold'], + weight: sampleTree3['left']['weight'], + members: [ + sampleTree3['left']['tree']['left'], + sampleTree3['left']['tree']['right']['left'], + sampleTree3['left']['tree']['right']['right'] + ] + }, + sampleTree3['right']['left']['left'], + sampleTree3['right']['left']['right'], + sampleTree3['right']['right'] + ], + threshold: 2, + checkpoint: 3 + }, config.merkleTopologyBuilder) + + expect(merkleConfig3).to.deep.equal({ + version: 2, + checkpoint: 3, + threshold: 2, + tree: { + left: { + left: { + weight: sampleTree3['left']['weight'], + threshold: sampleTree3['left']['threshold'], + tree: { + left: { + left: sampleTree3['left']['tree']['left'], + right: sampleTree3['left']['tree']['right']['left'] + }, + right: sampleTree3['left']['tree']['right']['right'] + } + }, + right: sampleTree3['right']['left']['left'] + }, + right: { + left: sampleTree3['right']['left']['right'], + right: sampleTree3['right']['right'] + } + } + }) + }) + }) +}) diff --git a/packages/core/tests/v2/signature.spec.ts b/packages/core/tests/v2/signature.spec.ts new file mode 100644 index 000000000..df3ee1721 --- /dev/null +++ b/packages/core/tests/v2/signature.spec.ts @@ -0,0 +1,586 @@ + +import { expect } from "chai" +import { ethers } from "ethers" +import { decodeSignature, encodeSignature, SignaturePartType, SignatureType } from "../../src/v2/signature" + +const sampleSignature1 = '0x0001636911b800019fa7b7e8ed25088c413074818ac10ab3bbcddb120bbec85083f3ba254e5547d953fe615a6474fd365326244dedd7afa3911ad39c956ca096d721064d6b29055d1b02' +const sampleSignature2 = '0x000263691389034a062f86183c9d46e129f0331f2a42f6ba22a3525a46ecd197fa23d177d75f2d040000a0033fce59919d0a4ee44a8066a3b1d0083760d89a06ae89edadf8a58e0e5c5ac5040400007b01016ffeccf6f31e0a469d55dede5651d34a6ecd9fc500017052a0438a13da22242bcd20c219630d839c364cd2b6042add1bee32774c37d72ba2ace8b7a79c95a536d4c0fed3fe05883c6e1188a4191a91623a903e4ec21c1b0203ad5831467806b6edd059ff5ac9809f2bb6e80512ceb5d466a67251ffb842fae1040000c50314b729622595218cdbef06c630daeea028e25e8ca048d97bc170d75feb9066ad0400007f030c8c0bb7e8c5ec8eed444ae25f3a1796597bcfacf5f6b758ae4fadd6fc416f560400005a0001e7618f1b7b012d7fc48f518f498bb6823dc2a8308984287501873cb535b6d5bf526fb91a220297f461ac5a2434d0e8e768c3bf166c329366ddc885bf2e1676271c0201014ef7ec718f66ae3920ea119b9d7ddf39337601f703fdea4c5fb23fb3cc2b2360057abef1ff7e7195acbdc4db555c27cc588a4585a6' +const sampleSignature3 = '0x0003636916740101a653f5900ef5c538142cd8aef1ce750390b29a3e0101a54e174d851bcffe8c1332c00e23156b4982204d0400002c0101ddfba5791de0b8da80d46b43915ae34c4876c4f80101f50834aa68dec4d9d151b1ff1c509c81431ddc450400008a0101e8e7c96af0d472a8d0e60e86009a97290fbc0f6d010188a175d23b41252823e7fd88297754f5c580c4ff0400005a0101653ca45307922091337376cb305485c0d889a7a10001d9b2a3142267255c50581c8023648916a3e8c3ae7ca50f6752b6874a20e76e496b30c4e1b653691b3ae9fea40a66966f3d1f2a35cedb52fbf07ae09269fb3c8e1b02040001180101a18522682c76e7e4083fcef379839347a533f782010159d7eb9085272adb317893df26e7f39dcfdda1ba0400002c0101c31ee68141cb47d2b260fe5a6e48b37d021d8f190101947ee7254d4de72f7a1b2e70ed3f8e8ae6510d77040000b8000147f646e6d13434b2df65fc1ab9086264bed1030e485e3513ed01686d03d127df510efc468bbeedde677c3af1fda7b0dbffc7186e07203eb09718cc256cf6b5d11b020101ce1977029e9398ec9f45327c81cf7a557f5d30b80400005a01010b6a69349728615d6e1c8d4fd133e49aafd5b91b0001aaac151a6ad4bf7f966db203164551a7c3c3969d15666dd2c75202231623f5ee2059711c84d2f216126bf3dc6cc63223eba079262e73c58da4f97583747c790b1c02' +const sampleSignature4 = '0x00010000000203f6dc189f16bb65c588ccd5c63aa805bcbeb6e90dd8a049cfba0936050f299087060400020000c3037c989a96925302993812c1ec3924bce3ba2ca0e8f7e3655e30f5b24d965aa18b040000880001a73ce16a9cc7075c18bd2b4fd2649812fecb51460353a55bf62f821bf884443a169e0d0e04113d7ef2c2d15f1ecf46531f291259542065c556f0e721a82b3c581b02000193f1f388009f68763df43632153155960ea6604723bb517e90788822ff21e38722be4387e8f67c0db677b74d9a0c2a804183e6a3eebd2ba53dbfc54432f1a10f1b020101907c144d2490f49838c6499507ee5914f4a22b5b' +const sampleSignature5 = '0x020001636a2c7d032b4c067647ee1f154214b4ad83bbbe7e57a528ca0df587e34ded382ca7348c100400006703c702696d354063d18d750cc686a1f356e503f85516c54375ef5878250a22758704000042054cd7065b01927d3429db64e0a7ec956fa5506dab23fa37c767eb4375fab7898b032acf6636e813600f741841733e57a7e0cb4131f3c68db7ba7014fb94525f5de20302c10a9634e89b4293346a7408364eeece764491bd465d043f7c826518c2bc9501011a9bd9f98e2c0c81bcf51da26c3a7cfcc18c43b4030c389524f715de03757bcbc7a084f52c5d54def431bb8080a18d0075e26b859c0101379b2a7a384376b420d3d19c5c5717abaad3a969' +const sampleSignature6 = '0x010002636a33a501012093ec341be249baa0c8afa35fef368a90a483900201cd907cf455a1a00a4ebe37ef5f4bb7abc3770a6900004228230cc5c4ee221c093054fef22c12d534f4d63782bc94a160c2f781cef142e019b84d82070b67cb750ec9ba46ae49e6687591810099f6e58811fbe35ea3db451c0202014bffabff5819087514d8db622543c3d0d89cd64d000042844e002b27098ba6144bc9eb7950cd20a4062d265bdd042bffbb7ec8405caf7f60f1c5bdcd8ea4f4acee17d5ac9eac6bcdb40a20a41796d40a153278ab062b211c020101e8c4a6eb40ece266c7a58670493ee0727be4d20a' + +describe('v2 signature utils', () => { + describe('Decode signatures', () => { + it('Decode simple signature', () => { + const decoded = decodeSignature(sampleSignature1) + + expect(decoded).to.deep.equal({ + version: 2, + type: SignatureType.Legacy, + decoded: { + threshold: 1, + checkpoint: 1667830200, + tree: { + isDynamic: false, + signature: "0x9fa7b7e8ed25088c413074818ac10ab3bbcddb120bbec85083f3ba254e5547d953fe615a6474fd365326244dedd7afa3911ad39c956ca096d721064d6b29055d1b02", + unrecovered: true, + weight: 1 + } + } + }) + }) + + it('Decode trimmed 2/N with 31 signers', () => { + /** + 0x9ce037be2c62dfec86f2cf5339f773b8fc22da992b9e33ee8ee050676a1fef48', + ├─ 0xcc049b7ee4891eb306511fb4019c104766fb97c73097a6ddd73858c1ba200292', + │ ├─ 0x4a062f86183c9d46e129f0331f2a42f6ba22a3525a46ecd197fa23d177d75f2d', + │ │ ├─ 0xe66f95b2257d7765d2af2a44f85bf9c9ecd220c686943595f4c7b87f42214b78', + │ │ │ ├─ 0xfccac93b8e71891c0647977a42447b037574deaa9d4cf7a6a6e6fd9275b75a5d', + │ │ │ │ ├─ weight: 1 - address: 0x39bc8F324dB1d2356E084b8c504F972f4A774fB2', + │ │ │ │ └─ weight: 1 - address: 0xb2C7368fA82d1Fd633f79FA9BcBE923cB1b84e4f', + │ │ │ └─ 0x85dab8bdc832396fb5f6f3dc3d86e589a6358edde9d5dfb567199ba81328f429', + │ │ │ ├─ weight: 1 - address: 0xAc9a3035638E36300DCd6e89cf7D3861bbb8dd1F', + │ │ │ └─ weight: 1 - address: 0x7Fb579CE8378EbcB953c6b1159cFF1d2DEEb6f74', + │ │ └─ 0xc0a464e50c14c3c9be84fcf19726f39298b1101b62da1ea093d058f574dc4075', + │ │ ├─ 0xa2ba648e377ddd25ccc5d55db2eaf2031d713ea63456cf60dbd88acb4fb9b826', + │ │ │ ├─ weight: 1 - address: 0x5dfc6cA7841DF26872BeF07C68fc18031908480c', + │ │ │ └─ weight: 1 - address: 0xA3B58D5778F59cF331693618f5E11b901029C3DE', + │ │ └─ 0x6ec7200199b3dad7a17e09b5a04df6518bc3eefecd59b6509f47bc478325384b', + │ │ ├─ weight: 1 - address: 0xAD4d6101f2fFda7C39D039d4c496B9005AaDBFaA', + │ │ └─ weight: 1 - address: 0x204De2Fa1FF302345CFd53bE37a5234c606783d8', + │ └─ 0x326e14238f8038db10e675efdf0c7648f8066c6a064738b73ec1db63a904c26c', + │ ├─ 0x3fce59919d0a4ee44a8066a3b1d0083760d89a06ae89edadf8a58e0e5c5ac504', + │ │ ├─ 0xa13a367336b680c598ffcc7738b9b18135000db5be559f35262b28e1701bb9a3', + │ │ │ ├─ weight: 1 - address: 0xD6BE598eD22A999f51BDCFD484454319CCe32b92', + │ │ │ └─ weight: 1 - address: 0x3347821222470CD136bAac735bf59A1734A80B83', + │ │ └─ 0x14b13f254e58655bf2d4dce5c7e3ec0566a4e025a70d1fc0d41a08e675c86358', + │ │ ├─ weight: 1 - address: 0x0aE2D84a35Eb1fD2B78dF00940A84c6a4954B4A6', + │ │ └─ weight: 1 - address: 0x598fD5791971eb873FA8147B1BdF3207068F7E56', + │ └─ 0xa507ba934d99995d74786ac057b7c2cd9e22ac9d4c3aee6739e0cc0d308065db', + │ ├─ 0x1df893b2ba851550922f4c3c6f60608f6c70fbe1f47670eaf9f5c3a6edbcd400', + │ │ ├─ weight: 1 - address: 0x6FFEcCF6F31e0a469D55DEdE5651D34A6ECd9FC5', + │ │ └─ weight: 1 - address: 0xE8D34A3999375ef56CD8eB41AC678f5332F7F223', + │ └─ 0xad5831467806b6edd059ff5ac9809f2bb6e80512ceb5d466a67251ffb842fae1', + │ ├─ weight: 1 - address: 0x103dD4E217C422839F3D4b1897C3b1100184d962', + │ └─ weight: 1 - address: 0x5adDAfA4498f9F54af54B8CD8a86728818Df911f', + └─ 0xb7a09a95298cc9bbeeb3c8fbe1f46d158976de898ca42470d0da75cea7be9b43', + ├─ 0x2ac4cc831b29dd447dc2d95a203a7b146ffbb8b9cf3fd0022d15bd0a490bc557', + │ ├─ 0x14b729622595218cdbef06c630daeea028e25e8ca048d97bc170d75feb9066ad', + │ │ ├─ 0xd08870ce28971831b6320b00d017b4351c75ca68432721c6e50145fc320bd900', + │ │ │ ├─ weight: 1 - address: 0x8881DFDBb650d55A440e7F40c3Fc890D327cE35C', + │ │ │ └─ weight: 1 - address: 0x133BC159421310c81E1045ba1e1f8fac34e2c5bB', + │ │ └─ 0x99a7e698bb471ec55f01f14f21a20d23b2f3c142fabe99b3294c526b50207a13', + │ │ ├─ weight: 1 - address: 0xCA9Ed033CB7E9D905942866cD2E593aEB2e05731', + │ │ └─ weight: 1 - address: 0x96613Fda8926dB718719c3c1CE9DaeeddbC520F1', + │ └─ 0xd508a67420b9138396432c9d6a89735a4f1bddf3800ce175fe54f5f80eea6fc7', + │ ├─ 0x0c8c0bb7e8c5ec8eed444ae25f3a1796597bcfacf5f6b758ae4fadd6fc416f56', + │ │ ├─ weight: 1 - address: 0x6d0fDa7520Bb48B6948f77214EE7411636853f30', + │ │ └─ weight: 1 - address: 0x1252c641DC898449490C7F145598b5A70c6738de', + │ └─ 0xc6eb96ebf4f10c3073d6b680efcb57d636b83fe5bc92912ae7c300d9e9cb232a', + │ ├─ weight: 1 - address: 0x3B69bC115e6D79E8adBD011020676750B169bEDd', + │ └─ weight: 1 - address: 0x4ef7Ec718f66ae3920ea119b9d7DDF39337601f7', + └─ 0xfdea4c5fb23fb3cc2b2360057abef1ff7e7195acbdc4db555c27cc588a4585a6', + ├─ 0x33b6f5aa2e0cc8d120a1ec31e74095d978b88fce7c34030579c1ea1ef372c4ad', + │ ├─ 0x5885c583c79ef1fe29477fcb82c7053518a99bedf73ebbf1948a160bdb8e2c0f', + │ │ ├─ weight: 1 - address: 0x89eD176B654F09024a8EFb0F9576D05f614E6f77', + │ │ └─ weight: 1 - address: 0xe8a3eb4CbEFF970eBd44e862f788C4CDB64009c1', + │ └─ 0x367a80d6704d73c6777aae2c7ed880a0536520df2d3a3f3a3a17d22925842833', + │ ├─ weight: 1 - address: 0x2C170AfE2D6c8489e4A272370DA494856E39BBDb', + │ └─ weight: 1 - address: 0x6c32dd456D1DD14d91739f777D37378D243AfF93', + └─ 0x6b8ac6478e09f9c92bed9532e1bdb2a2eefcfad542a6d5573bb16df0e50f7bdb', + ├─ 0x7206ea506e442d2a7ca309d52e4ebe6f0b8982261dbd45e87490bd86cfe77a2a', + │ ├─ weight: 1 - address: 0x72D0f36D4a0f18E22E7Ffd955C69C55D632d13Ae', + │ └─ weight: 1 - address: 0xfa79D7198d04b384735b8a24dE92014ECD59f777', + └─ weight: 1 - address: 0xFE3de6DF80c5890bAdBC24c1b4256A6c6E311933' + */ + + const decoded = decodeSignature(sampleSignature2) + + expect(decoded).to.deep.equal({ + version: 2, + type: SignatureType.Legacy, + decoded: { + threshold: 2, + checkpoint: 1667830665, + tree: { + left: { + left: { + nodeHash: '0x4a062f86183c9d46e129f0331f2a42f6ba22a3525a46ecd197fa23d177d75f2d', + }, + right: { + left: { + nodeHash: '0x3fce59919d0a4ee44a8066a3b1d0083760d89a06ae89edadf8a58e0e5c5ac504' + }, + right: { + left: { + left: { + address: '0x6FFEcCF6F31e0a469D55DEdE5651D34A6ECd9FC5', + weight: 1 + }, + right: { + // signature for: 0xE8D34A3999375ef56CD8eB41AC678f5332F7F223 + signature: '0x7052a0438a13da22242bcd20c219630d839c364cd2b6042add1bee32774c37d72ba2ace8b7a79c95a536d4c0fed3fe05883c6e1188a4191a91623a903e4ec21c1b02', + weight: 1, + unrecovered: true, + isDynamic: false + } + }, + right: { + nodeHash: '0xad5831467806b6edd059ff5ac9809f2bb6e80512ceb5d466a67251ffb842fae1' + } + } + } + }, + right: { + left: { + left: { + nodeHash: '0x14b729622595218cdbef06c630daeea028e25e8ca048d97bc170d75feb9066ad' + }, + right: { + left: { + nodeHash: '0x0c8c0bb7e8c5ec8eed444ae25f3a1796597bcfacf5f6b758ae4fadd6fc416f56', + }, + right: { + left: { + // signature for: 0x3B69bC115e6D79E8adBD011020676750B169bEDd + signature: '0xe7618f1b7b012d7fc48f518f498bb6823dc2a8308984287501873cb535b6d5bf526fb91a220297f461ac5a2434d0e8e768c3bf166c329366ddc885bf2e1676271c02', + weight: 1, + unrecovered: true, + isDynamic: false + }, + right: { + address: '0x4ef7Ec718f66ae3920ea119b9d7DDF39337601f7', + weight: 1 + } + } + } + }, + right: { + nodeHash: '0xfdea4c5fb23fb3cc2b2360057abef1ff7e7195acbdc4db555c27cc588a4585a6' + } + } + } + } + }) + }) + + it('Decode non-trimmed 3/N with 16 signers', () => { + /** + 0x0bd27b4a9a6a160ae92f5dc27a5d20156e81b049e451cc226db03be9454a9dbe', + ├─ 0xa9b9bb8f341ef4cba67d42b2c588d99f700a451f208d1d7ecb23d017ab23c3c5', + │ ├─ 0x24ac1effef0566192cd4ad878bc135c7d649b4989507f284fe5c66dae01117d3', + │ │ ├─ 0x67dff26d956ede906bbd0692a0cd573a78c7e345d54ccc93e2383337b4a46660', + │ │ │ ├─ weight: 1 - address: 0xA653F5900Ef5c538142Cd8Aef1CE750390B29a3E', + │ │ │ └─ weight: 1 - address: 0xA54e174d851bCFFE8C1332C00e23156B4982204D', + │ │ └─ 0x211bbe1253185da2e1f353cfb210c48378521ebfb3e103e18459e6aa9143848f', + │ │ ├─ weight: 1 - address: 0xDdfbA5791dE0b8Da80d46B43915Ae34C4876C4F8', + │ │ └─ weight: 1 - address: 0xF50834aa68DEc4D9D151b1ff1c509C81431DDC45', + │ └─ 0x0888e3e8bb7be34c21de30730e8f9cd91d03222bfea229eeabab03f3aa2183e0', + │ ├─ 0x360fe86d2a78344c383256a5509dac30c5046dd38cf6bfc54a880ac4f7e604ed', + │ │ ├─ weight: 1 - address: 0xe8e7C96aF0D472a8D0E60E86009a97290Fbc0F6d', + │ │ └─ weight: 1 - address: 0x88a175d23b41252823e7fD88297754f5C580c4Ff', + │ └─ 0x1235b94db1f48cebb5ebec7d345033d92801312f13086c1a79d032e703525bea', + │ ├─ weight: 1 - address: 0x653cA45307922091337376Cb305485c0D889A7A1', + │ └─ weight: 1 - address: 0xCf8BF768E2b69953577e1FF16b147c773faEc959', + └─ 0x86c8fbddf975589fecf3e2a5a543a916dedcf80aeb12f32abc26586110449059', + ├─ 0xcb4f6042dd1421bc59313c5a8e806514c2fbad361e706e6ec36a4dd6b815e03a', + │ ├─ 0x63fa3b020293428bfee299769b520e08641c66299922077cc91abd2ff31920f6', + │ │ ├─ weight: 1 - address: 0xa18522682c76e7e4083fCEF379839347a533f782', + │ │ └─ weight: 1 - address: 0x59d7eb9085272AdB317893Df26E7F39dCfdDa1bA', + │ └─ 0x4dc9c2311b9bfddc117ef646088b22d4a9548d9651a93c8246f7ad33acdf9431', + │ ├─ weight: 1 - address: 0xC31Ee68141cB47d2B260fE5A6e48b37d021D8F19', + │ └─ weight: 1 - address: 0x947EE7254D4dE72F7A1B2e70ed3f8E8aE6510D77', + └─ 0x7fe1e93c3a299dd8f6ebc06d4c94e5df6423b4ce919367f83f8c672e5e17cba8', + ├─ 0x8d0659c89c7f8de17801cf0178f4d32550b095187afac0d6b733797af881b41b', + │ ├─ weight: 1 - address: 0xb92E451800D78AA8f8492fFEA1a5afc77774f880', + │ └─ weight: 1 - address: 0xCE1977029e9398Ec9F45327c81cf7a557F5D30b8', + └─ 0xe4eaf15623516afc250692b6f8888be93638077ae5c78d95b01b7bf99b56cb67', + ├─ weight: 1 - address: 0x0b6a69349728615d6e1C8d4FD133e49AafD5b91b', + └─ weight: 1 - address: 0x8245B0c0C4319523c2D2616F86EBd02DaDA2FBD3' + */ + + const decoded = decodeSignature(sampleSignature3) + + expect(decoded).to.deep.equal({ + version: 2, + type: SignatureType.Legacy, + decoded: { + checkpoint: 1667831412, + threshold: 3, + tree: { + left: { + left: { + left: { + left: { + address: '0xA653F5900Ef5c538142Cd8Aef1CE750390B29a3E', + weight: 1 + }, + right: { + address: '0xA54e174d851bCFFE8C1332C00e23156B4982204D', + weight: 1 + }, + }, + right: { + left: { + address: '0xDdfbA5791dE0b8Da80d46B43915Ae34C4876C4F8', + weight: 1 + }, + right: { + address: '0xF50834aa68DEc4D9D151b1ff1c509C81431DDC45', + weight: 1 + }, + }, + }, + right: { + left: { + left: { + address: '0xe8e7C96aF0D472a8D0E60E86009a97290Fbc0F6d', + weight: 1 + }, + right: { + address: '0x88a175d23b41252823e7fD88297754f5C580c4Ff', + weight: 1 + }, + }, + right: { + left: { + address: '0x653cA45307922091337376Cb305485c0D889A7A1', + weight: 1 + }, + right: { + // address: '0xCf8BF768E2b69953577e1FF16b147c773faEc959', + signature: '0xd9b2a3142267255c50581c8023648916a3e8c3ae7ca50f6752b6874a20e76e496b30c4e1b653691b3ae9fea40a66966f3d1f2a35cedb52fbf07ae09269fb3c8e1b02', + isDynamic: false, + unrecovered: true, + weight: 1 + }, + }, + }, + }, + right: { + left: { + left: { + left: { + address: '0xa18522682c76e7e4083fCEF379839347a533f782', + weight: 1 + }, + right: { + address: '0x59d7eb9085272AdB317893Df26E7F39dCfdDa1bA', + weight: 1 + }, + }, + right: { + left: { + address: '0xC31Ee68141cB47d2B260fE5A6e48b37d021D8F19', + weight: 1 + }, + right: { + address: '0x947EE7254D4dE72F7A1B2e70ed3f8E8aE6510D77', + weight: 1 + }, + }, + }, + right: { + left: { + left: { + // address: '0xb92E451800D78AA8f8492fFEA1a5afc77774f880', + signature: '0x47f646e6d13434b2df65fc1ab9086264bed1030e485e3513ed01686d03d127df510efc468bbeedde677c3af1fda7b0dbffc7186e07203eb09718cc256cf6b5d11b02', + unrecovered: true, + isDynamic: false, + weight: 1 + }, + right: { + address: '0xCE1977029e9398Ec9F45327c81cf7a557F5D30b8', + weight: 1 + }, + }, + right: { + left: { + address: '0x0b6a69349728615d6e1C8d4FD133e49AafD5b91b', + weight: 1 + }, + right: { + // address: '0x8245B0c0C4319523c2D2616F86EBd02DaDA2FBD3', + signature: '0xaaac151a6ad4bf7f966db203164551a7c3c3969d15666dd2c75202231623f5ee2059711c84d2f216126bf3dc6cc63223eba079262e73c58da4f97583747c790b1c02', + unrecovered: true, + isDynamic: false, + weight: 1 + }, + }, + }, + }, + } + } + }) + }) + + it('Decode signature with nested trees', () => { + /** + 0xc62c3d8ab0422ccbab7339f13b987179c2583743b8af4728cd49b146c710c5c6', + ├─ 0xf6dc189f16bb65c588ccd5c63aa805bcbeb6e90dd8a049cfba0936050f299087', + │ ├─ 0x59276a9b2f7b735fd033d13fdfcf01391f6c112dc48418107c47faa292cda138', + │ │ ├─ 0x52b68b273da79cbad184ab5dc8e89825b373ab9af6ee97e0c556d3829126ba7c', + │ │ │ ├─ weight: 1 - address: 0xb159d82f98490c5Db1dB71b76bbb2C3a86DEce0C', + │ │ │ └─ weight: 1 - address: 0x29Fc57a0eb82688ad558A572C9E23e94243dB4d3', + │ │ └─ weight: 1 - address: 0x0B2b3abA8538639E6D9c1B1200942FA00148ABCB', + │ └─ weight: 1 - address: 0x3314715F5EE607A8988EC4c43351910CD6c76AE5', + └─ 0xd9b2fcc7c63fceaea59b7423cfda5e01307139ac078c2a1695fef1f9a4d9f50a', + └─ threshold: 2 - weight: 4', + ├─ 0x3c8cb8e47389edeee921bdb2efa8a8e664ef38790cfb4230ee51d5314e3a37d3', + │ ├─ 0x7c989a96925302993812c1ec3924bce3ba2ca0e8f7e3655e30f5b24d965aa18b', + │ │ ├─ weight: 1 - address: 0x711dD9c6D02010ABEfd5a4587298CB6a230d3877', + │ │ └─ weight: 1 - address: 0x05ead11721299d471d4e83b51ebfeB87F24A96c5', + │ └─ 0xfeac20f352af0c03f48d1eaeeacbde8e86b391bf97dd83665c218271da447be2', + │ ├─ weight: 1 - address: 0x4Faade320BBE1B9E31803A8A104305c3B5D5cC7E', + │ └─ weight: 1 - address: 0xE403b05AA84848604B40aFDbfE4977e9Be4ECCa9', + └─ weight: 1 - address: 0x907c144D2490f49838c6499507EE5914f4A22b5B' + */ + + const decoded = decodeSignature(sampleSignature4) + + expect(decoded).to.deep.equal({ + version: 2, + type: SignatureType.Legacy, + decoded: { + threshold: 1, + checkpoint: 2, + tree: { + left: { + nodeHash: '0xf6dc189f16bb65c588ccd5c63aa805bcbeb6e90dd8a049cfba0936050f299087' + }, + right: { + weight: 4, + threshold: 2, + tree: { + left: { + left: { + nodeHash: '0x7c989a96925302993812c1ec3924bce3ba2ca0e8f7e3655e30f5b24d965aa18b' + }, + right: { + left: { + signature: '0xa73ce16a9cc7075c18bd2b4fd2649812fecb51460353a55bf62f821bf884443a169e0d0e04113d7ef2c2d15f1ecf46531f291259542065c556f0e721a82b3c581b02', + weight: 1, + unrecovered: true, + isDynamic: false + }, + right: { + signature: '0x93f1f388009f68763df43632153155960ea6604723bb517e90788822ff21e38722be4387e8f67c0db677b74d9a0c2a804183e6a3eebd2ba53dbfc54432f1a10f1b02', + weight: 1, + unrecovered: true, + isDynamic: false + } + }, + }, + right: { + address: '0x907c144D2490f49838c6499507EE5914f4A22b5B', + weight: 1 + } + } + } + } + } + }) + }) + + it('Decode static subdigests signature', () => { + /* + 0xd039f8f363eec6e6580c04fba1dfa1a7586827d884cb4d98ed667e131a01c268', + ├─ 0x73c9ee2e965c95b829c86ef4849dbf2f0410f4ac4380d2fc58f9246f9d84d0d0', + │ ├─ 0x73b96511a817fcf95200cd76af547a767c2faea2d52aa9e759f2a8ced75c7c67', + │ │ ├─ 0x9be568b9b969ab8d1012696c56ff89db394dcac9881bef5e361a4ffed446d6f6', + │ │ │ ├─ 0x1915fb45c54b103485bf50f1afb0fa6a70c1546211c48d15480ecc991765ba7f', + │ │ │ │ ├─ X 0x2b4c067647ee1f154214b4ad83bbbe7e57a528ca0df587e34ded382ca7348c10', + │ │ │ │ │ ├─ 0xd82efd7c2419e1ce6ec9de6f51051f6376773cd727c032cd15823755f19e4356', + │ │ │ │ │ │ ├─ subDigest: 0xd151a051d91288c5c5f4688ec5c6f0977f41535747293bcdc6859885e2e3c8f9', + │ │ │ │ │ │ └─ subDigest: 0x746fba99dcf684e2b9eb7dceace9d00b1988c5ad13fb46bb7c6272b8dac15821', + │ │ │ │ │ └─ 0xbff3206ad6a9cb35896c77f154b2aa4f72b709c9f4ec756d0da521163b3bcb61', + │ │ │ │ │ ├─ subDigest: 0xd5f94f3099a2c78c8687c81e7e29a2193a7003383989be621ab864efead521dc', + │ │ │ │ │ └─ subDigest: 0x6f5f1a3fb35d99dbf84a5f23713fd168231dddf6589a990378b83cf03f02d9f0', + │ │ │ │ └─ 0x798573e5ebb023632eafafce765fe8227f302a6db5e4c123a5a997c593471749', + │ │ │ │ ├─ X 0xc702696d354063d18d750cc686a1f356e503f85516c54375ef5878250a227587', + │ │ │ │ │ ├─ subDigest: 0xced8ceaa611754f0824a3066c4e53a1e78113dad5d8c63985b076eba2912bf09', + │ │ │ │ │ └─ subDigest: 0x00b43843c7c77215b123e3471be7532c64180d872e2dd68cd739bb7f1bcca725', + │ │ │ │ └─ 0x47344ce248ff726cf13c68d1e4bb7f2ab3a0b52d0668e240ed0925877ac62a88', + │ │ │ │ ├─ -> subDigest: 0x4cd7065b01927d3429db64e0a7ec956fa5506dab23fa37c767eb4375fab7898b', + │ │ │ │ └─ X (hashed) subDigest: 0xc0b21c4464a6acf6d8451d3a077bb3ebaa3953bd2e01609dec557af47239c012', + │ │ │ └─ X 0x02c10a9634e89b4293346a7408364eeece764491bd465d043f7c826518c2bc95', + │ │ │ ├─ subDigest: 0xae6b3762bab90dcc5eccbb3a8d1f5f8d9d974b2458403779ff998636c99ec15e', + │ │ │ └─ subDigest: 0x5c9de17d821a60f691929cd6d475d155a27e4d3ce0c79b4412a8e5e50c0e4f1e', + │ │ └─ X weight: 1 - address: 0x1A9bD9f98E2C0C81BcF51DA26c3a7CFcC18c43B4', + │ └─ X 0x0c389524f715de03757bcbc7a084f52c5d54def431bb8080a18d0075e26b859c', + │ ├─ weight: 1 - address: 0xEdAE5e1bF8D80e20C9008479A07400e84BC1af9D', + │ └─ weight: 1 - address: 0xBf31A9f466Fc2844CDE7F12c87dc3e6676c8D0b2', + └─ X weight: 1 - address: 0x379b2A7A384376B420D3D19c5c5717ABAaD3a969' + */ + const decoded = decodeSignature(sampleSignature5) + + expect(decoded).to.deep.equal({ + version: 2, + type: SignatureType.NoChainIdDynamic, + decoded: { + threshold: 1, + checkpoint: 1667902589, + tree: { + left: { + left: { + left: { + left: { + left: { + nodeHash: '0x2b4c067647ee1f154214b4ad83bbbe7e57a528ca0df587e34ded382ca7348c10' + }, + right: { + left: { + nodeHash: '0xc702696d354063d18d750cc686a1f356e503f85516c54375ef5878250a227587' + }, + right: { + left: { + subdigest: '0x4cd7065b01927d3429db64e0a7ec956fa5506dab23fa37c767eb4375fab7898b' + }, + right: { + nodeHash: '0x2acf6636e813600f741841733e57a7e0cb4131f3c68db7ba7014fb94525f5de2' + } + } + } + }, + right: { + nodeHash: '0x02c10a9634e89b4293346a7408364eeece764491bd465d043f7c826518c2bc95' + } + }, + right: { + address: '0x1A9bD9f98E2C0C81BcF51DA26c3a7CFcC18c43B4', + weight: 1 + } + }, + right: { + nodeHash: '0x0c389524f715de03757bcbc7a084f52c5d54def431bb8080a18d0075e26b859c' + } + }, + right: { + address: '0x379b2A7A384376B420D3D19c5c5717ABAaD3a969', + weight: 1 + } + } + } + }) + }) + + it('Decode dynamic signatures', () => { + /* + 0xe916ef5f1e4c38acd77f793ab9fe6696272541dce1fc84ffb712e2faccd4be07', + ├─ 0x8554edff027c3cb80d02e3e233a778c85165fbc2c813e8b4148339f8cda1cfd1', + │ ├─ 0xd871650a4a126ee8112934486f91f28f4da3e64474d66c778d1f2bd84b6f9ec7', + │ │ ├─ weight: 1 - address: 0x2093ec341be249BAA0c8aFA35fEF368a90a48390', + │ │ └─ weight: 1 - address: 0xCd907CF455A1A00a4ebE37Ef5F4BB7aBc3770A69', + │ └─ weight: 1 - address: 0x4bfFABff5819087514d8dB622543c3d0d89cD64D', + └─ weight: 1 - address: 0xe8C4a6EB40EcE266C7a58670493eE0727be4D20A' + */ + + const decoded = decodeSignature(sampleSignature6) + + expect(decoded).to.deep.equal({ + version: 2, + type: SignatureType.Dynamic, + decoded: { + threshold: 2, + checkpoint: 1667904421, + tree: { + left: { + left: { + left: { + address: '0x2093ec341be249BAA0c8aFA35fEF368a90a48390', + weight: 1 + }, + right: { + address: '0xCd907CF455A1A00a4ebE37Ef5F4BB7aBc3770A69', + signature: '0x28230cc5c4ee221c093054fef22c12d534f4d63782bc94a160c2f781cef142e019b84d82070b67cb750ec9ba46ae49e6687591810099f6e58811fbe35ea3db451c02', + weight: 1, + isDynamic: true, + unrecovered: true + } + }, + right: { + address: '0x4bfFABff5819087514d8dB622543c3d0d89cD64D', + signature: '0x844e002b27098ba6144bc9eb7950cd20a4062d265bdd042bffbb7ec8405caf7f60f1c5bdcd8ea4f4acee17d5ac9eac6bcdb40a20a41796d40a153278ab062b211c02', + weight: 1, + isDynamic: true, + unrecovered: true + }, + }, + right: { + address: '0xe8C4a6EB40EcE266C7a58670493eE0727be4D20A', + weight: 1 + } + }, + } + }) + }) + + it('Fail to decode invalid signature part type', () => { + const invalidSignature = ethers.utils.solidityPack( + ['bytes', 'uint8'], + ['0x0001ffffffff', Object.keys(SignaturePartType).length / 2] + ) + + expect(() => decodeSignature(invalidSignature)).to.throw(`Unknown signature part type: ${Object.keys(SignaturePartType).length / 2}: 0x`) + }) + + it('Fail to decode empty tree signature', () => { + const invalidSignature = '0x0001ffffffff' + + expect(() => decodeSignature(invalidSignature)).to.throw('Empty signature tree') + }) + }) + + describe('Encode signatures', () => { + describe('Encode decoded signatures', () => { + it('Re-encode simple signature', () => { + const decoded = decodeSignature(sampleSignature1) + const reEncoded = encodeSignature(decoded) + expect(reEncoded).to.equal(sampleSignature1) + expect(decoded).to.deep.equal(decodeSignature(reEncoded)) + }) + + it('Re-encode trimmed 2/N with 31 signers', () => { + const decoded = decodeSignature(sampleSignature2) + const reEncoded = encodeSignature(decoded) + + expect(decoded).to.deep.equal(decodeSignature(reEncoded)) + expect(reEncoded).to.equal(sampleSignature2) + }) + + it('Re-encode non-trimmed 3/N with 16 signers', () => { + const decoded = decodeSignature(sampleSignature3) + const reEncoded = encodeSignature(decoded) + + expect(decoded).to.deep.equal(decodeSignature(reEncoded)) + expect(reEncoded).to.equal(sampleSignature3) + }) + + it('Re-encode signature with nested trees', () => { + const decoded = decodeSignature(sampleSignature4) + const reEncoded = encodeSignature(decoded) + + expect(decoded).to.deep.equal(decodeSignature(reEncoded)) + expect(reEncoded).to.equal(sampleSignature4) + }) + + it('Re-encode static subdigests signature', () => { + const decoded = decodeSignature(sampleSignature5) + const reEncoded = encodeSignature(decoded) + + expect(decoded).to.deep.equal(decodeSignature(reEncoded)) + expect(reEncoded).to.equal(sampleSignature5) + }) + + it('Re-encode dynamic signatures', () => { + const decoded = decodeSignature(sampleSignature6) + const reEncoded = encodeSignature(decoded) + + expect(decoded).to.deep.equal(decodeSignature(reEncoded)) + expect(reEncoded).to.equal(sampleSignature6) + }) + }) + }) +}) diff --git a/packages/deployer/package.json b/packages/deployer/package.json index aa06665f2..859b1f46b 100644 --- a/packages/deployer/package.json +++ b/packages/deployer/package.json @@ -17,7 +17,7 @@ "gen:typings": "rm -rf ./src/typings/contracts/* && typechain --target ethers-v5 --out-dir src/typings/contracts './artifacts/contracts/!(build-info)/**/*[^dbg].json'" }, "dependencies": { - "@0xsequence/utils": "^0.43.34" + "@0xsequence/utils": "workspace:*" }, "peerDependencies": { "ethers": ">=5.5 < 6", diff --git a/packages/estimator/package.json b/packages/estimator/package.json index fe42a6f1e..c4591ef3e 100644 --- a/packages/estimator/package.json +++ b/packages/estimator/package.json @@ -17,17 +17,17 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@0xsequence/abi": "^0.43.34", - "@0xsequence/config": "^0.43.34", - "@0xsequence/network": "^0.43.34", - "@0xsequence/transactions": "^0.43.34", - "@0xsequence/utils": "^0.43.34", - "@0xsequence/wallet-contracts": "1.10.0" + "@0xsequence/abi": "workspace:*", + "@0xsequence/core": "workspace:*", + "@0xsequence/utils": "workspace:*", + "@0xsequence/wallet-contracts": "^1.10.0" }, "peerDependencies": { "ethers": ">=5.5 < 6" }, "devDependencies": { + "@0xsequence/signhub": "workspace:*", + "@0xsequence/tests": "workspace:*", "@ethersproject/abstract-signer": "^5.7.0", "@ethersproject/properties": "^5.7.0", "ethers": "^5.7.2" diff --git a/packages/estimator/src/builds/MainModuleGasEstimation.ts b/packages/estimator/src/builds/MainModuleGasEstimation.ts new file mode 100644 index 000000000..c253deeab --- /dev/null +++ b/packages/estimator/src/builds/MainModuleGasEstimation.ts @@ -0,0 +1,852 @@ +export const mainModuleGasEstimation = { + "_format": "hh-sol-artifact-1", + "contractName": "MainModuleGasEstimation", + "sourceName": "contracts/modules/MainModuleGasEstimation.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "uint256", + "name": "_space", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_provided", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_current", + "type": "uint256" + } + ], + "name": "BadNonce", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_signature", + "type": "bytes4" + } + ], + "name": "HookAlreadyExists", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_signature", + "type": "bytes4" + } + ], + "name": "HookDoesNotExist", + "type": "error" + }, + { + "inputs": [], + "name": "ImageHashIsZero", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_implementation", + "type": "address" + } + ], + "name": "InvalidImplementation", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_addr", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "InvalidNestedSignature", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "_s", + "type": "bytes32" + } + ], + "name": "InvalidSValue", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "InvalidSignature", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_flag", + "type": "uint256" + } + ], + "name": "InvalidSignatureFlag", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "InvalidSignatureLength", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes1", + "name": "_type", + "type": "bytes1" + } + ], + "name": "InvalidSignatureType", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_v", + "type": "uint256" + } + ], + "name": "InvalidVValue", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "threshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_weight", + "type": "uint256" + } + ], + "name": "LowWeightChainedSignature", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_requested", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_available", + "type": "uint256" + } + ], + "name": "NotEnoughGas", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_sender", + "type": "address" + }, + { + "internalType": "address", + "name": "_self", + "type": "address" + } + ], + "name": "OnlySelfAuth", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "SignerIsAddress0", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_type", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "_recoverMode", + "type": "bool" + } + ], + "name": "UnsupportedSignatureType", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_current", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_prev", + "type": "uint256" + } + ], + "name": "WrongChainedCheckpointOrder", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_contract", + "type": "address" + } + ], + "name": "CreatedContract", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "newImageHash", + "type": "bytes32" + } + ], + "name": "ImageHashUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "ImplementationUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_space", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_newNonce", + "type": "uint256" + } + ], + "name": "NonceChange", + "type": "event" + }, + { + "anonymous": true, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "_tx", + "type": "bytes32" + } + ], + "name": "TxExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "_tx", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "_reason", + "type": "bytes" + } + ], + "name": "TxFailed", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "SET_IMAGE_HASH_TYPE_HASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_signature", + "type": "bytes4" + }, + { + "internalType": "address", + "name": "_implementation", + "type": "address" + } + ], + "name": "addHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_code", + "type": "bytes" + } + ], + "name": "createContract", + "outputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "delegateCall", + "type": "bool" + }, + { + "internalType": "bool", + "name": "revertOnError", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct IModuleCalls.Transaction[]", + "name": "_txs", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "_nonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "execute", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "imageHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_signatures", + "type": "bytes" + } + ], + "name": "isValidSignature", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_signatures", + "type": "bytes" + } + ], + "name": "isValidSignature", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155BatchReceived", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC721Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_signature", + "type": "bytes4" + } + ], + "name": "readHook", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_space", + "type": "uint256" + } + ], + "name": "readNonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_signature", + "type": "bytes4" + } + ], + "name": "removeHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "delegateCall", + "type": "bool" + }, + { + "internalType": "bool", + "name": "revertOnError", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct IModuleCalls.Transaction[]", + "name": "_txs", + "type": "tuple[]" + } + ], + "name": "selfExecute", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_digest", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "signatureRecovery", + "outputs": [ + { + "internalType": "uint256", + "name": "threshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "weight", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "imageHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "subDigest", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "checkpoint", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_interfaceID", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_imageHash", + "type": "bytes32" + } + ], + "name": "updateImageHash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_implementation", + "type": "address" + } + ], + "name": "updateImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b50612daa806100206000396000f3fe6080604052600436106101485760003560e01c806357c56d6b116100c057806390042baf11610074578063b93ea7ad11610059578063b93ea7ad146104da578063bc197c81146104fa578063f23a6e61146105425761014f565b806390042baf146104b2578063affed0e0146104c55761014f565b80637a9a1628116100a55780637a9a16281461042a578063853c50681461044a5780638c3f5563146104925761014f565b806357c56d6b146103d657806361c2926c1461040a5761014f565b80631a9b23371161011757806329561426116100fc57806329561426146103735780634fcf3eca1461039357806351605d80146103b35761014f565b80631a9b23371461030e57806320c13b0b146103535761014f565b806301ffc9a714610223578063025b22bc14610258578063150b7a02146102785780631626ba7e146102ee5761014f565b3661014f57005b600061017e6000357fffffffff0000000000000000000000000000000000000000000000000000000016610588565b905073ffffffffffffffffffffffffffffffffffffffff811615610221576000808273ffffffffffffffffffffffffffffffffffffffff166000366040516101c79291906122da565b600060405180830381855af49150503d8060008114610202576040519150601f19603f3d011682016040523d82523d6000602084013e610207565b606091505b50915091508161021957805160208201fd5b805160208201f35b005b34801561022f57600080fd5b5061024361023e366004612318565b6105dc565b60405190151581526020015b60405180910390f35b34801561026457600080fd5b5061022161027336600461235e565b6105e7565b34801561028457600080fd5b506102bd6102933660046123c2565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b6040517fffffffff00000000000000000000000000000000000000000000000000000000909116815260200161024f565b3480156102fa57600080fd5b506102bd610309366004612431565b610639565b34801561031a57600080fd5b5061032e610329366004612318565b610686565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161024f565b34801561035f57600080fd5b506102bd61036e36600461247d565b610691565b34801561037f57600080fd5b5061022161038e3660046124e9565b6106f6565b34801561039f57600080fd5b506102216103ae366004612318565b610740565b3480156103bf57600080fd5b506103c861086f565b60405190815260200161024f565b3480156103e257600080fd5b506103c87f8713a7c4465f6fbee2b6e9d6646d1d9f83fec929edfc4baf661f3c865bdd04d181565b34801561041657600080fd5b50610221610425366004612547565b61089e565b34801561043657600080fd5b50610221610445366004612589565b610924565b34801561045657600080fd5b5061046a610465366004612431565b6109ba565b604080519586526020860194909452928401919091526060830152608082015260a00161024f565b34801561049e57600080fd5b506103c86104ad3660046124e9565b610b82565b61032e6104c0366004612621565b610bae565b3480156104d157600080fd5b506103c8610c4a565b3480156104e657600080fd5b506102216104f53660046126f0565b610c56565b34801561050657600080fd5b506102bd610515366004612725565b7fbc197c810000000000000000000000000000000000000000000000000000000098975050505050505050565b34801561054e57600080fd5b506102bd61055d3660046127e0565b7ff23a6e61000000000000000000000000000000000000000000000000000000009695505050505050565b60006105d67fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1207fffffffff000000000000000000000000000000000000000000000000000000008416610d9b565b92915050565b60006105d682610df9565b33301461062d576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044015b60405180910390fd5b61063681610e55565b50565b600080610647858585610f10565b509050801561067957507f1626ba7e00000000000000000000000000000000000000000000000000000000905061067f565b50600090505b9392505050565b60006105d682610588565b6000806106b686866040516106a79291906122da565b60405180910390208585610f10565b50905080156106e857507f20c13b0b0000000000000000000000000000000000000000000000000000000090506106ee565b50600090505b949350505050565b333014610737576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610624565b61063681610f4e565b333014610781576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610624565b600061078c82610588565b73ffffffffffffffffffffffffffffffffffffffff16036107fd576040517f1c3812cc0000000000000000000000000000000000000000000000000000000081527fffffffff0000000000000000000000000000000000000000000000000000000082166004820152602401610624565b604080517fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1206020808301919091527fffffffff00000000000000000000000000000000000000000000000000000000841682840152825180830384018152606090920190925280519101206000905550565b60006108997fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf85490565b905090565b3330146108df576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610624565b600061091283836040516020016108f7929190612a00565b60405160208183030381529060405280519060200120610fde565b905061091f818484611063565b505050565b61092d836111c1565b60008061096585888860405160200161094893929190612a48565b604051602081830303815290604052805190602001208585610f10565b91509150816109a6578084846040517f8f4a234f00000000000000000000000000000000000000000000000000000000815260040161062493929190612a6b565b6109b1818888611063565b50505050505050565b600080600080600080878760008181106109d6576109d6612a85565b909101357fff00000000000000000000000000000000000000000000000000000000000000169150819050610a2c57610a0e89610fde565b9250610a1b8389896112ca565b92985090965094509150610b779050565b7fff0000000000000000000000000000000000000000000000000000000000000081811601610a6b57610a5e89610fde565b9250610a1b83898961131b565b7ffe000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821601610abd57610a5e89611347565b7ffd000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821601610b2157610b118989896113b4565b9550955095509550955050610b77565b6040517f6085cd820000000000000000000000000000000000000000000000000000000081527fff0000000000000000000000000000000000000000000000000000000000000082166004820152602401610624565b939792965093509350565b60006105d67f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e83610d9b565b6000333014610bf1576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610624565b81516020830134f060405173ffffffffffffffffffffffffffffffffffffffff821681529091507fa506ad4e7f05eceba62a023c3219e5bd98a615f4fa87e2afb08a2da5cf62bf0c9060200160405180910390a1919050565b60006108996000610b82565b333014610c97576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610624565b6000610ca283610588565b73ffffffffffffffffffffffffffffffffffffffff1614610d13576040517f5b4d6d6a0000000000000000000000000000000000000000000000000000000081527fffffffff0000000000000000000000000000000000000000000000000000000083166004820152602401610624565b604080517fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1206020808301919091527fffffffff000000000000000000000000000000000000000000000000000000008516828401528251808303840181526060909201909252805191012073ffffffffffffffffffffffffffffffffffffffff821690555050565b6000808383604051602001610dba929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012054949350505050565b60007f6ffbd451000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831601610e4c57506001919050565b6105d682611531565b73ffffffffffffffffffffffffffffffffffffffff81163b610ebb576040517f0c76093700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610624565b610ec3813055565b60405173ffffffffffffffffffffffffffffffffffffffff821681527f310ba5f1d2ed074b51e2eccd052a47ae9ab7c6b800d1fca3db3999d6a592ca03906020015b60405180910390a150565b6000806000806000610f238888886109ba565b50965091945092509050828210801590610f415750610f4181611672565b9450505050935093915050565b80610f85576040517f4294d12700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610fae7fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8829055565b6040518181527f307ed6bd941ee9fc80f369c94af5fa11e25bab5102a6140191756c5474a30bfa90602001610f05565b6040517f190100000000000000000000000000000000000000000000000000000000000060208201524660228201527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b166042820152605681018290526000906076015b604051602081830303815290604052805190602001209050919050565b8060005b818110156111ba573684848381811061108257611082612a85565b90506020028101906110949190612ab4565b90506040810135805a10156110e95782815a6040517f2bb3e3ba000000000000000000000000000000000000000000000000000000008152600481019390935260248301919091526044820152606401610624565b60006110f86020840184612af2565b1561113757611130611110608085016060860161235e565b831561111c578361111e565b5a5b61112b60a0870187612b0d565b61167d565b9050611172565b61116f61114a608085016060860161235e565b6080850135841561115b578461115d565b5a5b61116a60a0880188612b0d565b611698565b90505b801561118e5760405188815260200160405180910390a06111af565b6111af6111a16040850160208601612af2565b896111aa6116b5565b6116d4565b505050600101611067565b5050505050565b606081901c6bffffffffffffffffffffffff821660006111e083610b82565b90508181141580156111f0575060005b15611238576040517f9b6514f4000000000000000000000000000000000000000000000000000000008152600481018490526024810183905260448101829052606401610624565b604080517f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e60208083019190915281830186905282518083038401815260609092019092528051910120600183019081905560408051858152602081018390527f1f180c27086c7a39ea2a7b25239d1ab92348f07ca7bb59d1438fcf527568f881910160405180910390a15050505050565b60008080806112e5876112e0876006818b612b72565b611720565b6000908152873560f01c6020818152604080842084526002909a013560e01c908190529890912090999198509695509350505050565b600080808061133687611331876001818b612b72565b6112ca565b935093509350935093509350935093565b6040517f190100000000000000000000000000000000000000000000000000000000000060208201526000602282018190527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b1660428301526056820183905290607601611046565b6000808080806004600188013560e81c826113cf8383612bcb565b90506113e18b61046583868d8f612b72565b939b50919950975095509350878710156114395761140181848b8d612b72565b89896040517fb006aba00000000000000000000000000000000000000000000000000000000081526004016106249493929190612bde565b8092505b888310156115235760038301928a013560e81c915061145c8383612bcb565b9050600061147e61146c88611bb6565b8c8c8790869261046593929190612b72565b939c50919a50985090915050888810156114d65761149e82858c8e612b72565b8a8a6040517fb006aba00000000000000000000000000000000000000000000000000000000081526004016106249493929190612bde565b848110611519576040517f37daf62b0000000000000000000000000000000000000000000000000000000081526004810182905260248101869052604401610624565b935091508161143d565b505050939792965093509350565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fec6aba500000000000000000000000000000000000000000000000000000000014806115c457507fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e000000000000000000000000000000000000000000000000000000000145b8061161057507fffffffff0000000000000000000000000000000000000000000000000000000082167f150b7a0200000000000000000000000000000000000000000000000000000000145b8061165c57507fffffffff0000000000000000000000000000000000000000000000000000000082167fc0ee0b8a00000000000000000000000000000000000000000000000000000000145b1561166957506001919050565b6105d682611bea565b60006105d682611c46565b60006040518284823760008084838989f49695505050505050565b6000604051828482376000808483898b8af1979650505050505050565b60603d604051915060208201818101604052818352816000823e505090565b82156116e257805160208201fd5b7f3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd78282604051611713929190612c05565b60405180910390a1505050565b60008060005b83811015611bad57600181019085013560f81c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81016117c757601582019186013560f881901c9060581c73ffffffffffffffffffffffffffffffffffffffff81169074ff0000000000000000000000000000000000000000168117856117ad57806117bc565b60008681526020829052604090205b955050505050611726565b8061185d5760018201918681013560f81c9060430160006117f38a6117ee84888c8e612b72565b611c5f565b60ff841697909701969194508491905060a083901b74ff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff821617866118425780611851565b60008781526020829052604090205b96505050505050611726565b60028103611985576000808784013560f881901c9060581c73ffffffffffffffffffffffffffffffffffffffff16601586019550909250905060008885013560e81c600386018162ffffff1691508096508192505050600081860190506118d68b848c8c8a9086926118d193929190612b72565b611f22565b61191e578a836118e883898d8f612b72565b6040517f9a9462320000000000000000000000000000000000000000000000000000000081526004016106249493929190612c79565b60ff8416979097019694508460a084901b74ff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff841617876119695780611978565b60008881526020829052604090205b9750505050505050611726565b600381036119b8576020820191860135836119a057806119af565b60008481526020829052604090205b93505050611726565b60048103611a04576003808301928781013560e81c91908201016000806119e58b6112e085898d8f612b72565b6000988952602052604090972096909701965090935061172692505050565b60068103611b0c5760008287013560f81c60018401935060ff16905060008784013560f01c60028501945061ffff16905060008885013560e81c600386018162ffffff169150809650819250505060008186019050600080611a728d8d8d8b9087926112e093929190612b72565b93985088939092509050848210611a8857988501985b604080517f53657175656e6365206e657374656420636f6e6669673a0a0000000000000000602080830191909152603882018490526058820188905260788083018a9052835180840390910181526098909201909252805191012089611aee5780611afd565b60008a81526020829052604090205b99505050505050505050611726565b60058103611b78576020820191860135878103611b47577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff94505b6000611b52826120cf565b905084611b5f5780611b6e565b60008581526020829052604090205b9450505050611726565b6040517fb2505f7c00000000000000000000000000000000000000000000000000000000815260048101829052602401610624565b50935093915050565b7f8713a7c4465f6fbee2b6e9d6646d1d9f83fec929edfc4baf661f3c865bdd04d160009081526020829052604081206105d6565b60007ffda4dd44000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831601611c3d57506001919050565b6105d68261210a565b6000611c5182612166565b806105d65750600192915050565b600060428214611c9f5782826040517f2ee17a3d000000000000000000000000000000000000000000000000000000008152600401610624929190612cb9565b6000611cb8611caf600185612ccd565b85013560f81c90565b60ff169050604084013560f81c843560208601357f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0811115611d2c578686826040517fad4aac7600000000000000000000000000000000000000000000000000000000815260040161062493929190612ce0565b8260ff16601b14158015611d4457508260ff16601c14155b15611d81578686846040517fe578897e00000000000000000000000000000000000000000000000000000000815260040161062493929190612d04565b60018403611dee576040805160008152602081018083528a905260ff851691810191909152606081018390526080810182905260019060a0015b6020604051602081039080840390855afa158015611ddd573d6000803e3d6000fd5b505050602060405103519450611ec6565b60028403611e8b576040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101899052600190605c01604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff861690820152606081018490526080810183905260a001611dbb565b86868560016040517f9dfba8520000000000000000000000000000000000000000000000000000000081526004016106249493929190612d2b565b73ffffffffffffffffffffffffffffffffffffffff8516611f175786866040517f6c1719d2000000000000000000000000000000000000000000000000000000008152600401610624929190612cb9565b505050509392505050565b6000808383611f32600182612ccd565b818110611f4157611f41612a85565b919091013560f81c9150506001811480611f5b5750600281145b15611fa0578473ffffffffffffffffffffffffffffffffffffffff16611f82878686611c5f565b73ffffffffffffffffffffffffffffffffffffffff161491506120c6565b6003810361208b5773ffffffffffffffffffffffffffffffffffffffff8516631626ba7e8786600087611fd4600182612ccd565b92611fe193929190612b72565b6040518463ffffffff1660e01b8152600401611fff93929190612a6b565b602060405180830381865afa15801561201c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120409190612d57565b7fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e000000000000000000000000000000000000000000000000000000001491506120c6565b83838260006040517f9dfba8520000000000000000000000000000000000000000000000000000000081526004016106249493929190612d2b565b50949350505050565b6040517f53657175656e636520737461746963206469676573743a0a0000000000000000602082015260388101829052600090605801611046565b60007fe4a77bbc000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083160161215d57506001919050565b6105d682612199565b600081158015906105d65750507fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8541490565b60007fae9fa280000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316016121ec57506001919050565b6105d68260007fffffffff0000000000000000000000000000000000000000000000000000000082167fac6a444e00000000000000000000000000000000000000000000000000000000148061228357507fffffffff0000000000000000000000000000000000000000000000000000000082167f36e7817500000000000000000000000000000000000000000000000000000000145b1561229057506001919050565b7f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316146105d6565b8183823760009101908152919050565b7fffffffff000000000000000000000000000000000000000000000000000000008116811461063657600080fd5b60006020828403121561232a57600080fd5b813561067f816122ea565b803573ffffffffffffffffffffffffffffffffffffffff8116811461235957600080fd5b919050565b60006020828403121561237057600080fd5b61067f82612335565b60008083601f84011261238b57600080fd5b50813567ffffffffffffffff8111156123a357600080fd5b6020830191508360208285010111156123bb57600080fd5b9250929050565b6000806000806000608086880312156123da57600080fd5b6123e386612335565b94506123f160208701612335565b935060408601359250606086013567ffffffffffffffff81111561241457600080fd5b61242088828901612379565b969995985093965092949392505050565b60008060006040848603121561244657600080fd5b83359250602084013567ffffffffffffffff81111561246457600080fd5b61247086828701612379565b9497909650939450505050565b6000806000806040858703121561249357600080fd5b843567ffffffffffffffff808211156124ab57600080fd5b6124b788838901612379565b909650945060208701359150808211156124d057600080fd5b506124dd87828801612379565b95989497509550505050565b6000602082840312156124fb57600080fd5b5035919050565b60008083601f84011261251457600080fd5b50813567ffffffffffffffff81111561252c57600080fd5b6020830191508360208260051b85010111156123bb57600080fd5b6000806020838503121561255a57600080fd5b823567ffffffffffffffff81111561257157600080fd5b61257d85828601612502565b90969095509350505050565b6000806000806000606086880312156125a157600080fd5b853567ffffffffffffffff808211156125b957600080fd5b6125c589838a01612502565b90975095506020880135945060408801359150808211156125e557600080fd5b5061242088828901612379565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561263357600080fd5b813567ffffffffffffffff8082111561264b57600080fd5b818401915084601f83011261265f57600080fd5b813581811115612671576126716125f2565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156126b7576126b76125f2565b816040528281528760208487010111156126d057600080fd5b826020860160208301376000928101602001929092525095945050505050565b6000806040838503121561270357600080fd5b823561270e816122ea565b915061271c60208401612335565b90509250929050565b60008060008060008060008060a0898b03121561274157600080fd5b61274a89612335565b975061275860208a01612335565b9650604089013567ffffffffffffffff8082111561277557600080fd5b6127818c838d01612502565b909850965060608b013591508082111561279a57600080fd5b6127a68c838d01612502565b909650945060808b01359150808211156127bf57600080fd5b506127cc8b828c01612379565b999c989b5096995094979396929594505050565b60008060008060008060a087890312156127f957600080fd5b61280287612335565b955061281060208801612335565b94506040870135935060608701359250608087013567ffffffffffffffff81111561283a57600080fd5b61284689828a01612379565b979a9699509497509295939492505050565b8035801515811461235957600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b81835260006020808501808196508560051b810191508460005b878110156129f357828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4188360301811261290a57600080fd5b870160c061291782612858565b15158652612926878301612858565b15158688015260408281013590870152606073ffffffffffffffffffffffffffffffffffffffff612958828501612335565b16908701526080828101359087015260a080830135368490037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe101811261299e57600080fd5b90920187810192903567ffffffffffffffff8111156129bc57600080fd5b8036038413156129cb57600080fd5b82828901526129dd8389018286612868565b9c89019c975050509286019250506001016128cb565b5091979650505050505050565b60408152600560408201527f73656c663a00000000000000000000000000000000000000000000000000000060608201526080602082015260006106ee6080830184866128b1565b838152604060208201526000612a626040830184866128b1565b95945050505050565b838152604060208201526000612a62604083018486612868565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff41833603018112612ae857600080fd5b9190910192915050565b600060208284031215612b0457600080fd5b61067f82612858565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112612b4257600080fd5b83018035915067ffffffffffffffff821115612b5d57600080fd5b6020019150368190038213156123bb57600080fd5b60008085851115612b8257600080fd5b83861115612b8f57600080fd5b5050820193919092039150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808201808211156105d6576105d6612b9c565b606081526000612bf2606083018688612868565b6020830194909452506040015292915050565b82815260006020604081840152835180604085015260005b81811015612c3957858101830151858201606001528201612c1d565b5060006060828601015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116850101925050509392505050565b84815273ffffffffffffffffffffffffffffffffffffffff84166020820152606060408201526000612caf606083018486612868565b9695505050505050565b6020815260006106ee602083018486612868565b818103818111156105d6576105d6612b9c565b604081526000612cf4604083018587612868565b9050826020830152949350505050565b604081526000612d18604083018587612868565b905060ff83166020830152949350505050565b606081526000612d3f606083018688612868565b60208301949094525090151560409091015292915050565b600060208284031215612d6957600080fd5b815161067f816122ea56fea2646970667358221220d1c64e83cb54c2e1824f98a6e0664734329329620cf112737055416b4d68ea6a64736f6c63430008110033", + "deployedBytecode": "0x6080604052600436106101485760003560e01c806357c56d6b116100c057806390042baf11610074578063b93ea7ad11610059578063b93ea7ad146104da578063bc197c81146104fa578063f23a6e61146105425761014f565b806390042baf146104b2578063affed0e0146104c55761014f565b80637a9a1628116100a55780637a9a16281461042a578063853c50681461044a5780638c3f5563146104925761014f565b806357c56d6b146103d657806361c2926c1461040a5761014f565b80631a9b23371161011757806329561426116100fc57806329561426146103735780634fcf3eca1461039357806351605d80146103b35761014f565b80631a9b23371461030e57806320c13b0b146103535761014f565b806301ffc9a714610223578063025b22bc14610258578063150b7a02146102785780631626ba7e146102ee5761014f565b3661014f57005b600061017e6000357fffffffff0000000000000000000000000000000000000000000000000000000016610588565b905073ffffffffffffffffffffffffffffffffffffffff811615610221576000808273ffffffffffffffffffffffffffffffffffffffff166000366040516101c79291906122da565b600060405180830381855af49150503d8060008114610202576040519150601f19603f3d011682016040523d82523d6000602084013e610207565b606091505b50915091508161021957805160208201fd5b805160208201f35b005b34801561022f57600080fd5b5061024361023e366004612318565b6105dc565b60405190151581526020015b60405180910390f35b34801561026457600080fd5b5061022161027336600461235e565b6105e7565b34801561028457600080fd5b506102bd6102933660046123c2565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b6040517fffffffff00000000000000000000000000000000000000000000000000000000909116815260200161024f565b3480156102fa57600080fd5b506102bd610309366004612431565b610639565b34801561031a57600080fd5b5061032e610329366004612318565b610686565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161024f565b34801561035f57600080fd5b506102bd61036e36600461247d565b610691565b34801561037f57600080fd5b5061022161038e3660046124e9565b6106f6565b34801561039f57600080fd5b506102216103ae366004612318565b610740565b3480156103bf57600080fd5b506103c861086f565b60405190815260200161024f565b3480156103e257600080fd5b506103c87f8713a7c4465f6fbee2b6e9d6646d1d9f83fec929edfc4baf661f3c865bdd04d181565b34801561041657600080fd5b50610221610425366004612547565b61089e565b34801561043657600080fd5b50610221610445366004612589565b610924565b34801561045657600080fd5b5061046a610465366004612431565b6109ba565b604080519586526020860194909452928401919091526060830152608082015260a00161024f565b34801561049e57600080fd5b506103c86104ad3660046124e9565b610b82565b61032e6104c0366004612621565b610bae565b3480156104d157600080fd5b506103c8610c4a565b3480156104e657600080fd5b506102216104f53660046126f0565b610c56565b34801561050657600080fd5b506102bd610515366004612725565b7fbc197c810000000000000000000000000000000000000000000000000000000098975050505050505050565b34801561054e57600080fd5b506102bd61055d3660046127e0565b7ff23a6e61000000000000000000000000000000000000000000000000000000009695505050505050565b60006105d67fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1207fffffffff000000000000000000000000000000000000000000000000000000008416610d9b565b92915050565b60006105d682610df9565b33301461062d576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044015b60405180910390fd5b61063681610e55565b50565b600080610647858585610f10565b509050801561067957507f1626ba7e00000000000000000000000000000000000000000000000000000000905061067f565b50600090505b9392505050565b60006105d682610588565b6000806106b686866040516106a79291906122da565b60405180910390208585610f10565b50905080156106e857507f20c13b0b0000000000000000000000000000000000000000000000000000000090506106ee565b50600090505b949350505050565b333014610737576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610624565b61063681610f4e565b333014610781576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610624565b600061078c82610588565b73ffffffffffffffffffffffffffffffffffffffff16036107fd576040517f1c3812cc0000000000000000000000000000000000000000000000000000000081527fffffffff0000000000000000000000000000000000000000000000000000000082166004820152602401610624565b604080517fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1206020808301919091527fffffffff00000000000000000000000000000000000000000000000000000000841682840152825180830384018152606090920190925280519101206000905550565b60006108997fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf85490565b905090565b3330146108df576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610624565b600061091283836040516020016108f7929190612a00565b60405160208183030381529060405280519060200120610fde565b905061091f818484611063565b505050565b61092d836111c1565b60008061096585888860405160200161094893929190612a48565b604051602081830303815290604052805190602001208585610f10565b91509150816109a6578084846040517f8f4a234f00000000000000000000000000000000000000000000000000000000815260040161062493929190612a6b565b6109b1818888611063565b50505050505050565b600080600080600080878760008181106109d6576109d6612a85565b909101357fff00000000000000000000000000000000000000000000000000000000000000169150819050610a2c57610a0e89610fde565b9250610a1b8389896112ca565b92985090965094509150610b779050565b7fff0000000000000000000000000000000000000000000000000000000000000081811601610a6b57610a5e89610fde565b9250610a1b83898961131b565b7ffe000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821601610abd57610a5e89611347565b7ffd000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821601610b2157610b118989896113b4565b9550955095509550955050610b77565b6040517f6085cd820000000000000000000000000000000000000000000000000000000081527fff0000000000000000000000000000000000000000000000000000000000000082166004820152602401610624565b939792965093509350565b60006105d67f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e83610d9b565b6000333014610bf1576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610624565b81516020830134f060405173ffffffffffffffffffffffffffffffffffffffff821681529091507fa506ad4e7f05eceba62a023c3219e5bd98a615f4fa87e2afb08a2da5cf62bf0c9060200160405180910390a1919050565b60006108996000610b82565b333014610c97576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610624565b6000610ca283610588565b73ffffffffffffffffffffffffffffffffffffffff1614610d13576040517f5b4d6d6a0000000000000000000000000000000000000000000000000000000081527fffffffff0000000000000000000000000000000000000000000000000000000083166004820152602401610624565b604080517fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1206020808301919091527fffffffff000000000000000000000000000000000000000000000000000000008516828401528251808303840181526060909201909252805191012073ffffffffffffffffffffffffffffffffffffffff821690555050565b6000808383604051602001610dba929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012054949350505050565b60007f6ffbd451000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831601610e4c57506001919050565b6105d682611531565b73ffffffffffffffffffffffffffffffffffffffff81163b610ebb576040517f0c76093700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610624565b610ec3813055565b60405173ffffffffffffffffffffffffffffffffffffffff821681527f310ba5f1d2ed074b51e2eccd052a47ae9ab7c6b800d1fca3db3999d6a592ca03906020015b60405180910390a150565b6000806000806000610f238888886109ba565b50965091945092509050828210801590610f415750610f4181611672565b9450505050935093915050565b80610f85576040517f4294d12700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610fae7fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8829055565b6040518181527f307ed6bd941ee9fc80f369c94af5fa11e25bab5102a6140191756c5474a30bfa90602001610f05565b6040517f190100000000000000000000000000000000000000000000000000000000000060208201524660228201527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b166042820152605681018290526000906076015b604051602081830303815290604052805190602001209050919050565b8060005b818110156111ba573684848381811061108257611082612a85565b90506020028101906110949190612ab4565b90506040810135805a10156110e95782815a6040517f2bb3e3ba000000000000000000000000000000000000000000000000000000008152600481019390935260248301919091526044820152606401610624565b60006110f86020840184612af2565b1561113757611130611110608085016060860161235e565b831561111c578361111e565b5a5b61112b60a0870187612b0d565b61167d565b9050611172565b61116f61114a608085016060860161235e565b6080850135841561115b578461115d565b5a5b61116a60a0880188612b0d565b611698565b90505b801561118e5760405188815260200160405180910390a06111af565b6111af6111a16040850160208601612af2565b896111aa6116b5565b6116d4565b505050600101611067565b5050505050565b606081901c6bffffffffffffffffffffffff821660006111e083610b82565b90508181141580156111f0575060005b15611238576040517f9b6514f4000000000000000000000000000000000000000000000000000000008152600481018490526024810183905260448101829052606401610624565b604080517f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e60208083019190915281830186905282518083038401815260609092019092528051910120600183019081905560408051858152602081018390527f1f180c27086c7a39ea2a7b25239d1ab92348f07ca7bb59d1438fcf527568f881910160405180910390a15050505050565b60008080806112e5876112e0876006818b612b72565b611720565b6000908152873560f01c6020818152604080842084526002909a013560e01c908190529890912090999198509695509350505050565b600080808061133687611331876001818b612b72565b6112ca565b935093509350935093509350935093565b6040517f190100000000000000000000000000000000000000000000000000000000000060208201526000602282018190527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b1660428301526056820183905290607601611046565b6000808080806004600188013560e81c826113cf8383612bcb565b90506113e18b61046583868d8f612b72565b939b50919950975095509350878710156114395761140181848b8d612b72565b89896040517fb006aba00000000000000000000000000000000000000000000000000000000081526004016106249493929190612bde565b8092505b888310156115235760038301928a013560e81c915061145c8383612bcb565b9050600061147e61146c88611bb6565b8c8c8790869261046593929190612b72565b939c50919a50985090915050888810156114d65761149e82858c8e612b72565b8a8a6040517fb006aba00000000000000000000000000000000000000000000000000000000081526004016106249493929190612bde565b848110611519576040517f37daf62b0000000000000000000000000000000000000000000000000000000081526004810182905260248101869052604401610624565b935091508161143d565b505050939792965093509350565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fec6aba500000000000000000000000000000000000000000000000000000000014806115c457507fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e000000000000000000000000000000000000000000000000000000000145b8061161057507fffffffff0000000000000000000000000000000000000000000000000000000082167f150b7a0200000000000000000000000000000000000000000000000000000000145b8061165c57507fffffffff0000000000000000000000000000000000000000000000000000000082167fc0ee0b8a00000000000000000000000000000000000000000000000000000000145b1561166957506001919050565b6105d682611bea565b60006105d682611c46565b60006040518284823760008084838989f49695505050505050565b6000604051828482376000808483898b8af1979650505050505050565b60603d604051915060208201818101604052818352816000823e505090565b82156116e257805160208201fd5b7f3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd78282604051611713929190612c05565b60405180910390a1505050565b60008060005b83811015611bad57600181019085013560f81c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81016117c757601582019186013560f881901c9060581c73ffffffffffffffffffffffffffffffffffffffff81169074ff0000000000000000000000000000000000000000168117856117ad57806117bc565b60008681526020829052604090205b955050505050611726565b8061185d5760018201918681013560f81c9060430160006117f38a6117ee84888c8e612b72565b611c5f565b60ff841697909701969194508491905060a083901b74ff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff821617866118425780611851565b60008781526020829052604090205b96505050505050611726565b60028103611985576000808784013560f881901c9060581c73ffffffffffffffffffffffffffffffffffffffff16601586019550909250905060008885013560e81c600386018162ffffff1691508096508192505050600081860190506118d68b848c8c8a9086926118d193929190612b72565b611f22565b61191e578a836118e883898d8f612b72565b6040517f9a9462320000000000000000000000000000000000000000000000000000000081526004016106249493929190612c79565b60ff8416979097019694508460a084901b74ff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff841617876119695780611978565b60008881526020829052604090205b9750505050505050611726565b600381036119b8576020820191860135836119a057806119af565b60008481526020829052604090205b93505050611726565b60048103611a04576003808301928781013560e81c91908201016000806119e58b6112e085898d8f612b72565b6000988952602052604090972096909701965090935061172692505050565b60068103611b0c5760008287013560f81c60018401935060ff16905060008784013560f01c60028501945061ffff16905060008885013560e81c600386018162ffffff169150809650819250505060008186019050600080611a728d8d8d8b9087926112e093929190612b72565b93985088939092509050848210611a8857988501985b604080517f53657175656e6365206e657374656420636f6e6669673a0a0000000000000000602080830191909152603882018490526058820188905260788083018a9052835180840390910181526098909201909252805191012089611aee5780611afd565b60008a81526020829052604090205b99505050505050505050611726565b60058103611b78576020820191860135878103611b47577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff94505b6000611b52826120cf565b905084611b5f5780611b6e565b60008581526020829052604090205b9450505050611726565b6040517fb2505f7c00000000000000000000000000000000000000000000000000000000815260048101829052602401610624565b50935093915050565b7f8713a7c4465f6fbee2b6e9d6646d1d9f83fec929edfc4baf661f3c865bdd04d160009081526020829052604081206105d6565b60007ffda4dd44000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831601611c3d57506001919050565b6105d68261210a565b6000611c5182612166565b806105d65750600192915050565b600060428214611c9f5782826040517f2ee17a3d000000000000000000000000000000000000000000000000000000008152600401610624929190612cb9565b6000611cb8611caf600185612ccd565b85013560f81c90565b60ff169050604084013560f81c843560208601357f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0811115611d2c578686826040517fad4aac7600000000000000000000000000000000000000000000000000000000815260040161062493929190612ce0565b8260ff16601b14158015611d4457508260ff16601c14155b15611d81578686846040517fe578897e00000000000000000000000000000000000000000000000000000000815260040161062493929190612d04565b60018403611dee576040805160008152602081018083528a905260ff851691810191909152606081018390526080810182905260019060a0015b6020604051602081039080840390855afa158015611ddd573d6000803e3d6000fd5b505050602060405103519450611ec6565b60028403611e8b576040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101899052600190605c01604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff861690820152606081018490526080810183905260a001611dbb565b86868560016040517f9dfba8520000000000000000000000000000000000000000000000000000000081526004016106249493929190612d2b565b73ffffffffffffffffffffffffffffffffffffffff8516611f175786866040517f6c1719d2000000000000000000000000000000000000000000000000000000008152600401610624929190612cb9565b505050509392505050565b6000808383611f32600182612ccd565b818110611f4157611f41612a85565b919091013560f81c9150506001811480611f5b5750600281145b15611fa0578473ffffffffffffffffffffffffffffffffffffffff16611f82878686611c5f565b73ffffffffffffffffffffffffffffffffffffffff161491506120c6565b6003810361208b5773ffffffffffffffffffffffffffffffffffffffff8516631626ba7e8786600087611fd4600182612ccd565b92611fe193929190612b72565b6040518463ffffffff1660e01b8152600401611fff93929190612a6b565b602060405180830381865afa15801561201c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120409190612d57565b7fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e000000000000000000000000000000000000000000000000000000001491506120c6565b83838260006040517f9dfba8520000000000000000000000000000000000000000000000000000000081526004016106249493929190612d2b565b50949350505050565b6040517f53657175656e636520737461746963206469676573743a0a0000000000000000602082015260388101829052600090605801611046565b60007fe4a77bbc000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083160161215d57506001919050565b6105d682612199565b600081158015906105d65750507fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8541490565b60007fae9fa280000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316016121ec57506001919050565b6105d68260007fffffffff0000000000000000000000000000000000000000000000000000000082167fac6a444e00000000000000000000000000000000000000000000000000000000148061228357507fffffffff0000000000000000000000000000000000000000000000000000000082167f36e7817500000000000000000000000000000000000000000000000000000000145b1561229057506001919050565b7f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316146105d6565b8183823760009101908152919050565b7fffffffff000000000000000000000000000000000000000000000000000000008116811461063657600080fd5b60006020828403121561232a57600080fd5b813561067f816122ea565b803573ffffffffffffffffffffffffffffffffffffffff8116811461235957600080fd5b919050565b60006020828403121561237057600080fd5b61067f82612335565b60008083601f84011261238b57600080fd5b50813567ffffffffffffffff8111156123a357600080fd5b6020830191508360208285010111156123bb57600080fd5b9250929050565b6000806000806000608086880312156123da57600080fd5b6123e386612335565b94506123f160208701612335565b935060408601359250606086013567ffffffffffffffff81111561241457600080fd5b61242088828901612379565b969995985093965092949392505050565b60008060006040848603121561244657600080fd5b83359250602084013567ffffffffffffffff81111561246457600080fd5b61247086828701612379565b9497909650939450505050565b6000806000806040858703121561249357600080fd5b843567ffffffffffffffff808211156124ab57600080fd5b6124b788838901612379565b909650945060208701359150808211156124d057600080fd5b506124dd87828801612379565b95989497509550505050565b6000602082840312156124fb57600080fd5b5035919050565b60008083601f84011261251457600080fd5b50813567ffffffffffffffff81111561252c57600080fd5b6020830191508360208260051b85010111156123bb57600080fd5b6000806020838503121561255a57600080fd5b823567ffffffffffffffff81111561257157600080fd5b61257d85828601612502565b90969095509350505050565b6000806000806000606086880312156125a157600080fd5b853567ffffffffffffffff808211156125b957600080fd5b6125c589838a01612502565b90975095506020880135945060408801359150808211156125e557600080fd5b5061242088828901612379565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561263357600080fd5b813567ffffffffffffffff8082111561264b57600080fd5b818401915084601f83011261265f57600080fd5b813581811115612671576126716125f2565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156126b7576126b76125f2565b816040528281528760208487010111156126d057600080fd5b826020860160208301376000928101602001929092525095945050505050565b6000806040838503121561270357600080fd5b823561270e816122ea565b915061271c60208401612335565b90509250929050565b60008060008060008060008060a0898b03121561274157600080fd5b61274a89612335565b975061275860208a01612335565b9650604089013567ffffffffffffffff8082111561277557600080fd5b6127818c838d01612502565b909850965060608b013591508082111561279a57600080fd5b6127a68c838d01612502565b909650945060808b01359150808211156127bf57600080fd5b506127cc8b828c01612379565b999c989b5096995094979396929594505050565b60008060008060008060a087890312156127f957600080fd5b61280287612335565b955061281060208801612335565b94506040870135935060608701359250608087013567ffffffffffffffff81111561283a57600080fd5b61284689828a01612379565b979a9699509497509295939492505050565b8035801515811461235957600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b81835260006020808501808196508560051b810191508460005b878110156129f357828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4188360301811261290a57600080fd5b870160c061291782612858565b15158652612926878301612858565b15158688015260408281013590870152606073ffffffffffffffffffffffffffffffffffffffff612958828501612335565b16908701526080828101359087015260a080830135368490037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe101811261299e57600080fd5b90920187810192903567ffffffffffffffff8111156129bc57600080fd5b8036038413156129cb57600080fd5b82828901526129dd8389018286612868565b9c89019c975050509286019250506001016128cb565b5091979650505050505050565b60408152600560408201527f73656c663a00000000000000000000000000000000000000000000000000000060608201526080602082015260006106ee6080830184866128b1565b838152604060208201526000612a626040830184866128b1565b95945050505050565b838152604060208201526000612a62604083018486612868565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff41833603018112612ae857600080fd5b9190910192915050565b600060208284031215612b0457600080fd5b61067f82612858565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112612b4257600080fd5b83018035915067ffffffffffffffff821115612b5d57600080fd5b6020019150368190038213156123bb57600080fd5b60008085851115612b8257600080fd5b83861115612b8f57600080fd5b5050820193919092039150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808201808211156105d6576105d6612b9c565b606081526000612bf2606083018688612868565b6020830194909452506040015292915050565b82815260006020604081840152835180604085015260005b81811015612c3957858101830151858201606001528201612c1d565b5060006060828601015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116850101925050509392505050565b84815273ffffffffffffffffffffffffffffffffffffffff84166020820152606060408201526000612caf606083018486612868565b9695505050505050565b6020815260006106ee602083018486612868565b818103818111156105d6576105d6612b9c565b604081526000612cf4604083018587612868565b9050826020830152949350505050565b604081526000612d18604083018587612868565b905060ff83166020830152949350505050565b606081526000612d3f606083018688612868565b60208301949094525090151560409091015292915050565b600060208284031215612d6957600080fd5b815161067f816122ea56fea2646970667358221220d1c64e83cb54c2e1824f98a6e0664734329329620cf112737055416b4d68ea6a64736f6c63430008110033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/packages/estimator/src/builds/index.ts b/packages/estimator/src/builds/index.ts new file mode 100644 index 000000000..630bef12c --- /dev/null +++ b/packages/estimator/src/builds/index.ts @@ -0,0 +1 @@ +export * from './MainModuleGasEstimation' diff --git a/packages/estimator/src/estimator.ts b/packages/estimator/src/estimator.ts index ccfff9b59..f6ef89bff 100644 --- a/packages/estimator/src/estimator.ts +++ b/packages/estimator/src/estimator.ts @@ -1,15 +1,15 @@ -import { WalletConfig } from '@0xsequence/config' -import { WalletContext } from '@0xsequence/network' -import { Transaction } from '@0xsequence/transactions' import { ethers } from 'ethers' +import { commons, v2 } from '@0xsequence/core' export interface Estimator { estimateGasLimits( - config: WalletConfig, - context: WalletContext, - ...transactions: Transaction[] + address: string, + config: v2.config.WalletConfig, + context: commons.context.WalletContext, + nonce: ethers.BigNumberish, + ...transactions: commons.transaction.Transaction[] ): Promise<{ - transactions:Transaction[], + transactions: commons.transaction.Transaction[], total: ethers.BigNumber }> } diff --git a/packages/estimator/src/overwriter-estimator.ts b/packages/estimator/src/overwriter-estimator.ts index 167e9ac3f..ba038e054 100644 --- a/packages/estimator/src/overwriter-estimator.ts +++ b/packages/estimator/src/overwriter-estimator.ts @@ -1,7 +1,7 @@ import { ethers } from 'ethers' -import { isBigNumberish, Optionals } from '@0xsequence/utils' +import { getDefaultConnectionInfo, isBigNumberish, Optionals } from '@0xsequence/utils' -const GasEstimator = require("@0xsequence/wallet-contracts/artifacts/contracts/modules/utils/GasEstimator.sol/GasEstimator.json") +const GasEstimator = require('@0xsequence/wallet-contracts/artifacts/contracts/modules/utils/GasEstimator.sol/GasEstimator.json') function toQuantity(number: ethers.BigNumberish | string): string { if (isBigNumberish(number)) { @@ -24,9 +24,9 @@ function toHexNumber(number: ethers.BigNumberish): string { } export type OverwriterEstimatorOptions = { - rpc: string | ethers.providers.JsonRpcProvider, - dataZeroCost?: number, - dataOneCost?: number, + rpc: string | ethers.providers.JsonRpcProvider + dataZeroCost?: number + dataOneCost?: number baseCost?: number } @@ -41,79 +41,92 @@ export class OverwriterEstimator { public options: Required constructor(options: OverwriterEstimatorOptions) { - this.provider = typeof(options.rpc) === 'string' ? new ethers.providers.JsonRpcProvider(options.rpc) : options.rpc + this.provider = + typeof options.rpc === 'string' + ? new ethers.providers.StaticJsonRpcProvider(getDefaultConnectionInfo(options.rpc)) + : options.rpc this.options = { ...OverwriterEstimatorDefaults, ...options } } txBaseCost(data: ethers.BytesLike): number { const bytes = ethers.utils.arrayify(data) - return bytes.reduce((p, c) => c == 0 ? p.add(this.options.dataZeroCost) : p.add(this.options.dataOneCost), ethers.constants.Zero).add(this.options.baseCost).toNumber() + return bytes + .reduce((p, c) => (c == 0 ? p.add(this.options.dataZeroCost) : p.add(this.options.dataOneCost)), ethers.constants.Zero) + .add(this.options.baseCost) + .toNumber() } async estimate(args: { - to: string, - from?: string, - data?: ethers.BytesLike, - gasPrice?: ethers.BigNumberish, - gas?: ethers.BigNumberish, + to: string + from?: string + data?: ethers.BytesLike + gasPrice?: ethers.BigNumberish + gas?: ethers.BigNumberish overwrites?: { [address: string]: { - code?: string, - balance?: ethers.BigNumberish, - nonce?: ethers.BigNumberish, + code?: string + balance?: ethers.BigNumberish + nonce?: ethers.BigNumberish stateDiff?: { - key: string, - value: string, - }[], + key: string + value: string + }[] state?: { - key: string, - value: string, + key: string + value: string }[] } - }, + } blockTag?: string | ethers.BigNumberish }): Promise { - const blockTag = args.blockTag ? toQuantity(args.blockTag) : "latest" + const blockTag = args.blockTag ? toQuantity(args.blockTag) : 'latest' const data = args.data ? args.data : [] const from = args.from ? ethers.utils.getAddress(args.from) : ethers.Wallet.createRandom().address const gasEstimatorInterface = new ethers.utils.Interface(GasEstimator.abi) - const encodedEstimate = gasEstimatorInterface.encodeFunctionData("estimate", [args.to, data]) + const encodedEstimate = gasEstimatorInterface.encodeFunctionData('estimate', [args.to, data]) - const providedOverwrites = args.overwrites ? Object.keys(args.overwrites).reduce((p, a) => { - const address = ethers.utils.getAddress(a) - const o = args.overwrites![a] + const providedOverwrites = args.overwrites + ? Object.keys(args.overwrites).reduce((p, a) => { + const address = ethers.utils.getAddress(a) + const o = args.overwrites![a] - if (address === from) { - throw Error("Can't overwrite from address values") - } + if (address === from) { + throw Error("Can't overwrite from address values") + } - return { - ...p, - [address]: { - code: o.code ? ethers.utils.hexlify(o.code) : undefined, - nonce: o.nonce ? toHexNumber(o.nonce) : undefined, - balance: o.balance ? toHexNumber(o.balance) : undefined, - state: o.state ? o.state : undefined, - stateDiff: o.stateDiff ? o.stateDiff : undefined + return { + ...p, + [address]: { + code: o.code ? ethers.utils.hexlify(o.code) : undefined, + nonce: o.nonce ? toHexNumber(o.nonce) : undefined, + balance: o.balance ? toHexNumber(o.balance) : undefined, + state: o.state ? o.state : undefined, + stateDiff: o.stateDiff ? o.stateDiff : undefined + } } - } - }, {}) : {} + }, {}) + : {} - const overwrites = { ...providedOverwrites, + const overwrites = { + ...providedOverwrites, [from]: { code: GasEstimator.deployedBytecode } } - const response = await this.provider.send("eth_call", [{ - to: from, - data: encodedEstimate, - gasPrice: args.gasPrice, - gas: args.gas, - }, blockTag, overwrites]) - - const decoded = gasEstimatorInterface.decodeFunctionResult("estimate", response) + const response = await this.provider.send('eth_call', [ + { + to: from, + data: encodedEstimate, + gasPrice: args.gasPrice, + gas: args.gas + }, + blockTag, + overwrites + ]) + + const decoded = gasEstimatorInterface.decodeFunctionResult('estimate', response) if (!decoded.success) { throw Error(`Failed gas estimation with ${tryDecodeError(decoded.result)}`) diff --git a/packages/estimator/src/overwriter-sequence-estimator.ts b/packages/estimator/src/overwriter-sequence-estimator.ts index 3efbeffb6..7ed8c6f5f 100644 --- a/packages/estimator/src/overwriter-sequence-estimator.ts +++ b/packages/estimator/src/overwriter-sequence-estimator.ts @@ -1,104 +1,128 @@ -import { WalletContext } from '@0xsequence/network' -import { WalletConfig, addressOf, encodeSignature, DecodedFullSigner, DecodedEOASigner } from '@0xsequence/config' -import { readSequenceNonce, sequenceTxAbiEncode, Transaction } from '@0xsequence/transactions' import { OverwriterEstimator } from './overwriter-estimator' import { walletContracts } from '@0xsequence/abi' import { ethers, utils } from 'ethers' import { Estimator } from './estimator' - -const MainModuleGasEstimation = require("@0xsequence/wallet-contracts/artifacts/contracts/modules/MainModuleGasEstimation.sol/MainModuleGasEstimation.json") +import { commons, v2 } from '@0xsequence/core' +import { mainModuleGasEstimation } from './builds' export class OverwriterSequenceEstimator implements Estimator { constructor(public estimator: OverwriterEstimator) {} - async estimateGasLimits(config: WalletConfig, context: WalletContext, ...transactions: Transaction[]): Promise<{ transactions:Transaction[], total: ethers.BigNumber }> { - const wallet = addressOf(config, context) + async estimateGasLimits( + address: string, + config: v2.config.WalletConfig, + context: commons.context.WalletContext, + nonce: ethers.BigNumberish, + ...transactions: commons.transaction.Transaction[] + ): Promise<{ transactions: commons.transaction.Transaction[], total: ethers.BigNumber }> { const walletInterface = new utils.Interface(walletContracts.mainModule.abi) - // Get non-eoa signers - // required for computing worse case scenario - const signers = await Promise.all(config.signers.map(async (s, i) => ({ - ...s, - index: i, - isEOA: ethers.utils.arrayify(await this.estimator.provider.getCode(s.address)).length === 0 - }))) + const allSigners = await Promise.all( + v2.config.signersOf(config.tree).map(async (s, i) => ({ + index: i, + address: s.address, + weight: ethers.BigNumber.from(s.weight), + isEOA: await this.estimator.provider.getCode(s.address) + .then((c) => ethers.utils.arrayify(c).length === 0) + })) + ) - // Define designated signers - let weightSum = 0 + let totalWeight = 0 - const definedSigners = signers - // Contract signers sign first, then lowest weight signers - .sort((a, b) => !a.isEOA && b.isEOA ? -1 : a.isEOA && !b.isEOA ? +1 : a.weight - b.weight) - // Define signers and not signers - .map((s) => { - if (weightSum >= config.threshold) { - return { ...s, signs: false } + // Pick NOT EOA signers until we reach the threshold + // if we can't reach the threshold, then we'll use the lowest weight EOA signers + // TODO: if EOAs have the same weight, then we should pick the ones further apart from each other (in the tree) + const designatedSigners = allSigners + .sort((a, b) => { + if (a.isEOA && !b.isEOA) return 1 + if (!a.isEOA && b.isEOA) return -1 + if (a.weight.eq(b.weight)) return a.index - b.index + return a.weight.sub(b.weight).toNumber() + }) + .filter((s) => { + if (totalWeight >= config.threshold) { + return false + } else { + totalWeight += s.weight.toNumber() + return true } - - weightSum += s.weight - return { ...s, signs: true } }) - .sort((a, b) => a.index - b.index) // Sort back to original configuration // Generate a fake signature, meant to resemble the final signature of the transaction // this "fake" signature is provided to compute a more accurate gas estimation - const stubSignature = encodeSignature({ threshold: config.threshold, signers: await Promise.all(definedSigners.map(async (s) => { - if (!s.signs) return s - + const fakeSignatures = new Map() + for (const s of designatedSigners) { if (s.isEOA) { - return { - weight: s.weight, - signature: (await ethers.Wallet.createRandom().signMessage("")) + '02' - } as DecodedEOASigner - } - - // Assume a 2/3 nested contract signature - // TODO: Improve this, how do we get the nested signer config? - const nestedSignature = encodeSignature({ - threshold: 2, - signers: [{ - address: ethers.Wallet.createRandom().address, - weight: 1 - }, { - signature: (await ethers.Wallet.createRandom().signMessage("")) + '02', - weight: 1 - }, { + fakeSignatures.set(s.address, { signature: (await ethers.Wallet.createRandom().signMessage("")) + '02', - weight: 1 - }] - }) + '03' + isDynamic: false + }) + } else { + // Assume a 2/3 nested contract signature + const signer1 = ethers.Wallet.createRandom() + const signer2 = ethers.Wallet.createRandom() + const signer3 = ethers.Wallet.createRandom() - return { - weight: s.weight, - address: s.address, - signature: nestedSignature - } as DecodedFullSigner - }))}) + const nestedSignature = v2.signature.encodeSigners( + v2.config.ConfigCoder.fromSimple({ + threshold: 2, + checkpoint: 0, + signers: [{ + address: signer1.address, + weight: 1 + }, { + address: signer2.address, + weight: 1 + }, { + address: signer3.address, + weight: 1 + }] + }), + new Map([ + [signer1.address, { signature: (await signer1.signMessage("")) + '02', isDynamic: false }], + [signer2.address, { signature: (await signer2.signMessage("")) + '02', isDynamic: false }] + ]), + [], + 0 + ) + + fakeSignatures.set(s.address, { + signature: nestedSignature.encoded + '03', + isDynamic: true + }) + } + } + + const stubSignature = v2.signature.encodeSigners( + config, + fakeSignatures, + [], + 0 + ).encoded // Use the provided nonce // TODO: Maybe ignore if this fails on the MainModuleGasEstimation // it could help reduce the edge cases for when the gas estimation fails - const nonce = readSequenceNonce(...transactions) - const encoded = sequenceTxAbiEncode(transactions) + const encoded = commons.transaction.sequenceTxAbiEncode(transactions) const sequenceOverwrites = { [context.mainModule]: { - code: MainModuleGasEstimation.deployedBytecode + code: mainModuleGasEstimation.deployedBytecode }, [context.mainModuleUpgradable]: { - code: MainModuleGasEstimation.deployedBytecode + code: mainModuleGasEstimation.deployedBytecode } } const estimates = await Promise.all([ ...encoded.map(async (_, i) => { return this.estimator.estimate({ - to: wallet, + to: address, data: walletInterface.encodeFunctionData(walletInterface.getFunction('execute'), [encoded.slice(0, i), nonce, stubSignature]), overwrites: sequenceOverwrites }) }), this.estimator.estimate({ - to: wallet, // Compute full gas estimation with all transaction + to: address, // Compute full gas estimation with all transaction data: walletInterface.encodeFunctionData(walletInterface.getFunction('execute'), [encoded, nonce, stubSignature]), overwrites: sequenceOverwrites }) diff --git a/packages/estimator/tests/sequence-estimator.spec.ts b/packages/estimator/tests/sequence-estimator.spec.ts index 7a80e011d..e71168e7b 100644 --- a/packages/estimator/tests/sequence-estimator.spec.ts +++ b/packages/estimator/tests/sequence-estimator.spec.ts @@ -1,17 +1,22 @@ -import { CallReceiverMock, HookCallerMock } from '@0xsequence/wallet-contracts' -import { Transaction } from '@0xsequence/transactions' +import { CallReceiverMock, HookCallerMock } from '@0xsequence/wallet-contracts' import { LocalRelayer } from '@0xsequence/relayer' - -import { WalletContext, NetworkConfig } from '@0xsequence/network' -import { ethers, Signer as AbstractSigner, providers } from 'ethers' +import { ethers } from 'ethers' import { configureLogger } from '@0xsequence/utils' +import { commons, v2 } from '@0xsequence/core' import chaiAsPromised from 'chai-as-promised' import * as chai from 'chai' +import { SequenceOrchestratorWrapper, Wallet, WalletV2 } from '@0xsequence/wallet' +import { OverwriterSequenceEstimator } from '../src' +import { OverwriterEstimator } from '../dist/0xsequence-estimator.cjs' +import { encodeData } from '@0xsequence/wallet/tests/utils' +import { context } from '@0xsequence/tests' +import { Orchestrator } from '@0xsequence/signhub' + const CallReceiverMockArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/mocks/CallReceiverMock.sol/CallReceiverMock.json') const HookCallerMockArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/mocks/HookCallerMock.sol/HookCallerMock.json') @@ -19,93 +24,47 @@ const { expect } = chai.use(chaiAsPromised) configureLogger({ logLevel: 'DEBUG', silence: false }) -import { Wallet } from '@0xsequence/wallet' -import { deployWalletContext } from '@0xsequence/wallet/tests/utils/deploy-wallet-context' -import { OverwriterSequenceEstimator } from '../src' -import { OverwriterEstimator } from '../dist/0xsequence-estimator.cjs' -import { encodeData } from '@0xsequence/wallet/tests/utils' - -type EthereumInstance = { - chainId: number - provider: providers.JsonRpcProvider - signer: AbstractSigner -} - describe('Wallet integration', function () { - let ethnode: EthereumInstance - let relayer: LocalRelayer let callReceiver: CallReceiverMock let hookCaller: HookCallerMock - let context: WalletContext - let networks: NetworkConfig[] + let contexts: Awaited> + let provider: ethers.providers.JsonRpcProvider + let signers: ethers.Signer[] let estimator: OverwriterSequenceEstimator before(async () => { - // Provider from hardhat without a server instance const url = 'http://127.0.0.1:10045/' - const provider = new ethers.providers.JsonRpcProvider(url) + provider = new ethers.providers.JsonRpcProvider(url) - ethnode = { - chainId: (await provider.getNetwork()).chainId, - provider: provider, - signer: provider.getSigner() - } - - networks = [ - { - name: 'local', - chainId: ethnode.chainId, - provider: ethnode.provider, - isDefaultChain: true, - isAuthChain: true - } - ] + signers = new Array(8).fill(0).map((_, i) => provider.getSigner(i)) - // Deploy Sequence env - const [factory, mainModule, mainModuleUpgradable, guestModule, sequenceUtils, requireFreshSigner] = await deployWalletContext( - ethnode.signer - ) - - // Create fixed context obj - context = { - factory: factory.address, - mainModule: mainModule.address, - mainModuleUpgradable: mainModuleUpgradable.address, - guestModule: guestModule.address, - sequenceUtils: sequenceUtils.address, - libs: { - requireFreshSigner: requireFreshSigner.address - } - } + contexts = await context.deploySequenceContexts(signers[0]) + relayer = new LocalRelayer(signers[0]) // Deploy call receiver mock callReceiver = (await new ethers.ContractFactory( CallReceiverMockArtifact.abi, CallReceiverMockArtifact.bytecode, - ethnode.signer - ).deploy()) as CallReceiverMock + signers[0] + ).deploy({ gasLimit: 1000000 })) as CallReceiverMock // Deploy hook caller mock hookCaller = (await new ethers.ContractFactory( HookCallerMockArtifact.abi, HookCallerMockArtifact.bytecode, - ethnode.signer - ).deploy()) as HookCallerMock + signers[0] + ).deploy({ gasLimit: 1000000 })) as HookCallerMock // Deploy local relayer - relayer = new LocalRelayer({ signer: ethnode.signer }) + relayer = new LocalRelayer({ signer: signers[0] }) // Create gas estimator - estimator = new OverwriterSequenceEstimator(new OverwriterEstimator({ rpc: ethnode.provider })) + estimator = new OverwriterSequenceEstimator(new OverwriterEstimator({ rpc: provider })) }) - function sleep(ms: number) { - return new Promise(resolve => setTimeout(resolve, ms)) - } - beforeEach(async () => { await callReceiver.setRevertFlag(false) await callReceiver.testCall(0, []) @@ -116,9 +75,25 @@ describe('Wallet integration', function () { { name: 'single signer wallet', getWallet: async () => { - const pk = ethers.utils.randomBytes(32) - const wallet = await Wallet.singleOwner(pk, context) - return wallet.connect(ethnode.provider, relayer) + // const pk = ethers.utils.randomBytes(32) + // const wallet = await Wallet.singleOwner(pk, context) + // return wallet.connect(ethnode.provider, relayer) + const signer = ethers.Wallet.createRandom() + const config = v2.config.ConfigCoder.fromSimple({ + threshold: 1, + checkpoint: 0, + signers: [{ weight: 1, address: signer.address }] + }) + + return Wallet.newWallet({ + context: contexts[2], + coders: v2.coders, + config, + provider, + relayer, + orchestrator: new Orchestrator([signer]), + chainId: provider.network.chainId + }) } }, { @@ -126,59 +101,94 @@ describe('Wallet integration', function () { getWallet: async () => { const signers = new Array(4).fill(0).map(() => ethers.Wallet.createRandom()) - const config = { + const config = v2.config.ConfigCoder.fromSimple({ threshold: 3, + checkpoint: 100, signers: signers.map(s => ({ weight: 1, address: s.address })) - } + }) - const wallet = new Wallet({ context, config }, ...signers.slice(0, 3)) - return wallet.connect(ethnode.provider, relayer) + return Wallet.newWallet({ + context: contexts[2], + coders: v2.coders, + config, + provider, + relayer, + orchestrator: new Orchestrator([signers[0], signers[1], signers[2]]), + chainId: provider.network.chainId + }) } }, + // TODO: This test fails because the gas estimation uses signers that are packed together + // in the tree, we need to modify the estimator so it picks a sparse set of signers + // { + // name: 'many multiple signers wallet', + // getWallet: async () => { + // const signers = new Array(111).fill(0).map(() => ethers.Wallet.createRandom()) + + // const config = v2.config.ConfigCoder.fromSimple({ + // threshold: 11, + // checkpoint: 100, + // signers: signers.map(s => ({ weight: 1, address: s.address })) + // }) + + // console.log(JSON.stringify(config, null, 2)) + + // return Wallet.newWallet({ + // context: contexts[2], + // coders: v2.coders, + // config, + // provider, + // relayer, + // orchestrator: new Orchestrator(signers.slice(0, 12)), + // chainId: provider.network.chainId + // }) + // } + // }, { - name: 'many multiple signers wallet', + name: 'nested wallet', getWallet: async () => { - const signers = new Array(111).fill(0).map(() => ethers.Wallet.createRandom()) + const EOAsigners = new Array(3).fill(0).map(() => ethers.Wallet.createRandom()) - const config = { - threshold: 11, - signers: signers.map(s => ({ weight: 1, address: s.address })) - } + const nestedSigners = new Array(3).fill(0).map(() => ethers.Wallet.createRandom()) + const nestedConfig = v2.config.ConfigCoder.fromSimple({ + threshold: 2, + checkpoint: 0, + signers: nestedSigners.map(s => ({ weight: 1, address: s.address })) + }) - const wallet = new Wallet({ context, config }, ...signers.slice(0, 12)) - return wallet.connect(ethnode.provider, relayer) - } - }, - { - name: 'nested wallet', - getWallet: async () => { - const EOAsigners = new Array(2).fill(0).map(() => ethers.Wallet.createRandom()) - - const NestedSigners = await Promise.all( - new Array(2).fill(0).map(async () => { - const signers = new Array(3).fill(0).map(() => ethers.Wallet.createRandom()) - const config = { - threshold: 2, - signers: signers.map(s => ({ weight: 1, address: s.address })) - } - const wallet = new Wallet({ context: context, config: config }, ...signers.slice(0, 2)).connect( - ethnode.provider, - relayer - ) - await relayer.deployWallet(wallet.config, wallet.context) - return wallet.connect(ethnode.provider, relayer) - }) - ) + const nestedWallet = Wallet.newWallet({ + context: contexts[2], + coders: v2.coders, + config: nestedConfig, + provider, + relayer, + orchestrator: new Orchestrator([nestedSigners[0], nestedSigners[1]]), + chainId: provider.network.chainId + }) + + await nestedWallet.deploy() - const signers = [...NestedSigners, ...EOAsigners] + const signers = [nestedWallet, ...EOAsigners] - const config = { + const config = v2.config.ConfigCoder.fromSimple({ threshold: 3, - signers: signers.map(s => ({ weight: 1, address: s.address })) - } + checkpoint: 0, + signers: signers.map((s) => ({ weight: 1, address: s.address })) + }) - const wallet = new Wallet({ context, config }, ...signers) - return wallet.connect(ethnode.provider, relayer) + return Wallet.newWallet({ + context: contexts[2], + coders: v2.coders, + config, + provider, + relayer, + orchestrator: new Orchestrator([ + new SequenceOrchestratorWrapper(nestedWallet), + EOAsigners[0], + EOAsigners[1] + ]), + chainId: provider.network.chainId + }) } }, { @@ -189,31 +199,39 @@ describe('Wallet integration', function () { const signers = [...signersA, ...signersB] - const config = { + const config = v2.config.ConfigCoder.fromSimple({ threshold: 5, + checkpoint: 0, signers: signers.map((s, i) => ({ weight: i <= signersA.length ? 1 : 10, address: s.address })) - } + }) - const wallet = new Wallet({ context, config }, ...signersA) - return wallet.connect(ethnode.provider, relayer) + return Wallet.newWallet({ + context: contexts[2], + coders: v2.coders, + config, + provider, + relayer, + orchestrator: new Orchestrator(signersA), + chainId: provider.network.chainId + }) } } ] options.map(o => { describe(`with ${o.name}`, () => { - let wallet: Wallet + let wallet: WalletV2 beforeEach(async () => { wallet = await o.getWallet() }) describe('with deployed wallet', () => { - let txs: Transaction[] + let txs: commons.transaction.Transaction[] beforeEach(async () => { await callReceiver.testCall(0, []) - await relayer.deployWallet(wallet.config, wallet.context) + await wallet.deploy() }) describe('a single transaction', () => { @@ -225,14 +243,13 @@ describe('Wallet integration', function () { gasLimit: 0, to: callReceiver.address, value: ethers.constants.Zero, - data: await encodeData(callReceiver, 'testCall', 14442, '0x112233'), - nonce: 0 + data: await encodeData(callReceiver, 'testCall', 14442, '0x112233') } ] }) it('should use estimated gas for a single transaction', async () => { - const estimation = await estimator.estimateGasLimits(wallet.config, wallet.context, ...txs) + const estimation = await estimator.estimateGasLimits(wallet.address, wallet.config, wallet.context, 0, ...txs) const realTx = await (await wallet.sendTransaction(estimation.transactions)).wait(1) expect(realTx.gasUsed.toNumber()).to.be.approximately(estimation.total.toNumber(), 10000) @@ -242,7 +259,7 @@ describe('Wallet integration', function () { }) it('should predict gas usage for a single transaction', async () => { - const estimation = await estimator.estimateGasLimits(wallet.config, wallet.context, ...txs) + const estimation = await estimator.estimateGasLimits(wallet.address, wallet.config, wallet.context, 0, ...txs) const realTx = await (await wallet.sendTransaction(txs)).wait(1) expect(realTx.gasUsed.toNumber()).to.be.approximately(estimation.total.toNumber(), 10000) @@ -253,7 +270,7 @@ describe('Wallet integration', function () { it('should use estimated gas for a single failing transaction', async () => { await callReceiver.setRevertFlag(true) - const estimation = await estimator.estimateGasLimits(wallet.config, wallet.context, ...txs) + const estimation = await estimator.estimateGasLimits(wallet.address, wallet.config, wallet.context, 0, ...txs) const realTx = await (await wallet.sendTransaction(estimation.transactions)).wait(1) expect(realTx.gasUsed.toNumber()).to.be.approximately(estimation.total.toNumber(), 10000) @@ -277,8 +294,7 @@ describe('Wallet integration', function () { gasLimit: 0, to: callReceiver.address, value: ethers.constants.Zero, - data: await encodeData(callReceiver, 'setRevertFlag', false), - nonce: 0 + data: await encodeData(callReceiver, 'setRevertFlag', false) }, { delegateCall: false, @@ -286,14 +302,13 @@ describe('Wallet integration', function () { gasLimit: 0, to: callReceiver.address, value: ethers.constants.Zero, - data: await encodeData(callReceiver, 'testCall', 2, valB), - nonce: 0 + data: await encodeData(callReceiver, 'testCall', 2, valB) } ] }) it('should use estimated gas for a batch of transactions', async () => { - const estimation = await estimator.estimateGasLimits(wallet.config, wallet.context, ...txs) + const estimation = await estimator.estimateGasLimits(wallet.address, wallet.config, wallet.context, 0, ...txs) const realTx = await (await wallet.sendTransaction(estimation.transactions)).wait(1) expect(realTx.gasUsed.toNumber()).to.be.approximately(estimation.total.toNumber(), 30000) diff --git a/packages/guard/package.json b/packages/guard/package.json index 9613eeaca..350340588 100644 --- a/packages/guard/package.json +++ b/packages/guard/package.json @@ -12,9 +12,11 @@ "test": "echo", "typecheck": "tsc --noEmit" }, - "dependencies": {}, - "peerDependencies": {}, - "devDependencies": {}, + "dependencies": { + "@0xsequence/core": "workspace:*", + "@0xsequence/signhub": "workspace:*", + "ethers": "^5.7.2" + }, "files": [ "src", "dist" diff --git a/packages/guard/src/guard.gen.ts b/packages/guard/src/guard.gen.ts index 67726f048..2e28467b7 100644 --- a/packages/guard/src/guard.gen.ts +++ b/packages/guard/src/guard.gen.ts @@ -1,5 +1,5 @@ /* eslint-disable */ -// sequence-guard v0.4.0 98e0726794b2b5922a0e356280daac3a409602e4 +// sequence-guard v0.4.0 a29651d1d5f63268e8d03b51e46557e0632c144d // -- // Code generated by webrpc-gen@v0.10.x-dev with typescript generator. DO NOT EDIT. // @@ -12,7 +12,7 @@ export const WebRPCVersion = "v1" export const WebRPCSchemaVersion = "v0.4.0" // Schema hash generated from your RIDL schema -export const WebRPCSchemaHash = "98e0726794b2b5922a0e356280daac3a409602e4" +export const WebRPCSchemaHash = "a29651d1d5f63268e8d03b51e46557e0632c144d" // // Types @@ -35,18 +35,9 @@ export interface RuntimeStatus { commitHash: string } -export interface SequenceContext { - factory: string - mainModule: string - mainModuleUpgradable: string - guestModule: string - utils: string -} - export interface WalletConfig { address: string - signers: Array - threshold: number + content: string } export interface WalletSigner { @@ -64,9 +55,9 @@ export interface Guard { ping(headers?: object): Promise version(headers?: object): Promise runtimeStatus(headers?: object): Promise - getSequenceContext(headers?: object): Promise - getSignerConfig(headers?: object): Promise + getSignerConfig(args: GetSignerConfigArgs, headers?: object): Promise sign(args: SignArgs, headers?: object): Promise + signWith(args: SignWithArgs, headers?: object): Promise } export interface PingArgs { @@ -87,13 +78,8 @@ export interface RuntimeStatusArgs { export interface RuntimeStatusReturn { status: RuntimeStatus } -export interface GetSequenceContextArgs { -} - -export interface GetSequenceContextReturn { - data: SequenceContext -} export interface GetSignerConfigArgs { + signer: string } export interface GetSignerConfigReturn { @@ -106,6 +92,14 @@ export interface SignArgs { export interface SignReturn { sig: string } +export interface SignWithArgs { + signer: string + request: SignRequest +} + +export interface SignWithReturn { + sig: string +} @@ -165,35 +159,33 @@ export class Guard implements Guard { }) } - getSequenceContext = (headers?: object): Promise => { + getSignerConfig = (args: GetSignerConfigArgs, headers?: object): Promise => { return this.fetch( - this.url('GetSequenceContext'), - createHTTPRequest({}, headers) - ).then((res) => { + this.url('GetSignerConfig'), + createHTTPRequest(args, headers)).then((res) => { return buildResponse(res).then(_data => { return { - data: (_data.data) + signerConfig: (_data.signerConfig) } }) }) } - getSignerConfig = (headers?: object): Promise => { + sign = (args: SignArgs, headers?: object): Promise => { return this.fetch( - this.url('GetSignerConfig'), - createHTTPRequest({}, headers) - ).then((res) => { + this.url('Sign'), + createHTTPRequest(args, headers)).then((res) => { return buildResponse(res).then(_data => { return { - signerConfig: (_data.signerConfig) + sig: (_data.sig) } }) }) } - sign = (args: SignArgs, headers?: object): Promise => { + signWith = (args: SignWithArgs, headers?: object): Promise => { return this.fetch( - this.url('Sign'), + this.url('SignWith'), createHTTPRequest(args, headers)).then((res) => { return buildResponse(res).then(_data => { return { diff --git a/packages/guard/src/index.ts b/packages/guard/src/index.ts index 2351cb4ff..6b416e74a 100644 --- a/packages/guard/src/index.ts +++ b/packages/guard/src/index.ts @@ -1 +1,2 @@ export * from './guard.gen' +export * from './signer' diff --git a/packages/guard/src/signer.ts b/packages/guard/src/signer.ts new file mode 100644 index 000000000..efaff820f --- /dev/null +++ b/packages/guard/src/signer.ts @@ -0,0 +1,117 @@ + +import { signers, Status } from '@0xsequence/signhub' +import { BytesLike, ethers } from 'ethers' +import { Guard } from './guard.gen' +import { commons, universal } from '@0xsequence/core' + +export class GuardSigner implements signers.SapientSigner { + private guard: Guard + private requests: Map void; + onRejection: (error: string) => void; + onStatus: (situation: string) => void; + }> = new Map() + + constructor( + public readonly address: string, + public readonly url: string, + public readonly appendSuffix: boolean = false + ) { + this.guard = new Guard(url, global.fetch) + } + + async getAddress(): Promise { + return this.address + } + + async requestSignature( + id: string, + _message: BytesLike, + metadata: Object, + callbacks: { + onSignature: (signature: BytesLike) => void; + onRejection: (error: string) => void; + onStatus: (situation: string) => void; + } + ): Promise { + if (!commons.isWalletSignRequestMetadata(metadata)) { + callbacks.onRejection('Expected Sequence-like metadata') + } else { + // Queue the request first, this method only does that + // the requesting to the API is later handled on every status change + this.requests.set(id, callbacks) + } + + return true + } + + notifyStatusChange( + id: string, + status: Status, + metadata: Object + ): void { + if (!this.requests.has(id)) return + + if (!commons.isWalletSignRequestMetadata(metadata)) { + this.requests.get(id)!.onRejection('Expected Sequence-like metadata (status update)') + return + } + + this.evaluateRequest(id, status.message, status, metadata) + } + + private packMsgAndSig(address: string, msg: BytesLike, sig: BytesLike, chainId: ethers.BigNumberish): string { + return ethers.utils.defaultAbiCoder.encode( + ['address', 'uint256', 'bytes', 'bytes'], + [address, chainId, msg, sig] + ) + } + + private keyOfRequest(signer: string, msg: BytesLike, auxData: BytesLike, chainId: ethers.BigNumberish): string { + return ethers.utils.solidityKeccak256( + ['address', 'uint256', 'bytes', 'bytes'], + [signer, chainId, msg, auxData] + ) + } + + private async evaluateRequest(id: string, message: BytesLike, _: Status, metadata: commons.WalletSignRequestMetadata): Promise { + // Building auxData, notice: this uses the old v1 format + // TODO: We should update the guard API so we can pass the metadata directly + const coder = universal.genericCoderFor(metadata.config.version) + const { encoded } = coder.signature.encodeSigners(metadata.config, metadata.parts ?? new Map(), [], metadata.chainId) + + try { + const key = this.keyOfRequest(this.address, message, encoded, metadata.chainId) + const lastAttempt = this.requests.get(id)?.lastAttempt + if (lastAttempt === key) { + return + } + + this.requests.get(id)!.lastAttempt = key + + const result = await this.guard.signWith({ + signer: this.address, + request: { + msg: ethers.utils.hexlify(message), + auxData: this.packMsgAndSig(metadata.address, metadata.digest, encoded, metadata.chainId), + chainId: ethers.BigNumber.from(metadata.chainId).toNumber() // TODO: This should be a string (in the API) + } + }) + + if (ethers.utils.arrayify(result.sig).length !== 0) { + this.requests.get(id)!.onSignature(result.sig) + this.requests.delete(id) + } + } catch (e) { + // The guard signer may reject the request for a number of reasons + // like for example, if it's being the first signer (it waits for other signers to sign first) + // for now we ignore all errors, but we should probably handle them + // TODO: Filter real errors from control flow errors + } + } + + suffix(): BytesLike { + return this.appendSuffix ? [ 3 ] : [] + } +} diff --git a/packages/migration/package.json b/packages/migration/package.json new file mode 100644 index 000000000..6bec1df1a --- /dev/null +++ b/packages/migration/package.json @@ -0,0 +1,28 @@ +{ + "name": "@0xsequence/migration", + "version": "0.43.4", + "description": "tools for migrating sequence wallets to new versions", + "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/migration", + "source": "src/index.ts", + "main": "dist/0xsequence-migration.cjs.js", + "module": "dist/0xsequence-migration.esm.js", + "author": "Horizon Blockchain Games", + "license": "Apache-2.0", + "scripts": { + "test": "echo 'TODO: Migration tests'" + }, + "dependencies": { + "@0xsequence/abi": "workspace:*", + "@0xsequence/core": "workspace:*", + "@0xsequence/wallet": "workspace:*", + "ethers": "^5.5.2" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "nyc": "^15.1.0" + }, + "files": [ + "src", + "dist" + ] +} diff --git a/packages/migration/src/defaults.ts b/packages/migration/src/defaults.ts new file mode 100644 index 000000000..0130b8afe --- /dev/null +++ b/packages/migration/src/defaults.ts @@ -0,0 +1,6 @@ +import { v1v2 } from "./migrations" +import { Migrations } from "./migrator" + +export const DefaultMigrations: Migrations = { + 1: v1v2 +} diff --git a/packages/migration/src/index.ts b/packages/migration/src/index.ts new file mode 100644 index 000000000..4fa919aed --- /dev/null +++ b/packages/migration/src/index.ts @@ -0,0 +1,5 @@ + +export * as version from './version' +export * as migration from './migrations' +export * as migrator from './migrator' +export * as defaults from './defaults' diff --git a/packages/migration/src/migrations/index.ts b/packages/migration/src/migrations/index.ts new file mode 100644 index 000000000..bd57958d6 --- /dev/null +++ b/packages/migration/src/migrations/index.ts @@ -0,0 +1,36 @@ +import { commons } from "@0xsequence/core" +import { UnsignedMigration } from "../migrator" +import { Migration_v1v2 } from "./migration_01_02" + +// = uint160(keccak256("org.sequence.sdk.migration.space.nonce")) +export const MIGRATION_NONCE_SPACE = "0xa04263acf755e8bd19c0d7e20eea39a9ff3729eb" + +export interface Migration< + P extends commons.config.Config, + C extends commons.config.Config, +> { + version: number, + + buildTransaction: ( + address: string, + contexts: commons.context.VersionedContext, + newConfig: P | C + ) => UnsignedMigration + + decodeTransaction: ( + tx: commons.transaction.TransactionBundle, + contexts: commons.context.VersionedContext + ) => { + address: string, + newImageHash: string + } + + configCoder: commons.config.ConfigCoder + signatureCoder: commons.signature.SignatureCoder< + C, + commons.signature.Signature, + commons.signature.UnrecoveredSignature + > +} + +export const v1v2 = new Migration_v1v2() diff --git a/packages/migration/src/migrations/migration_01_02.ts b/packages/migration/src/migrations/migration_01_02.ts new file mode 100644 index 000000000..eb0f81b53 --- /dev/null +++ b/packages/migration/src/migrations/migration_01_02.ts @@ -0,0 +1,120 @@ +import { commons, v1, v2 } from "@0xsequence/core" +import { ethers } from "ethers" + +import { Migration, MIGRATION_NONCE_SPACE } from "." +import { walletContracts } from "@0xsequence/abi" +import { UnsignedMigration } from "../migrator" + +export class Migration_v1v2 implements Migration< + v1.config.WalletConfig, + v2.config.WalletConfig +> { + version = 2 + + configCoder = v2.config.ConfigCoder + signatureCoder = v2.signature.SignatureCoder + + buildTransaction( + address: string, + contexts: commons.context.VersionedContext, + newConfig: v1.config.WalletConfig | v2.config.WalletConfig + ): UnsignedMigration { + // If new config is not v2, then we need to convert it to v2 + if (!v2.config.ConfigCoder.isWalletConfig(newConfig)) { + const v2Config = v2.config.toWalletConfig({ + threshold: newConfig.threshold, + members: newConfig.signers, + checkpoint: 0 + }) + + return this.buildTransaction(address, contexts, v2Config) + } + + const context = contexts[2] + const contract = new ethers.utils.Interface(walletContracts.mainModule.abi) + + // WARNING: v1 wallets CAN NOT use v2 configurations so we ALWAYS need to update + // both the implementation and the configuration at the same time + + const updateBundle = v2.config.ConfigCoder.update.buildTransaction(address, newConfig, context, 'first') + + const tx = { + entrypoint: address, + nonce: commons.transaction.encodeNonce(MIGRATION_NONCE_SPACE, 0), + transactions: [ + { + to: address, + value: 0, + gasLimit: 0, + revertOnError: true, + delegateCall: false, + data: contract.encodeFunctionData(contract.getFunction('updateImplementation'), [ + context.mainModuleUpgradable + ]) + }, + ...updateBundle.transactions + ] + } + + return { + tx, + fromVersion: this.version - 1, + toVersion: this.version, + toConfig: newConfig + } + } + + decodeTransaction( + tx: commons.transaction.TransactionBundle, + contexts: commons.context.VersionedContext + ): { + address: string, + newImageHash: string + } { + const address = tx.entrypoint + + if (tx.transactions.length < 2) { + throw new Error('Invalid transaction bundle size') + } + + if (!tx.nonce || !commons.transaction.encodeNonce(MIGRATION_NONCE_SPACE, 0).eq(tx.nonce)) { + throw new Error('Invalid transaction bundle nonce') + } + + if( + tx.transactions[0].to !== address || + tx.transactions[1].to !== address || + tx.transactions[0].delegateCall || + tx.transactions[1].delegateCall || + !tx.transactions[0].revertOnError || + !tx.transactions[1].revertOnError || + (tx.transactions[0].value && !ethers.constants.Zero.eq(tx.transactions[0].value)) || + (tx.transactions[1].value && !ethers.constants.Zero.eq(tx.transactions[1].value)) || + (tx.transactions[0].gasLimit && !ethers.constants.Zero.eq(tx.transactions[0].gasLimit)) || + (tx.transactions[1].gasLimit && !ethers.constants.Zero.eq(tx.transactions[1].gasLimit)) + ) { + throw new Error('Invalid transaction bundle format') + } + + const context = contexts[2] + const contract = new ethers.utils.Interface(walletContracts.mainModule.abi) + + const data1 = ethers.utils.hexlify(tx.transactions[0].data || []) + const expectData1 = ethers.utils.hexlify( + contract.encodeFunctionData(contract.getFunction('updateImplementation'), [ + context.mainModuleUpgradable + ]) + ) + + if (data1 !== expectData1) { + throw new Error('Invalid new implementation on transaction') + } + + const decoded2 = v2.config.ConfigCoder.update.decodeTransaction({ entrypoint: address, transactions: [tx.transactions[1]] }) + if (decoded2.address !== address) { + throw new Error('Invalid transaction bundle address') + } + + return decoded2 + } +} diff --git a/packages/migration/src/migrator.ts b/packages/migration/src/migrator.ts new file mode 100644 index 000000000..c3dbf1c53 --- /dev/null +++ b/packages/migration/src/migrator.ts @@ -0,0 +1,127 @@ +import { commons } from '@0xsequence/core' +import { Wallet } from '@0xsequence/wallet' +import { ethers } from 'ethers' + +import { Migration } from "./migrations" + +export type UnsignedMigration = { + tx: commons.transaction.TransactionBundle + fromVersion: number + toVersion: number + toConfig: commons.config.Config +} + +export type SignedMigration = Omit & { + tx: commons.transaction.SignedTransactionBundle +} + +export interface PresignedMigrationTracker { + getMigration( + address: string, + fromImageHash: string, + fromVersion: number, + chainId: ethers.BigNumberish + ): Promise + + saveMigration( + address: string, + signed: SignedMigration, + contexts: commons.context.VersionedContext + ): Promise +} + +export type Migrations = { [version: number]: Migration } + +function validateMigrations(migrations: Migrations) { + for (const [version, migration] of Object.entries(migrations)) { + if (version !== String(migration.version - 1)) { + throw new Error(`Migration with key ${version} has version ${migration.version}, expected version to be key + 1`) + } + } +} + +export class Migrator { + constructor( + public readonly tracker: PresignedMigrationTracker, + public readonly migrations: Migrations, + public readonly contexts: commons.context.VersionedContext + ) { + validateMigrations(migrations) + } + + lastMigration(): Migration { + let last: Migration | undefined + for (const migration of Object.values(this.migrations)) { + if (last === undefined || migration.version > last.version) { + last = migration + } + } + if (last === undefined) { + throw new Error('No migrations') + } + return last + } + + async getAllMigratePresignedTransaction(args: { + address: string, + fromImageHash: string, + fromVersion: number, + chainId: ethers.BigNumberish + }): Promise<{ + lastVersion: number, + lastImageHash: string, + signedMigrations: SignedMigration[], + missing: boolean + }> { + const { address, fromImageHash, fromVersion, chainId } = args + + let fih = fromImageHash + let fversion = fromVersion + + const versions = Object.values(this.contexts) + const migs: SignedMigration[] = [] + + for (let i = 1; i < versions.length; i++) { + const mig = await this.tracker.getMigration(address, fih, fversion, chainId) + if (!mig) return { signedMigrations: migs, missing: true, lastImageHash: fih, lastVersion: fversion } + + migs.push(mig) + + const migration = this.migrations[fversion] + if (!migration) { + throw new Error(`No migration found for version ${fversion}`) + } + + const decoded = migration.decodeTransaction(mig.tx, this.contexts) + if (decoded.address !== address) { + throw new Error(`Migration transaction address does not match expected address`) + } + + fih = decoded.newImageHash + fversion += 1 + } + + return { signedMigrations: migs, missing: false, lastImageHash: fih, lastVersion: fversion } + } + + async signNextMigration( + address: string, + fromVersion: number, + wallet: Wallet, + nextConfig: commons.config.Config + ): Promise { + const migration = this.migrations[fromVersion] + + if (!migration) { + return undefined + } + + const unsignedMigration = migration.buildTransaction(address, this.contexts, nextConfig) + const signedBundle = await wallet.signTransactionBundle(unsignedMigration.tx) + + return { + ...unsignedMigration, + tx: signedBundle + } + } +} diff --git a/packages/migration/src/version.ts b/packages/migration/src/version.ts new file mode 100644 index 000000000..f1e17b58e --- /dev/null +++ b/packages/migration/src/version.ts @@ -0,0 +1,30 @@ +import { ethers } from "ethers" +import { commons } from '@0xsequence/core' + +export function counterfactualVersion( + address: string, + firstImageHash: string, + versions: commons.context.WalletContext[] +): number { + for (let i = 0; i < versions.length; i++) { + if (commons.context.addressOf(versions[i], firstImageHash) === address) { + return versions[i].version + } + } + + // if we can't find the version then either the address is invalid, + // the version is not in VersionedContext, or the firstImageHash is not correct + throw new Error('Could not find version for counterfactual address') +} + +export interface Version< + C extends commons.config.Config, + S extends commons.signature.Signature, + U extends commons.signature.UnrecoveredSignature +> { + version: number, + coders: { + config: commons.config.ConfigCoder, + signature: commons.signature.SignatureCoder + } +} \ No newline at end of file diff --git a/packages/multicall/package.json b/packages/multicall/package.json index a80b3677b..88e4af935 100644 --- a/packages/multicall/package.json +++ b/packages/multicall/package.json @@ -14,15 +14,15 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@0xsequence/abi": "^0.43.34", - "@0xsequence/network": "^0.43.34", - "@0xsequence/utils": "^0.43.34" + "@0xsequence/abi": "workspace:*", + "@0xsequence/network": "workspace:*", + "@0xsequence/utils": "workspace:*" }, "peerDependencies": { "ethers": ">=5.5 < 6" }, "devDependencies": { - "@0xsequence/wallet-contracts": "1.10.0", + "@0xsequence/wallet-contracts": "^1.10.0", "@ethersproject/providers": "^5.7.2", "@types/web3-provider-engine": "^14.0.1", "eth-json-rpc-middleware": "^9.0.1", diff --git a/packages/multicall/src/multicall.ts b/packages/multicall/src/multicall.ts index 4b5e7288c..5151a37d5 100644 --- a/packages/multicall/src/multicall.ts +++ b/packages/multicall/src/multicall.ts @@ -3,7 +3,7 @@ import { walletContracts } from '@0xsequence/abi' import { JsonRpcMethod } from './constants' import { BlockTag, eqBlockTag, parseBlockTag, partition, safeSolve } from './utils' import { promisify, getRandomInt } from '@0xsequence/utils' -import { JsonRpcVersion, JsonRpcRequest, JsonRpcResponseCallback, JsonRpcHandlerFunc, sequenceContext } from "@0xsequence/network" +import { JsonRpcVersion, JsonRpcRequest, JsonRpcResponseCallback, JsonRpcHandlerFunc } from "@0xsequence/network" export type MulticallOptions = { // number of calls to enqueue before calling. @@ -31,7 +31,8 @@ type QueueEntry = { const DefaultMulticallOptions = { batchSize: 50, timeWindow: 50, - contract: sequenceContext.sequenceUtils!, + // TODO: Import from global pre-defiend contracts + contract: '0xD7042Bf5C8Cc253301B01D7143166E17D5BF4f74', verbose: false } diff --git a/packages/network/package.json b/packages/network/package.json index 62989fcf5..cb2e4d3a4 100644 --- a/packages/network/package.json +++ b/packages/network/package.json @@ -13,10 +13,10 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@0xsequence/indexer": "^0.43.34", - "@0xsequence/provider": "^0.43.34", - "@0xsequence/relayer": "^0.43.34", - "@0xsequence/utils": "^0.43.34" + "@0xsequence/core": "workspace:*", + "@0xsequence/indexer": "workspace:*", + "@0xsequence/relayer": "workspace:*", + "@0xsequence/utils": "workspace:*" }, "peerDependencies": { "ethers": ">=5.5 < 6" diff --git a/packages/network/src/config.ts b/packages/network/src/config.ts index 161deecc9..70a41d249 100644 --- a/packages/network/src/config.ts +++ b/packages/network/src/config.ts @@ -1,7 +1,8 @@ -import { BigNumberish, providers } from 'ethers' +import { BigNumberish, ethers, providers } from 'ethers' import { Indexer } from '@0xsequence/indexer' import { Relayer, RpcRelayerOptions } from '@0xsequence/relayer' -import { stringTemplate, validateAndSortNetworks } from './utils' +import { findNetworkConfig, stringTemplate, validateAndSortNetworks } from './utils' +import { isBigNumberish } from '@0xsequence/utils' export enum ChainId { // Ethereum @@ -47,7 +48,11 @@ export enum ChainId { AURORA_TESTNET = 1313161556, // BASE - BASE_GOERLI = 84531 + BASE_GOERLI = 84531, + + // HARDHAT TESTNETS + HARDHAT = 31337, + HARDHAT_2 = 31338, } export interface NetworkConfig { @@ -59,8 +64,8 @@ export interface NetworkConfig { blockExplorer?: BlockExplorerConfig ensAddress?: string - rpcUrl?: string - provider?: providers.JsonRpcProvider + rpcUrl: string + provider?: providers.Provider indexerUrl?: string indexer?: Indexer relayer?: Relayer | RpcRelayerOptions @@ -69,13 +74,12 @@ export interface NetworkConfig { // network and may configure the wallet to use it as its main/default chain. isDefaultChain?: boolean - // isAuthChain identifies the network containing wallet config contents. - isAuthChain?: boolean - // Disabled / deprecated chain disabled?: boolean } +type LegacyNetworkConfig = NetworkConfig & { isAuthChain?: boolean } + export type BlockExplorerConfig = { name?: string rootUrl: string @@ -87,7 +91,7 @@ export const indexerURL = (network: string) => stringTemplate('https://${network export const relayerURL = (network: string) => stringTemplate('https://${network}-relayer.sequence.app', { network: network }) export const nodesURL = (network: string) => stringTemplate('https://nodes.sequence.app/${network}', { network: network }) -export const networks: Record = { +export const networks: Record> = { [ChainId.MAINNET]: { chainId: ChainId.MAINNET, name: 'mainnet', @@ -149,8 +153,11 @@ export const networks: Record = { blockExplorer: { name: 'Polygonscan', rootUrl: 'https://polygonscan.com/' - } - }, + }, + // TODO: Remove default and auth chains from here + isDefaultChain: true, + isAuthChain: true + } as LegacyNetworkConfig, [ChainId.POLYGON_MUMBAI]: { chainId: ChainId.POLYGON_MUMBAI, name: 'mumbai', @@ -309,107 +316,130 @@ export const networks: Record = { name: 'Base Goerli Explorer', rootUrl: 'https://goerli.basescan.org/' } + }, + [ChainId.HARDHAT]: { + chainId: ChainId.HARDHAT, + name: 'hardhat', + title: 'Hardhat (local testnet)' + }, + [ChainId.HARDHAT_2]: { + chainId: ChainId.HARDHAT_2, + name: 'hardhat2', + title: 'Hardhat (local testnet)' } } +export function findSupportedNetwork(chainIdOrName: string | ChainIdLike): NetworkConfig | undefined { + return findNetworkConfig(allNetworks, chainIdOrName) +} + export type ChainIdLike = NetworkConfig | BigNumberish -export const mainnetNetworks = validateAndSortNetworks([ +export function toChainIdNumber(chainIdLike: ChainIdLike): ethers.BigNumber { + if (ethers.BigNumber.isBigNumber(chainIdLike)) { + return chainIdLike + } + + if (isBigNumberish(chainIdLike)) { + return ethers.BigNumber.from(chainIdLike) + } + + return ethers.BigNumber.from(chainIdLike.chainId) +} + +const genUrls = (network: string) => { + const rpcUrl = nodesURL(network) + return { + rpcUrl, + relayer: { + url: relayerURL(network), + provider: { + url: rpcUrl, + } + }, + indexerUrl: indexerURL(network) + } +} + +export const allNetworks = validateAndSortNetworks([ { ...networks[ChainId.MAINNET], - rpcUrl: nodesURL('mainnet'), - relayer: { url: relayerURL('mainnet') }, - indexerUrl: indexerURL('mainnet') + ...genUrls('mainnet') }, { ...networks[ChainId.POLYGON], - rpcUrl: nodesURL('polygon'), - relayer: { url: relayerURL('polygon') }, - indexerUrl: indexerURL('polygon'), - isDefaultChain: true, - isAuthChain: true + ...genUrls('polygon') }, { ...networks[ChainId.BSC], - rpcUrl: nodesURL('bsc'), - indexerUrl: indexerURL('bsc'), - relayer: { url: relayerURL('bsc') } + ...genUrls('bsc') }, { ...networks[ChainId.AVALANCHE], - rpcUrl: nodesURL('avalanche'), - indexerUrl: indexerURL('avalanche'), - relayer: { url: relayerURL('avalanche') } + ...genUrls('avalanche') }, { ...networks[ChainId.ARBITRUM], - rpcUrl: nodesURL('arbitrum'), - indexerUrl: indexerURL('arbitrum'), - relayer: { url: relayerURL('arbitrum') } + ...genUrls('arbitrum') }, { ...networks[ChainId.ARBITRUM_NOVA], - rpcUrl: nodesURL('arbitrum-nova'), - indexerUrl: indexerURL('arbitrum-nova'), - relayer: { url: relayerURL('arbitrum-nova') } + ...genUrls('arbitrum-nova') }, { ...networks[ChainId.OPTIMISM], - rpcUrl: nodesURL('optimism'), - indexerUrl: indexerURL('optimism'), - relayer: { url: relayerURL('optimism') } + ...genUrls('optimism') }, { ...networks[ChainId.POLYGON_ZKEVM], - rpcUrl: nodesURL('polygon-zkevm'), - indexerUrl: indexerURL('polygon-zkevm'), - relayer: { url: relayerURL('polygon-zkevm') } + ...genUrls('polygon-zkevm') }, { ...networks[ChainId.GNOSIS], - rpcUrl: nodesURL('gnosis'), - indexerUrl: indexerURL('gnosis'), - relayer: { url: relayerURL('gnosis') } - } -]) - -export const testnetNetworks = validateAndSortNetworks([ + ...genUrls('gnosis') + }, { ...networks[ChainId.RINKEBY], - rpcUrl: nodesURL('rinkeby'), - relayer: { url: relayerURL('rinkeby') }, - indexerUrl: indexerURL('rinkeby') + ...genUrls('rinkeby') }, { ...networks[ChainId.GOERLI], - rpcUrl: nodesURL('goerli'), - relayer: { url: relayerURL('goerli') }, - indexerUrl: indexerURL('goerli') + ...genUrls('goerli') }, { ...networks[ChainId.POLYGON_MUMBAI], - rpcUrl: nodesURL('mumbai'), - relayer: { url: relayerURL('mumbai') }, - indexerUrl: indexerURL('mumbai'), - isDefaultChain: true, - isAuthChain: true + ...genUrls('mumbai'), }, { ...networks[ChainId.BSC_TESTNET], - rpcUrl: nodesURL('bsc-testnet'), - relayer: { url: relayerURL('bsc-testnet') }, - indexerUrl: indexerURL('bsc-testnet') + ...genUrls('bsc-testnet') }, { ...networks[ChainId.ARBITRUM_GOERLI], - rpcUrl: nodesURL('arbitrum-goerli'), - relayer: { url: relayerURL('arbitrum-goerli') }, - indexerUrl: indexerURL('arbitrum-goerli') + ...genUrls('arbitrum-goerli') }, { ...networks[ChainId.BASE_GOERLI], - rpcUrl: nodesURL('base-goerli'), - relayer: { url: relayerURL('base-goerli') }, - indexerUrl: indexerURL('base-goerli') + ...genUrls('base-goerli') + }, + { + ...networks[ChainId.HARDHAT], + rpcUrl: 'http://localhost:8545', + relayer: { + url: 'http://localhost:3000', + provider: { + url: 'http://localhost:8545', + } + } + }, + { + ...networks[ChainId.HARDHAT_2], + rpcUrl: 'http://localhost:9545', + relayer: { + url: 'http://localhost:3000', + provider: { + url: 'http://localhost:9545', + } + } } ]) diff --git a/packages/network/src/context.ts b/packages/network/src/context.ts deleted file mode 100644 index 350ba3f2f..000000000 --- a/packages/network/src/context.ts +++ /dev/null @@ -1,27 +0,0 @@ -// WalletContext is the module addresses deployed on a network, aka the context / environment -// of the Sequence Smart Wallet system on Ethereum. -export interface WalletContext { - factory: string - mainModule: string - mainModuleUpgradable: string - guestModule?: string - sequenceUtils?: string - - libs?: { - requireFreshSigner?: string - } - - nonStrict?: boolean -} - -// sequenceContext are the deployed addresses of modules available on public networks. -export const sequenceContext: WalletContext = { - factory: '0xf9D09D634Fb818b05149329C1dcCFAeA53639d96', - mainModule: '0xd01F11855bCcb95f88D7A48492F66410d4637313', - mainModuleUpgradable: '0x7EFE6cE415956c5f80C6530cC6cc81b4808F6118', - guestModule: '0x02390F3E6E5FD1C6786CB78FD3027C117a9955A7', - sequenceUtils: '0xd130B43062D875a4B7aF3f8fc036Bc6e9D3E1B3E', - libs: { - requireFreshSigner: '0xE6B9B21C077F382333220a072e4c44280b873907' - } -} diff --git a/packages/network/src/index.ts b/packages/network/src/index.ts index 29a081481..affd82537 100644 --- a/packages/network/src/index.ts +++ b/packages/network/src/index.ts @@ -1,5 +1,4 @@ export * from './config' -export * from './context' export * from './json-rpc' export * from './json-rpc-provider' export * from './utils' diff --git a/packages/network/src/json-rpc-provider.ts b/packages/network/src/json-rpc-provider.ts index 9c0dbf06e..1d2518106 100644 --- a/packages/network/src/json-rpc-provider.ts +++ b/packages/network/src/json-rpc-provider.ts @@ -1,10 +1,19 @@ import { ethers } from 'ethers' -import { JsonRpcRouter, JsonRpcSender, loggingProviderMiddleware, EagerProvider, SingleflightMiddleware, CachedProvider, JsonRpcMiddleware, JsonRpcMiddlewareHandler } from './json-rpc' +import { + JsonRpcRouter, + JsonRpcSender, + loggingProviderMiddleware, + EagerProvider, + SingleflightMiddleware, + CachedProvider, + JsonRpcMiddleware, + JsonRpcMiddlewareHandler +} from './json-rpc' import { networks, ChainId } from './config' export interface JsonRpcProviderOptions { // .. - chainId? :number + chainId?: number // .. middlewares?: Array @@ -32,8 +41,7 @@ export class JsonRpcProvider extends ethers.providers.JsonRpcProvider { // so if you set middlewares, make sure you set the caching middleware yourself if you'd // like to keep using it. const router = new JsonRpcRouter( - middlewares ?? - [ + middlewares ?? [ // loggingProviderMiddleware, new EagerProvider({ chainId }), new SingleflightMiddleware(), @@ -71,21 +79,24 @@ export class JsonRpcProvider extends ethers.providers.JsonRpcProvider { const request = { method: method, params: params, - id: (this._nextId++), + id: this._nextId++, jsonrpc: '2.0' } - const result = ethers.utils.fetchJson(this.connection, JSON.stringify(request), getResult).then((result) => { - return result - }, (error) => { - throw error - }) + const result = ethers.utils.fetchJson(this.connection, JSON.stringify(request), getResult).then( + result => { + return result + }, + error => { + throw error + } + ) return result } } -function getResult(payload: { error?: { code?: number, data?: any, message?: string }, result?: any }): any { +function getResult(payload: { error?: { code?: number; data?: any; message?: string }; result?: any }): any { if (payload.error) { // @TODO: not any const error: any = new Error(payload.error.message) diff --git a/packages/network/src/json-rpc/middleware/eager-provider.ts b/packages/network/src/json-rpc/middleware/eager-provider.ts index 46418240a..c284d0ea0 100644 --- a/packages/network/src/json-rpc/middleware/eager-provider.ts +++ b/packages/network/src/json-rpc/middleware/eager-provider.ts @@ -1,6 +1,6 @@ +import { commons } from '@0xsequence/core' import { ethers } from 'ethers' import { JsonRpcHandlerFunc, JsonRpcRequest, JsonRpcResponseCallback, JsonRpcResponse, JsonRpcMiddlewareHandler } from '../types' -import { WalletContext } from '../../context' // EagerProvider will eagerly respond to a provider request from pre-initialized data values. // @@ -10,7 +10,7 @@ import { WalletContext } from '../../context' export type EagerProviderOptions = { accountAddress?: string, chainId?: number, - walletContext?: WalletContext + walletContext?: commons.context.VersionedContext } export class EagerProvider implements JsonRpcMiddlewareHandler { diff --git a/packages/network/src/json-rpc/middleware/signing-provider.ts b/packages/network/src/json-rpc/middleware/signing-provider.ts index 6cda9cb22..6464c262f 100644 --- a/packages/network/src/json-rpc/middleware/signing-provider.ts +++ b/packages/network/src/json-rpc/middleware/signing-provider.ts @@ -1,16 +1,31 @@ -import { JsonRpcHandlerFunc, JsonRpcRequest, JsonRpcResponse, JsonRpcResponseCallback, JsonRpcMiddlewareHandler, JsonRpcHandler } from '../types' +import { JsonRpcHandlerFunc, JsonRpcRequest, JsonRpcResponseCallback, JsonRpcMiddlewareHandler, JsonRpcHandler } from '../types' export const SignerJsonRpcMethods = [ - 'personal_sign', 'eth_sign', 'eth_signTypedData', 'eth_signTypedData_v4', - 'eth_sendTransaction', 'eth_sendRawTransaction', - - 'sequence_getWalletContext', 'sequence_getWalletConfig', 'sequence_getWalletState', 'sequence_getNetworks', - 'sequence_updateConfig', 'sequence_publishConfig', 'sequence_gasRefundOptions', - 'sequence_getNonce', 'sequence_relay', - - 'eth_decrypt', 'eth_getEncryptionPublicKey', - 'wallet_addEthereumChain', 'wallet_switchEthereumChain', - 'wallet_registerOnboarding', 'wallet_watchAsset', + 'personal_sign', + 'eth_sign', + 'eth_signTypedData', + 'eth_signTypedData_v4', + 'eth_sendTransaction', + 'eth_sendRawTransaction', + 'sequence_sign', // sequence-aware personal_sign + 'sequence_signTypedData_v4', // sequence-aware eth_signTypedData_v4 + + 'sequence_getWalletContext', + 'sequence_getWalletConfig', + 'sequence_getWalletState', + 'sequence_getNetworks', + 'sequence_updateConfig', + 'sequence_publishConfig', + 'sequence_gasRefundOptions', + 'sequence_getNonce', + 'sequence_relay', + + 'eth_decrypt', + 'eth_getEncryptionPublicKey', + 'wallet_addEthereumChain', + 'wallet_switchEthereumChain', + 'wallet_registerOnboarding', + 'wallet_watchAsset', 'wallet_scanQRCode' ] diff --git a/packages/network/src/json-rpc/types.ts b/packages/network/src/json-rpc/types.ts index 5706d2f4d..cfe45f8fb 100644 --- a/packages/network/src/json-rpc/types.ts +++ b/packages/network/src/json-rpc/types.ts @@ -1,5 +1,3 @@ -import { ProviderRpcError } from '@0xsequence/provider' - export const JsonRpcVersion = '2.0' export interface JsonRpcRequest { @@ -34,3 +32,8 @@ export type JsonRpcMiddleware = (next: JsonRpcHandlerFunc) => JsonRpcHandlerFunc export interface JsonRpcMiddlewareHandler { sendAsyncMiddleware: JsonRpcMiddleware } + +export interface ProviderRpcError extends Error { + code?: number + data?: { [key: string]: any } +} diff --git a/packages/network/src/utils.ts b/packages/network/src/utils.ts index 1c73d3491..9bed6a48e 100644 --- a/packages/network/src/utils.ts +++ b/packages/network/src/utils.ts @@ -21,10 +21,6 @@ export const maybeChainId = (chainId?: ChainIdLike): number | undefined => { return getChainId(chainId) } -export const getAuthNetwork = (networks: NetworkConfig[]): NetworkConfig | undefined => { - return networks.find(network => network.isAuthChain) -} - export const isValidNetworkConfig = ( networkConfig: NetworkConfig | NetworkConfig[], raise: boolean = false, @@ -68,7 +64,6 @@ export const isValidNetworkConfig = ( // Ensure one default chain // Ensure one auth chain let defaultChain = false - let authChain = false for (let i = 0; i < configs.length; i++) { const c = configs[i] if ((!c.rpcUrl || c.rpcUrl === '') && !c.provider) { @@ -89,22 +84,12 @@ export const isValidNetworkConfig = ( } defaultChain = true } - if (c.isAuthChain) { - if (authChain) { - if (raise) throw new Error(`invalid network config for chainId ${c.chainId}: AuthChain is already set by another config`) - } - authChain = true - } } if (!defaultChain) { if (raise) throw new Error(`invalid network config: DefaultChain must be set`) return false } - if (!authChain) { - if (raise) throw new Error(`invalid network config: AuthChain must be set`) - return false - } return true } @@ -144,13 +129,6 @@ export const updateNetworkConfig = (src: Partial, dest: NetworkCo if (src.relayer) { dest.relayer = src.relayer } - // NOTE: we do not set default or auth chain from here - // if (src.isDefaultChain) { - // dest.isDefaultChain = src.isDefaultChain - // } - // if (src.isAuthChain) { - // dest.isAuthChain = src.isAuthChain - // } } export const validateAndSortNetworks = (networks: NetworkConfig[]) => { @@ -163,7 +141,7 @@ export const findNetworkConfig = (networks: NetworkConfig[], chainId: ChainIdLik const id = ethers.BigNumber.from(chainId).toNumber() return networks.find(n => n.chainId === id) } else { - return networks.find(n => n.name === chainId) + return networks.find(n => n.name === chainId || `${n.chainId}` === chainId) } } else if (typeof chainId === 'number') { return networks.find(n => n.chainId === chainId) @@ -205,10 +183,6 @@ export const sortNetworks = (networks: NetworkConfig[]): NetworkConfig[] => { const defaultConfigIdx = config.findIndex(c => c.isDefaultChain) if (defaultConfigIdx > 0) config.splice(0, 0, config.splice(defaultConfigIdx, 1)[0]) - // AuthChain goes second - const authConfigIdx = config.findIndex(c => c.isAuthChain && c.isDefaultChain !== true) - if (authConfigIdx > 0) config.splice(1, 0, config.splice(authConfigIdx, 1)[0]) - return config } diff --git a/packages/provider/package.json b/packages/provider/package.json index 296f8d1a0..3cb0a2d92 100644 --- a/packages/provider/package.json +++ b/packages/provider/package.json @@ -14,14 +14,15 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@0xsequence/abi": "^0.43.34", - "@0xsequence/auth": "^0.43.34", - "@0xsequence/config": "^0.43.34", - "@0xsequence/network": "^0.43.34", - "@0xsequence/relayer": "^0.43.34", - "@0xsequence/transactions": "^0.43.34", - "@0xsequence/utils": "^0.43.34", - "@0xsequence/wallet": "^0.43.34", + "@0xsequence/abi": "workspace:*", + "@0xsequence/account": "workspace:*", + "@0xsequence/auth": "workspace:*", + "@0xsequence/core": "workspace:*", + "@0xsequence/migration": "workspace:*", + "@0xsequence/network": "workspace:*", + "@0xsequence/relayer": "workspace:*", + "@0xsequence/utils": "workspace:*", + "@0xsequence/wallet": "workspace:*", "eventemitter2": "^6.4.5", "webextension-polyfill": "^0.10.0" }, diff --git a/packages/provider/src/extended.ts b/packages/provider/src/extended.ts new file mode 100644 index 000000000..f76482678 --- /dev/null +++ b/packages/provider/src/extended.ts @@ -0,0 +1,23 @@ + +import { ethers } from "ethers" + +export type ExtendedTransactionRequest = ethers.providers.TransactionRequest & { + auxiliary?: ethers.providers.TransactionRequest[] +} + +export function toExtended(transactions: ethers.providers.TransactionRequest[]): ExtendedTransactionRequest { + if (transactions.length === 0) { + throw new Error("No transaction provided") + } + + const [first, ...rest] = transactions + + return { + ...first, + auxiliary: rest, + } +} + +export function fromExtended(transaction: ExtendedTransactionRequest): ethers.providers.TransactionRequest[] { + return [transaction, ...(transaction.auxiliary || [])] +} diff --git a/packages/provider/src/provider.ts b/packages/provider/src/provider.ts index 2d702dd21..4631504cb 100644 --- a/packages/provider/src/provider.ts +++ b/packages/provider/src/provider.ts @@ -1,21 +1,23 @@ -import { ethers, BytesLike, Bytes, providers, TypedDataDomain, TypedDataField, BigNumber } from 'ethers' +import { ethers, BytesLike, Bytes, providers, TypedDataDomain, TypedDataField } from 'ethers' + import { NetworkConfig, - WalletContext, ChainIdLike, JsonRpcHandler, JsonRpcFetchFunc, JsonRpcRequest, JsonRpcResponseCallback, - maybeChainId, - JsonRpcSender + JsonRpcSender, + maybeChainId } from '@0xsequence/network' + import { resolveArrayProperties, Signer } from '@0xsequence/wallet' -import { WalletConfig, WalletState } from '@0xsequence/config' import { Relayer } from '@0xsequence/relayer' import { Deferrable, shallowCopy, resolveProperties, Forbid } from '@0xsequence/utils' -import { TransactionRequest, TransactionResponse, SignedTransactions } from '@0xsequence/transactions' import { WalletRequestHandler } from './transports/wallet-request-handler' +import { commons, universal } from '@0xsequence/core' +import { Account, AccountStatus } from '@0xsequence/account' +import { ExtendedTransactionRequest, toExtended } from './extended' export class Web3Provider extends providers.Web3Provider implements JsonRpcHandler { static isSequenceProvider(cand: any): cand is Web3Provider { @@ -62,15 +64,16 @@ export class Web3Provider extends providers.Web3Provider implements JsonRpcHandl } async getChainId(): Promise { - // TODO: is it safe to memoize this? - const result = await this.send('eth_chainId', []) - const chainId = ethers.BigNumber.from(result).toNumber() + // If we already have a default chainId, then we can just return it + const defaultChainId = this._defaultChainId + if (defaultChainId) return defaultChainId - if (this._defaultChainId && this._defaultChainId !== chainId) { - throw new Error(`provider chainId (${chainId}) does not match provider-bound chainId ${this._defaultChainId}`) - } + // If there is no default chain, then we can just return the chainId of the provider + return this.send('eth_chainId', []) + } - return chainId + getNetworks(): Promise { + return this.send('sequence_getNetworks', []) } } @@ -80,8 +83,8 @@ export function isSequenceProvider(provider: any): provider is Web3Provider { } export class LocalWeb3Provider extends Web3Provider { - constructor(signer: Signer, networks?: NetworkConfig[]) { - const walletRequestHandler = new WalletRequestHandler(signer, null, networks || []) + constructor(account: Account, networks?: NetworkConfig[]) { + const walletRequestHandler = new WalletRequestHandler(account, null, networks || []) super(walletRequestHandler) } } @@ -108,7 +111,7 @@ export class Web3Signer extends Signer implements TypedDataSigner { // memoized _address: string _index: number - _context: WalletContext + _context: commons.context.VersionedContext _networks: NetworkConfig[] private _providers: { [key: number]: Web3Provider } = {} @@ -124,7 +127,7 @@ export class Web3Signer extends Signer implements TypedDataSigner { return ethers.utils.getAddress(this._address) } - signTransaction(transaction: Deferrable): Promise { + signTransaction(transaction: Deferrable): Promise { // TODO .. since ethers isn't using this method, perhaps we will? throw new Error('signTransaction is unsupported, use signTransactions instead') } @@ -175,55 +178,53 @@ export class Web3Signer extends Signer implements TypedDataSigner { throw new Error('TODO') } - async getWalletContext(): Promise { + async getWalletContext(): Promise { if (!this._context) { this._context = await this.provider.send('sequence_getWalletContext', []) } return this._context } - async getWalletConfig(chainId?: ChainIdLike): Promise { - return await this.provider.send( + async getWalletConfig(chainId?: ChainIdLike): Promise { + const reqChainId = maybeChainId(chainId) || this.defaultChainId + if (!reqChainId) throw new Error('chainId is required') + return (await this.provider.send( 'sequence_getWalletConfig', - [maybeChainId(chainId)], - maybeChainId(chainId) || this.defaultChainId - ) + [reqChainId], + reqChainId + ))[0] } - async getWalletState(chainId?: ChainIdLike): Promise { - return await this.provider.send( + async getWalletState(chainId?: ChainIdLike): Promise { + const reqChainId = maybeChainId(chainId) || this.defaultChainId + if (!reqChainId) throw new Error('chainId is required') + return (await this.provider.send( 'sequence_getWalletState', - [maybeChainId(chainId)], - maybeChainId(chainId) || this.defaultChainId - ) + [reqChainId], + reqChainId + ))[0].status } async getNetworks(): Promise { - if (!this._networks) { - this._networks = await this.provider.send('sequence_getNetworks', []) - } + if (!this._networks) this._networks = await this.provider.getNetworks() return this._networks } async getSigners(): Promise { const networks = await this.getNetworks() - const authChainId = networks.find(n => n.isAuthChain) - if (!authChainId) { - throw new Error('authChainId could not be determined from network list') - } - - const walletConfig = await this.getWalletConfig(authChainId) - if (!walletConfig || walletConfig.length === 0) { + // TODO: Replace this with a method that aggregates signer addresses from all chains + const config = await this.getWalletConfig(networks[0].chainId) + if (!config) { throw new Error(`walletConfig returned zero results for authChainId {authChainId}`) } - return walletConfig[0].signers.map(s => s.address) + return universal.genericCoderFor(config.version).config.signersOf(config).map((s) => s.address) } // signMessage matches implementation from ethers JsonRpcSigner for compatibility, but with // multi-chain support. - async signMessage(message: BytesLike, chainId?: ChainIdLike, allSigners?: boolean): Promise { + async signMessage(message: BytesLike, chainId?: ChainIdLike, sequenceVerified: boolean = true): Promise { const provider = await this.getSender(maybeChainId(chainId) || this.defaultChainId) const data = typeof message === 'string' ? ethers.utils.toUtf8Bytes(message) : message @@ -232,7 +233,10 @@ export class Web3Signer extends Signer implements TypedDataSigner { // NOTE: as of ethers v5.5, it switched to using personal_sign, see // https://github.com/ethers-io/ethers.js/pull/1542 and see // https://github.com/WalletConnect/walletconnect-docs/issues/32 for additional info. - return await provider!.send('personal_sign', [ethers.utils.hexlify(data), address]) + return provider!.send( + sequenceVerified ? 'sequence_sign' : 'personal_sign', + [ethers.utils.hexlify(data), address] + ) } // signTypedData matches implementation from ethers JsonRpcSigner for compatibility, but with @@ -242,15 +246,15 @@ export class Web3Signer extends Signer implements TypedDataSigner { types: Record>, message: Record, chainId?: ChainIdLike, - allSigners?: boolean + sequenceVerified: boolean = true ): Promise { // Populate any ENS names (in-place) // const populated = await ethers.utils._TypedDataEncoder.resolveNames(domain, types, message, (name: string) => { // return this.provider.resolveName(name) // }) - return await this.provider.send( - 'eth_signTypedData_v4', + return this.provider.send( + sequenceVerified ? 'sequence_signTypedData_v4' : 'eth_signTypedData_v4', [await this.getAddress(), ethers.utils._TypedDataEncoder.getPayload(domain, types, message)], maybeChainId(chainId) || this.defaultChainId ) @@ -259,17 +263,16 @@ export class Web3Signer extends Signer implements TypedDataSigner { // sendTransaction matches implementation from ethers JsonRpcSigner for compatibility, but with // multi-chain support. async sendTransaction( - transaction: Deferrable, - chainId?: ChainIdLike, - allSigners?: boolean - ): Promise { + transaction: Deferrable, + chainId?: ChainIdLike + ): Promise { const provider = await this.getSender(maybeChainId(chainId) || this.defaultChainId) const tx = this.sendUncheckedTransaction(transaction, chainId).then(hash => { return ethers.utils .poll( () => { - return provider!.getTransaction(hash).then((tx: TransactionResponse) => { + return provider!.getTransaction(hash).then((tx: ethers.providers.TransactionResponse) => { if (tx === null) { return undefined } @@ -291,11 +294,10 @@ export class Web3Signer extends Signer implements TypedDataSigner { // sendTransactionBatch is a convenience method to call sendTransaction in a batch format, allowing you to // send multiple transaction as a single payload and just one on-chain transaction. async sendTransactionBatch( - transactions: Deferrable[]>, - chainId?: ChainIdLike, - allSigners?: boolean - ): Promise { - const batch = await resolveArrayProperties[]>(transactions) + transactions: Deferrable, + chainId?: ChainIdLike + ): Promise { + const batch = await resolveArrayProperties[]>(transactions) if (!batch || batch.length === 0) { throw new Error('cannot send empty batch') } @@ -305,33 +307,31 @@ export class Web3Signer extends Signer implements TypedDataSigner { throw new Error('transaction request expected for sendTransactionBatch, transaction response found') } - const tx: TransactionRequest = { ...batch[0] } - if (batch.length > 1) { - tx.auxiliary = batch.splice(1) - } - - return this.sendTransaction(tx, chainId, allSigners) + const asExtended = toExtended(batch) + return this.sendTransaction(asExtended, chainId) } signTransactions( - transaction: Deferrable, - chainId?: ChainIdLike, - allSigners?: boolean - ): Promise { + transaction: Deferrable, + chainId?: ChainIdLike + ): Promise { transaction = shallowCopy(transaction) // TODO: transaction argument..? make sure to resolve any properties and serialize property before sending over // the wire.. see sendUncheckedTransaction and resolveProperties return this.provider.send('eth_signTransaction', [transaction], maybeChainId(chainId) || this.defaultChainId) } - sendSignedTransactions(signedTxs: SignedTransactions, chainId?: ChainIdLike): Promise { + sendSignedTransactions( + signedTxs: commons.transaction.SignedTransactionBundle, + chainId?: ChainIdLike + ): Promise { // sequence_relay throw new Error('TODO') } // updateConfig.. // NOTE: this is not supported by the remote wallet by default. - async updateConfig(newConfig?: WalletConfig): Promise<[WalletConfig, TransactionResponse | undefined]> { + async updateConfig(newConfig?: commons.config.Config): Promise<[commons.config.Config, ethers.providers.TransactionResponse | undefined]> { // sequence_updateConfig const [config, tx] = await this.provider.send('sequence_updateConfig', [newConfig], this.defaultChainId) if (tx === null) { @@ -344,7 +344,7 @@ export class Web3Signer extends Signer implements TypedDataSigner { // publishConfig.. // NOTE: this is not supported by the remote wallet by default. - async publishConfig(): Promise { + async publishConfig(): Promise { const provider = await this.getSender(this.defaultChainId) const tx = await provider!.send('sequence_publishConfig', []) @@ -364,7 +364,7 @@ export class Web3Signer extends Signer implements TypedDataSigner { // ethers JsonRpcSigner methods // - async _legacySignMessage(message: Bytes | string, chainId?: ChainIdLike, allSigners?: boolean): Promise { + async _legacySignMessage(message: Bytes | string, chainId?: ChainIdLike): Promise { const provider = await this.getSender(maybeChainId(chainId) || this.defaultChainId) const data = typeof message === 'string' ? ethers.utils.toUtf8Bytes(message) : message @@ -379,13 +379,12 @@ export class Web3Signer extends Signer implements TypedDataSigner { domain: TypedDataDomain, types: Record>, message: Record, - chainId?: ChainIdLike, - allSigners?: boolean + chainId?: ChainIdLike ): Promise { - return this.signTypedData(domain, types, message, chainId, allSigners) + return this.signTypedData(domain, types, message, chainId) } - async sendUncheckedTransaction(transaction: Deferrable, chainId?: ChainIdLike): Promise { + async sendUncheckedTransaction(transaction: Deferrable, chainId?: ChainIdLike): Promise { transaction = shallowCopy(transaction) const fromAddress = this.getAddress() @@ -460,7 +459,7 @@ const allowedTransactionKeys: { [key: string]: boolean } = { } const hexlifyTransaction = ( - transaction: TransactionRequest, + transaction: ExtendedTransactionRequest, allowExtra?: { [key: string]: boolean } ): { [key: string]: string } => { // Check only allowed properties are given @@ -513,9 +512,9 @@ const hexlifyTransaction = ( } class UncheckedJsonRpcSigner extends Web3Signer { - sendTransaction(transaction: Deferrable): Promise { + sendTransaction(transaction: Deferrable): Promise { return this.sendUncheckedTransaction(transaction).then(hash => { - return { + return { chainId: 0, confirmations: 0, data: '', diff --git a/packages/provider/src/transports/base-provider-transport.ts b/packages/provider/src/transports/base-provider-transport.ts index c8749ba90..4940d1e70 100644 --- a/packages/provider/src/transports/base-provider-transport.ts +++ b/packages/provider/src/transports/base-provider-transport.ts @@ -17,9 +17,10 @@ import { TypedEventEmitter } from '../types' -import { NetworkConfig, WalletContext, JsonRpcRequest, JsonRpcResponseCallback, JsonRpcResponse } from '@0xsequence/network' +import { NetworkConfig, JsonRpcRequest, JsonRpcResponseCallback } from '@0xsequence/network' import { logger } from '@0xsequence/utils' import { ethers } from 'ethers' +import { commons } from '@0xsequence/core' export const PROVIDER_OPEN_TIMEOUT = 30000 // in ms @@ -39,7 +40,7 @@ export abstract class BaseProviderTransport implements ProviderTransport { protected connectPayload: ConnectDetails | undefined protected accountsChangedPayload: { accounts: string[]; origin?: string } | undefined protected networksPayload: NetworkConfig[] | undefined - protected walletContextPayload: WalletContext | undefined + protected walletContextPayload: commons.context.VersionedContext | undefined protected _sessionId?: string protected _init: InitState diff --git a/packages/provider/src/transports/base-wallet-transport.ts b/packages/provider/src/transports/base-wallet-transport.ts index 7bb8b6456..cac2939a1 100644 --- a/packages/provider/src/transports/base-wallet-transport.ts +++ b/packages/provider/src/transports/base-wallet-transport.ts @@ -5,23 +5,22 @@ import { ProviderMessageRequest, EventType, ProviderMessageResponse, - ProviderMessageTransport, ProviderRpcError, InitState, ConnectDetails, - OpenWalletIntent, WalletSession, TransportSession } from '../types' import { WalletRequestHandler } from './wallet-request-handler' -import { NetworkConfig, WalletContext, JsonRpcRequest, JsonRpcResponseCallback } from '@0xsequence/network' +import { NetworkConfig, JsonRpcRequest, JsonRpcResponseCallback } from '@0xsequence/network' import { logger, sanitizeAlphanumeric, sanitizeHost, sanitizeNumberString } from '@0xsequence/utils' import { AuthorizationOptions } from '@0xsequence/auth' import { PROVIDER_OPEN_TIMEOUT } from './base-provider-transport' import { isBrowserExtension, LocalStorage } from '../utils' +import { commons } from '@0xsequence/core' const TRANSPORT_SESSION_LS_KEY = '@sequence.transportSession' @@ -70,7 +69,7 @@ export abstract class BaseWalletTransport implements WalletTransport { } }) - this.walletRequestHandler.on('walletContext', (walletContext: WalletContext) => { + this.walletRequestHandler.on('walletContext', (walletContext: commons.context.VersionedContext) => { if (!this.registered || !walletContext) return this.notifyWalletContext(walletContext) }) @@ -231,7 +230,7 @@ export abstract class BaseWalletTransport implements WalletTransport { }) } - notifyWalletContext(walletContext: WalletContext) { + notifyWalletContext(walletContext: commons.context.VersionedContext) { this.sendMessage({ idx: -1, type: EventType.WALLET_CONTEXT, @@ -368,7 +367,7 @@ export abstract class BaseWalletTransport implements WalletTransport { } // ensure signer is ready - await this.walletRequestHandler.getSigner() + await this.walletRequestHandler.getAccount() // Notify open and proceed to prompt for connection if intended if (!(await this.walletRequestHandler.isSignedIn())) { @@ -386,9 +385,10 @@ export abstract class BaseWalletTransport implements WalletTransport { let chainId: number | undefined = undefined try { if (networkId) { - chainId = await this.walletRequestHandler.setDefaultNetwork(networkId, false) + const networkIdNumber = ethers.BigNumber.from(networkId).toNumber() + chainId = await this.walletRequestHandler.setDefaultChainId(networkIdNumber) } else { - chainId = await this.walletRequestHandler.getChainId() + chainId = this.walletRequestHandler.defaultChainId() } } catch (err) { console.error(err) @@ -423,9 +423,10 @@ export abstract class BaseWalletTransport implements WalletTransport { let chainId: number | undefined = undefined try { if (networkId) { - chainId = await this.walletRequestHandler.setDefaultNetwork(networkId, false) + const networkIdNumber = ethers.BigNumber.from(networkId).toNumber() + chainId = await this.walletRequestHandler.setDefaultChainId(networkIdNumber) } else { - chainId = await this.walletRequestHandler.getChainId() + chainId = this.walletRequestHandler.defaultChainId() } } catch (err) { console.error(err) diff --git a/packages/provider/src/transports/wallet-request-handler.ts b/packages/provider/src/transports/wallet-request-handler.ts index 86346dbc3..d2640169e 100644 --- a/packages/provider/src/transports/wallet-request-handler.ts +++ b/packages/provider/src/transports/wallet-request-handler.ts @@ -1,5 +1,12 @@ +import { Account, AccountStatus } from '@0xsequence/account' +import { signAuthorization, AuthorizationOptions } from '@0xsequence/auth' +import { commons } from '@0xsequence/core' +import { NetworkConfig, JsonRpcHandler, JsonRpcRequest, JsonRpcResponseCallback, JsonRpcResponse, getChainId } from '@0xsequence/network' +import { logger, TypedData } from '@0xsequence/utils' +import { BigNumber, ethers, providers } from 'ethers' import { EventEmitter2 as EventEmitter } from 'eventemitter2' +import { fromExtended } from '../extended' import { ProviderMessageRequest, ProviderMessageResponse, @@ -15,15 +22,6 @@ import { ProviderEventTypes, TypedEventEmitter } from '../types' - -import { BigNumber, ethers, providers } from 'ethers' - -import { NetworkConfig, JsonRpcHandler, JsonRpcRequest, JsonRpcResponseCallback, JsonRpcResponse } from '@0xsequence/network' -import { Signer } from '@0xsequence/wallet' -import { isSignedTransactions, TransactionRequest } from '@0xsequence/transactions' -import { signAuthorization, AuthorizationOptions } from '@0xsequence/auth' -import { logger, TypedData } from '@0xsequence/utils' - import { isWalletUpToDate, prefixEIP191Message } from '../utils' type ExternalProvider = providers.ExternalProvider @@ -32,67 +30,57 @@ const SIGNER_READY_TIMEOUT = 10000 export interface WalletSignInOptions { connect?: boolean - mainnetNetworks?: NetworkConfig[] - testnetNetworks?: NetworkConfig[] - defaultNetworkId?: string | number + defaultNetworkId?: number } export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, ProviderMessageRequestHandler { // signer interface of the wallet. A null value means there is no signer (ie. user not signed in). An undefined // value means the signer state is unknown, usually meaning the wallet app is booting up and initializing. Of course // a Signer value is the actually interface to a signed-in account - private signer: Signer | null | undefined + private account: Account | null | undefined private signerReadyCallbacks: Array<() => void> = [] private prompter: WalletUserPrompter | null - private mainnetNetworks: NetworkConfig[] - private testnetNetworks: NetworkConfig[] + private networks: NetworkConfig[] private _openIntent?: OpenWalletIntent private _connectOptions?: ConnectOptions - private _defaultNetworkId?: string | number - private _chainId?: number private events: TypedEventEmitter = new EventEmitter() as TypedEventEmitter onConnectOptionsChange: ((connectOptions: ConnectOptions | undefined) => void) | undefined = undefined constructor( - signer: Signer | null | undefined, + account: Account | null | undefined, prompter: WalletUserPrompter | null, - mainnetNetworks: NetworkConfig[], - testnetNetworks: NetworkConfig[] = [] + networks: NetworkConfig[] ) { - this.signer = signer + this.account = account this.prompter = prompter - this.mainnetNetworks = mainnetNetworks - this.testnetNetworks = testnetNetworks + this.networks = networks } - async signIn(signer: Signer | null, options: WalletSignInOptions = {}) { - this.setSigner(signer) + defaultChainId(): number { + return this.prompter?.getDefaultChainId() ?? this.networks[0].chainId + } - const { connect, mainnetNetworks, testnetNetworks, defaultNetworkId } = options + private findNetworkID(network: string | number) { + const lnet = typeof network === 'string' ? network.toLowerCase() : network + const networkId = this.networks.find(n => { + return n.name.toLowerCase() === lnet || n.chainId.toString() === lnet || n.chainId === lnet + }) - if (mainnetNetworks && mainnetNetworks.length > 0) { - this.mainnetNetworks = mainnetNetworks - } - if (testnetNetworks && testnetNetworks.length > 0) { - this.testnetNetworks = testnetNetworks - } - if ( - (!this.mainnetNetworks || this.mainnetNetworks.length === 0) && - (!this.testnetNetworks || this.testnetNetworks.length === 0) - ) { - throw new Error('signIn failed as network configuration is empty') + if (!networkId) { + throw new Error(`Network ${network} not found`) } - const networkId = defaultNetworkId || this._defaultNetworkId - if (networkId) { - if (!(await this.setDefaultNetwork(networkId, false))) { - throw new Error(`WalletRequestHandler setup unable to set defaultNetworkId ${networkId}`) - } - } + return networkId.chainId + } + + async signIn(account: Account | null, options: WalletSignInOptions = {}) { + this.setAccount(account) + + const { connect, defaultNetworkId } = options // Optionally, connect the dapp and wallet. In case connectOptions are provided, we will perform // necessary auth request, and then notify the dapp of the 'connect' details. @@ -120,26 +108,30 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P this.notifyClose() } } + + if (defaultNetworkId && this.defaultChainId() !== defaultNetworkId) { + await this.prompter?.promptChangeNetwork(defaultNetworkId) + } } signOut() { // signed out state - this.setSigner(null) + this.setAccount(null) } signerReset() { // resetting signer puts the wallet in an uninitialized state, which requires the app to // re-initiatize and set the signer either as "null" (ie. no signer) or "Signer" (ie. signed in). - this.signer = undefined + this.account = undefined } signerReady(timeout: number = SIGNER_READY_TIMEOUT): Promise { return new Promise((resolve, reject) => { - if (this.signer !== undefined) { + if (this.account !== undefined) { resolve() } else { setTimeout(() => { - if (this.signer === undefined) { + if (this.account === undefined) { this.signerReadyCallbacks = [] reject(`signerReady timed out`) } @@ -150,7 +142,7 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P } async connect(options?: ConnectOptions): Promise { - if (!this.signer) { + if (!this.account) { return { connected: false, chainId: '0x0', @@ -158,9 +150,29 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P } } + let chainId: number | string + switch (typeof options?.networkId) { + case 'string': + // First see if it matches the name of a network + try { + chainId = this.findNetworkID(options.networkId) + } catch (err) { + // If not, try to parse it as a big number + chainId = getChainId(options.networkId) + } + + break + case 'number': + chainId = options.networkId + break + default: + chainId = this.prompter?.getDefaultChainId() ?? 1 + break + } + const connectDetails: ConnectDetails = { connected: true, - chainId: ethers.utils.hexlify(await this.getChainId()) + chainId: ethers.BigNumber.from(chainId).toHexString() } if (options && options.authorize) { @@ -176,7 +188,8 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P // } try { - connectDetails.proof = await signAuthorization(this.signer, authOptions) + // TODO: Either implement account as a signer, or change signAuthorization to accept an account + connectDetails.proof = await signAuthorization(this.account, chainId, authOptions) } catch (err) { logger.warn(`connect, signAuthorization failed for options: ${JSON.stringify(options)}, due to: ${err.message}`) return { @@ -248,38 +261,47 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P result: null } - await this.getSigner() + await this.getAccount() try { // only allow public json rpc method to the provider when user is not logged in, aka signer is not set - if ((!this.signer || this.signer === null) && !permittedJsonRpcMethods.includes(request.method)) { + if ((!this.account || this.account === null) && !permittedJsonRpcMethods.includes(request.method)) { // throw new Error(`not logged in. ${request.method} is unavailable`) throw ErrSignedInRequired } - // wallet signer - const signer = this.signer - if (!signer) throw new Error('WalletRequestHandler: wallet signer is not configured') + // wallet account + const account = this.account + if (!account) throw new Error('WalletRequestHandler: wallet account is not configured') // fetch the provider for the specific chain, or undefined will select defaultChain - const provider = await signer.getProvider(chainId) + const provider = this.account?.provider(chainId ?? this.defaultChainId()) if (!provider) throw new Error(`WalletRequestHandler: wallet provider is not configured for chainId ${chainId}`) + const jsonRpcProvider = provider instanceof ethers.providers.JsonRpcProvider ? provider : undefined switch (request.method) { case 'net_version': { - const result = await provider.send('net_version', []) + if (!jsonRpcProvider) { + throw new Error(`Account provider doesn't support send method`) + } + + const result = await jsonRpcProvider.send('net_version', []) response.result = result break } case 'eth_chainId': { - const result = await provider.send('eth_chainId', []) + if (!jsonRpcProvider) { + throw new Error(`Account provider doesn't support send method`) + } + + const result = await jsonRpcProvider.send('eth_chainId', []) response.result = result break } case 'eth_accounts': { - const walletAddress = await signer.getAddress() + const walletAddress = account.address response.result = [walletAddress] break } @@ -291,20 +313,27 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P break } + case 'sequence_sign': case 'personal_sign': case 'eth_sign': { // note: message from json-rpc input is in hex format let message: any // there is a difference in the order of the params: - // personal_sign: [data, address] + // sequence_sign, personal_sign: [data, address] // eth_sign: [address, data] - if (request.method === 'personal_sign') { - const [data, address] = request.params! - message = data - } else { - const [address, data] = request.params! - message = data + switch (request.method) { + case 'sequence_sign': + case 'personal_sign': { + const [data, _address] = request.params! + message = data + break + } + case 'eth_sign': { + const [_address, data] = request.params! + message = data + break + } } let sig = '' @@ -315,14 +344,16 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P // TODO: // if (process.env.TEST_MODE === 'true' && this.prompter === null) { + const sequenceVerified = request.method === 'sequence_sign' if (this.prompter === null) { // prompter is null, so we'll sign from here - sig = await signer.signMessage(prefixedMessage, chainId) + sig = await account.signMessage(prefixedMessage, chainId ?? this.defaultChainId(), sequenceVerified ? 'eip6492' : 'ignore') } else { - const promptResultForDeployment = await this.handleConfirmWalletDeployPrompt(this.prompter, signer, chainId) - if (promptResultForDeployment) { - sig = await this.prompter.promptSignMessage({ chainId: chainId, message: prefixedMessage }, this.connectOptions) - } + sig = await this.prompter.promptSignMessage({ + chainId: chainId, + message: prefixedMessage, + eip6492: sequenceVerified + }, this.connectOptions) } if (sig && sig.length > 0) { @@ -334,6 +365,7 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P break } + case 'sequence_signTypedData_v4': case 'eth_signTypedData': case 'eth_signTypedData_v4': { // note: signingAddress from json-rpc input is in hex format, and typedDataObject @@ -344,7 +376,9 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P if (typeof typedDataObject === 'string') { try { typedData = JSON.parse(typedDataObject) - } catch (e) {} + } catch (e) { + console.warn('walletRequestHandler: error parsing typedData', e) + } } else { typedData = typedDataObject } @@ -355,14 +389,16 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P let sig = '' + const sequenceVerified = request.method === 'sequence_signTypedData_v4' if (this.prompter === null) { // prompter is null, so we'll sign from here - sig = await signer.signTypedData(typedData.domain, typedData.types, typedData.message, chainId) + sig = await account.signTypedData(typedData.domain, typedData.types, typedData.message, chainId ?? this.defaultChainId(), sequenceVerified ? 'eip6492' : 'ignore') } else { - const promptResultForDeployment = await this.handleConfirmWalletDeployPrompt(this.prompter, signer, chainId) - if (promptResultForDeployment) { - sig = await this.prompter.promptSignMessage({ chainId: chainId, typedData: typedData }, this.connectOptions) - } + sig = await this.prompter.promptSignMessage({ + chainId: chainId, + typedData: typedData, + eip6492: sequenceVerified + }, this.connectOptions) } if (sig && sig.length > 0) { @@ -376,19 +412,22 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P case 'eth_sendTransaction': { // https://eth.wiki/json-rpc/API#eth_sendtransaction - const [transactionParams] = request.params! - - // eth_sendTransaction uses 'gas' - // ethers and sequence use 'gasLimit' - if ('gas' in transactionParams && transactionParams.gasLimit === undefined) { - transactionParams.gasLimit = transactionParams.gas - delete transactionParams.gas - } + const transactionParams = fromExtended(request.params![0]) + .map((tx) => { + // eth_sendTransaction uses 'gas' + // ethers and sequence use 'gasLimit' + if ('gas' in tx && tx.gasLimit === undefined) { + tx.gasLimit = tx.gas as any + delete tx.gas + } + + return tx + }) let txnHash = '' if (this.prompter === null) { // prompter is null, so we'll send from here - const txnResponse = await signer.sendTransaction(transactionParams, chainId) + const txnResponse = await account.sendTransaction(transactionParams, chainId ?? this.defaultChainId()) txnHash = txnResponse.hash } else { // prompt user to provide the response @@ -409,7 +448,7 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P const [transaction] = request.params! const sender = ethers.utils.getAddress(transaction.from) - if (sender !== (await signer.getAddress())) { + if (sender !== account.address) { throw new Error('sender address does not match wallet') } @@ -418,8 +457,8 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P // this can only be broadcasted using an RPC provider with support for signed Sequence transactions, like this one. // // TODO: verify serializing / transporting the SignedTransaction object works as expected, most likely however - // we will want to resolveProperties the bignumber values to hex strings - response.result = await signer.signTransactions(transaction, chainId) + // we will want to resolveProperties the big number values to hex strings + response.result = await account.signTransactions(transaction, chainId ?? this.defaultChainId()) } else { response.result = await this.prompter.promptSignTransaction(transaction, chainId, this.connectOptions) } @@ -432,10 +471,10 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P // and would have prompted the user upon signing. // https://eth.wiki/json-rpc/API#eth_sendRawTransaction - if (isSignedTransactions(request.params![0])) { + if (commons.transaction.isSignedTransactionBundle(request.params![0])) { const txChainId = BigNumber.from(request.params![0].chainId).toNumber() - const tx = await (await signer.getRelayer(txChainId))!.relay(request.params![0]) - response.result = (await tx).hash + const tx = await account.relayer(txChainId)!.relay(request.params![0]) + response.result = tx.hash } else { const tx = await provider.sendTransaction(request.params![0]) response.result = tx.hash @@ -447,15 +486,12 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P const address = ethers.utils.getAddress(request.params![0] as string) const tag = request.params![1] - const walletAddress = ethers.utils.getAddress(await signer.getAddress()) + // TODO: Maybe we should fetch this data from the relayer or from the reader + // but for now we keep it simple and just use the provider + + const count = await provider.getTransactionCount(address, tag) + response.result = ethers.BigNumber.from(count).toHexString() - if (address === walletAddress) { - const count = await signer.getTransactionCount(tag) - response.result = ethers.BigNumber.from(count).toHexString() - } else { - const count = await provider.getTransactionCount(address, tag) - response.result = ethers.BigNumber.from(count).toHexString() - } break } @@ -511,10 +547,7 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P const chainId = ethers.BigNumber.from(switchParams.chainId) - const ok = await this.setDefaultNetwork(chainId.toString(), true) - if (!ok) { - throw new Error(`unable to set chainId ${chainId}`) - } + this.setDefaultChainId(chainId.toNumber()) response.result = null // success break @@ -522,21 +555,36 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P // smart wallet method case 'sequence_getWalletContext': { - response.result = await signer.getWalletContext() + response.result = account.contexts break } // smart wallet method case 'sequence_getWalletConfig': { const [chainId] = request.params! - response.result = await signer.getWalletConfig(chainId) + if (chainId) { + response.result = [(await account.status(chainId)).onChain.config] + } else { + response.result = await Promise.all(account.networks.map(async network => { + const status = await account.status(network.chainId) + return status.onChain.config + })) + } break } // smart wallet method case 'sequence_getWalletState': { const [chainId] = request.params! - response.result = await signer.getWalletState(chainId) + // TODO: Add getWalletState to the Signer interface + if (chainId) { + response.result = [getLegacyWalletState(chainId, await account.status(chainId))] + } else { + response.result = await Promise.all(account.networks.map(async network => { + const status = await account.status(network.chainId) + return getLegacyWalletState(network.chainId, status) + })) + } break } @@ -592,23 +640,24 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P // set default network of wallet case 'sequence_setDefaultNetwork': { - const [defaultNetworkId] = request.params! + const [defaultChainId] = request.params! - if (!defaultNetworkId) { - throw new Error('invalid request, method argument defaultNetworkId cannot be empty') - } - const ok = await this.setDefaultNetwork(defaultNetworkId) - if (!ok) { - throw new Error(`unable to set default network ${defaultNetworkId}`) + if (!defaultChainId) { + throw new Error('invalid request, method argument defaultChainId cannot be empty') } + this.setDefaultChainId(defaultChainId) response.result = await this.getNetworks(true) break } default: { + if (!jsonRpcProvider) { + throw new Error(`Account provider doesn't support send method`) + } + // NOTE: provider here will be chain-bound if chainId is provided - const providerResponse = await provider.send(request.method, request.params!) + const providerResponse = await jsonRpcProvider.send(request.method, request.params!) response.result = providerResponse } } @@ -635,21 +684,7 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P } async getAddress(): Promise { - if (!this.signer) { - return '' - } else { - return this.signer.getAddress() - } - } - - async getChainId(): Promise { - if (!this.signer) { - return 0 - } else { - if (this._chainId) return this._chainId // memoized - this._chainId = await this.signer.getChainId() - return this._chainId - } + return this.account?.address ?? '' } get openIntent(): OpenWalletIntent | undefined { @@ -670,55 +705,38 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P this.onConnectOptionsChange?.(options) } - get defaultNetworkId(): string | number | undefined { - return this._defaultNetworkId - } - - async setDefaultNetwork(chainId: string | number, notifyNetworks: boolean = true): Promise { - if (!chainId) return undefined - this._defaultNetworkId = chainId - this._chainId = undefined - - if (this.signer && (this.signer).setNetworks) { - const defaultChainId: number = (this.signer).setNetworks(this.mainnetNetworks, this.testnetNetworks, chainId) - if (defaultChainId && notifyNetworks) { - await this.notifyNetworks() - } - return defaultChainId - } else { - return undefined - } + async setDefaultChainId(chainId: number): Promise { + await this.prompter?.promptChangeNetwork(chainId) + return this.defaultChainId() } async getNetworks(jsonRpcResponse?: boolean): Promise { - if (!this.signer) { + if (!this.account) { logger.warn('signer not set: getNetworks is returning an empty list') return [] } - const networks = await this.signer.getNetworks() - if (jsonRpcResponse) { // omit provider and relayer objects as they are not serializable - return networks.map(n => { + return this.account.networks.map(n => { const network: NetworkConfig = { ...n } network.provider = undefined network.relayer = undefined return network }) } else { - return networks + return this.account.networks } } - async walletSession(): Promise { - return !this.signer - ? undefined - : { - walletContext: await this.signer.getWalletContext(), - accountAddress: await this.signer.getAddress(), - networks: await this.getNetworks(true) - } + walletSession(): WalletSession | undefined { + if (!this.account) return undefined + return { + walletContext: this.account.contexts, + accountAddress: this.account.address, + // The dapp shouldn't access the relayer directly, and the provider (as an object) is not serializable. + networks: this.account.networks.map((n) => ({ ...n, provider: undefined, relayer: undefined })) + } } notifyConnect(connectDetails: ConnectDetails, origin?: string) { @@ -737,7 +755,7 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P const n = networks || (await this.getNetworks(true)) this.events.emit('networks', n) if (n.length > 0) { - const defaultNetwork = n.find(network => network.isDefaultChain) + const defaultNetwork = n.find(network => network.chainId === this.defaultChainId()) if (defaultNetwork) { this.events.emit('chainChanged', ethers.utils.hexlify(defaultNetwork.chainId)) } @@ -747,11 +765,11 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P } async notifyWalletContext() { - if (!this.signer) { + if (!this.account) { logger.warn('signer not set: skipping to notify wallet context') return } - const walletContext = await this.signer.getWalletContext() + const walletContext = this.account.contexts this.events.emit('walletContext', walletContext) } @@ -761,21 +779,21 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P isSignedIn = async (): Promise => { await this.signerReady() - return !!this.signer + return !!this.account } - getSigner = async (): Promise => { + getAccount = async (): Promise => { await this.signerReady() - if (this.signer === undefined) { + if (this.account === undefined) { throw new Error('signerReady failed resolve') } - return this.signer + return this.account } - setSigner(signer: Signer | null | undefined) { - this.signer = signer + setAccount(account: Account | null | undefined) { + this.account = account - if (signer !== undefined) { + if (account !== undefined) { for (let i = 0; i < this.signerReadyCallbacks.length; i++) { this.signerReadyCallbacks[i]() } @@ -785,7 +803,8 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P private async handleConfirmWalletDeployPrompt( prompter: WalletUserPrompter, - signer: Signer, + account: Account, + sequenceVerified: boolean, chainId?: number ): Promise { // check if wallet is deployed and up to date, if not, prompt user to deploy @@ -793,33 +812,85 @@ export class WalletRequestHandler implements ExternalProvider, JsonRpcHandler, P if (!chainId) { return true } - const isUpToDate = await isWalletUpToDate(signer, chainId) - if (isUpToDate) { + + const skipsDeploy = (status: AccountStatus) => { + return status.canOnchainValidate || (status.original.version === 2 && sequenceVerified) + } + + const status = await account.status(chainId) + if (skipsDeploy(status)) { return true } + const promptResult = await prompter.promptConfirmWalletDeploy(chainId, this.connectOptions) + // if client returned true, check again to make sure wallet is deployed and up to date if (promptResult) { - const isPromptResultCorrect = await isWalletUpToDate(signer, chainId) - if (!isPromptResultCorrect) { + const status2 = await account.status(chainId) + + if (skipsDeploy(status2)) { + return true + } else { logger.error('WalletRequestHandler: result for promptConfirmWalletDeploy is not correct') return false - } else { - return true } } + return false } } export interface WalletUserPrompter { + getDefaultChainId(): number + promptConnect(options?: ConnectOptions): Promise promptSignInConnect(options?: ConnectOptions): Promise promptSignMessage(message: MessageToSign, options?: ConnectOptions): Promise - promptSignTransaction(txn: TransactionRequest, chaindId?: number, options?: ConnectOptions): Promise - promptSendTransaction(txn: TransactionRequest, chaindId?: number, options?: ConnectOptions): Promise + promptSignTransaction(txn: commons.transaction.Transactionish, chainId?: number, options?: ConnectOptions): Promise + promptSendTransaction(txn: commons.transaction.Transactionish, chainId?: number, options?: ConnectOptions): Promise promptConfirmWalletDeploy(chainId: number, options?: ConnectOptions): Promise + + promptChangeNetwork(chainId: number): Promise +} + +interface LegacyWalletState { + context: commons.context.WalletContext + config?: commons.config.Config + + // the wallet address + address: string + + // the chainId of the network + chainId: number + + // whether the wallet has been ever deployed + deployed: boolean + + // the imageHash of the `config` WalletConfig + imageHash: string + + // the last imageHash of a WalletConfig, stored on-chain + lastImageHash?: string + + // whether the WalletConfig object itself has been published to logs + published?: boolean + + status: AccountStatus +} + +function getLegacyWalletState(chainId: number, status: AccountStatus): LegacyWalletState { + return { + context: status.original.context, + config: status.onChain.config, + address: commons.context.addressOf(status.original.context, status.original.imageHash), + chainId, + deployed: status.onChain.deployed, + imageHash: status.imageHash, + lastImageHash: status.onChain.imageHash, + published: true, + status + } } const permittedJsonRpcMethods = [ diff --git a/packages/provider/src/types.ts b/packages/provider/src/types.ts index ce9b9bbff..52f258855 100644 --- a/packages/provider/src/types.ts +++ b/packages/provider/src/types.ts @@ -1,4 +1,6 @@ -import { NetworkConfig, WalletContext, JsonRpcRequest, JsonRpcResponse, JsonRpcHandler } from '@0xsequence/network' +import { ETHAuthProof as AuthETHAuthProof } from '@0xsequence/auth' +import { commons } from '@0xsequence/core' +import { JsonRpcHandler, JsonRpcRequest, JsonRpcResponse, NetworkConfig, ProviderRpcError as NetworkProviderRpcError } from '@0xsequence/network' import { TypedData } from '@0xsequence/utils' export interface ProviderTransport extends JsonRpcHandler, ProviderMessageTransport, ProviderMessageRequestHandler { @@ -49,11 +51,7 @@ export type ProviderMessageResponse = ProviderMessage // which may contain the result or an error payload from the wallet. export type ProviderMessageResponseCallback = (error?: ProviderRpcError, response?: ProviderMessageResponse) => void -export interface ProviderRpcError extends Error { - message: string - code?: number - data?: { [key: string]: any } -} +export type ProviderRpcError = NetworkProviderRpcError export interface ProviderMessageRequestHandler { // sendMessageRequest sends a ProviderMessageRequest over the wire to the wallet. @@ -116,7 +114,7 @@ export interface WalletEventTypes { chainChanged: (chainIdHex: string) => void networks: (networks: NetworkConfig[]) => void - walletContext: (walletContext: WalletContext) => void + walletContext: (walletContext: commons.context.VersionedContext) => void } export interface ProviderEventTypes extends WalletEventTypes { @@ -156,6 +154,9 @@ export interface ConnectOptions { /** authorize will perform an ETHAuth eip712 signing and return the proof to the dapp. */ authorize?: boolean + /** authorizeVersion is the version of the SDK that will validate the ETHAuth proof. */ + authorizeVersion?: number + /** askForEmail will prompt to give permission to the dapp to access email address */ askForEmail?: boolean @@ -263,19 +264,15 @@ export interface MessageToSign { message?: Uint8Array typedData?: TypedData chainId?: number -} -export interface ETHAuthProof { - // eip712 typed-data payload for ETHAuth domain as input - typedData: TypedData - - // signature encoded in an ETHAuth proof string - proofString: string + eip6492?: boolean } +export type ETHAuthProof = AuthETHAuthProof + export interface WalletSession { // Wallet context - walletContext?: WalletContext + walletContext?: commons.context.VersionedContext // Account address of the wallet accountAddress?: string diff --git a/packages/provider/src/utils.ts b/packages/provider/src/utils.ts index 88ac23681..58d4f6081 100644 --- a/packages/provider/src/utils.ts +++ b/packages/provider/src/utils.ts @@ -1,10 +1,8 @@ -import { ethers, BigNumberish, BytesLike } from 'ethers' -import { WalletContext } from '@0xsequence/network' -import { WalletConfig, addressOf, DecodedSignature, isConfigEqual } from '@0xsequence/config' -import { packMessageData, encodeMessageDigest, TypedData, encodeTypedDataDigest } from '@0xsequence/utils' +import { ethers, BytesLike } from 'ethers' import { Web3Provider } from './provider' -import { isValidSignature as _isValidSignature, recoverConfig, Signer } from '@0xsequence/wallet' import { messageIsExemptFromEIP191Prefix } from './eip191exceptions' +import { AccountStatus } from '@0xsequence/account' +import { commons } from '@0xsequence/core' const eip191prefix = ethers.utils.toUtf8Bytes('\x19Ethereum Signed Message:\n') @@ -25,112 +23,115 @@ export const prefixEIP191Message = (message: BytesLike): Uint8Array => { } } -export const isValidSignature = async ( - address: string, - digest: Uint8Array, - sig: string, - provider: Web3Provider | ethers.providers.Web3Provider | ethers.providers.Provider, - chainId?: number, - walletContext?: WalletContext -): Promise => { - if (!chainId) { - chainId = (await provider.getNetwork())?.chainId +export const trimEIP191Prefix = (prefixedMessage: Uint8Array): Uint8Array => { + // If the message is not prefixed, we return the message as is. + if (JSON.stringify(prefixedMessage.slice(0, eip191prefix.length)) !== JSON.stringify(eip191prefix)) { + return prefixedMessage } - if (!walletContext && Web3Provider.isSequenceProvider(provider)) { - walletContext = await provider.getSigner().getWalletContext() + + // We have two parts to remove. + // First is the EIP-191 prefix. + const ethereumSignedMessagePartSlicedArray = prefixedMessage.slice(eip191prefix.length) + + // Second is the digits added which represent length of the message without the prefix + // and we need to find the prefix that will match this. + // Here first we take the max prefix char length, and check if as a number it is bigger + // than the length of the message (since prefix is added to represent length of original message), + // if it is we remove 1 from char length, if not we keep the max prefix char length. + // As an example for the case where , if the message is 123456789, the expected prefix char is 9, with starting value 9123456789 + // the char length of the total message with the prefix is 10, so the max prefix char length we start is 2 from [1,0], and as a number 10, it is longer + // than the length of the message after removing prefix (10 - 2 = 8), so we slice 1 char less, which is 9, and we get the correct prefix. + const maxPrefixCharLength = String(ethereumSignedMessagePartSlicedArray.length).length + + let prefixCharLenght: number + let prefixAsNumber: number + + try { + prefixAsNumber = Number(ethers.utils.toUtf8String(ethereumSignedMessagePartSlicedArray.slice(0, maxPrefixCharLength))) + } catch { + prefixAsNumber = Number(ethers.utils.hexlify(ethereumSignedMessagePartSlicedArray.slice(0, maxPrefixCharLength))) } - return _isValidSignature(address, digest, sig, provider, walletContext, chainId) -} -export const isValidMessageSignature = async ( - address: string, - message: string | Uint8Array, - signature: string, - provider: Web3Provider | ethers.providers.Web3Provider | ethers.providers.Provider, - chainId?: number, - walletContext?: WalletContext -): Promise => { - const prefixed = prefixEIP191Message(message) - const digest = encodeMessageDigest(prefixed) - return isValidSignature(address, digest, signature, provider, chainId, walletContext) + if (prefixAsNumber > ethereumSignedMessagePartSlicedArray.length || !Number.isInteger(prefixAsNumber)) { + prefixCharLenght = maxPrefixCharLength - 1 + } else { + prefixCharLenght = maxPrefixCharLength + } + + const prefixRevertedMessage = ethereumSignedMessagePartSlicedArray.slice(prefixCharLenght) + + return prefixRevertedMessage } -export const isValidTypedDataSignature = ( +export const isValidSignature = async ( address: string, - typedData: TypedData, - signature: string, - provider: Web3Provider | ethers.providers.Web3Provider | ethers.providers.Provider, - chainId?: number, - walletContext?: WalletContext + digest: Uint8Array, + sig: string, + provider: Web3Provider | ethers.providers.Web3Provider, + contexts: { [key: number]: commons.context.WalletContext } ): Promise => { - return isValidSignature(address, encodeTypedDataDigest(typedData), signature, provider, chainId, walletContext) + const reader = new commons.reader.OnChainReader(provider, contexts) + return reader.isValidSignature(address, digest, sig) } -export const recoverWalletConfig = async ( - address: string, - digest: BytesLike, - signature: string | DecodedSignature, - chainId: BigNumberish, - walletContext?: WalletContext -): Promise => { - const subDigest = packMessageData(address, chainId, digest) - const config = await recoverConfig(subDigest, signature) - - if (walletContext) { - const recoveredWalletAddress = addressOf(config, walletContext) - if (config.address && config.address !== recoveredWalletAddress) { - throw new Error('recovered address does not match the WalletConfig address, check the WalletContext') - } else { - config.address = recoveredWalletAddress - } - } - - return config -} +// export const isValidMessageSignature = async ( +// address: string, +// message: string | Uint8Array, +// signature: string, +// provider: Web3Provider | ethers.providers.Web3Provider, +// chainId?: number, +// walletContext?: WalletContext +// ): Promise => { +// const prefixed = prefixEIP191Message(message) +// const digest = encodeMessageDigest(prefixed) +// return isValidSignature(address, digest, signature, provider, chainId, walletContext) +// } + +// export const isValidTypedDataSignature = ( +// address: string, +// typedData: TypedData, +// signature: string, +// provider: Web3Provider | ethers.providers.Web3Provider, +// chainId?: number, +// walletContext?: WalletContext +// ): Promise => { +// return isValidSignature(address, encodeTypedDataDigest(typedData), signature, provider, chainId, walletContext) +// } + +// export const recoverWalletConfig = async ( +// address: string, +// digest: BytesLike, +// signature: string | commons.signature.UnrecoveredSignature | commons.signature.Signature, +// chainId: BigNumberish, +// walletContext?: WalletContext +// ): Promise => { +// const subDigest = packMessageData(address, chainId, digest) +// const config = await recoverConfig(subDigest, signature) + +// if (walletContext) { +// const recoveredWalletAddress = addressOf(config, walletContext) +// if (config.address && config.address !== recoveredWalletAddress) { +// throw new Error('recovered address does not match the WalletConfig address, check the WalletContext') +// } else { +// config.address = recoveredWalletAddress +// } +// } + +// return config +// } export const isBrowserExtension = (): boolean => window.location.protocol === 'chrome-extension:' || window.location.protocol === 'moz-extension:' export const isUnityPlugin = (): boolean => !!navigator.userAgent.match(/UnitySequence/i) -/** - * Returns the status of a signer's wallet on given chain by checking wallet deployment and config status - * - * @param {Signer} signer - * @param {number} chainId - * @return {Promise} Promise that returns true if the wallet is up to date, false otherwise - */ -export const isWalletUpToDate = async (signer: Signer, chainId: number): Promise => { - const walletState = await signer.getWalletState() - const networks = await signer.getNetworks() - - const walletStateForRequiredChain = walletState.find(state => state.chainId === chainId) - if (!walletStateForRequiredChain) { - throw new Error(`WalletRequestHandler: could not find wallet state for chainId ${chainId}`) - } - - const isDeployed = walletStateForRequiredChain.deployed - - if (!networks) { - throw new Error(`isWalletUpToDate util: could not get networks from signer`) - } - const authChain = networks.find(network => network.isAuthChain) - if (!authChain) { - throw new Error(`isWalletUpToDate util: could not get auth chain network information`) - } - const authChainId = authChain.chainId - const authChainConfig = walletState.find(state => state.chainId === authChainId)?.config - if (!authChainConfig) { - throw new Error(`isWalletUpToDate util: could not get auth chain config`) - } - const requiredChainConfig = walletStateForRequiredChain.config - if (!requiredChainConfig) { - throw new Error(`isWalletUpToDate util: could not get config for chainId ${chainId}`) - } - - const isUpToDate = isConfigEqual(authChainConfig, requiredChainConfig) - - return isDeployed && isUpToDate +// /** +// * Returns the status of a signer's wallet on given chain by checking wallet deployment and config status +// * +// * @param {Status} of the wallet +// */ +export const isWalletUpToDate = (status: AccountStatus): boolean => { + return status.onChain.deployed && status.fullyMigrated } export interface ItemStore { diff --git a/packages/provider/src/utils/index.ts b/packages/provider/src/utils/index.ts index 8867e6653..e7d2b9fd5 100644 --- a/packages/provider/src/utils/index.ts +++ b/packages/provider/src/utils/index.ts @@ -1,10 +1,8 @@ -import { BigNumberish, BytesLike, TypedDataDomain, TypedDataField } from 'ethers' -import { WalletContext, ChainIdLike } from '@0xsequence/network' +import { BytesLike, TypedDataDomain, TypedDataField } from 'ethers' +import { ChainIdLike } from '@0xsequence/network' import { encodeMessageDigest, TypedData, encodeTypedDataDigest } from '@0xsequence/utils' -import { DecodedSignature, WalletConfig } from '@0xsequence/config' import { Wallet } from '../wallet' -import { isValidSignature, prefixEIP191Message, recoverWalletConfig } from '../utils' -import { isValidEIP712Signature, isValidEthSignSignature } from '@0xsequence/wallet' +import { isValidSignature, prefixEIP191Message } from '../utils' export class WalletUtils { private wallet: Wallet @@ -22,7 +20,7 @@ export class WalletUtils { // Sign message on the AuthChain async signAuthMessage(message: BytesLike, allSigners?: boolean): Promise { - const signer = await this.wallet.getAuthSigner() + const signer = this.wallet.getSigner() if (!signer) throw new Error('unable to get AuthChain signer') return signer.signMessage(message, await signer.getChainId(), allSigners) } @@ -47,7 +45,7 @@ export class WalletUtils { message: Record, allSigners?: boolean ): Promise { - const signer = await this.wallet.getAuthSigner() + const signer = this.wallet.getSigner() if (!signer) throw new Error('unable to get AuthChain signer') return signer.signTypedData(domain, types, message, await signer.getChainId(), allSigners) } @@ -57,12 +55,11 @@ export class WalletUtils { address: string, digest: Uint8Array, signature: string, - chainId: number, - walletContext?: WalletContext + chainId: number ): Promise { const provider = this.wallet.getProvider(chainId) if (!provider) throw new Error(`unable to get provider for chainId ${chainId}`) - return isValidSignature(address, digest, signature, provider, chainId, walletContext) + return isValidSignature(address, digest, signature, provider, await this.wallet.getWalletContext()) } // Verify message signature @@ -70,14 +67,13 @@ export class WalletUtils { address: string, message: string | Uint8Array, signature: string, - chainId: number, - walletContext?: WalletContext + chainId: number ): Promise { const provider = this.wallet.getProvider(chainId) if (!provider) throw new Error(`unable to get provider for chainId ${chainId}`) const prefixed = prefixEIP191Message(message) const digest = encodeMessageDigest(prefixed) - return isValidSignature(address, digest, signature, provider, chainId, walletContext) + return isValidSignature(address, digest, signature, provider, await this.wallet.getWalletContext()) } // Verify typedData signature @@ -85,47 +81,46 @@ export class WalletUtils { address: string, typedData: TypedData, signature: string, - chainId: number, - walletContext?: WalletContext + chainId: number ): Promise { - return this.isValidSignature(address, encodeTypedDataDigest(typedData), signature, chainId, walletContext) - } - - // Recover the WalletConfig from a signature + digest combo - recoverWalletConfig = async ( - address: string, - digest: BytesLike, - signature: string | DecodedSignature, - chainId: BigNumberish, - walletContext?: WalletContext - ): Promise => { - walletContext = walletContext || (await this.wallet.getWalletContext()) - return recoverWalletConfig(address, digest, signature, chainId, walletContext) - } - - // Recover the WalletConfig from a signature of a message - recoverWalletConfigFromMessage = async ( - address: string, - message: string | Uint8Array, - signature: string | DecodedSignature, - chainId: BigNumberish, - walletContext?: WalletContext - ): Promise => { - walletContext = walletContext || (await this.wallet.getWalletContext()) - return recoverWalletConfig(address, encodeMessageDigest(prefixEIP191Message(message)), signature, chainId, walletContext) + return this.isValidSignature(address, encodeTypedDataDigest(typedData), signature, chainId) } - // Recover the WalletConfig from a signature of a typedData object - recoverWalletConfigFromTypedData = async ( - address: string, - typedData: TypedData, - signature: string | DecodedSignature, - chainId: BigNumberish, - walletContext?: WalletContext - ): Promise => { - walletContext = walletContext || (await this.wallet.getWalletContext()) - return recoverWalletConfig(address, encodeTypedDataDigest(typedData), signature, chainId, walletContext) - } + // // Recover the WalletConfig from a signature + digest combo + // recoverWalletConfig = async ( + // address: string, + // digest: BytesLike, + // signature: string | DecodedSignature, + // chainId: BigNumberish, + // walletContext?: WalletContext + // ): Promise => { + // walletContext = walletContext || (await this.wallet.getWalletContext()) + // return recoverWalletConfig(address, digest, signature, chainId, walletContext) + // } + + // // Recover the WalletConfig from a signature of a message + // recoverWalletConfigFromMessage = async ( + // address: string, + // message: string | Uint8Array, + // signature: string | DecodedSignature, + // chainId: BigNumberish, + // walletContext?: WalletContext + // ): Promise => { + // walletContext = walletContext || (await this.wallet.getWalletContext()) + // return recoverWalletConfig(address, encodeMessageDigest(prefixEIP191Message(message)), signature, chainId, walletContext) + // } + + // // Recover the WalletConfig from a signature of a typedData object + // recoverWalletConfigFromTypedData = async ( + // address: string, + // typedData: TypedData, + // signature: string | DecodedSignature, + // chainId: BigNumberish, + // walletContext?: WalletContext + // ): Promise => { + // walletContext = walletContext || (await this.wallet.getWalletContext()) + // return recoverWalletConfig(address, encodeTypedDataDigest(typedData), signature, chainId, walletContext) + // } // sendTransaction() // sendTransactions() diff --git a/packages/provider/src/wallet.ts b/packages/provider/src/wallet.ts index 46918154a..adec7eaa4 100644 --- a/packages/provider/src/wallet.ts +++ b/packages/provider/src/wallet.ts @@ -1,6 +1,5 @@ import { NetworkConfig, - WalletContext, ChainIdLike, JsonRpcSender, JsonRpcRouter, @@ -16,10 +15,10 @@ import { findNetworkConfig, updateNetworkConfig, ensureValidNetworks, - sortNetworks + sortNetworks, + findSupportedNetwork } from '@0xsequence/network' -import { WalletConfig, WalletState } from '@0xsequence/config' -import { logger } from '@0xsequence/utils' +import { getDefaultConnectionInfo, logger } from '@0xsequence/utils' import { Web3Provider, Web3Signer } from './provider' import { MuxMessageProvider, @@ -35,6 +34,10 @@ import { LocalStore, ItemStore, LocalStorage } from './utils' import { WalletUtils } from './utils/index' import { Runtime } from 'webextension-polyfill' +import { commons } from '@0xsequence/core' +import { AccountStatus } from '@0xsequence/account' + +export const SESSION_LOCALSTORE_KEY = '@sequence.session' export interface WalletProvider { connect(options?: ConnectOptions): Promise @@ -46,7 +49,6 @@ export interface WalletProvider { getAddress(): Promise getNetworks(chainId?: ChainIdLike): Promise getChainId(): Promise - getAuthChainId(): Promise isOpened(): boolean openWallet(path?: string, intent?: OpenWalletIntent, networkId?: string | number): Promise @@ -55,9 +57,9 @@ export interface WalletProvider { getProvider(chainId?: ChainIdLike): Web3Provider | undefined getSigner(chainId?: ChainIdLike): Web3Signer - getWalletContext(): Promise - getWalletConfig(chainId?: ChainIdLike): Promise - getWalletState(chainId?: ChainIdLike): Promise + getWalletContext(): Promise + getWalletConfig(chainId?: ChainIdLike): Promise + getWalletState(chainId?: ChainIdLike): Promise isDeployed(chainId?: ChainIdLike): Promise getProviderConfig(): ProviderConfig @@ -177,7 +179,7 @@ export class Wallet implements WalletProvider { if (!this.networks || this.networks.length === 0) return 0 // return the default chainId as we're connected - return this.networks.find(network => network.isDefaultChain)!.chainId + return findNetworkConfig(this.networks, this.config.defaultNetworkId!)!.chainId }) // Provider proxy to support middleware stack of logging, caching and read-only rpc calls @@ -200,7 +202,11 @@ export class Wallet implements WalletProvider { this.transport.messageProvider ) - this.transport.provider = new Web3Provider(this.transport.router) + // TODO: Move finding this config to upper in the stack + this.transport.provider = new Web3Provider( + this.transport.router, + findSupportedNetwork(this.config.defaultNetworkId!)?.chainId + ) // NOTE: we don't listen on 'connect' even here as we handle it within connect() method // in more synchronous flow. @@ -244,13 +250,13 @@ export class Wallet implements WalletProvider { }) // below will update the wallet context automatically - this.transport.messageProvider.on('walletContext', (walletContext: WalletContext) => { + this.transport.messageProvider.on('walletContext', (walletContext: commons.context.VersionedContext) => { this.useSession({ walletContext: walletContext }, true) }) } loadSession = async (preferredNetwork?: string | number): Promise => { - const data = await LocalStorage.getInstance().getItem('@sequence.session') + const data = await LocalStorage.getInstance().getItem(SESSION_LOCALSTORE_KEY) if (!data || data === '') { return undefined } @@ -282,6 +288,11 @@ export class Wallet implements WalletProvider { } connect = async (options?: ConnectOptions): Promise => { + if (options && options?.authorizeVersion === undefined) { + // Populate default authorize version if not provided + options.authorizeVersion = 2 + } + if (options?.refresh === true) { this.disconnect() } @@ -301,8 +312,13 @@ export class Wallet implements WalletProvider { } if (options) { - if (options.authorize && (!options.app || options.app === '')) { - throw new Error(`connecting with 'authorize' option also requires 'app' to be set`) + if (options.authorize) { + if (!options.app) { + throw new Error(`connecting with 'authorize' option also requires 'app' to be set`) + } + if (options.authorizeVersion === undefined) { + options.authorizeVersion = 2 + } } } @@ -366,11 +382,11 @@ export class Wallet implements WalletProvider { return this.connect({ ...options, authorize: true }) } - disconnect(): void { + disconnect(): Promise { if (this.isOpened()) { this.closeWallet() } - this.clearSession() + return this.clearSession() } // TODO: add switchNetwork(network: string | number) which will call wallet_switchEthereumChain @@ -429,7 +445,7 @@ export class Wallet implements WalletProvider { throw new Error('networks have not been set by session. connect first.') } - const network = this.networks.find(network => network.isDefaultChain) + const network = findNetworkConfig(this.networks, this.config.defaultNetworkId!) if (!network) { throw new Error('networks must have a default chain specified') @@ -438,20 +454,6 @@ export class Wallet implements WalletProvider { return network.chainId } - getAuthChainId = async (): Promise => { - if (!this.networks || this.networks.length < 1) { - throw new Error('networks have not been set by session. connect first.') - } - - const network = this.networks.find(network => network.isAuthChain) - - if (!network) { - throw new Error('networks must have an auth chain specified') - } - - return network.chainId - } - openWallet = async (path?: string, intent?: OpenWalletIntent, networkId?: string | number): Promise => { if (intent?.type !== 'connect' && !this.isConnected()) { throw new Error('connect first') @@ -486,7 +488,7 @@ export class Wallet implements WalletProvider { } } - let network: NetworkConfig | undefined = this.networks.find(network => network.isDefaultChain)! + let network: NetworkConfig | undefined = findNetworkConfig(this.networks, this.config.defaultNetworkId!)! if (chainId) { network = findNetworkConfig(this.networks, chainId) if (!network) { @@ -503,7 +505,7 @@ export class Wallet implements WalletProvider { let provider: Web3Provider // network.provider may be set by the ProviderConfig override - const rpcProvider = network.provider ? network.provider : new providers.JsonRpcProvider(network.rpcUrl, network.chainId) + const rpcProvider = new providers.JsonRpcProvider(getDefaultConnectionInfo(network.rpcUrl), network.chainId) if (network.isDefaultChain) { // communicating with defaultChain will prioritize the wallet message transport @@ -544,14 +546,6 @@ export class Wallet implements WalletProvider { return provider } - async getAuthProvider(): Promise { - return this.getProvider((await this.getAuthNetwork()).chainId)! - } - - async getAuthNetwork(): Promise { - return (await this.getNetworks()).find(n => n.isAuthChain)! - } - getAllProviders(): { [chainId: number]: Web3Provider } { return this.providers } @@ -560,19 +554,15 @@ export class Wallet implements WalletProvider { return this.getProvider(chainId)!.getSigner() } - async getAuthSigner(): Promise { - return (await this.getAuthProvider()).getSigner() - } - - getWalletConfig(chainId?: ChainIdLike): Promise { + getWalletConfig(chainId?: ChainIdLike): Promise { return this.getSigner().getWalletConfig(chainId) } - getWalletState(chainId?: ChainIdLike): Promise { + getWalletState(chainId?: ChainIdLike): Promise { return this.getSigner().getWalletState(chainId) } - getWalletContext(): Promise { + getWalletContext(): Promise { return this.getSigner().getWalletContext() } @@ -596,7 +586,7 @@ export class Wallet implements WalletProvider { private saveSession = async (session: WalletSession) => { logger.debug('wallet provider: saving session') const data = JSON.stringify(session) - await LocalStorage.getInstance().setItem('@sequence.session', data) + await LocalStorage.getInstance().setItem(SESSION_LOCALSTORE_KEY, data) } private useSession = async (session: WalletSession, autoSave: boolean = true) => { @@ -678,9 +668,9 @@ export class Wallet implements WalletProvider { } } - private clearSession(): void { + private async clearSession(): Promise { logger.debug('wallet provider: clearing session') - LocalStorage.getInstance().removeItem('@sequence.session') + await LocalStorage.getInstance().removeItem(SESSION_LOCALSTORE_KEY) this.session = undefined this.networks = [] this.providers = {} @@ -741,7 +731,7 @@ export interface ProviderConfig { // WalletContext used the one returned by the wallet app upon login. // // NOTE: do not use this option unless you know what you're doing - walletContext?: WalletContext + walletContext?: commons.context.VersionedContext } export const DefaultProviderConfig: ProviderConfig = { diff --git a/packages/provider/tests/messages.ts b/packages/provider/tests/messages.ts index fdb114774..48f17a576 100644 --- a/packages/provider/tests/messages.ts +++ b/packages/provider/tests/messages.ts @@ -1,4 +1,5 @@ import { ethers } from 'ethers' +import { prefixEIP191Message } from '../src/utils' // Ethereum personal sign: Hello, World! export const message1 = new Uint8Array([ @@ -6,10 +7,11 @@ export const message1 = new Uint8Array([ 72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33 ]) - -export const dclLogin = ethers.utils.toUtf8Bytes(`Decentraland Login +const dclText = `Decentraland Login Ephemeral address: 0xe1bCF3CAc83534a055f7254C1FD88B21159fCc67 -Expiration: 2022-10-27T16:03:29.191Z`) +Expiration: 2022-10-27T16:03:29.191Z` + +export const dclLogin = ethers.utils.toUtf8Bytes(dclText) // Ethereum personal sign 0x v3 order export const zeroExV3Order = new Uint8Array([ @@ -47,3 +49,45 @@ export const zeroExV3Order = new Uint8Array([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]) + +// Messages for testing trim-eip191prefix + +export const trimEIP191Prefix_test1_raw = `1915 Robert Frost +The Road Not Taken + +Two roads diverged in a yellow wood, +And sorry I could not travel both +And be one traveler, long I stood +And looked down one as far as I could +To where it bent in the undergrowth + +Then took the other, as just as fair, +And having perhaps the better claim, +Because it was grassy and wanted wear +Though as for that the passing there +Had worn them really about the same, + +And both that morning equally lay +In leaves no step had trodden black. +Oh, I kept the first for another day! +Yet knowing how way leads on to way, +I doubted if I should ever come back. + +I shall be telling this with a sigh +Somewhere ages and ages hence: +Two roads diverged in a wood, and I— +I took the one less traveled by, +And that has made all the difference. + +\u2601 \u2600 \u2602` +export const trimEIP191Prefix_test2_raw = dclText +export const trimEIP191Prefix_test3_raw = '1915 Robe' // 9 chars +export const trimEIP191Prefix_test4_raw = + '123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789' // 99 chars +export const trimEIP191Prefix_test5_raw = 'Robe 1915' + +export const trimEIP191Prefix_test1_prefixed = prefixEIP191Message(trimEIP191Prefix_test1_raw) +export const trimEIP191Prefix_test2_prefixed = prefixEIP191Message(dclText) +export const trimEIP191Prefix_test3_prefixed = prefixEIP191Message(trimEIP191Prefix_test3_raw) +export const trimEIP191Prefix_test4_prefixed = prefixEIP191Message(trimEIP191Prefix_test4_raw) +export const trimEIP191Prefix_test5_prefixed = prefixEIP191Message(trimEIP191Prefix_test5_raw) diff --git a/packages/provider/tests/remove-eip191prefix.spec.ts b/packages/provider/tests/remove-eip191prefix.spec.ts new file mode 100644 index 000000000..1a4c31f7c --- /dev/null +++ b/packages/provider/tests/remove-eip191prefix.spec.ts @@ -0,0 +1,34 @@ +import chaiAsPromised from 'chai-as-promised' +import * as chai from 'chai' + +import { + trimEIP191Prefix_test1_prefixed, + trimEIP191Prefix_test1_raw, + trimEIP191Prefix_test2_prefixed, + trimEIP191Prefix_test2_raw, + trimEIP191Prefix_test3_prefixed, + trimEIP191Prefix_test3_raw, + trimEIP191Prefix_test4_prefixed, + trimEIP191Prefix_test4_raw, + trimEIP191Prefix_test5_prefixed, + trimEIP191Prefix_test5_raw +} from './messages' +import { trimEIP191Prefix } from '../src/utils' +import { ethers } from 'ethers' +const { expect } = chai.use(chaiAsPromised) + +describe('trimming eip191prefix', () => { + it('should trim prefix', () => { + expect(ethers.utils.toUtf8String(trimEIP191Prefix(trimEIP191Prefix_test1_prefixed))).equal(trimEIP191Prefix_test1_raw) + }) + + it('should handle eip191 exempt messages (by returning early)', () => { + expect(ethers.utils.toUtf8String(trimEIP191Prefix(trimEIP191Prefix_test2_prefixed))).equal(trimEIP191Prefix_test2_raw) + }) + + it('should trim prefix for case where max prefix char as number is bigger than the length of the message', () => { + expect(ethers.utils.toUtf8String(trimEIP191Prefix(trimEIP191Prefix_test3_prefixed))).equal(trimEIP191Prefix_test3_raw) + expect(ethers.utils.toUtf8String(trimEIP191Prefix(trimEIP191Prefix_test4_prefixed))).equal(trimEIP191Prefix_test4_raw) + expect(ethers.utils.toUtf8String(trimEIP191Prefix(trimEIP191Prefix_test5_prefixed))).equal(trimEIP191Prefix_test5_raw) + }) +}) diff --git a/packages/relayer/package.json b/packages/relayer/package.json index 1fbc00202..7dad37303 100644 --- a/packages/relayer/package.json +++ b/packages/relayer/package.json @@ -17,17 +17,17 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@0xsequence/abi": "^0.43.34", - "@0xsequence/config": "^0.43.34", - "@0xsequence/network": "^0.43.34", - "@0xsequence/transactions": "^0.43.34", - "@0xsequence/utils": "^0.43.34" + "@0xsequence/abi": "workspace:*", + "@0xsequence/core": "workspace:*", + "@0xsequence/utils": "workspace:*" }, "peerDependencies": { "ethers": ">=5.5 < 6" }, "devDependencies": { - "@0xsequence/wallet-contracts": "1.10.0", + "@0xsequence/signhub": "workspace:*", + "@0xsequence/tests": "workspace:*", + "@0xsequence/wallet-contracts": "^1.10.0", "ethers": "^5.7.2" }, "files": [ diff --git a/packages/relayer/src/base-relayer.ts b/packages/relayer/src/base-relayer.ts deleted file mode 100644 index 2b548addb..000000000 --- a/packages/relayer/src/base-relayer.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { ethers, providers, utils } from 'ethers' -import { walletContracts } from '@0xsequence/abi' -import { WalletContext } from '@0xsequence/network' -import { WalletConfig, addressOf, imageHash, DecodedSignature, encodeSignature } from '@0xsequence/config' -import { SignedTransactions, Transaction, sequenceTxAbiEncode, readSequenceNonce } from '@0xsequence/transactions' -import { isBigNumberish, Optionals } from '@0xsequence/utils' - - -export interface BaseRelayerOptions { - bundleCreation?: boolean - creationGasLimit?: ethers.BigNumberish - provider?: ethers.providers.Provider -} - -export function isBaseRelayerOptions(obj: any): obj is BaseRelayerOptions { - return ( - (obj.bundleCreation !== undefined && typeof obj.bundleCreation === 'boolean') || - (obj.creationGasLimit !== undefined && isBigNumberish(obj.creationGasLimit)) || - (obj.provider !== undefined && (providers.Provider.isProvider(obj.provider) || typeof obj.provider === 'string')) - ) -} - -export const BaseRelayerDefaults: Omit>, 'provider'> = { - bundleCreation: true, - creationGasLimit: ethers.constants.Two.pow(17) -} - -export class BaseRelayer { - readonly provider: providers.Provider | undefined - public readonly bundleCreation: boolean - public creationGasLimit: ethers.BigNumber - - constructor(options?: BaseRelayerOptions) { - const opts = { ...BaseRelayerDefaults, ...options } - this.bundleCreation = opts.bundleCreation - this.provider = opts.provider - this.creationGasLimit = ethers.BigNumber.from(opts.creationGasLimit) - } - - async isWalletDeployed(walletAddress: string): Promise { - if (!this.provider) throw new Error('Bundled creation provider not found') - return (await this.provider.getCode(walletAddress)) !== '0x' - } - - prepareWalletDeploy( - config: WalletConfig, - context: WalletContext - ): { to: string, data: string} { - const factoryInterface = new utils.Interface(walletContracts.factory.abi) - - return { - to: context.factory, - data: factoryInterface.encodeFunctionData(factoryInterface.getFunction('deploy'), - [context.mainModule, imageHash(config)] - ) - } - } - - async prependWalletDeploy( - signedTransactions: Pick - ): Promise<{ to: string, execute: { transactions: Transaction[], nonce: ethers.BigNumber, signature: string } }> { - const { config, context, transactions, nonce, signature } = signedTransactions - const walletAddress = addressOf(config, context) - const walletInterface = new utils.Interface(walletContracts.mainModule.abi) - - const encodedSignature = (async () => { - const sig = await signature - - if (typeof sig === 'string') return sig - return encodeSignature(sig) - })() - - if (this.bundleCreation && !(await this.isWalletDeployed(walletAddress))) { - return { - to: context.guestModule!, - execute: { - transactions: [ - { - ...this.prepareWalletDeploy(config, context), - delegateCall: false, - revertOnError: false, - gasLimit: this.creationGasLimit, - value: ethers.constants.Zero - }, - { - delegateCall: false, - revertOnError: true, - gasLimit: ethers.constants.Zero, - to: walletAddress, - value: ethers.constants.Zero, - data: walletInterface.encodeFunctionData(walletInterface.getFunction('execute'), - [ - sequenceTxAbiEncode(transactions), - nonce, - await encodedSignature - ] - ) - } - ], - nonce: ethers.constants.Zero, - signature: '0x' - } - } - } else { - return { - to: walletAddress, - execute: { - transactions, - nonce: ethers.BigNumber.from(nonce), - signature: await encodedSignature - } - } - } - } - - async prepareTransactions( - config: WalletConfig, - context: WalletContext, - signature: string | Promise | DecodedSignature | Promise, - ...transactions: Transaction[] - ): Promise<{ to: string, data: string }> { //, gasLimit?: ethers.BigNumberish }> { - const nonce = readSequenceNonce(...transactions) - if (!nonce) { - throw new Error('Unable to prepare transactions without a defined nonce') - } - const { to, execute } = await this.prependWalletDeploy({ config, context, transactions, nonce, signature }) - const walletInterface = new utils.Interface(walletContracts.mainModule.abi) - return { - to, data: walletInterface.encodeFunctionData(walletInterface.getFunction('execute'), [ - sequenceTxAbiEncode(execute.transactions), - execute.nonce, - execute.signature - ]) - } - } -} diff --git a/packages/relayer/src/index.ts b/packages/relayer/src/index.ts index 122c94d99..3db200104 100644 --- a/packages/relayer/src/index.ts +++ b/packages/relayer/src/index.ts @@ -1,48 +1,52 @@ import { ethers, providers } from 'ethers' -import { SignedTransactions, Transaction, TransactionResponse } from '@0xsequence/transactions' -import { WalletContext } from '@0xsequence/network' -import { WalletConfig } from '@0xsequence/config' import { proto } from './rpc-relayer' +import { commons } from '@0xsequence/core' + export interface Relayer { // simulate returns the execution results for a list of transactions. - simulate(wallet: string, ...transactions: Transaction[]): Promise + simulate(wallet: string, ...transactions: commons.transaction.Transaction[]): Promise // getFeeOptions returns the fee options that the relayer will accept as payment. // If a quote is returned, it may be passed back to the relayer for dispatch. getFeeOptions( - config: WalletConfig, - context: WalletContext, - ...transactions: Transaction[] + address: string, + ...transactions: commons.transaction.Transaction[] ): Promise<{ options: FeeOption[], quote?: FeeQuote }> + // getFeeOptionsRaw returns the fee options that the relayer will accept as payment. + // If a quote is returned, it may be passed back to the relayer for dispatch. + // It doesn't make any assumptions about the transaction format. + getFeeOptionsRaw( + entrypoint: string, + data: ethers.utils.BytesLike + ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> + // gasRefundOptions returns the transactions which can be included to refund a // relayer for submitting your transaction to a network. gasRefundOptions( - config: WalletConfig, - context: WalletContext, - ...transactions: Transaction[] + address: string, + ...transactions: commons.transaction.Transaction[] ): Promise // getNonce returns the transaction count/nonce for a wallet, encoded with nonce space. // If space is undefined, the relayer can choose a nonce space to encode the result with. // Otherwise, the relayer must return a nonce encoded for the given nonce space. - getNonce(config: WalletConfig, context: WalletContext, space?: ethers.BigNumberish, blockTag?: providers.BlockTag): Promise + getNonce(address: string, space?: ethers.BigNumberish, blockTag?: providers.BlockTag): Promise // relayer will submit the transaction(s) to the network and return the transaction response. // The quote should be the one returned from getFeeOptions, if any. // waitForReceipt must default to true. - relay(signedTxs: SignedTransactions, quote?: FeeQuote, waitForReceipt?: boolean): Promise + relay(signedTxs: commons.transaction.IntendedTransactionBundle, quote?: FeeQuote, waitForReceipt?: boolean): Promise // wait for transaction confirmation // timeout is the maximum time to wait for the transaction response // delay is the polling interval, i.e. the time to wait between requests // maxFails is the maximum number of hard failures to tolerate before giving up - wait(metaTxnId: string | SignedTransactions, timeout?: number, delay?: number, maxFails?: number): Promise + wait(metaTxnId: string | commons.transaction.SignedTransactionBundle, timeout?: number, delay?: number, maxFails?: number): Promise } export * from './local-relayer' -export * from './base-relayer' export * from './provider-relayer' export * from './rpc-relayer' export { proto as RpcRelayerProto } from './rpc-relayer' diff --git a/packages/relayer/src/local-relayer.ts b/packages/relayer/src/local-relayer.ts index a3a55a0e5..6de2267d1 100644 --- a/packages/relayer/src/local-relayer.ts +++ b/packages/relayer/src/local-relayer.ts @@ -1,11 +1,8 @@ -import { Signer as AbstractSigner, ethers, providers } from 'ethers' -import { walletContracts } from '@0xsequence/abi' -import { SignedTransactions, Transaction, sequenceTxAbiEncode, TransactionResponse } from '@0xsequence/transactions' -import { WalletContext } from '@0xsequence/network' -import { WalletConfig } from '@0xsequence/config' +import { Signer as AbstractSigner, providers, BytesLike } from 'ethers' import { logger } from '@0xsequence/utils' import { FeeOption, FeeQuote, Relayer } from '.' import { ProviderRelayer, ProviderRelayerOptions } from './provider-relayer' +import { commons } from '@0xsequence/core' export type LocalRelayerOptions = Omit & { signer: AbstractSigner @@ -25,30 +22,22 @@ export class LocalRelayer extends ProviderRelayer implements Relayer { if (!this.signer.provider) throw new Error("Signer must have a provider") } - async deployWallet(config: WalletConfig, context: WalletContext): Promise { - // NOTE: on hardhat some tests fail on HookCallerMock when not passing gasLimit directly as below, - // and using eth_gasEstimate. Perhaps review HookCallerMock.sol and fix it to avoid what looks - // like an infinite loop? - const walletDeployTxn = this.prepareWalletDeploy(config, context) - - // NOTE: for hardhat to pass, we have to set the gasLimit directly, as its unable to estimate - return this.signer.sendTransaction({ ...walletDeployTxn, gasLimit: ethers.constants.Two.pow(17) } ) - } - async getFeeOptions( - _config: WalletConfig, - _context: WalletContext, - ..._transactions: Transaction[] + _address: string, + ..._transactions: commons.transaction.Transaction[] ): Promise<{ options: FeeOption[] }> { return { options: [] } } + async getFeeOptionsRaw(_entrypoint: string, _data: BytesLike): Promise<{ options: FeeOption[] }> { + return { options: [] } + } + async gasRefundOptions( - config: WalletConfig, - context: WalletContext, - ...transactions: Transaction[] + address: string, + ...transactions:commons.transaction.Transaction[] ): Promise { - const { options } = await this.getFeeOptions(config, context, ...transactions) + const { options } = await this.getFeeOptions(address, ...transactions) return options } @@ -56,33 +45,30 @@ export class LocalRelayer extends ProviderRelayer implements Relayer { this.txnOptions = transactionRequest } - async relay(signedTxs: SignedTransactions, quote?: FeeQuote, waitForReceipt: boolean = true): Promise> { + async relay( + signedTxs: commons.transaction.IntendedTransactionBundle, + quote?: FeeQuote, waitForReceipt: boolean = true + ): Promise> { if (quote !== undefined) { logger.warn(`LocalRelayer doesn't accept fee quotes`) } - - if (!signedTxs.context.guestModule || signedTxs.context.guestModule.length !== 42) { - throw new Error('LocalRelayer requires the context.guestModule address') - } - - const { to, execute } = await this.prependWalletDeploy(signedTxs) - - const walletInterface = new ethers.utils.Interface(walletContracts.mainModule.abi) - const data = walletInterface.encodeFunctionData(walletInterface.getFunction('execute'), [ - sequenceTxAbiEncode(execute.transactions), - execute.nonce, - execute.signature - ]) + + const data = commons.transaction.encodeBundleExecData(signedTxs) // TODO: think about computing gas limit individually, summing together and passing across // NOTE: we expect that all txns have set their gasLimit ahead of time through proper estimation // const gasLimit = signedTxs.transactions.reduce((sum, tx) => sum.add(tx.gasLimit), ethers.BigNumber.from(0)) // txRequest.gasLimit = gasLimit - const responsePromise = this.signer.sendTransaction({ to, data, ...this.txnOptions }) + const responsePromise = this.signer.sendTransaction({ + to: signedTxs.entrypoint, + data, + ...this.txnOptions, + gasLimit: 9000000 + }) if (waitForReceipt) { - const response: TransactionResponse = await responsePromise + const response: commons.transaction.TransactionResponse = await responsePromise response.receipt = await response.wait() return response } else { diff --git a/packages/relayer/src/provider-relayer.ts b/packages/relayer/src/provider-relayer.ts index f356be4b0..80a4f06c4 100644 --- a/packages/relayer/src/provider-relayer.ts +++ b/packages/relayer/src/provider-relayer.ts @@ -1,22 +1,19 @@ import { ethers, providers } from 'ethers' import { walletContracts } from '@0xsequence/abi' -import { computeMetaTxnHash, encodeNonce, SignedTransactions, Transaction, TransactionResponse } from '@0xsequence/transactions' -import { WalletContext } from '@0xsequence/network' -import { WalletConfig, addressOf } from '@0xsequence/config' -import { BaseRelayer, BaseRelayerOptions } from './base-relayer' import { FeeOption, FeeQuote, Relayer, SimulateResult } from '.' -import { logger, Optionals, Mask } from '@0xsequence/utils' +import { logger, Optionals } from '@0xsequence/utils' +import { commons } from '@0xsequence/core' const DEFAULT_GAS_LIMIT = ethers.BigNumber.from(800000) -export interface ProviderRelayerOptions extends BaseRelayerOptions { +export interface ProviderRelayerOptions { provider: providers.Provider, waitPollRate?: number, deltaBlocksLog?: number, fromBlockLog?: number } -export const ProviderRelayerDefaults: Required>> = { +export const ProviderRelayerDefaults: Required> = { waitPollRate: 1000, deltaBlocksLog: 12, fromBlockLog: -1024 @@ -26,36 +23,35 @@ export function isProviderRelayerOptions(obj: any): obj is ProviderRelayerOption return obj.provider !== undefined && providers.Provider.isProvider(obj.provider) } -export abstract class ProviderRelayer extends BaseRelayer implements Relayer { +export abstract class ProviderRelayer implements Relayer { public provider: providers.Provider public waitPollRate: number public deltaBlocksLog: number public fromBlockLog: number constructor(options: ProviderRelayerOptions) { - super(options) const opts = { ...ProviderRelayerDefaults, ...options } this.provider = opts.provider - this.waitPollRate = opts.waitPollRate - this.deltaBlocksLog = opts.deltaBlocksLog - this.fromBlockLog = opts.fromBlockLog } abstract getFeeOptions( - config: WalletConfig, - context: WalletContext, - ...transactions: Transaction[] + address: string, + ...transactions: commons.transaction.Transaction[] + ): Promise<{ options: FeeOption[], quote?: FeeQuote }> + + abstract getFeeOptionsRaw( + entrypoint: string, + data: ethers.utils.BytesLike ): Promise<{ options: FeeOption[], quote?: FeeQuote }> abstract gasRefundOptions( - config: WalletConfig, - context: WalletContext, - ...transactions: Transaction[] + address: string, + ...transactions: commons.transaction.Transaction[] ): Promise - abstract relay(signedTxs: SignedTransactions, quote?: FeeQuote, waitForReceipt?: boolean): Promise + abstract relay(signedTxs: commons.transaction.IntendedTransactionBundle, quote?: FeeQuote, waitForReceipt?: boolean): Promise - async simulate(wallet: string, ...transactions: Transaction[]): Promise { + async simulate(wallet: string, ...transactions: commons.transaction.Transaction[]): Promise { return (await Promise.all(transactions.map(async tx => { // Respect gasLimit request of the transaction (as long as its not 0) if (tx.gasLimit && !ethers.BigNumber.from(tx.gasLimit || 0).eq(ethers.constants.Zero)) { @@ -68,7 +64,7 @@ export abstract class ProviderRelayer extends BaseRelayer implements Relayer { } // Fee can't be estimated for self-called if wallet hasn't been deployed - if (tx.to === wallet && !(await this.isWalletDeployed(wallet))) { + if (tx.to === wallet && await this.provider.getCode(wallet).then((code) => ethers.utils.arrayify(code).length === 0)) { return DEFAULT_GAS_LIMIT } @@ -93,8 +89,7 @@ export abstract class ProviderRelayer extends BaseRelayer implements Relayer { } async getNonce( - config: WalletConfig, - context: WalletContext, + address: string, space?: ethers.BigNumberish, blockTag?: providers.BlockTag ): Promise { @@ -102,9 +97,7 @@ export abstract class ProviderRelayer extends BaseRelayer implements Relayer { throw new Error('provider is not set') } - const addr = addressOf(config, context) - - if ((await this.provider.getCode(addr)) === '0x') { + if ((await this.provider.getCode(address)) === '0x') { return 0 } @@ -112,21 +105,19 @@ export abstract class ProviderRelayer extends BaseRelayer implements Relayer { space = 0 } - const module = new ethers.Contract(addr, walletContracts.mainModule.abi, this.provider) + const module = new ethers.Contract(address, walletContracts.mainModule.abi, this.provider) const nonce = await module.readNonce(space, { blockTag: blockTag }) - return encodeNonce(space, nonce) + return commons.transaction.encodeNonce(space, nonce) } async wait( - metaTxnId: string | SignedTransactions, + metaTxnId: string | commons.transaction.SignedTransactionBundle, timeout?: number, delay: number = this.waitPollRate, maxFails: number = 5 ): Promise { if (typeof metaTxnId !== 'string') { - logger.info('computing id', metaTxnId.config, metaTxnId.context, metaTxnId.chainId, ...metaTxnId.transactions) - - metaTxnId = computeMetaTxnHash(addressOf(metaTxnId.config, metaTxnId.context), metaTxnId.chainId, ...metaTxnId.transactions) + metaTxnId = commons.transaction.intendedTransactionID(metaTxnId) } let timedOut = false diff --git a/packages/relayer/src/rpc-relayer/index.ts b/packages/relayer/src/rpc-relayer/index.ts index 0cda048ab..0c1cb7b6f 100644 --- a/packages/relayer/src/rpc-relayer/index.ts +++ b/packages/relayer/src/rpc-relayer/index.ts @@ -1,22 +1,8 @@ import { ethers } from 'ethers' -import { walletContracts } from '@0xsequence/abi' -import { - Transaction, - readSequenceNonce, - appendNonce, - MetaTransactionsType, - sequenceTxAbiEncode, - SignedTransactions, - computeMetaTxnHash, - decodeNonce, - TransactionResponse -} from '@0xsequence/transactions' -import { BaseRelayer, BaseRelayerOptions } from '../base-relayer' import { FeeOption, FeeQuote, Relayer, SimulateResult } from '..' -import { WalletContext } from '@0xsequence/network' -import { WalletConfig, addressOf, buildStubSignature } from '@0xsequence/config' -import { logger } from '@0xsequence/utils' import * as proto from './relayer.gen' +import { commons } from '@0xsequence/core' +import { getDefaultConnectionInfo, logger } from '@0xsequence/utils' export { proto } @@ -29,34 +15,41 @@ const FINAL_STATUSES = [ const FAILED_STATUSES = [proto.ETHTxnStatus.DROPPED, proto.ETHTxnStatus.PARTIALLY_FAILED, proto.ETHTxnStatus.FAILED] -export interface RpcRelayerOptions extends BaseRelayerOptions { +export interface RpcRelayerOptions { + provider: ethers.providers.Provider | { url: string } url: string } export function isRpcRelayerOptions(obj: any): obj is RpcRelayerOptions { - return obj.url !== undefined && typeof obj.url === 'string' + return ( + obj.url !== undefined && + typeof obj.url === 'string' && + obj.provider !== undefined && + ethers.providers.Provider.isProvider(obj.provider) + ) } const fetch = typeof global === 'object' ? global.fetch : window.fetch -export class RpcRelayer extends BaseRelayer implements Relayer { +export class RpcRelayer implements Relayer { private readonly service: proto.Relayer + public readonly provider: ethers.providers.Provider constructor(options: RpcRelayerOptions) { - super(options) this.service = new proto.Relayer(options.url, fetch) + this.provider = ethers.providers.Provider.isProvider(options.provider) + ? options.provider + : new ethers.providers.StaticJsonRpcProvider(getDefaultConnectionInfo(options.provider.url)) } async waitReceipt( - metaTxnId: string | SignedTransactions, + metaTxnId: string | commons.transaction.SignedTransactionBundle, delay: number = 1000, maxFails: number = 5, isCancelled?: () => boolean ): Promise { if (typeof metaTxnId !== 'string') { - logger.info('computing id', metaTxnId.config, metaTxnId.context, metaTxnId.chainId, ...metaTxnId.transactions) - - metaTxnId = computeMetaTxnHash(addressOf(metaTxnId.config, metaTxnId.context), metaTxnId.chainId, ...metaTxnId.transactions) + metaTxnId = commons.transaction.intendedTransactionID(metaTxnId) } logger.info(`[rpc-relayer/waitReceipt] waiting for ${metaTxnId}`) @@ -91,16 +84,18 @@ export class RpcRelayer extends BaseRelayer implements Relayer { throw new Error(`Cancelled waiting for transaction receipt ${metaTxnId}`) } - async simulate(wallet: string, ...transactions: Transaction[]): Promise { + async simulate(wallet: string, ...transactions: commons.transaction.Transaction[]): Promise { const coder = ethers.utils.defaultAbiCoder - const encoded = coder.encode([MetaTransactionsType], [sequenceTxAbiEncode(transactions)]) + const encoded = coder.encode( + [commons.transaction.MetaTransactionsType], + [commons.transaction.sequenceTxAbiEncode(transactions)] + ) return (await this.service.simulate({ wallet, transactions: encoded })).results } async getFeeOptions( - config: WalletConfig, - context: WalletContext, - ...transactions: Transaction[] + address: string, + ...transactions: commons.transaction.Transaction[] ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { // NOTE/TODO: for a given `service` the feeTokens will not change between execution, so we should memoize this value // for a short-period of time, perhaps for 1 day or in memory. Perhaps one day we can make this happen automatically @@ -111,35 +106,23 @@ export class RpcRelayer extends BaseRelayer implements Relayer { const symbols = feeTokens.tokens.map(token => token.symbol).join(', ') logger.info(`[rpc-relayer/getFeeOptions] relayer fees are required, accepted tokens are ${symbols}`) - const wallet = addressOf(config, context) - - let nonce = readSequenceNonce(...transactions) - if (nonce === undefined) { - nonce = await this.getNonce(config, context) - } + const nonce = await this.getNonce(address) if (!this.provider) { logger.warn(`[rpc-relayer/getFeeOptions] provider not set, needed for stub signature`) throw new Error('provider is not set') } - const { to, execute } = await this.prependWalletDeploy({ - config, - context, - transactions, - nonce, - signature: buildStubSignature(this.provider, config) + const { options, quote } = await this.service.feeOptions({ + wallet: address, + to: address, + data: commons.transaction.encodeBundleExecData({ + entrypoint: address, + transactions, + nonce + }) }) - const walletInterface = new ethers.utils.Interface(walletContracts.mainModule.abi) - const data = walletInterface.encodeFunctionData(walletInterface.getFunction('execute'), [ - sequenceTxAbiEncode(execute.transactions), - execute.nonce, - execute.signature - ]) - - const { options, quote } = await this.service.feeOptions({ wallet, to, data }) - logger.info(`[rpc-relayer/getFeeOptions] got refund options ${JSON.stringify(options)}`) return { options, quote: { _tag: 'FeeQuote', _quote: quote } } } else { @@ -148,27 +131,36 @@ export class RpcRelayer extends BaseRelayer implements Relayer { } } - async gasRefundOptions(config: WalletConfig, context: WalletContext, ...transactions: Transaction[]): Promise { - const { options } = await this.getFeeOptions(config, context, ...transactions) + async getFeeOptionsRaw(entrypoint: string, data: ethers.utils.BytesLike): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { + const { options, quote } = await this.service.feeOptions({ + wallet: entrypoint, + to: entrypoint, + data: ethers.utils.hexlify(data) + }) + + return { options, quote: { _tag: 'FeeQuote', _quote: quote } } + } + + async gasRefundOptions(address: string, ...transactions: commons.transaction.Transaction[]): Promise { + const { options } = await this.getFeeOptions(address, ...transactions) return options } - async getNonce(config: WalletConfig, context: WalletContext, space?: ethers.BigNumberish): Promise { - const addr = addressOf(config, context) - logger.info(`[rpc-relayer/getNonce] get nonce for wallet ${addr} space: ${space}`) + async getNonce(address: string, space?: ethers.BigNumberish): Promise { + logger.info(`[rpc-relayer/getNonce] get nonce for wallet ${address} space: ${space}`) const encodedNonce = space !== undefined ? ethers.BigNumber.from(space).toHexString() : undefined - const resp = await this.service.getMetaTxnNonce({ walletContractAddress: addr, space: encodedNonce }) + const resp = await this.service.getMetaTxnNonce({ walletContractAddress: address, space: encodedNonce }) const nonce = ethers.BigNumber.from(resp.nonce) - const [decodedSpace, decodedNonce] = decodeNonce(nonce) - logger.info(`[rpc-relayer/getNonce] got next nonce for wallet ${addr} ${decodedNonce} space: ${decodedSpace}`) + const [decodedSpace, decodedNonce] = commons.transaction.decodeNonce(nonce) + logger.info(`[rpc-relayer/getNonce] got next nonce for wallet ${address} ${decodedNonce} space: ${decodedSpace}`) return nonce } async relay( - signedTxs: SignedTransactions, + signedTxs: commons.transaction.IntendedTransactionBundle, quote?: FeeQuote, waitForReceipt: boolean = true - ): Promise> { + ): Promise> { logger.info( `[rpc-relayer/relay] relaying signed meta-transactions ${JSON.stringify(signedTxs)} with quote ${JSON.stringify(quote)}` ) @@ -187,27 +179,25 @@ export class RpcRelayer extends BaseRelayer implements Relayer { throw new Error('provider is not set') } - const { to: contract, execute } = await this.prependWalletDeploy(signedTxs) - - const walletAddress = addressOf(signedTxs.config, signedTxs.context) - const walletInterface = new ethers.utils.Interface(walletContracts.mainModule.abi) - const input = walletInterface.encodeFunctionData(walletInterface.getFunction('execute'), [ - sequenceTxAbiEncode(execute.transactions), - execute.nonce, - execute.signature - ]) - - const metaTxn = await this.service.sendMetaTxn({ call: { walletAddress, contract, input }, quote: typecheckedQuote }) + const data = commons.transaction.encodeBundleExecData(signedTxs) + const metaTxn = await this.service.sendMetaTxn({ + call: { + walletAddress: signedTxs.intent.wallet, + contract: signedTxs.entrypoint, + input: data + }, + quote: typecheckedQuote + }) logger.info(`[rpc-relayer/relay] got relay result ${JSON.stringify(metaTxn)}`) if (waitForReceipt) { - return this.wait(metaTxn.txnHash) + return this.wait(signedTxs.intent.id) } else { const response = { - hash: metaTxn.txnHash, + hash: signedTxs.intent.id, confirmations: 0, - from: walletAddress, + from: signedTxs.intent.wallet, wait: (_confirmations?: number): Promise => Promise.reject(new Error('impossible')) } @@ -216,7 +206,7 @@ export class RpcRelayer extends BaseRelayer implements Relayer { throw new Error('cannot wait for receipt, relayer has no provider set') } - const waitResponse = await this.wait(metaTxn.txnHash) + const waitResponse = await this.wait(signedTxs.intent.id) const transactionHash = waitResponse.receipt?.transactionHash if (!transactionHash) { @@ -230,16 +220,16 @@ export class RpcRelayer extends BaseRelayer implements Relayer { response.wait = wait - return response as TransactionResponse + return response as commons.transaction.TransactionResponse } } async wait( - metaTxnId: string | SignedTransactions, + metaTxnId: string | commons.transaction.SignedTransactionBundle, timeout?: number, delay: number = 1000, maxFails: number = 5 - ): Promise> { + ): Promise> { let timedOut = false const { receipt } = await (timeout !== undefined @@ -264,12 +254,12 @@ export class RpcRelayer extends BaseRelayer implements Relayer { blockHash: txReceipt.blockHash, blockNumber: ethers.BigNumber.from(txReceipt.blockNumber).toNumber(), confirmations: 1, - from: typeof metaTxnId === 'string' ? undefined : addressOf(metaTxnId.config, metaTxnId.context), + from: typeof metaTxnId === 'string' ? undefined : metaTxnId.intent.wallet, hash: txReceipt.transactionHash, raw: receipt.txnReceipt, receipt: txReceipt, // extended type which is Sequence-specific. Contains the decoded metaTxReceipt wait: async (confirmations?: number) => this.provider!.waitForTransaction(txReceipt.transactionHash, confirmations) - } as TransactionResponse + } as commons.transaction.TransactionResponse } } diff --git a/packages/relayer/tests/provider-relayer.spec.ts b/packages/relayer/tests/provider-relayer.spec.ts index b3df4d745..ce7955beb 100644 --- a/packages/relayer/tests/provider-relayer.spec.ts +++ b/packages/relayer/tests/provider-relayer.spec.ts @@ -1,91 +1,46 @@ -import { deployWalletContext } from './utils/deploy-wallet-context' - +import { commons, v2 } from '@0xsequence/core' +import { Orchestrator } from '@0xsequence/signhub' +import { context } from '@0xsequence/tests' +import { Wallet, WalletV2 } from '@0xsequence/wallet' import { CallReceiverMock, HookCallerMock } from '@0xsequence/wallet-contracts' - -import { Wallet } from '@0xsequence/wallet' -import { LocalRelayer } from '@0xsequence/relayer' - -import { WalletContext, NetworkConfig } from '@0xsequence/network' -import { ethers, Signer as AbstractSigner, providers } from 'ethers' - -import chaiAsPromised from 'chai-as-promised' import * as chai from 'chai' +import chaiAsPromised from 'chai-as-promised' +import { ethers } from 'ethers' +import hardhat from 'hardhat' +import { LocalRelayer } from '../src' const CallReceiverMockArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/mocks/CallReceiverMock.sol/CallReceiverMock.json') const HookCallerMockArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/mocks/HookCallerMock.sol/HookCallerMock.json') const { expect } = chai.use(chaiAsPromised) -import { computeMetaTxnHash, encodeNonce } from '@0xsequence/transactions' - -type EthereumInstance = { - chainId: number - providerUrl?: string - provider: providers.JsonRpcProvider - signer: AbstractSigner -} - describe('Wallet integration', function () { - const ethnode: EthereumInstance = {} as any - let relayer: LocalRelayer let callReceiver: CallReceiverMock let hookCaller: HookCallerMock - let context: WalletContext - let networks: NetworkConfig[] + let contexts: Awaited> + let provider: ethers.providers.Web3Provider + let signers: ethers.Signer[] before(async () => { - // Provider from hardhat without a server instance - ethnode.providerUrl = `http://127.0.0.1:9547/` - ethnode.provider = new ethers.providers.JsonRpcProvider(ethnode.providerUrl) - - ethnode.signer = ethnode.provider.getSigner() - ethnode.chainId = 31337 - - // Deploy local relayer - relayer = new LocalRelayer(ethnode.signer) - - networks = [ - { - name: 'local', - chainId: ethnode.chainId, - provider: ethnode.provider, - isDefaultChain: true, - isAuthChain: true, - relayer: relayer - } - ] as NetworkConfig[] - - // Deploy Sequence env - const [factory, mainModule, mainModuleUpgradable, guestModule, sequenceUtils, requireFreshSigner] = await deployWalletContext( - ethnode.provider - ) - - // Create fixed context obj - context = { - factory: factory.address, - mainModule: mainModule.address, - mainModuleUpgradable: mainModuleUpgradable.address, - guestModule: guestModule.address, - sequenceUtils: sequenceUtils.address, - libs: { - requireFreshSigner: requireFreshSigner.address - } - } + provider = new ethers.providers.Web3Provider(hardhat.network.provider.send) + signers = new Array(8).fill(0).map((_, i) => provider.getSigner(i)) + contexts = await context.deploySequenceContexts(signers[0]) + relayer = new LocalRelayer(signers[1]) // Deploy call receiver mock callReceiver = (await new ethers.ContractFactory( CallReceiverMockArtifact.abi, CallReceiverMockArtifact.bytecode, - ethnode.signer + signers[0] ).deploy()) as CallReceiverMock // Deploy hook caller mock hookCaller = (await new ethers.ContractFactory( HookCallerMockArtifact.abi, HookCallerMockArtifact.bytecode, - ethnode.signer + signers[0] ).deploy()) as HookCallerMock }) @@ -100,13 +55,34 @@ describe('Wallet integration', function () { deployed: false } ].map(c => { - let wallet: Wallet + let wallet: WalletV2 beforeEach(async () => { - wallet = (await Wallet.singleOwner(ethers.Wallet.createRandom(), context)).connect(networks[0].provider!, relayer) - if (c.deployed) await relayer.deployWallet(wallet.config, wallet.context) + const signer = ethers.Wallet.createRandom() + const orchestrator = new Orchestrator([signer]) + + const config = v2.config.ConfigCoder.fromSimple({ + threshold: 1, + checkpoint: 0, + signers: [{ + address: signer.address, + weight: 1 + }], + }) + + wallet = Wallet.newWallet({ + coders: v2.coders, + context: contexts[2], + config, + orchestrator, + chainId: provider.network.chainId, + provider, + relayer + }) - expect(await wallet.isDeployed()).to.equal(c.deployed) + if (c.deployed) await wallet.deploy() + + expect(await wallet.reader().isDeployed(wallet.address)).to.equal(c.deployed) }) describe(`For ${c.name} wallet`, () => { @@ -117,11 +93,10 @@ describe('Wallet integration', function () { delegateCall: false, revertOnError: false, gasLimit: 140000, - value: 0, - nonce: 0 + value: 0 } - const id = computeMetaTxnHash(wallet.address, ethnode.chainId, txn) + const id = commons.transaction.subdigestOfTransactions(wallet.address, provider.network.chainId, 0, [txn]) const receiptPromise = relayer.wait(id, 10000) await new Promise(r => setTimeout(r, 1000)) @@ -132,6 +107,7 @@ describe('Wallet integration', function () { expect(receipt).to.not.be.undefined expect(receipt.hash).to.equal(ogtx.hash) }) + it('Should get receipt of success batch transaction', async () => { const txns = [ { @@ -154,7 +130,7 @@ describe('Wallet integration', function () { } ] - const id = computeMetaTxnHash(wallet.address, ethnode.chainId, ...txns) + const id = commons.transaction.subdigestOfTransactions(wallet.address, provider.network.chainId, 0, txns) const receiptPromise = relayer.wait(id, 10000) await new Promise(r => setTimeout(r, 1000)) @@ -165,6 +141,7 @@ describe('Wallet integration', function () { expect(receipt).to.not.be.undefined expect(receipt.hash).to.equal(ogtx.hash) }) + it('Should get receipt of batch transaction with failed meta-txs', async () => { const txns = [ { @@ -177,7 +154,7 @@ describe('Wallet integration', function () { nonce: 0 }, { - to: context.factory, + to: contexts[2].factory, // 0xff not a valid factory method data: '0xffffffffffff', delegateCall: false, @@ -188,7 +165,7 @@ describe('Wallet integration', function () { } ] - const id = computeMetaTxnHash(wallet.address, ethnode.chainId, ...txns) + const id = commons.transaction.subdigestOfTransactions(wallet.address, provider.network.chainId, 0, txns) const receiptPromise = relayer.wait(id, 10000) await new Promise(r => setTimeout(r, 1000)) @@ -199,9 +176,10 @@ describe('Wallet integration', function () { expect(receipt).to.not.be.undefined expect(receipt.hash).to.equal(ogtx.hash) }) + it('Should get receipt of failed transaction', async () => { const txn = { - to: context.factory, + to: contexts[1].factory, // 0xff not a valid factory method data: '0xffffffffffff', delegateCall: false, @@ -211,7 +189,7 @@ describe('Wallet integration', function () { nonce: 0 } - const id = computeMetaTxnHash(wallet.address, ethnode.chainId, txn) + const id = commons.transaction.subdigestOfTransactions(wallet.address, provider.network.chainId, 0, [txn]) const receiptPromise = relayer.wait(id, 10000) await new Promise(r => setTimeout(r, 1000)) @@ -222,14 +200,33 @@ describe('Wallet integration', function () { expect(receipt).to.not.be.undefined expect(receipt.hash).to.equal(ogtx.hash) }) + it('Find correct receipt between multiple other transactions', async () => { - // Pre-txs - const altWallet = (await Wallet.singleOwner(ethers.Wallet.createRandom(), context)).connect( - networks[0].provider!, - relayer - ) - await relayer.deployWallet(altWallet.config, altWallet.context) - expect(await altWallet.isDeployed()).to.equal(true) + const altSigner = ethers.Wallet.createRandom() + const orchestrator = new Orchestrator([altSigner]) + + const config = v2.config.ConfigCoder.fromSimple({ + threshold: 1, + checkpoint: 0, + signers: [{ + address: altSigner.address, + weight: 1 + }] + }) + + const altWallet = Wallet.newWallet({ + coders: v2.coders, + context: contexts[2], + config, + provider, + relayer, + orchestrator, + chainId: provider.network.chainId + }) + + await altWallet.deploy() + + expect(await altWallet.reader().isDeployed(altWallet.address)).to.be.true await Promise.all( new Array(8).fill(0).map(async (_, i) => { @@ -239,9 +236,8 @@ describe('Wallet integration', function () { delegateCall: false, revertOnError: false, gasLimit: 140000, - value: 0, - nonce: encodeNonce(i, 0) - }) + value: 0 + }, commons.transaction.encodeNonce(i, 0)) }) ) @@ -255,7 +251,7 @@ describe('Wallet integration', function () { nonce: 0 } - const id = computeMetaTxnHash(wallet.address, ethnode.chainId, txn) + const id = commons.transaction.subdigestOfTransactions(wallet.address, provider.network.chainId, 0, [txn]) const receiptPromise = relayer.wait(id, 10000) await new Promise(r => setTimeout(r, 1000)) @@ -271,9 +267,8 @@ describe('Wallet integration', function () { delegateCall: false, revertOnError: false, gasLimit: 140000, - value: 0, - nonce: encodeNonce(i + 1000, 0) - }) + value: 0 + }, commons.transaction.encodeNonce(i + 1000, 0)) }) ) @@ -282,14 +277,30 @@ describe('Wallet integration', function () { expect(receipt).to.not.be.undefined expect(receipt.hash).to.equal(ogtx.hash) }) + it('Find correct receipt between multiple other failed transactions', async () => { // Pre-txs - const altWallet = (await Wallet.singleOwner(ethers.Wallet.createRandom(), context)).connect( - networks[0].provider!, - relayer - ) - await relayer.deployWallet(altWallet.config, altWallet.context) - expect(await altWallet.isDeployed()).to.equal(true) + const altSigner = ethers.Wallet.createRandom() + const orchestrator = new Orchestrator([altSigner]) + + const config = v2.config.ConfigCoder.fromSimple({ + threshold: 1, + checkpoint: 0, + signers: [{ + address: altSigner.address, + weight: 1 + }] + }) + + const altWallet = Wallet.newWallet({ + coders: v2.coders, + context: contexts[2], + config, + provider, + relayer, + orchestrator, + chainId: provider.network.chainId + }) await Promise.all( new Array(8).fill(0).map(async (_, i) => { @@ -299,24 +310,22 @@ describe('Wallet integration', function () { delegateCall: false, revertOnError: false, gasLimit: 140000, - value: 0, - nonce: encodeNonce(i, 0) - }) + value: 0 + }, commons.transaction.encodeNonce(i, 0)) }) ) await Promise.all( new Array(8).fill(0).map(async (_, i) => { await altWallet.sendTransaction({ - to: context.factory, + to: contexts[2].factory, // 0xff not a valid factory method data: '0xffffffffffff', delegateCall: false, revertOnError: false, gasLimit: 140000, - value: 0, - nonce: encodeNonce(i + 1000, 0) - }) + value: 0 + }, commons.transaction.encodeNonce(i + 1000, 0)) }) ) @@ -330,7 +339,7 @@ describe('Wallet integration', function () { nonce: 0 } - const id = computeMetaTxnHash(wallet.address, ethnode.chainId, txn) + const id = commons.transaction.subdigestOfTransactions(wallet.address, provider.network.chainId, 0, [txn]) const receiptPromise = relayer.wait(id, 10000) await new Promise(r => setTimeout(r, 1000)) @@ -342,14 +351,30 @@ describe('Wallet integration', function () { expect(receipt).to.not.be.undefined expect(receipt.hash).to.equal(ogtx.hash) }) + it('Find failed tx receipt between multiple other failed transactions', async () => { // Pre-txs - const altWallet = (await Wallet.singleOwner(ethers.Wallet.createRandom(), context)).connect( - networks[0].provider!, - relayer - ) - await relayer.deployWallet(altWallet.config, altWallet.context) - expect(await altWallet.isDeployed()).to.equal(true) + const altSigner = ethers.Wallet.createRandom() + const orchestrator = new Orchestrator([altSigner]) + + const config = v2.config.ConfigCoder.fromSimple({ + threshold: 1, + checkpoint: 0, + signers: [{ + address: altSigner.address, + weight: 1 + }] + }) + + const altWallet = Wallet.newWallet({ + coders: v2.coders, + context: contexts[2], + config, + provider, + relayer, + orchestrator, + chainId: provider.network.chainId + }) await Promise.all( new Array(8).fill(0).map(async (_, i) => { @@ -358,30 +383,26 @@ describe('Wallet integration', function () { data: ethers.utils.randomBytes(43), delegateCall: false, revertOnError: false, - gasLimit: 140000, - value: 0, - nonce: encodeNonce(i, 0) - }) + gasLimit: 140000 + }, commons.transaction.encodeNonce(i, 0)) }) ) await Promise.all( new Array(8).fill(0).map(async (_, i) => { await altWallet.sendTransaction({ - to: context.factory, + to: contexts[1].factory, // 0xff not a valid factory method data: '0xffffffffffff', delegateCall: false, revertOnError: false, - gasLimit: 140000, - value: 0, - nonce: encodeNonce(i + 1000, 0) - }) + gasLimit: 140000 + }, commons.transaction.encodeNonce(i + 1000, 0)) }) ) const txn = { - to: context.factory, + to: contexts[2].factory, // 0xff not a valid factory method data: '0xffffffffffff', delegateCall: false, @@ -391,7 +412,7 @@ describe('Wallet integration', function () { nonce: 0 } - const id = computeMetaTxnHash(wallet.address, ethnode.chainId, txn) + const id = commons.transaction.subdigestOfTransactions(wallet.address, provider.network.chainId, 0, [txn]) const receiptPromise = relayer.wait(id, 10000) await new Promise(r => setTimeout(r, 1000)) @@ -402,6 +423,7 @@ describe('Wallet integration', function () { expect(receipt).to.not.be.undefined expect(receipt.hash).to.equal(ogtx.hash) }) + it('Should timeout receipt if transaction is never sent', async () => { const txn = { to: ethers.Wallet.createRandom().address, @@ -413,11 +435,12 @@ describe('Wallet integration', function () { nonce: 0 } - const id = computeMetaTxnHash(wallet.address, ethnode.chainId, txn) + const id = commons.transaction.subdigestOfTransactions(wallet.address, provider.network.chainId, 0, [txn]) const receiptPromise = relayer.wait(id, 2000) await expect(receiptPromise).to.be.rejectedWith(`Timeout waiting for transaction receipt ${id}`) }) + if (c.deployed) { it('Find correct receipt between multiple other failed transactions of the same wallet', async () => { // Pre-txs @@ -429,24 +452,22 @@ describe('Wallet integration', function () { delegateCall: false, revertOnError: false, gasLimit: 140000, - value: 0, - nonce: encodeNonce(i + 1000, 0) - }) + value: 0 + }, commons.transaction.encodeNonce(i + 1000, 0)) }) ) await Promise.all( new Array(8).fill(0).map(async (_, i) => { await wallet.sendTransaction({ - to: context.factory, + to: contexts[1].factory, // 0xff not a valid factory method data: '0xffffffffffff', delegateCall: false, revertOnError: false, gasLimit: 140000, - value: 0, - nonce: encodeNonce(i + 2000, 0) - }) + value: 0 + }, commons.transaction.encodeNonce(i + 2000, 0)) }) ) @@ -455,12 +476,10 @@ describe('Wallet integration', function () { data: ethers.utils.randomBytes(43), delegateCall: false, revertOnError: false, - gasLimit: 140000, - value: 0, - nonce: 0 + gasLimit: 140000 } - const id = computeMetaTxnHash(wallet.address, ethnode.chainId, txn) + const id = commons.transaction.subdigestOfTransactions(wallet.address, provider.network.chainId, 0, [txn]) const receiptPromise = relayer.wait(id, 10000) await new Promise(r => setTimeout(r, 1000)) diff --git a/packages/relayer/tests/utils/deploy-wallet-context.ts b/packages/relayer/tests/utils/deploy-wallet-context.ts deleted file mode 100644 index 24818efd5..000000000 --- a/packages/relayer/tests/utils/deploy-wallet-context.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { ethers, providers } from 'ethers' - -import { - Factory, - GuestModule, - MainModule, - MainModuleUpgradable, - SequenceUtils, - RequireFreshSigner -} from '@0xsequence/wallet-contracts' - -const FactoryArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/Factory.sol/Factory.json') -const GuestModuleArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/modules/GuestModule.sol/GuestModule.json') -const MainModuleArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/modules/MainModule.sol/MainModule.json') -const MainModuleUpgradableArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/modules/MainModuleUpgradable.sol/MainModuleUpgradable.json') -const SequenceUtilsArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/modules/utils/SequenceUtils.sol/SequenceUtils.json') -const RequireFreshSignerArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/modules/utils/libs/RequireFreshSigner.sol/RequireFreshSigner.json') - -export async function deployWalletContext( - provider: providers.Provider -): Promise<[Factory, MainModule, MainModuleUpgradable, GuestModule, SequenceUtils, RequireFreshSigner]> { - const factory = (await new ethers.ContractFactory( - FactoryArtifact.abi, - FactoryArtifact.bytecode, - (provider as any).getSigner() - ).deploy()) as unknown as Factory - - const mainModule = (await new ethers.ContractFactory( - MainModuleArtifact.abi, - MainModuleArtifact.bytecode, - (provider as any).getSigner() - ).deploy(factory.address)) as unknown as MainModule - - const mainModuleUpgradable = (await new ethers.ContractFactory( - MainModuleUpgradableArtifact.abi, - MainModuleUpgradableArtifact.bytecode, - (provider as any).getSigner() - ).deploy()) as unknown as MainModuleUpgradable - - const guestModule = (await new ethers.ContractFactory( - GuestModuleArtifact.abi, - GuestModuleArtifact.bytecode, - (provider as any).getSigner() - ).deploy()) as unknown as GuestModule - - const sequenceUtils = (await new ethers.ContractFactory( - SequenceUtilsArtifact.abi, - SequenceUtilsArtifact.bytecode, - (provider as any).getSigner() - ).deploy(factory.address, mainModule.address)) as unknown as SequenceUtils - - const requireFreshSigner = (await new ethers.ContractFactory( - RequireFreshSignerArtifact.abi, - RequireFreshSignerArtifact.bytecode, - (provider as any).getSigner() - ).deploy(sequenceUtils.address)) as unknown as RequireFreshSigner - - return [factory, mainModule, mainModuleUpgradable, guestModule, sequenceUtils, requireFreshSigner] -} diff --git a/packages/replacer/package.json b/packages/replacer/package.json new file mode 100644 index 000000000..2cc2f83c6 --- /dev/null +++ b/packages/replacer/package.json @@ -0,0 +1,26 @@ +{ + "name": "@0xsequence/replacer", + "version": "0.43.4", + "description": "EIP-5719 client implementation", + "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/replacer", + "source": "src/index.ts", + "main": "dist/0xsequence-replacer.cjs.js", + "module": "dist/0xsequence-replacer.esm.js", + "author": "Horizon Blockchain Games", + "license": "Apache-2.0", + "scripts": { + "test": "echo 'TODO: replacer tests'" + }, + "dependencies": { + "@0xsequence/abi": "workspace:*", + "@0xsequence/core": "workspace:*" + }, + "peerDependencies": { + "ethers": ">=5.5" + }, + "devDependencies": {}, + "files": [ + "src", + "dist" + ] +} diff --git a/packages/replacer/src/cached.ts b/packages/replacer/src/cached.ts new file mode 100644 index 000000000..7bac99df6 --- /dev/null +++ b/packages/replacer/src/cached.ts @@ -0,0 +1,32 @@ +import { ethers } from "ethers"; +import { runByEIP5719, URISolver } from "."; + +export class CachedEIP5719 { + constructor( + public provider: ethers.providers.Provider, + public solver?: URISolver, + public window: number = 1000 + ) {} + + private pending: Map + }> = new Map() + + async runByEIP5719( + address: string, + digest: ethers.BytesLike, + signature: ethers.BytesLike + ): Promise { + const key = `${address}-${digest}-${signature}` + const now = Date.now() + + if (this.pending.has(key) && now - this.pending.get(key)!.timestamp < this.window) { + return this.pending.get(key)!.promise + } + + const promise = runByEIP5719(address, this.provider, digest, signature, this.solver) + this.pending.set(key, { timestamp: now, promise }) + return promise + } +} \ No newline at end of file diff --git a/packages/replacer/src/index.ts b/packages/replacer/src/index.ts new file mode 100644 index 000000000..80cecdba2 --- /dev/null +++ b/packages/replacer/src/index.ts @@ -0,0 +1,119 @@ + +import { ethers } from "ethers" +import { walletContracts } from "@0xsequence/abi" +import { isIPFS, useGateway } from "./ipfs" +import { commons } from "@0xsequence/core" + +export * from "./cached" + +export function eip5719Contract(address: string, provider: ethers.providers.Provider): ethers.Contract { + // TODO: for some reason walletContracts is not being loaded from local + // remove this code once fixed + const abi = [ + { + "inputs": [ + { + "internalType": "bytes32", + "type": "bytes32" + } + ], + "name": "getAlternativeSignature", + "outputs": [ + { + "internalType": "string", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } + ] + + return new ethers.Contract(address, abi, provider) +} + +export function eip1271Contract(address: string, provider: ethers.providers.Provider): ethers.Contract { + return new ethers.Contract(address, walletContracts.erc1271.abi, provider) +} + +export async function isValidSignature( + address: string, + provider: ethers.providers.Provider, + digest: ethers.BytesLike, + signature: ethers.BytesLike +): Promise { + // First we try to validate the signature using Ethers + try { + const addr = ethers.utils.recoverAddress(digest, signature) + if (addr.toLowerCase() === address.toLowerCase()) return true + } catch {} + + // Then we try to validate the signature using EIP1271 + try { + const contract = eip1271Contract(address, provider) + const value = await contract.isValidSignature(digest, signature) + if (value === walletContracts.erc1271.returns) return true + } catch {} + + // If all else fails, we return false + return false +} + +export interface URISolver { + resolve: (uri: string) => Promise +} + +async function tryAwait(promise: Promise): Promise { + try { + return await promise + } catch { + return undefined + } +} + +export async function runByEIP5719( + address: string, + provider: ethers.providers.Provider, + digest: ethers.BytesLike, + signature: ethers.BytesLike, + solver?: URISolver, + tries: number = 0 +): Promise { + if (tries > 10) throw new Error('EIP5719 - Too many tries') + + if (commons.signer.canRecover(signature)) { + const recoveredAddr = commons.signer.recoverSigner(digest, signature) + if (recoveredAddr && recoveredAddr.toLowerCase() === address.toLowerCase()) return signature + } + + try { + if (await commons.signer.isValidSignature(address, digest, signature, provider)) { + return signature + } + } catch {} + + const altUri = await tryAwait(eip5719Contract(address, provider).getAlternativeSignature(digest) as Promise) + if (!altUri || altUri === '') throw new Error('EIP5719 - Invalid signature and no alternative signature') + + const altSignature = ethers.utils.hexlify(await (solver || new URISolverIPFS()).resolve(altUri)) + if (!altSignature || altSignature === '') throw new Error('EIP5719 - Empty alternative signature') + if (altSignature === ethers.utils.hexlify(signature)) throw new Error('EIP5719 - Alternative signature is invalid or the same') + + return runByEIP5719(address, provider, digest, altSignature, solver, tries + 1) +} + +export class URISolverIPFS implements URISolver { + constructor(public gateway: string = 'https://cloudflare-ipfs.com/ipfs/') {} + + uri = (uri: string): string => { + if (isIPFS(uri)) return useGateway(uri, this.gateway) + return uri + } + + resolve = async (uri: string): Promise => { + const url = this.uri(uri) + const res = await fetch(url) + if (!res.ok) throw new Error(`URISolverIPFS - Failed to fetch ${url}`) + return await res.text() + } +} diff --git a/packages/replacer/src/ipfs.ts b/packages/replacer/src/ipfs.ts new file mode 100644 index 000000000..c4c8f2059 --- /dev/null +++ b/packages/replacer/src/ipfs.ts @@ -0,0 +1,10 @@ + +export function useGateway(uri: string, gateway: string) { + const clean = uri.replace('ipfs://ipfs/', '').replace('ipfs://', '') + if (uri.startsWith('ipfs://')) return `${gateway}${clean}` + return uri +} + +export function isIPFS(uri: string): boolean { + return uri.startsWith('ipfs://') +} diff --git a/packages/sessions/hardhat.config.js b/packages/sessions/hardhat.config.js new file mode 100644 index 000000000..51bc6d710 --- /dev/null +++ b/packages/sessions/hardhat.config.js @@ -0,0 +1,11 @@ + +module.exports = { + networks: { + hardhat: { + chainId: 31337, + accounts: { + mnemonic: 'ripple axis someone ridge uniform wrist prosper there frog rate olympic knee' + }, + }, + } +} diff --git a/packages/sessions/package.json b/packages/sessions/package.json new file mode 100644 index 000000000..ee750157d --- /dev/null +++ b/packages/sessions/package.json @@ -0,0 +1,34 @@ +{ + "name": "@0xsequence/sessions", + "version": "0.43.4", + "description": "tools for migrating sequence wallets to new versions", + "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/sessions", + "source": "src/index.ts", + "main": "dist/0xsequence-sessions.cjs.js", + "module": "dist/0xsequence-sessions.esm.js", + "author": "Horizon Blockchain Games", + "license": "Apache-2.0", + "scripts": { + "test": "pnpm test:file tests/**/*.spec.ts", + "test:file": "TS_NODE_PROJECT=../../tsconfig.test.json mocha -r ts-node/register --timeout 30000", + "test:coverage": "nyc pnpm test" + }, + "dependencies": { + "@0xsequence/core": "workspace:*", + "@0xsequence/migration": "workspace:*", + "@0xsequence/replacer": "workspace:*", + "ethers": "^5.5.2", + "idb": "^7.1.1" + }, + "devDependencies": { + "@0xsequence/signhub": "workspace:*", + "@0xsequence/tests": "workspace:*", + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "fake-indexeddb": "^4.0.1", + "nyc": "^15.1.0" + }, + "files": [ + "src", + "dist" + ] +} diff --git a/packages/sessions/src/index.ts b/packages/sessions/src/index.ts new file mode 100644 index 000000000..fc69b4bb1 --- /dev/null +++ b/packages/sessions/src/index.ts @@ -0,0 +1,3 @@ + +export * as tracker from './tracker' +export * as trackers from './trackers' diff --git a/packages/sessions/src/tracker.ts b/packages/sessions/src/tracker.ts new file mode 100644 index 000000000..30a9844a7 --- /dev/null +++ b/packages/sessions/src/tracker.ts @@ -0,0 +1,65 @@ +import { commons } from '@0xsequence/core' +import { ethers } from 'ethers' + +export type PresignedConfig = { + wallet: string + nextConfig: commons.config.Config + signature: string +} + +export type PresignedConfigLink = Omit & { nextImageHash: string } + +export type ConfigDataDump = { + configurations: commons.config.Config[], + wallets: { + imageHash: string, + context: commons.context.WalletContext + }[], + presignedTransactions: PresignedConfigLink[] +} + +export abstract class ConfigTracker { + loadPresignedConfiguration: (args: { + wallet: string, + fromImageHash: string, + longestPath?: boolean + }) => Promise + + savePresignedConfiguration: (args: PresignedConfig) => Promise + + saveWitnesses: (args: { wallet: string; digest: string; chainId: ethers.BigNumberish; signatures: string[] }) => Promise + + configOfImageHash: (args: { + imageHash: string, + noCache?: boolean + }) => Promise + + saveWalletConfig: (args: { + config: commons.config.Config + }) => Promise + + imageHashOfCounterfactualWallet: (args: { + wallet: string, + noCache?: boolean + }) => Promise<{ + imageHash: string, + context: commons.context.WalletContext + } | undefined> + + saveCounterfactualWallet: (args: { + config: commons.config.Config; + context: commons.context.WalletContext[] + }) => Promise + + walletsOfSigner: (args: { + signer: string, + noCache?: boolean + }) => Promise<{ + wallet: string, + proof: { + digest: string, + chainId: ethers.BigNumber, + signature: string + } + }[]> +} diff --git a/packages/sessions/src/trackers/cached.ts b/packages/sessions/src/trackers/cached.ts new file mode 100644 index 000000000..9ef519a70 --- /dev/null +++ b/packages/sessions/src/trackers/cached.ts @@ -0,0 +1,151 @@ + +import { commons, universal } from '@0xsequence/core' +import { migrator } from '@0xsequence/migration' +import { ethers } from 'ethers' +import { ConfigTracker, PresignedConfig, PresignedConfigLink } from '../tracker' + +export class CachedTracker implements migrator.PresignedMigrationTracker, ConfigTracker { + constructor( + private readonly tracker: migrator.PresignedMigrationTracker & ConfigTracker, + private readonly cache: migrator.PresignedMigrationTracker & ConfigTracker, + public readonly contexts: commons.context.VersionedContext + ) {} + + async loadPresignedConfiguration(args: { wallet: string; fromImageHash: string; longestPath?: boolean | undefined }): Promise { + // We need to check both, and return the one with the highest checkpoint + // eventually we could try to combine them, but for now we'll just return + // the one with the highest checkpoint + const results = [this.tracker.loadPresignedConfiguration(args), this.cache.loadPresignedConfiguration(args)] + + let best: PresignedConfigLink[] + + // If both results end with the same image hash, we can just return the longest/shortest one + const [result1, result2] = await Promise.all(results) + if ( + result1.length > 0 && + result2.length > 0 && + result1[result1.length - 1].nextImageHash === result2[result2.length - 1].nextImageHash + ) { + best = ( + args.longestPath === true ? + result1.length > result2.length ? result1 : result2 : + result1.length < result2.length ? result1 : result2 + ) + } else { + // Otherwise we need to check the checkpoints + // this requires us to fetch the config for each image hash + const checkpoints = await Promise.all(results.map(async result => { + const r = await result + const last = r[r.length - 1] + if (!last) return undefined + + // TODO: This will fire a lot of requests, optimize it + const config = await this.configOfImageHash({ imageHash: last.nextImageHash }) + if (!config) return undefined + + return { checkpoint: universal.genericCoderFor(config.version).config.checkpointOf(config), result: r } + })) + + best = checkpoints.reduce((acc, val) => { + if (!val) return acc + if (!acc) return val + if (val.checkpoint.gt(acc.checkpoint)) return val + return acc + })?.result ?? [] + } + + if (!best) return [] + + return best + } + + async savePresignedConfiguration(args: PresignedConfig): Promise { + await Promise.all([this.tracker.savePresignedConfiguration(args), this.cache.savePresignedConfiguration(args)]) + } + + async configOfImageHash(args: { imageHash: string, noCache?: boolean }): Promise { + // We first check the cache, if it's not there, we check the tracker + // and then we save it to the cache + if (args.noCache !== true) { + const config = await this.cache.configOfImageHash(args) + if (config) return config + } + + const config2 = await this.tracker.configOfImageHash(args) + if (config2) { + await this.cache.saveWalletConfig({ config: config2 }) + } + + return config2 + } + + async saveWalletConfig(args: { config: commons.config.Config }): Promise { + await Promise.all([this.tracker.saveWalletConfig(args), this.cache.saveWalletConfig(args)]) + } + + async imageHashOfCounterfactualWallet(args: { wallet: string, noCache?: boolean }): Promise<{ imageHash: string; context: commons.context.WalletContext } | undefined> { + // We first check the cache, if it's not there, we check the tracker + // and then we save it to the cache + if (args.noCache !== true) { + const result1 = await this.cache.imageHashOfCounterfactualWallet(args) + if (result1) return result1 + } + + const result2 = await this.tracker.imageHashOfCounterfactualWallet(args) + if (result2) { + // TODO: We shouldn't need to get the config to save the counterfactual wallet + const config = await this.configOfImageHash({ imageHash: result2.imageHash }) + if (config) { + await this.cache.saveCounterfactualWallet({ config, context: [result2.context] }) + } + } + + return result2 + } + + async saveCounterfactualWallet(args: { config: commons.config.Config; context: commons.context.WalletContext[] }): Promise { + await Promise.all([this.tracker.saveCounterfactualWallet(args), this.cache.saveCounterfactualWallet(args)]) + } + + async walletsOfSigner(args: { signer: string, noCache?: boolean }): Promise<{ wallet: string; proof: { digest: string; chainId: ethers.BigNumber; signature: string } }[]> { + if (args.noCache) { + return this.tracker.walletsOfSigner(args) + } + + // In this case we need to both aggregate the results from the cache and the tracker + // and then dedupe the results + const results = await Promise.all([this.tracker.walletsOfSigner(args), this.cache.walletsOfSigner(args)]) + const wallets = new Map() + + for (const result of results) { + for (const wallet of result) { + wallets.set(wallet.wallet, wallet) + } + } + + return Array.from(wallets.values()) + } + + async saveWitnesses(args: { wallet: string; digest: string; chainId: ethers.BigNumberish; signatures: string[] }): Promise { + await Promise.all([this.tracker.saveWitnesses(args), this.cache.saveWitnesses(args)]) + } + + async getMigration(address: string, fromImageHash: string, fromVersion: number, chainId: ethers.BigNumberish): Promise { + // We first check the cache, if it's not there, we check the tracker + // NOTICE: we could eventually try to combine the two, but now we just have 1 migration + // so it's not worth it. + const migration1 = await this.cache.getMigration(address, fromImageHash, fromVersion, chainId) + if (migration1) return migration1 + + const migration2 = await this.tracker.getMigration(address, fromImageHash, fromVersion, chainId) + if (migration2) { + await this.cache.saveMigration(address, migration2, this.contexts) + } + + return migration2 + } + + async saveMigration(address: string, signed: migrator.SignedMigration, contexts: commons.context.VersionedContext): Promise { + await Promise.all([this.tracker.saveMigration(address, signed, contexts), this.cache.saveMigration(address, signed, contexts)]) + } +} diff --git a/packages/sessions/src/trackers/debug.ts b/packages/sessions/src/trackers/debug.ts new file mode 100644 index 000000000..3a7d8bfa9 --- /dev/null +++ b/packages/sessions/src/trackers/debug.ts @@ -0,0 +1,96 @@ +import { commons } from '@0xsequence/core' +import { migrator } from '@0xsequence/migration' +import { ethers } from 'ethers' +import { ConfigTracker, PresignedConfig, PresignedConfigLink } from '../tracker' + +export class DebugConfigTracker implements ConfigTracker, migrator.PresignedMigrationTracker { + constructor(private readonly tracker: ConfigTracker & migrator.PresignedMigrationTracker) {} + + async loadPresignedConfiguration(args: { + wallet: string + fromImageHash: string + longestPath?: boolean + }): Promise { + console.debug('? loadPresignedConfiguration') + debug(args, '? ') + return debug(await this.tracker.loadPresignedConfiguration(args), '! ') + } + + savePresignedConfiguration(args: PresignedConfig): Promise { + console.debug('? savePresignedConfiguration') + debug(args, '? ') + return this.tracker.savePresignedConfiguration(args) + } + + saveWitnesses(args: { wallet: string; digest: string; chainId: ethers.BigNumberish; signatures: string[] }): Promise { + console.debug('? saveWitnesses') + debug(args, '? ') + return this.tracker.saveWitnesses(args) + } + + async configOfImageHash(args: { imageHash: string }): Promise { + console.debug('? configOfImageHash') + debug(args, '? ') + return debug(await this.tracker.configOfImageHash(args), '! ') + } + + saveWalletConfig(args: { config: commons.config.Config }): Promise { + console.debug('? saveWalletConfig') + debug(args, '? ') + return this.tracker.saveWalletConfig(args) + } + + async imageHashOfCounterfactualWallet(args: { + wallet: string + }): Promise<{ imageHash: string; context: commons.context.WalletContext } | undefined> { + console.debug('? imageHashOfCounterfactualWallet') + debug(args, '? ') + return debug(await this.tracker.imageHashOfCounterfactualWallet(args), '! ') + } + + saveCounterfactualWallet(args: { config: commons.config.Config; context: commons.context.WalletContext[] }): Promise { + console.debug('? saveCounterfactualWallet') + debug(args, '? ') + return this.tracker.saveCounterfactualWallet(args) + } + + async walletsOfSigner(args: { + signer: string + }): Promise<{ wallet: string; proof: { digest: string; chainId: ethers.BigNumber; signature: string } }[]> { + console.debug('? walletsOfSigner') + debug(args, '? ') + return debug(await this.tracker.walletsOfSigner(args), '! ') + } + + async getMigration( + address: string, + fromImageHash: string, + fromVersion: number, + chainId: ethers.BigNumberish + ): Promise { + console.debug('? getMigration') + debug({ address, fromImageHash, fromVersion, chainId }, '? ') + return debug(await this.tracker.getMigration(address, fromImageHash, fromVersion, chainId), '! ') + } + + saveMigration(address: string, signed: migrator.SignedMigration, contexts: commons.context.VersionedContext): Promise { + console.debug('? saveMigration') + debug({ address, signed, contexts }, '? ') + return this.tracker.saveMigration(address, signed, contexts) + } +} + +function debug(value: T, prefix: string = ''): T { + switch (value) { + case undefined: + console.debug(prefix + 'undefined') + break + default: + JSON.stringify(value, undefined, 2) + .split('\n') + .map(line => prefix + line) + .forEach(line => console.debug(line)) + break + } + return value +} diff --git a/packages/sessions/src/trackers/deduped.ts b/packages/sessions/src/trackers/deduped.ts new file mode 100644 index 000000000..7dc37ee07 --- /dev/null +++ b/packages/sessions/src/trackers/deduped.ts @@ -0,0 +1,66 @@ +import { commons } from "@0xsequence/core" +import { migrator } from "@0xsequence/migration"; +import { BigNumber, BigNumberish } from "ethers"; +import { ConfigTracker, PresignedConfig, PresignedConfigLink } from "../tracker"; +import { PromiseCache } from "./promise-cache"; + +export function isDedupedTracker(tracker: any): tracker is DedupedTracker { + return tracker instanceof DedupedTracker +} + +// This tracks wraps another tracker and dedupes calls to it, so in any calls +// are sent in short succession, only the first call is forwarded to the +// underlying tracker, and the rest are ignored. +export class DedupedTracker implements migrator.PresignedMigrationTracker, ConfigTracker { + private cache: PromiseCache = new PromiseCache(); + + constructor( + private readonly tracker: migrator.PresignedMigrationTracker & ConfigTracker, + public readonly window = 50, + public verbose = false + ) {} + + invalidateCache() { + this.cache = new PromiseCache() + } + + configOfImageHash(args: { imageHash: string; }): Promise { + return this.cache.do('configOfImageHash', this.window, args => this.tracker.configOfImageHash(args), args) + } + + getMigration(address: string, fromImageHash: string, fromVersion: number, chainId: BigNumberish): Promise { + return this.cache.do('getMigration', this.window, (...args) => this.tracker.getMigration(...args), address, fromImageHash, fromVersion, chainId) + } + + saveMigration(address: string, signed: migrator.SignedMigration, contexts: commons.context.VersionedContext): Promise { + return this.cache.do('saveMigration', undefined, (...args) => this.tracker.saveMigration(...args), address, signed, contexts) + } + + loadPresignedConfiguration(args: { wallet: string; fromImageHash: string; longestPath?: boolean | undefined; }): Promise { + return this.cache.do('loadPresignedConfiguration', this.window, args => this.tracker.loadPresignedConfiguration(args), args) + } + + savePresignedConfiguration(args: PresignedConfig): Promise { + return this.cache.do('savePresignedConfiguration', undefined, args => this.tracker.savePresignedConfiguration(args), args) + } + + saveWitnesses(args: { wallet: string; digest: string; chainId: BigNumberish; signatures: string[]; }): Promise { + return this.cache.do('saveWitnesses', undefined, args => this.tracker.saveWitnesses(args), args) + } + + saveWalletConfig(args: { config: commons.config.Config; }): Promise { + return this.cache.do('saveWalletConfig', undefined, args => this.tracker.saveWalletConfig(args), args) + } + + imageHashOfCounterfactualWallet(args: { wallet: string; }): Promise<{ imageHash: string; context: commons.context.WalletContext; } | undefined> { + return this.cache.do('imageHashOfCounterfactualWallet', undefined, args => this.tracker.imageHashOfCounterfactualWallet(args), args) + } + + saveCounterfactualWallet(args: { config: commons.config.Config; context: commons.context.WalletContext[]; }): Promise { + return this.cache.do('saveCounterfactualWallet', undefined, args => this.tracker.saveCounterfactualWallet(args), args) + } + + walletsOfSigner(args: { signer: string; }): Promise<{ wallet: string; proof: { digest: string; chainId: BigNumber; signature: string; }; }[]> { + return this.cache.do('walletsOfSigner', this.window, args => this.tracker.walletsOfSigner(args), args) + } +} diff --git a/packages/sessions/src/trackers/index.ts b/packages/sessions/src/trackers/index.ts new file mode 100644 index 000000000..05dddeb00 --- /dev/null +++ b/packages/sessions/src/trackers/index.ts @@ -0,0 +1,7 @@ +export * as debug from './debug' +export * as local from './local' +export * as remote from './remote' +export * as stores from './stores' +export * from './multiple' +export * from './cached' +export * from './deduped' diff --git a/packages/sessions/src/trackers/local.ts b/packages/sessions/src/trackers/local.ts new file mode 100644 index 000000000..24ce9d50a --- /dev/null +++ b/packages/sessions/src/trackers/local.ts @@ -0,0 +1,585 @@ +import { commons, universal, v1, v2 } from '@0xsequence/core' +import { migration, migrator } from '@0xsequence/migration' +import { ethers } from 'ethers' +import { CachedEIP5719 } from '@0xsequence/replacer' +import { ConfigTracker, PresignedConfig, PresignedConfigLink } from '../tracker' +import { isPlainNested, isPlainNode, isPlainV2Config, MemoryTrackerStore, PlainNested, PlainNode, TrackerStore } from './stores' + +export class LocalConfigTracker implements ConfigTracker, migrator.PresignedMigrationTracker { + private cachedEIP5719: CachedEIP5719 + + constructor( + // TODO: The provider is only used to determine that EIP1271 signatures have *some* validity + // but when reconstructing a presigned transaction we should do the replacement once per chain. + // For now, it's recommended to use Mainnet as the provider. + public provider: ethers.providers.Provider, + private store: TrackerStore = new MemoryTrackerStore(), + public useEIP5719: boolean = false + ) { + this.cachedEIP5719 = new CachedEIP5719(provider) + } + + private loadTopology = async (hash: string): Promise => { + const node = await this.store.loadV2Node(hash) + if (!node) return { nodeHash: hash } + + if (isPlainNode(node)) { + const [left, right] = await Promise.all([this.loadTopology(node.left), this.loadTopology(node.right)]) + return { left, right } + } + + if (isPlainNested(node)) { + return { + weight: ethers.BigNumber.from(node.weight), + threshold: ethers.BigNumber.from(node.threshold), + tree: await this.loadTopology(node.tree) + } + } + + return node + } + + private saveTopology = async (node: v2.config.Topology): Promise => { + if (v2.config.isNodeLeaf(node)) { + return // Nothing to do, this is a dead-end + } + + const hash = v2.config.hashNode(node) + + if (v2.config.isNode(node)) { + const saveLeft = this.saveTopology(node.left) + const saveRight = this.saveTopology(node.right) + const saveThis = this.store.saveV2Node(hash, { + left: v2.config.hashNode(node.left), + right: v2.config.hashNode(node.right) + } as PlainNode) + + await Promise.all([saveLeft, saveRight, saveThis]) + + return + } + + if (v2.config.isNestedLeaf(node)) { + const saveTree = this.saveTopology(node.tree) + const saveThis = this.store.saveV2Node(hash, { + weight: ethers.BigNumber.from(node.weight).toString(), + threshold: ethers.BigNumber.from(node.threshold).toString(), + tree: v2.config.hashNode(node.tree) + } as PlainNested) + + await Promise.all([saveTree, saveThis]) + + return + } + + // If it's a normal leaf, then we just store it + if (v2.config.isSignerLeaf(node)) { + return this.store.saveV2Node(hash, { + address: node.address, + weight: node.weight + }) + } + + if (v2.config.isSubdigestLeaf(node)) { + return this.store.saveV2Node(hash, { + subdigest: node.subdigest + }) + } + + throw new Error(`Unknown topology type: ${node}`) + } + + saveWalletConfig = async (args: { + config: commons.config.Config + }): Promise => { + const { config } = args + if (v1.config.ConfigCoder.isWalletConfig(config)) { + // We can store the configuration as-is + const imageHash = v1.config.ConfigCoder.imageHashOf(config) + return this.store.saveConfig(imageHash, config) + } + + if (v2.config.ConfigCoder.isWalletConfig(config)) { + // We split the configuration in a list of nodes, and store them individually + // then we can reconstruct it. This also means we can combine multiple configurations + // if they share information + const imageHash = v2.config.ConfigCoder.imageHashOf(config) + + // This is an optimization, it allows us to avoid splitting the tree if it's already complete + if (v2.config.isComplete(config.tree)) { + return this.store.saveConfig(imageHash, config) + } + + // TODO: Re-enable storing partial v2 configs once + // we have more performant code to reconstructing them + // in the meantime, rely on the remote tracker + + // const storeTree = this.saveTopology(config.tree) + // const storeConfig = this.store.saveConfig(imageHash, { + // version: 2, + // threshold: ethers.BigNumber.from(config.threshold).toString(), + // checkpoint: ethers.BigNumber.from(config.checkpoint).toString(), + // tree: v2.config.hashNode(config.tree) + // }) + + // await Promise.all([storeTree, storeConfig]) + } + + return + } + + private configOfImageHashCache = {} as { [key: string]: commons.config.Config } + + configOfImageHash = async (args: { + imageHash: string + }): Promise => { + const { imageHash } = args + + if (this.configOfImageHashCache[args.imageHash]) { + return this.configOfImageHashCache[args.imageHash] + } + + const config = await this.store.loadConfig(imageHash) + if (!config) { + return undefined + } + + if (config.version === 1 || (config.version === 2 && !isPlainV2Config(config))) { + this.configOfImageHashCache[args.imageHash] = config + return config + } + + if (isPlainV2Config(config)) { + const fullConfig = { + version: 2, + threshold: ethers.BigNumber.from(config.threshold), + checkpoint: ethers.BigNumber.from(config.checkpoint), + tree: await this.loadTopology(config.tree) + } as v2.config.WalletConfig + this.configOfImageHashCache[args.imageHash] = fullConfig + return fullConfig + } + + throw new Error(`Unknown config type: ${config}`) + } + + saveCounterfactualWallet = async (args: { + config: commons.config.Config + context: commons.context.WalletContext[] + }): Promise => { + const { config, context } = args + const imageHash = universal.genericCoderFor(config.version).config.imageHashOf(config) + await Promise.all([ + this.saveWalletConfig({ config }), + ...context.map(ctx => { + const address = commons.context.addressOf(ctx, imageHash) + return this.store.saveCounterfactualWallet(address, imageHash, ctx) + }) + ]) + } + + imageHashOfCounterfactualWallet = async (args: { + wallet: string + }): Promise<{ + imageHash: string, + context: commons.context.WalletContext + } | undefined> => { + const { wallet } = args + const result = await this.store.loadCounterfactualWallet(wallet) + + if (!result) return undefined + + return { + imageHash: result.imageHash, + context: result.context + } + } + + savePayload = async (args: { + payload: commons.signature.SignedPayload + }): Promise => { + const { payload } = args + + const subdigest = commons.signature.subdigestOf(payload) + await this.store.savePayloadOfSubdigest(subdigest, payload) + } + + private payloadOfSubdigestCache = {} as { [key: string]: commons.signature.SignedPayload } + + payloadOfSubdigest = async (args: { + subdigest: string + }): Promise => { + if (this.payloadOfSubdigestCache[args.subdigest]) { + return this.payloadOfSubdigestCache[args.subdigest] + } + + const { subdigest } = args + const res = await this.store.loadPayloadOfSubdigest(subdigest) + + if (res) { + this.payloadOfSubdigestCache[subdigest] = res + } + + return res + } + + savePresignedConfiguration = async (args: PresignedConfig): Promise => { + // Presigned configurations only work with v2 (for now) + // so we can assume that the signature is for a v2 configuration + const decoded = v2.signature.SignatureCoder.decode(args.signature) + const nextImageHash = universal.genericCoderFor(args.nextConfig.version).config.imageHashOf(args.nextConfig) + const message = v2.chained.messageSetImageHash(nextImageHash) + const digest = ethers.utils.keccak256(message) + const payload = { + message, + address: args.wallet, + chainId: 0, + digest + } + + const savePayload = this.savePayload({ payload }) + const saveNextConfig = this.saveWalletConfig({ config: args.nextConfig }) + + const recovered = await v2.signature.SignatureCoder.recover(decoded, payload, this.provider) + + // Save the recovered configuration and all signature parts + const signatures = v2.signature.signaturesOf(recovered.config.tree) + await Promise.all([ + savePayload, + saveNextConfig, + this.saveWalletConfig({ config: recovered.config }), + ...signatures.map(sig => this.store.saveSignatureOfSubdigest(sig.address, recovered.subdigest, sig.signature)) + ]) + } + + loadPresignedConfiguration = async (args: { + wallet: string, + fromImageHash: string, + longestPath?: boolean + }): Promise => { + const { wallet, fromImageHash, longestPath } = args + + const fromConfig = await this.configOfImageHash({ imageHash: fromImageHash }) + if (!fromConfig || !v2.config.ConfigCoder.isWalletConfig(fromConfig)) { + return [] + } + + // Get all subdigests for the config members + const signers = v2.config.signersOf(fromConfig.tree).map((s) => s.address) + const subdigestsOfSigner = await Promise.all(signers.map((s) => this.store.loadSubdigestsOfSigner(s))) + const subdigests = [...new Set(subdigestsOfSigner.flat())] + + // Get all unique payloads + const payloads = await Promise.all([...new Set(subdigests)] + .map(async (s) => ({ ...(await this.payloadOfSubdigest({ subdigest: s })), subdigest: s }))) + + // Get all possible next imageHashes based on the payloads + const nextImageHashes = payloads + .filter((p) => p?.message && p?.address && p.address === wallet) + .map((p) => ({ payload: p, nextImageHash: v2.chained.decodeMessageSetImageHash(p!.message!) })) + .filter((p) => p?.nextImageHash) as { payload: commons.signature.SignedPayload & { subdigest: string }, nextImageHash: string }[] + + // Build a signature for each next imageHash + // and filter out the ones that don't have enough weight + let bestCandidate: { + nextImageHash: string, + checkpoint: ethers.BigNumber, + signature: string, + } | undefined + + const nextConfigsAndCheckpoints = await Promise.all(nextImageHashes.map(async ({ nextImageHash, payload }) => { + const nextConfig = await this.configOfImageHash({ imageHash: nextImageHash }) + if (!nextConfig || !v2.config.isWalletConfig(nextConfig)) return undefined + const nextCheckpoint = ethers.BigNumber.from(nextConfig.checkpoint) + return { nextConfig, nextCheckpoint, nextImageHash, payload } + })) + + const sortedNextConfigsAndCheckpoints = nextConfigsAndCheckpoints + .filter((c) => c !== undefined) + .filter((c) => c!.nextCheckpoint.gt(fromConfig.checkpoint)) + .sort((a, b) => ( + // If we are looking for the longest path, sort by ascending checkpoint + // because we want to find the smalles jump, and we should start with the + // closest one. If we are not looking for the longest path, sort by + // descending checkpoint, because we want to find the largest jump. + // + // We don't have a guarantee that all "next configs" will be valid + // so worst case scenario we will need to try all of them. + // But we can try to optimize for the most common case. + a!.nextCheckpoint.gt(b!.nextCheckpoint) ? ( + longestPath ? 1 : -1 + ) : ( + longestPath ? -1 : 1 + ) + )) + + for (const entry of sortedNextConfigsAndCheckpoints) { + const { nextConfig, nextCheckpoint, nextImageHash, payload } = entry! + + if (bestCandidate) { + const bestCheckpoint = bestCandidate.checkpoint + if (longestPath) { + // Only consider candidates earlier than our current best + if (nextCheckpoint.gte(bestCheckpoint)) continue + } else { + // Only consider candidates later than our current best + if (nextCheckpoint.lte(bestCheckpoint)) continue + } + } + + // Get all signatures (for all signers) for this subdigest + const signatures = new Map( + ( + await Promise.all( + signers.map(async signer => { + const signature = await this.store.loadSignatureOfSubdigest(signer, payload.subdigest) + if (!signature) { + return [signer, undefined] + } + + const replacedSignature = ethers.utils.hexlify( + this.useEIP5719 ? await this.cachedEIP5719.runByEIP5719(signer, payload.subdigest, signature) : signature + ) + + const isDynamic = commons.signer.tryRecoverSigner(payload.subdigest, replacedSignature) !== signer + + return [signer, { isDynamic, signature: replacedSignature }] + }) + ) + ).filter((signature): signature is [string, commons.signature.SignaturePart] => Boolean(signature[1])) + ) + + // Skip if we don't have ANY signatures (it can never reach the threshold) + if (signatures.size === 0) continue + + // Encode the full signature (to see if it has enough weight) + const encoded = v2.signature.SignatureCoder.encodeSigners(fromConfig, signatures, [], 0) + if (encoded.weight.lt(fromConfig.threshold)) continue + + // Save the new best candidate + bestCandidate = { + nextImageHash, + checkpoint: ethers.BigNumber.from(nextConfig.checkpoint), + signature: encoded.encoded + } + } + + if (!bestCandidate) { + return [] + } + + // Get the next step + const nextStep = await this.loadPresignedConfiguration({ + wallet, + fromImageHash: bestCandidate.nextImageHash, + longestPath + }) + + return [{ + wallet, + nextImageHash: bestCandidate.nextImageHash, + signature: bestCandidate.signature + }, ...nextStep] + } + + saveWitnesses = async (args: { + wallet: string + digest: string + chainId: ethers.BigNumberish + signatures: string[] + }): Promise => { + const payload = { + digest: args.digest, + address: args.wallet, + chainId: args.chainId, + } + + const subdigest = commons.signature.subdigestOf(payload) + + await Promise.all([ + this.savePayload({ payload }), + ...args.signatures + .filter(signature => { + // We don't support saving witnesses for non-recoverable signatures + // we could change this eventually, but the issue is that the witness may become invalid + return commons.signer.canRecover(signature) + }) + .map(signature => { + const signer = commons.signer.recoverSigner(subdigest, signature) + return this.store.saveSignatureOfSubdigest(signer, subdigest, signature) + }) + ]) + } + + walletsOfSigner = async (args: { + signer: string + }): Promise<{ + wallet: string, + proof: { + digest: string, + chainId: ethers.BigNumber, + signature: string + } + }[]> => { + const subdigests = await this.store.loadSubdigestsOfSigner(args.signer) + const payloads = await Promise.all(subdigests.map((s) => this.payloadOfSubdigest({ subdigest: s }))) + .then((p) => p.filter((p) => p !== undefined) as commons.signature.SignedPayload[]) + + // filter unique wallets, and provide a proof for each wallet + const result: { + wallet: string, + proof: { + digest: string, + chainId: ethers.BigNumber, + signature: string + } + }[] = [] + + for (const payload of payloads) { + const wallet = payload.address + if (result.find((r) => r.wallet === wallet)) continue + + const subdigest = commons.signature.subdigestOf(payload) + const signature = await this.store.loadSignatureOfSubdigest(args.signer, subdigest) + if (!signature) continue + + result.push({ + wallet, + proof: { + digest: payload.digest, + chainId: ethers.BigNumber.from(payload.chainId), + signature: ethers.utils.hexlify(signature) + } + }) + } + + return result + } + + async saveMigration( + address: string, + signed: migrator.SignedMigration, + contexts: commons.context.VersionedContext + ): Promise { + const fromVersion = signed.fromVersion + if (fromVersion !== 1) throw new Error("Migration not supported") + if (!v2.config.isWalletConfig(signed.toConfig)) throw new Error("Invalid to config") + + // Validate migration transaction + const { newImageHash, address: decodedAddress } = migration.v1v2.decodeTransaction(signed.tx, contexts) + if (decodedAddress !== address) throw new Error("Invalid migration transaction - address") + if ( + v2.config.ConfigCoder.imageHashOf(signed.toConfig) != + newImageHash + ) throw new Error("Invalid migration transaction - config") + + // Split signature and save each part + const message = commons.transaction.packMetaTransactionsData(signed.tx.nonce, signed.tx.transactions) + const digest = ethers.utils.keccak256(message) + const payload = { chainId: signed.tx.chainId, message, address, digest } + const subdigest = commons.signature.subdigestOf(payload) + + const savePayload = this.savePayload({ payload }) + const saveToConfig = this.saveWalletConfig({ config: signed.toConfig }) + + const decoded = v1.signature.SignatureCoder.decode(signed.tx.signature) + const recovered = await v1.signature.SignatureCoder.recover(decoded, payload, this.provider) + + // Save the recovered config, the migrate transaction, and all signature parts + const signatures = v1.signature.SignatureCoder.signaturesOf(recovered.config) + + await Promise.all([ + savePayload, + saveToConfig, + this.saveWalletConfig({ config: recovered.config }), + this.store.saveMigrationsSubdigest(address, fromVersion, fromVersion + 1, subdigest, newImageHash), + ...signatures.map(sig => this.store.saveSignatureOfSubdigest(sig.address, recovered.subdigest, sig.signature)) + ]) + } + + async getMigration( + address: string, + fromImageHash: string, + fromVersion: number, + chainId: ethers.BigNumberish + ): Promise { + // Get the current config and all possible migration payloads + const [currentConfig, txs] = await Promise.all([ + this.configOfImageHash({ imageHash: fromImageHash }), + this.store.loadMigrationsSubdigest(address, fromVersion, fromVersion + 1) + ]) + + const coder = universal.coderFor(fromVersion) + if (!currentConfig) { + // We may not be able to find the config, because the migration is still not copied locally + // in that case we consider as we don't have any migration + return undefined + } + + if (!coder.config.isWalletConfig(currentConfig)) { + // throw new Error("Invalid from config - version") + // better to not fail here, some other tracker may be able to handle this migration + return undefined + } + + // We need to process every migration candidate individually + // and see which one has enough signers to be valid (for the current config) + const candidates = await Promise.all(txs.map(async (tx) => { + const { subdigest, toImageHash } = tx + const payload = await this.payloadOfSubdigest({ subdigest }) + if (!payload || !payload.message) return undefined + if (!ethers.BigNumber.from(chainId).eq(payload.chainId)) return undefined + + const signers = coder.config.signersOf(currentConfig as any).map((s) => s.address) + + // Get all signatures (for all signers) for this subdigest + const signatures = new Map( + ( + await Promise.all( + signers.map(async signer => { + const signature = await this.store.loadSignatureOfSubdigest(signer, subdigest) + if (!signature) { + return [signer, undefined] + } + + const replacedSignature = ethers.utils.hexlify( + this.useEIP5719 ? await this.cachedEIP5719.runByEIP5719(signer, subdigest, signature) : signature + ) + + const isDynamic = commons.signer.tryRecoverSigner(subdigest, replacedSignature) !== signer + + return [signer, { isDynamic, signature: replacedSignature }] + }) + ) + ).filter((signature): signature is [string, commons.signature.SignaturePart] => Boolean(signature[1])) + ) + + // Encode signature parts into a single signature + const encoded = coder.signature.encodeSigners(currentConfig as any, signatures, [], chainId) + if (!encoded || encoded.weight < currentConfig.threshold) return undefined + + // Unpack payload (it should have transactions) + const [nonce, transactions] = commons.transaction.unpackMetaTransactionsData(payload.message) + + return { + tx: { + entrypoint: address, + transactions: commons.transaction.fromTxAbiEncode(transactions), + chainId: chainId, + nonce: nonce, + signature: encoded.encoded, + intent: { + id: subdigest, + wallet: address + } + }, + toConfig: await this.configOfImageHash({ imageHash: toImageHash }), + fromVersion, + toVersion: fromVersion + 1 + } as migrator.SignedMigration + })).then(c => c.filter(c => c !== undefined)) + + // Return the first valid candidate + return candidates[0] + } +} diff --git a/packages/sessions/src/trackers/multiple.ts b/packages/sessions/src/trackers/multiple.ts new file mode 100644 index 000000000..a98713ecf --- /dev/null +++ b/packages/sessions/src/trackers/multiple.ts @@ -0,0 +1,200 @@ +import { ConfigTracker, PresignedConfig, PresignedConfigLink } from '../tracker' +import { migrator } from '@0xsequence/migration' +import { BigNumber, BigNumberish, ethers } from 'ethers' +import { commons, universal } from '@0xsequence/core' +import { LocalConfigTracker } from './local'; + +export function raceUntil(promises: Promise[], fallback: T, evalRes: (val: T) => boolean): Promise { + return new Promise((resolve) => { + let count = 0 + + promises.forEach(p => p.then((val: T) => { + if (evalRes(val)) { + resolve(val) + } else { + count++ + if (count === promises.length) { + resolve(fallback) + } + } + }).catch(() => { + // Ignore + count++ + if (count === promises.length) { + resolve(fallback) + } + })) + }) +} + +export async function allSafe(promises: Promise[], fallback: T): Promise { + return Promise.all(promises.map(promise => promise.catch(() => fallback))) +} + +export class MultipleTracker implements migrator.PresignedMigrationTracker, ConfigTracker { + constructor(private trackers: (migrator.PresignedMigrationTracker & ConfigTracker)[]) {} + + async configOfImageHash(args: { imageHash: string }): Promise { + const requests = this.trackers.map(async (t, i) => ({ res: await t.configOfImageHash(args), i })) + + // We try to find a complete configuration, we race so that we don't wait for all trackers to respond + const result1 = await raceUntil(requests, undefined, (val) => { + if (val?.res === undefined) return false + return universal.genericCoderFor(val.res.version).config.isComplete(val.res) + }) + + if (result1?.res) { + // Skip saving the config to the tracker that returned the result + this.saveWalletConfig({ config: result1.res, skipTracker: result1.i }) + return result1.res + } + + // If we haven't found a complete configuration yet, it either means that the configuration is not complete + // (and thus we need to combine all results) or that the configuration is not found at all + // but we try to combine all results anyway + const tmptracker = new LocalConfigTracker(undefined as any) // TODO: Fix this, provider not needed anyway + + const results = await allSafe(requests, undefined) + + for (const r of results) { + if (r?.res) await tmptracker.saveWalletConfig({ config: r.res }) + } + + const result2 = await tmptracker.configOfImageHash(args) + if (result2) this.saveWalletConfig({ config: result2 }) + return result2 + } + + async saveWalletConfig(args: { config: commons.config.Config, skipTracker?: number }): Promise { + await Promise.all(this.trackers.map((t, i) => { + if (i === args.skipTracker) return + return t.saveWalletConfig(args) + })) + } + + async imageHashOfCounterfactualWallet(args: { + wallet: string + }): Promise<{ imageHash: string; context: commons.context.WalletContext } | undefined> { + const imageHash = await raceUntil( + this.trackers.map(t => t.imageHashOfCounterfactualWallet(args)), + undefined, + result => Boolean(result) + ) + + if (imageHash) { + this.configOfImageHash({ imageHash: imageHash.imageHash }).then(config => { + if (config) { + this.saveCounterfactualWallet({ config, context: [imageHash.context] }) + } + }) + } + + return imageHash + } + + async saveCounterfactualWallet(args: { + config: commons.config.Config + context: commons.context.WalletContext[] + skipTracker?: number + }): Promise { + await Promise.all( + this.trackers.map((t, i) => { + if (i === args.skipTracker) return + return t.saveCounterfactualWallet(args) + }) + ) + } + + async walletsOfSigner(args: { signer: string }): Promise<{ wallet: string; proof: { digest: string; chainId: BigNumber; signature: string } }[]> { + // We can't race here, because there is no "correct" response + // we just return the union of all results, skipping duplicates + const results = await allSafe(this.trackers.map(t => t.walletsOfSigner(args)), []).then((r) => r.flat()) + + const wallets: { [wallet: string]: { digest: string; chainId: BigNumber; signature: string } } = {} + for (const r of results) { + wallets[r.wallet] = r.proof + } + + // TODO: This will send redundant information back to the trackers + // consider optimizing this for better performance during login + + const result = Object.keys(wallets).map(w => ({ wallet: w, proof: wallets[w] })) + + const witnesses = new Map() + result.forEach(({ wallet, proof: { digest, chainId, signature } }) => { + const key = `${wallet}-${digest}-${chainId}` + let signatures = witnesses.get(key) + if (!signatures) { + signatures = { wallet, digest, chainId, signatures: [] } + witnesses.set(key, signatures) + } + signatures.signatures.push(signature) + }) + witnesses.forEach(witnesses => this.saveWitnesses(witnesses)) + + return result + } + + async saveWitnesses(args: { wallet: string; digest: string; chainId: BigNumberish; signatures: string[] }): Promise { + await Promise.all(this.trackers.map(t => t.saveWitnesses(args))) + } + + async loadPresignedConfiguration(args: { wallet: string; fromImageHash: string; longestPath?: boolean | undefined }): Promise { + // We can't race here, because any of the trackers could have a new "link" in the chain + const results = await allSafe(this.trackers.map((t) => t.loadPresignedConfiguration(args)), []) + + // The "best" result is the one with the highest checkpoint + const checkpoints = await allSafe(results.map(async (r) => { + const last = r[r.length - 1] + + // TODO: This will fire a lot of requests, optimize it + const config = await this.configOfImageHash({ imageHash: last.nextImageHash }) + if (!config) return undefined + + return { checkpoint: universal.genericCoderFor(config.version).config.checkpointOf(config), result: r } + }), undefined) + + const best = checkpoints.reduce((acc, val) => { + if (!val) return acc + if (!acc) return val + if (val.checkpoint.gt(acc.checkpoint)) return val + return acc + }) + + if (!best) return [] + + const configs = new Map>() + const config = (imageHash: string): Promise => { + if (!configs.has(imageHash)) { + configs.set(imageHash, this.configOfImageHash({ imageHash })) + } + return configs.get(imageHash)! + } + best.result.forEach(async res => { + const nextConfig = await config(res.nextImageHash) + if (nextConfig) { + this.savePresignedConfiguration({ + wallet: args.wallet, + nextConfig, + signature: res.signature + }) + } + }) + + return best.result + } + + async savePresignedConfiguration(args: PresignedConfig): Promise { + await Promise.all(this.trackers.map(t => t.savePresignedConfiguration(args))) + } + + async getMigration(address: string, fromImageHash: string, fromVersion: number, chainId: BigNumberish): Promise { + // TODO: Backfeed migration results to other trackers + const results = await Promise.all(this.trackers.map(t => t.getMigration(address, fromImageHash, fromVersion, chainId))) + return results.find(r => !!r) + } + + async saveMigration(address: string, signed: migrator.SignedMigration, contexts: commons.context.VersionedContext): Promise { + await Promise.all(this.trackers.map(t => t.saveMigration(address, signed, contexts))) + } +} diff --git a/packages/sessions/src/trackers/promise-cache.ts b/packages/sessions/src/trackers/promise-cache.ts new file mode 100644 index 000000000..0504adda8 --- /dev/null +++ b/packages/sessions/src/trackers/promise-cache.ts @@ -0,0 +1,58 @@ +import { ethers } from 'ethers' + +export class PromiseCache { + private readonly cache: Map + + constructor() { + this.cache = new Map() + } + + do, T>( + key: string, + validMilliseconds: number | undefined, + task: (...args: S) => Promise, + ...args: S + ): Promise { + key = `${key}:${ethers.utils.keccak256(ethers.utils.toUtf8Bytes(JSON.stringify(args, deterministically)))}` + + let entry = this.cache.get(key) + + if (entry) { + if (entry.expiration) { + if (new Date() >= entry.expiration) { + entry = undefined + this.cache.delete(key) + } + } + } + + if (!entry) { + const entry_: Entry = { promise: task(...args) } + + if (validMilliseconds !== undefined) { + entry_.promise = entry_.promise.then(result => { + entry_.expiration = new Date(Date.now() + validMilliseconds) + return result + }) + } + + entry = entry_ + this.cache.set(key, entry) + } + + return entry.promise as Promise + } +} + +type Entry = { + promise: Promise + expiration?: Date +} + +function deterministically(_key: string, value: any): any { + if (typeof value === 'object' && value !== null && !Array.isArray(value)) { + return Object.fromEntries(Object.entries(value).sort()) + } + + return value +} diff --git a/packages/sessions/src/trackers/remote/index.ts b/packages/sessions/src/trackers/remote/index.ts new file mode 100644 index 000000000..7c5fea0f2 --- /dev/null +++ b/packages/sessions/src/trackers/remote/index.ts @@ -0,0 +1,352 @@ +import { commons, universal, v1, v2 } from '@0xsequence/core' +import { migrator } from '@0xsequence/migration' +import { ethers } from 'ethers' +import { ConfigTracker, PresignedConfig, PresignedConfigLink } from '../../tracker' +import { Sessions, SignatureType, Transaction } from './sessions.gen' + +export class RemoteConfigTracker implements ConfigTracker, migrator.PresignedMigrationTracker { + private readonly sessions: Sessions + + constructor(hostname: string, public readonly onlyRecoverable: boolean = true) { + this.sessions = new Sessions(hostname, fetch) + } + + async loadPresignedConfiguration(args: { + wallet: string + fromImageHash: string + longestPath?: boolean + }): Promise { + try { + const { updates } = await this.sessions.configUpdates({ + wallet: args.wallet, + fromImageHash: args.fromImageHash, + allUpdates: args.longestPath + }) + + return updates.map(({ toImageHash, signature }) => ({ wallet: args.wallet, nextImageHash: toImageHash, signature })) + } catch (error) { + if (is404NotFound(error)) { + return [] + } else { + throw error + } + } + } + + async savePresignedConfiguration(args: PresignedConfig): Promise { + const config = args.nextConfig + const imageHash = universal.genericCoderFor(config.version).config.imageHashOf(config) + const message = v2.signature.setImageHashStruct(imageHash) + const digest = ethers.utils.keccak256(message) + + await this.sessions.saveSignature({ + wallet: args.wallet, + digest, + chainID: '0', + signature: args.signature, + toConfig: encodeConfig(config) + }) + } + + async saveWitnesses(args: { + wallet: string + digest: string + chainId: ethers.BigNumberish + signatures: string[] + }): Promise { + let filteredSignatures = args.signatures + if (this.onlyRecoverable) { + filteredSignatures = filteredSignatures.filter((signature) => { + return commons.signer.canRecover(signature) + }) + } + + await this.sessions.saveSignerSignatures({ + wallet: args.wallet, + digest: args.digest, + chainID: numberString(args.chainId), + signatures: filteredSignatures + }) + } + + async configOfImageHash(args: { imageHash: string }): Promise { + try { + const { version, config } = await this.sessions.config(args) + return decodeConfig(version, config) + } catch (error) { + if (is404NotFound(error)) { + return + } else { + throw error + } + } + } + + async saveWalletConfig(args: { config: commons.config.Config }): Promise { + const config = encodeConfig(args.config) + await this.sessions.saveConfig({ version: args.config.version, config }) + } + + async imageHashOfCounterfactualWallet(args: { + wallet: string + }): Promise<{ imageHash: string; context: commons.context.WalletContext } | undefined> { + try { + const { deployHash, context } = await this.sessions.deployHash(args) + return { imageHash: deployHash, context } + } catch (error) { + if (is404NotFound(error)) { + return + } else { + throw error + } + } + } + + async saveCounterfactualWallet(args: { + config: commons.config.Config + context: commons.context.WalletContext[] + }): Promise { + const deployConfig = encodeConfig(args.config) + await this.sessions.saveWallet({ version: args.config.version, deployConfig }) + } + + async walletsOfSigner(args: { + signer: string + }): Promise<{ wallet: string; proof: { digest: string; chainId: ethers.BigNumber; signature: string } }[]> { + const { wallets } = await this.sessions.wallets(args) + return Object.entries(wallets).map(([wallet, { digest, chainID, type, signature }]) => { + switch (type) { + case SignatureType.EIP712: + signature += ethers.utils.hexlify(commons.signer.SigType.EIP712).slice(2) + break + case SignatureType.EthSign: + signature += ethers.utils.hexlify(commons.signer.SigType.ETH_SIGN).slice(2) + break + case SignatureType.EIP1271: + signature += ethers.utils.hexlify(commons.signer.SigType.WALLET_BYTES32).slice(2) + break + } + + return { + wallet, + proof: { + digest, + signature, + chainId: ethers.BigNumber.from(chainID) + } + } + }) + } + + async getMigration( + wallet: string, + fromImageHash: string, + fromVersion: number, + chainId: ethers.BigNumberish + ): Promise { + const chainIdString = numberString(chainId) + const { migrations } = await this.sessions.migrations({ wallet, fromVersion, fromImageHash, chainID: chainIdString }) + + const chooseMigration = async (chainId: string): Promise => { + const migrations_ = migrations[chainId] + if (migrations_) { + const toVersions = Object.keys(migrations_) + .map(Number) + .sort((a: number, b: number) => b - a) + + for (const toVersion of toVersions) { + for (const [toHash, transactions] of Object.entries(migrations_[toVersion])) { + try { + const toConfig = await this.configOfImageHash({ imageHash: toHash }) + if (toConfig) { + return { + fromVersion, + toVersion, + toConfig, + tx: { + entrypoint: transactions.executor, + transactions: transactions.transactions, + nonce: transactions.nonce, + signature: transactions.signature, + chainId, + intent: { + id: commons.transaction.subdigestOfTransactions( + wallet, + chainId, + transactions.nonce, + transactions.transactions + ), + wallet + } + } + } + } + } catch (error) { + console.error(error) + } + } + } + } + return + } + + const migration = await chooseMigration(chainIdString) + if (migration) { + return migration + } + + for (const chainId in migrations) { + if (chainId !== chainIdString) { + const migration = await chooseMigration(chainId) + if (migration) { + return migration + } + } + } + + return + } + + async saveMigration(wallet: string, signed: migrator.SignedMigration, _contexts: commons.context.VersionedContext): Promise { + await this.sessions.saveMigration({ + wallet, + fromVersion: signed.fromVersion, + toVersion: signed.toVersion, + toConfig: encodeConfig(signed.toConfig), + executor: signed.tx.entrypoint, + transactions: signed.tx.transactions.map(encodeTransaction), + nonce: numberString(signed.tx.nonce), + signature: signed.tx.signature, + chainID: numberString(signed.tx.chainId) + }) + } +} + +type SessionsConfig = { + 1: { threshold: number; signers: Array<{ weight: number; address: string }> } + 2: { threshold: number; checkpoint: number; tree: V2SessionsConfigTree } +} + +type V2SessionsConfigTree = + | { left: V2SessionsConfigTree; right: V2SessionsConfigTree } + | { weight: number; address: string } + | { node: string } + | { weight: number; threshold: number; tree: V2SessionsConfigTree } + | { subdigest: string } + +function encodeConfig(config: commons.config.Config): SessionsConfig[1 | 2] { + switch (config.version) { + case 1: + if (v1.config.ConfigCoder.isWalletConfig(config)) { + return { + threshold: numberNumber(config.threshold), + signers: config.signers.map(({ weight, address }) => ({ weight: numberNumber(weight), address })) + } + } else { + throw new Error(`not a v${config.version} config: ${config}`) + } + + case 2: + if (v2.config.ConfigCoder.isWalletConfig(config)) { + return { + threshold: numberNumber(config.threshold), + checkpoint: numberNumber(config.checkpoint), + tree: encodeV2ConfigTree(config.tree) + } + } else { + throw new Error(`not a v${config.version} config: ${config}`) + } + + default: + throw new Error(`unknown version ${config.version}`) + } +} + +function encodeV2ConfigTree(tree: v2.config.Topology): V2SessionsConfigTree { + if (v2.config.isNode(tree)) { + return { + left: encodeV2ConfigTree(tree.left), + right: encodeV2ConfigTree(tree.right) + } + } else if (v2.config.isSignerLeaf(tree)) { + return { + weight: numberNumber(tree.weight), + address: tree.address + } + } else if (v2.config.isNestedLeaf(tree)) { + return { + weight: numberNumber(tree.weight), + threshold: numberNumber(tree.threshold), + tree: encodeV2ConfigTree(tree.tree) + } + } else if (v2.config.isNodeLeaf(tree)) { + return { node: tree.nodeHash } + } else { + return { ...tree } + } +} + +function decodeConfig(version: number, config: any): commons.config.Config { + switch (version) { + case 1: + return { ...config, version } + + case 2: + return { ...config, version, tree: decodeV2ConfigTree(config.tree) } + + default: + throw new Error(`unknown version ${version}`) + } +} + +function decodeV2ConfigTree(tree: any): v2.config.Topology { + switch (typeof tree) { + case 'object': + const tree_ = { ...tree } + + if (tree_.left !== undefined) { + tree_.left = decodeV2ConfigTree(tree_.left) + } + + if (tree_.right !== undefined) { + tree_.right = decodeV2ConfigTree(tree_.right) + } + + if (tree_.tree !== undefined) { + tree_.tree = decodeV2ConfigTree(tree_.tree) + } + + if (tree_.node !== undefined) { + tree_.nodeHash = tree_.node + delete tree_.node + } + + return tree_ + + default: + throw new Error(`v2 config tree ${tree} is not an object`) + } +} + +function encodeTransaction(transaction: commons.transaction.Transaction): Transaction { + return { + to: transaction.to, + value: transaction.value !== undefined ? numberString(transaction.value) : undefined, + data: transaction.data !== undefined ? ethers.utils.hexlify(transaction.data) : undefined, + gasLimit: transaction.gasLimit !== undefined ? numberString(transaction.gasLimit) : undefined, + delegateCall: transaction.delegateCall, + revertOnError: transaction.revertOnError + } +} + +function numberNumber(n: ethers.BigNumberish): number { + return ethers.BigNumber.from(n).toNumber() +} + +function numberString(n: ethers.BigNumberish): string { + return ethers.BigNumber.from(n).toString() +} + +function is404NotFound(error: any): boolean { + return typeof error === 'object' && error.status === 404 +} diff --git a/packages/sessions/src/trackers/remote/sessions.gen.ts b/packages/sessions/src/trackers/remote/sessions.gen.ts new file mode 100644 index 000000000..ed75d185f --- /dev/null +++ b/packages/sessions/src/trackers/remote/sessions.gen.ts @@ -0,0 +1,355 @@ +/* eslint-disable */ +// sessions v0.0.1 b96502864e03f0bea75f41cafaf2178946a9e3b7 +// -- +// Code generated by webrpc-gen@v0.10.x-dev with typescript generator. DO NOT EDIT. +// +// webrpc-gen -schema=sessions.ridl -target=typescript -client -out=./sessions.gen.ts + +// WebRPC description and code-gen version +export const WebRPCVersion = "v1" + +// Schema version of your RIDL schema +export const WebRPCSchemaVersion = "v0.0.1" + +// Schema hash generated from your RIDL schema +export const WebRPCSchemaHash = "b96502864e03f0bea75f41cafaf2178946a9e3b7" + +// +// Types +// + +export enum SignatureType { + EIP712 = 'EIP712', + EthSign = 'EthSign', + EIP1271 = 'EIP1271' +} + +export interface Context { + version: number + factory: string + mainModule: string + mainModuleUpgradable: string + guestModule: string + walletCreationCode: string +} + +export interface Signature { + digest: string + toImageHash?: string + chainID: string + type: SignatureType + signature: string +} + +export interface ConfigUpdate { + toImageHash: string + signature: string +} + +export interface Transaction { + to: string + value?: string + data?: string + gasLimit?: string + delegateCall?: boolean + revertOnError?: boolean +} + +export interface TransactionBundle { + executor: string + transactions: Array + nonce: string + signature: string +} + +export interface Sessions { + ping(headers?: object): Promise + config(args: ConfigArgs, headers?: object): Promise + wallets(args: WalletsArgs, headers?: object): Promise + deployHash(args: DeployHashArgs, headers?: object): Promise + configUpdates(args: ConfigUpdatesArgs, headers?: object): Promise + migrations(args: MigrationsArgs, headers?: object): Promise + saveConfig(args: SaveConfigArgs, headers?: object): Promise + saveWallet(args: SaveWalletArgs, headers?: object): Promise + saveSignature(args: SaveSignatureArgs, headers?: object): Promise + saveSignerSignatures(args: SaveSignerSignaturesArgs, headers?: object): Promise + saveMigration(args: SaveMigrationArgs, headers?: object): Promise +} + +export interface PingArgs { +} + +export interface PingReturn { +} +export interface ConfigArgs { + imageHash: string +} + +export interface ConfigReturn { + version: number + config: any +} +export interface WalletsArgs { + signer: string +} + +export interface WalletsReturn { + wallets: {[key: string]: Signature} +} +export interface DeployHashArgs { + wallet: string +} + +export interface DeployHashReturn { + deployHash: string + context: Context +} +export interface ConfigUpdatesArgs { + wallet: string + fromImageHash: string + allUpdates?: boolean +} + +export interface ConfigUpdatesReturn { + updates: Array +} +export interface MigrationsArgs { + wallet: string + fromVersion: number + fromImageHash: string + chainID?: string +} + +export interface MigrationsReturn { + migrations: {[key: string]: {[key: number]: {[key: string]: TransactionBundle}}} +} +export interface SaveConfigArgs { + version: number + config: any +} + +export interface SaveConfigReturn { +} +export interface SaveWalletArgs { + version: number + deployConfig: any +} + +export interface SaveWalletReturn { +} +export interface SaveSignatureArgs { + wallet: string + digest: string + chainID: string + signature: string + toConfig?: any +} + +export interface SaveSignatureReturn { +} +export interface SaveSignerSignaturesArgs { + wallet: string + digest: string + chainID: string + signatures: Array + toConfig?: any +} + +export interface SaveSignerSignaturesReturn { +} +export interface SaveMigrationArgs { + wallet: string + fromVersion: number + toVersion: number + toConfig: any + executor: string + transactions: Array + nonce: string + signature: string + chainID?: string +} + +export interface SaveMigrationReturn { +} + + + +// +// Client +// +export class Sessions implements Sessions { + protected hostname: string + protected fetch: Fetch + protected path = '/rpc/Sessions/' + + constructor(hostname: string, fetch: Fetch) { + this.hostname = hostname + this.fetch = (input: RequestInfo, init?: RequestInit) => fetch(input, init) + } + + private url(name: string): string { + return this.hostname + this.path + name + } + + ping = (headers?: object): Promise => { + return this.fetch( + this.url('Ping'), + createHTTPRequest({}, headers) + ).then((res) => { + return buildResponse(res).then(_data => { + return { + } + }) + }) + } + + config = (args: ConfigArgs, headers?: object): Promise => { + return this.fetch( + this.url('Config'), + createHTTPRequest(args, headers)).then((res) => { + return buildResponse(res).then(_data => { + return { + version: (_data.version), + config: (_data.config) + } + }) + }) + } + + wallets = (args: WalletsArgs, headers?: object): Promise => { + return this.fetch( + this.url('Wallets'), + createHTTPRequest(args, headers)).then((res) => { + return buildResponse(res).then(_data => { + return { + wallets: <{[key: string]: Signature}>(_data.wallets) + } + }) + }) + } + + deployHash = (args: DeployHashArgs, headers?: object): Promise => { + return this.fetch( + this.url('DeployHash'), + createHTTPRequest(args, headers)).then((res) => { + return buildResponse(res).then(_data => { + return { + deployHash: (_data.deployHash), + context: (_data.context) + } + }) + }) + } + + configUpdates = (args: ConfigUpdatesArgs, headers?: object): Promise => { + return this.fetch( + this.url('ConfigUpdates'), + createHTTPRequest(args, headers)).then((res) => { + return buildResponse(res).then(_data => { + return { + updates: >(_data.updates) + } + }) + }) + } + + migrations = (args: MigrationsArgs, headers?: object): Promise => { + return this.fetch( + this.url('Migrations'), + createHTTPRequest(args, headers)).then((res) => { + return buildResponse(res).then(_data => { + return { + migrations: <{[key: string]: {[key: number]: {[key: string]: TransactionBundle}}}>(_data.migrations) + } + }) + }) + } + + saveConfig = (args: SaveConfigArgs, headers?: object): Promise => { + return this.fetch( + this.url('SaveConfig'), + createHTTPRequest(args, headers)).then((res) => { + return buildResponse(res).then(_data => { + return { + } + }) + }) + } + + saveWallet = (args: SaveWalletArgs, headers?: object): Promise => { + return this.fetch( + this.url('SaveWallet'), + createHTTPRequest(args, headers)).then((res) => { + return buildResponse(res).then(_data => { + return { + } + }) + }) + } + + saveSignature = (args: SaveSignatureArgs, headers?: object): Promise => { + return this.fetch( + this.url('SaveSignature'), + createHTTPRequest(args, headers)).then((res) => { + return buildResponse(res).then(_data => { + return { + } + }) + }) + } + + saveSignerSignatures = (args: SaveSignerSignaturesArgs, headers?: object): Promise => { + return this.fetch( + this.url('SaveSignerSignatures'), + createHTTPRequest(args, headers)).then((res) => { + return buildResponse(res).then(_data => { + return { + } + }) + }) + } + + saveMigration = (args: SaveMigrationArgs, headers?: object): Promise => { + return this.fetch( + this.url('SaveMigration'), + createHTTPRequest(args, headers)).then((res) => { + return buildResponse(res).then(_data => { + return { + } + }) + }) + } + +} + + +export interface WebRPCError extends Error { + code: string + msg: string + status: number +} + +const createHTTPRequest = (body: object = {}, headers: object = {}): object => { + return { + method: 'POST', + headers: { ...headers, 'Content-Type': 'application/json' }, + body: JSON.stringify(body || {}) + } +} + +const buildResponse = (res: Response): Promise => { + return res.text().then(text => { + let data + try { + data = JSON.parse(text) + } catch(err) { + throw { code: 'unknown', msg: `expecting JSON, got: ${text}`, status: res.status } as WebRPCError + } + if (!res.ok) { + throw data // webrpc error response + } + return data + }) +} + +export type Fetch = (input: RequestInfo, init?: RequestInit) => Promise diff --git a/packages/sessions/src/trackers/stores/index.ts b/packages/sessions/src/trackers/stores/index.ts new file mode 100644 index 000000000..9901d8069 --- /dev/null +++ b/packages/sessions/src/trackers/stores/index.ts @@ -0,0 +1,68 @@ +import { commons, v1, v2 } from "@0xsequence/core" +import { ethers } from "ethers" + +export type PlainNode = { + left: string, + right: string +} + +export type PlainNested = { + weight: string, + threshold: string, + tree: string +} + +export type PlainV2Config = { + version: 2, + threshold: string, + checkpoint: string, + tree: string +} + +export function isPlainNode(node: any): node is PlainNode { + return node.left !== undefined && node.right !== undefined +} + +export function isPlainNested(node: any): node is PlainNested { + return node.weight !== undefined && node.threshold !== undefined && node.tree !== undefined +} + +export function isPlainV2Config(config: any): config is PlainV2Config { + return ( + config.version === 2 && + config.threshold !== undefined && + config.checkpoint !== undefined && + config.tree !== undefined && + typeof config.tree === 'string' + ) +} + +export interface TrackerStore { + // top level configurations store + loadConfig: (imageHash: string) => Promise + saveConfig: (imageHash: string, config: v1.config.WalletConfig | PlainV2Config | v2.config.WalletConfig) => Promise + + // v2 configurations store + loadV2Node: (nodeHash: string) => Promise + saveV2Node: (nodeHash: string, node: PlainNode | PlainNested | v2.config.Topology) => Promise + + // counterfactual wallets + loadCounterfactualWallet: (wallet: string) => Promise<{ imageHash: string, context: commons.context.WalletContext } | undefined> + saveCounterfactualWallet: (wallet: string, imageHash: string, context: commons.context.WalletContext) => Promise + + // payloads + loadPayloadOfSubdigest: (subdigest: string) => Promise + savePayloadOfSubdigest: (subdigest: string, payload: commons.signature.SignedPayload) => Promise + + // signatures + loadSubdigestsOfSigner: (signer: string) => Promise + loadSignatureOfSubdigest: (signer: string, subdigest: string) => Promise + saveSignatureOfSubdigest: (signer: string, subdigest: string, payload: ethers.BytesLike) => Promise + + // migrations + loadMigrationsSubdigest: (wallet: string, fromVersion: number, toVersion: number) => Promise<{ subdigest: string, toImageHash: string }[]> + saveMigrationsSubdigest: (wallet: string, fromVersion: number, toVersion: number, subdigest: string, toImageHash: string) => Promise +} + +export * from './memoryStore' +export * from './indexedDBStore' diff --git a/packages/sessions/src/trackers/stores/indexedDBStore.ts b/packages/sessions/src/trackers/stores/indexedDBStore.ts new file mode 100644 index 000000000..012e31aa6 --- /dev/null +++ b/packages/sessions/src/trackers/stores/indexedDBStore.ts @@ -0,0 +1,179 @@ +import { commons, v1, v2 } from "@0xsequence/core" +import { ethers } from "ethers" +import { PlainNested, PlainNode, PlainV2Config, TrackerStore } from "." + +import { DBSchema, IDBPDatabase, openDB } from 'idb' + +export interface LocalTrackerDBSchema extends DBSchema { + 'configs': { + key: string, + value: v1.config.WalletConfig | v2.config.WalletConfig | PlainV2Config + }, + 'v2Nodes': { + key: string, + value: v2.config.Topology | PlainNode | PlainNested + }, + 'counterfactualWallets': { + key: string, + value: { + imageHash: string, + context: commons.context.WalletContext + } + }, + 'payloads': { + key: string, + value: commons.signature.SignedPayload + }, + 'signatures': { + key: string, // `${signer}-${subdigest}` + value: { + signature: ethers.BytesLike + signer: string + }, + indexes: { + 'signer': string + } + }, + 'migrations': { + key: string, + value: { + wallet: string, + fromVersion: number, + toVersion: number, + subdigest: string, + toImageHash: string + }, + indexes: { + 'jump': string // '${wallet}-${fromVersion}-${toVersion} + } + } +} + +export function recreateBigNumbers(object: T): T | undefined { + if (object === undefined) return undefined + + const result = {} as any + + for (const key of Object.keys(object)) { + const val = (object as any)[key as string] + + if ( + val._isBigNumber === true && + val._hex !== undefined && + typeof val._hex === 'string' && + val._hex.length !== '' + ) { + // Entry is a big number + result[key] = ethers.BigNumber.from(val) + } else if (Array.isArray(val)) { + // Entry is an array, recurse + result[key] = val.map((v) => recreateBigNumbers(v)) + } else if (typeof val === 'object' && val !== null) { + // Entry is another object, recurse + result[key] = recreateBigNumbers(val) + } else { + // Entry is a primitive, just copy + result[key] = val + } + } + + return result +} + +export class IndexedDBStore implements TrackerStore { + private _lazyDb: IDBPDatabase | undefined + + constructor(public dbName: string) {} + + async getDb() { + if (this._lazyDb) return this._lazyDb + + const dbName = this.dbName + this._lazyDb = await openDB(dbName, 1, { + upgrade(db, oldVersion, newVersion, transaction) { + console.log(`upgrading ${dbName} from ${oldVersion} to ${newVersion} - ${transaction}`) + if (oldVersion === 0) { + db.createObjectStore('configs') + db.createObjectStore('v2Nodes') + db.createObjectStore('counterfactualWallets') + db.createObjectStore('payloads') + + const signatures = db.createObjectStore('signatures') + signatures.createIndex('signer', 'signer', { unique: false }) + + const migrations = db.createObjectStore('migrations') + migrations.createIndex('jump', ['wallet', 'fromVersion', 'toVersion']) + } + }, + }) + return this._lazyDb + } + + loadConfig = async (imageHash: string): Promise => { + const db = await this.getDb() + return db.get('configs', imageHash).then((c) => recreateBigNumbers(c)) + } + + saveConfig = async (imageHash: string, config: v1.config.WalletConfig | v2.config.WalletConfig | PlainV2Config): Promise => { + const db = await this.getDb() + await db.put('configs', config, imageHash) + } + + loadV2Node = async (nodeHash: string): Promise => { + const db = await this.getDb() + return db.get('v2Nodes', nodeHash).then((c) => recreateBigNumbers(c)) + } + + saveV2Node = async (nodeHash: string, node: v2.config.Topology | PlainNode | PlainNested): Promise => { + const db = await this.getDb() + await db.put('v2Nodes', node, nodeHash) + } + + loadCounterfactualWallet = async (wallet: string): Promise<{ imageHash: string; context: commons.context.WalletContext } | undefined> => { + const db = await this.getDb() + return db.get('counterfactualWallets', wallet) + } + + saveCounterfactualWallet = async (wallet: string, imageHash: string, context: commons.context.WalletContext): Promise => { + const db = await this.getDb() + await db.put('counterfactualWallets', { imageHash, context }, wallet) + } + + loadPayloadOfSubdigest = async (subdigest: string): Promise => { + const db = await this.getDb() + return db.get('payloads', subdigest).then((c) => recreateBigNumbers(c)) + } + + savePayloadOfSubdigest = async (subdigest: string, payload: commons.signature.SignedPayload): Promise => { + const db = await this.getDb() + await db.put('payloads', payload, subdigest) + } + + loadSubdigestsOfSigner = async (signer: string): Promise => { + const db = await this.getDb() + const index = await db.getAllKeysFromIndex('signatures', 'signer', IDBKeyRange.only(signer)) + return index.map(key => key.split('-')[0]) + } + + loadSignatureOfSubdigest = async (signer: string, subdigest: string): Promise => { + const db = await this.getDb() + const signature = await db.get('signatures', [subdigest, signer].join('-')) + return signature?.signature + } + + saveSignatureOfSubdigest = async (signer: string, subdigest: string, payload: ethers.BytesLike): Promise => { + const db = await this.getDb() + await db.put('signatures', { signature: payload, signer }, [subdigest, signer].join('-')) + } + + loadMigrationsSubdigest = async (wallet: string, fromVersion: number, toVersion: number): Promise<{subdigest: string, toImageHash: string}[]> => { + const db = await this.getDb() + const index = await db.getAllFromIndex('migrations', 'jump', IDBKeyRange.only([wallet, fromVersion, toVersion])) + return index.map(key => ({ subdigest: key.subdigest, toImageHash: key.toImageHash })) + } + + saveMigrationsSubdigest = async (wallet: string, fromVersion: number, toVersion: number, subdigest: string, toImageHash: string): Promise => { + const db = await this.getDb() + await db.put('migrations', { wallet, fromVersion, toVersion, subdigest, toImageHash }, subdigest) + } +} diff --git a/packages/sessions/src/trackers/stores/memoryStore.ts b/packages/sessions/src/trackers/stores/memoryStore.ts new file mode 100644 index 000000000..5522189ec --- /dev/null +++ b/packages/sessions/src/trackers/stores/memoryStore.ts @@ -0,0 +1,74 @@ +import { commons, v1, v2 } from "@0xsequence/core" +import { ethers } from "ethers" +import { PlainNested, PlainNode, PlainV2Config, TrackerStore } from "." + +export class MemoryTrackerStore implements TrackerStore { + private configs: { [imageHash: string]: v1.config.WalletConfig | v2.config.WalletConfig | PlainV2Config } = {} + private v2Nodes: { [nodeHash: string]: PlainNode | PlainNested | v2.config.Topology } = {} + private counterfactualWallets: { [wallet: string]: { imageHash: string, context: commons.context.WalletContext } } = {} + private payloads: { [subdigest: string]: commons.signature.SignedPayload } = {} + private signatures: { [signer: string]: { [subdigest: string]: ethers.BytesLike } } = {} + private migrations: { [wallet: string]: { [fromVersion: number]: { [toVersion: number]: { subdigest: string, toImageHash: string }[] } } } = {} + + loadConfig = (imageHash: string): Promise => { + return Promise.resolve(this.configs[imageHash]) + } + + saveConfig = (imageHash: string, config: v1.config.WalletConfig | v2.config.WalletConfig | PlainV2Config): Promise => { + this.configs[imageHash] = config + return Promise.resolve() + } + + loadV2Node = (nodeHash: string): Promise => { + return Promise.resolve(this.v2Nodes[nodeHash]) + } + + saveV2Node = (nodeHash: string, node: v2.config.Topology | PlainNode | PlainNested): Promise => { + this.v2Nodes[nodeHash] = node + return Promise.resolve() + } + + loadCounterfactualWallet = (wallet: string): Promise<{ imageHash: string; context: commons.context.WalletContext } | undefined> => { + return Promise.resolve(this.counterfactualWallets[wallet]) + } + + saveCounterfactualWallet = (wallet: string, imageHash: string, context: commons.context.WalletContext): Promise => { + this.counterfactualWallets[wallet] = { imageHash, context } + return Promise.resolve() + } + + loadPayloadOfSubdigest = (subdigest: string): Promise => { + return Promise.resolve(this.payloads[subdigest]) + } + + savePayloadOfSubdigest = (subdigest: string, payload: commons.signature.SignedPayload): Promise => { + this.payloads[subdigest] = payload + return Promise.resolve() + } + + loadSubdigestsOfSigner = (signer: string): Promise => { + return Promise.resolve(Object.keys(this.signatures[signer] || {})) + } + + loadSignatureOfSubdigest = (signer: string, subdigest: string): Promise => { + return Promise.resolve(this.signatures[signer]?.[subdigest]) + } + + saveSignatureOfSubdigest = (signer: string, subdigest: string, payload: ethers.BytesLike): Promise => { + if (!this.signatures[signer]) this.signatures[signer] = {} + this.signatures[signer][subdigest] = payload + return Promise.resolve() + } + + loadMigrationsSubdigest = (wallet: string, fromVersion: number, toVersion: number): Promise<{ subdigest: string, toImageHash: string }[]> => { + return Promise.resolve(this.migrations[wallet]?.[fromVersion]?.[toVersion] || []) + } + + saveMigrationsSubdigest = (wallet: string, fromVersion: number, toVersion: number, subdigest: string, toImageHash: string): Promise => { + if (!this.migrations[wallet]) this.migrations[wallet] = {} + if (!this.migrations[wallet][fromVersion]) this.migrations[wallet][fromVersion] = {} + if (!this.migrations[wallet][fromVersion][toVersion]) this.migrations[wallet][fromVersion][toVersion] = [] + this.migrations[wallet][fromVersion][toVersion].push({ subdigest, toImageHash }) + return Promise.resolve() + } +} diff --git a/packages/sessions/tests/local.spec.ts b/packages/sessions/tests/local.spec.ts new file mode 100644 index 000000000..4b75e1a91 --- /dev/null +++ b/packages/sessions/tests/local.spec.ts @@ -0,0 +1,1068 @@ + +import hardhat from 'hardhat' +import * as chai from 'chai' +import * as utils from '@0xsequence/tests' + +import { trackers, tracker } from '../src/index' +import { commons, universal, v2 } from '@0xsequence/core' +import { ethers } from 'ethers' +import { Wallet } from '@0xsequence/wallet' +import { Orchestrator } from '@0xsequence/signhub' + +// This is a hack to get around the fact that indexedDB is not available in nodejs +import "fake-indexeddb/auto" + +const { expect } = chai + +const ConfigCases = [{ + name: 'v1, random', + config: () => utils.configs.random.genRandomV1Config() +}, { + name: 'v1, no signers', + config: () => utils.configs.random.genRandomV1Config(undefined, 0) +}, { + name: 'v1, 1 signer', + config: () => utils.configs.random.genRandomV1Config(undefined, 1) +}, { + name: 'v1, 2 signers', + config: () => utils.configs.random.genRandomV1Config(undefined, 2) +}, { + name: 'v1, 3 signers', + config: () => utils.configs.random.genRandomV1Config(undefined, 3) +}, { + name: 'v1, 4 signers', + config: () => utils.configs.random.genRandomV1Config(undefined, 4) +}, { + name: 'v1, 100 signers', + config: () => utils.configs.random.genRandomV1Config(undefined, 100) +}, { + name: 'v1, 101 signers', + config: () => utils.configs.random.genRandomV1Config(undefined, 101) +}, { + name: 'v2 (random)', + config: () => utils.configs.random.genRandomV2Config() +}, { + name: 'v2, 1 signer', + config: () => utils.configs.random.genRandomV2Config(undefined, undefined, 1, 0) +}, { + name: 'v2, 2 signers', + config: () => utils.configs.random.genRandomV2Config(undefined, undefined, 2, 0) +}, { + name: 'v2, 3 signers', + config: () => utils.configs.random.genRandomV2Config(undefined, undefined, 3, 0) +}, { + name: 'v2, 4 signers', + config: () => utils.configs.random.genRandomV2Config(undefined, undefined, 4, 0) +}, { + name: 'v2, 5 signers', + config: () => utils.configs.random.genRandomV2Config(undefined, undefined, 5, 0) +}, { + name: 'v2, 59 signers', + config: () => utils.configs.random.genRandomV2Config(undefined, undefined, 59, 0) +}, { + name: 'v2, 5 signers (merkle)', + config: () => utils.configs.random.genRandomV2Config(undefined, undefined, 5, 0, true) +}, { + name: 'v2, 11 signers (merkle)', + config: () => utils.configs.random.genRandomV2Config(undefined, undefined, 11, 0, true) +}, { + name: 'v2, 101 signers (merkle)', + config: () => utils.configs.random.genRandomV2Config(undefined, undefined, 101, 0, true) +}, { + name: 'v2, 1 subdigest', + config: () => utils.configs.random.genRandomV2Config(undefined, undefined, 0, 1) +}, { + name: 'v2, 10 subdigest (merkle)', + config: () => utils.configs.random.genRandomV2Config(undefined, undefined, 0, 10, true) +}, { + name: 'v2, 12 signers, 55 subdigest (merkle)', + config: () => utils.configs.random.genRandomV2Config(undefined, undefined, 12, 55, true) +}, { + name: 'v2, random nested configs', + config: () => { + const nested1 = utils.configs.random.genRandomV2Config(undefined, undefined, 11, 10, true) + const nested2 = utils.configs.random.genRandomV2Config() + + return { + version: 2, + threshold: ethers.BigNumber.from(2), + checkpoint: ethers.BigNumber.from(392919), + tree: { + left: { + subdigest: ethers.utils.hexlify(ethers.utils.randomBytes(32)), + }, + right: { + left: { + weight: ethers.BigNumber.from(1), + threshold: ethers.BigNumber.from(99), + tree: nested1.tree + }, + right: { + weight: ethers.BigNumber.from(99), + threshold: ethers.BigNumber.from(1), + tree: nested2.tree + } + } + } + } as v2.config.WalletConfig + } +}] + +const randomContext = () => { + return { + version: Math.floor(Math.random() * 10) + 1, + factory: ethers.Wallet.createRandom().address, + mainModule: ethers.Wallet.createRandom().address, + mainModuleUpgradable: ethers.Wallet.createRandom().address, + guestModule: ethers.Wallet.createRandom().address, + + walletCreationCode: ethers.utils.hexlify(ethers.utils.randomBytes(32)), + } +} + +const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) + +describe('Local config tracker', () => { + let provider: ethers.providers.Web3Provider + + before(async () => { + provider = new ethers.providers.Web3Provider(hardhat.network.provider.send) + }); + + ([{ + name: 'Using memory store', + getTracker: () => new trackers.local.LocalConfigTracker(provider, new trackers.stores.MemoryTrackerStore()) + }, { + name: 'Using IndexedDB store', + getTracker: () => new trackers.local.LocalConfigTracker(provider, new trackers.stores.IndexedDBStore('test')) + }, { + name: 'Using multiple trackers (2)', + getTracker: () => { + const tracker1 = new trackers.local.LocalConfigTracker(provider, new trackers.stores.MemoryTrackerStore()) + const tracker2 = new trackers.local.LocalConfigTracker(provider, new trackers.stores.MemoryTrackerStore()) + + return new trackers.MultipleTracker([tracker1, tracker2]) + } + }, { + name: 'Using multiple trackers (3)', + getTracker: () => { + const tracker1 = new trackers.local.LocalConfigTracker(provider, new trackers.stores.MemoryTrackerStore()) + const tracker2 = new trackers.local.LocalConfigTracker(provider, new trackers.stores.MemoryTrackerStore()) + const tracker3 = new trackers.local.LocalConfigTracker(provider, new trackers.stores.IndexedDBStore('test-2')) + + return new trackers.MultipleTracker([tracker1, tracker2, tracker3]) + } + }, { + name: 'Using a cached tracker', + getTracker: () => { + const tracker = new trackers.local.LocalConfigTracker(provider, new trackers.stores.MemoryTrackerStore()) + const cache = new trackers.local.LocalConfigTracker(provider, new trackers.stores.MemoryTrackerStore()) + return new trackers.CachedTracker(tracker, cache, {}) + } + }, { + name: 'Using a deduped tracker', + getTracker: () => { + const tracker = new trackers.local.LocalConfigTracker(provider, new trackers.stores.MemoryTrackerStore()) + return new trackers.DedupedTracker(tracker, 50) + } + }]).map(({ name, getTracker }) => { + describe(name, () => { + let tracker: tracker.ConfigTracker + + beforeEach(() => { + tracker = getTracker() + }) + + describe('Configuration', () => { + ConfigCases.map((o) => { + it(`Should be able to set and get ${o.name}`, async () => { + const config = o.config() + + await tracker.saveWalletConfig({ config }) + + const imageHash = universal.genericCoderFor(config.version).config.imageHashOf(config) + const getConfig = await tracker.configOfImageHash({ imageHash }) + + expect(normalize(getConfig)).to.deep.equal(normalize(config)) + }) + }) + + it('Should handle all cases at once', async () => { + const shuffled = ConfigCases.sort(() => Math.random() - 0.5) + const configs = shuffled.map((o) => o.config()) + + for (const config of configs) { + await tracker.saveWalletConfig({ config }) + + const imageHash = universal.genericCoderFor(config.version).config.imageHashOf(config) + const getConfig = await tracker.configOfImageHash({ imageHash }) + + expect(normalize(getConfig)).to.deep.equal(normalize(config)) + } + + for (const config of configs) { + const imageHash = universal.genericCoderFor(config.version).config.imageHashOf(config) + const getConfig = await tracker.configOfImageHash({ imageHash }) + + expect(normalize(getConfig)).to.deep.equal(normalize(config)) + } + + // Adding the configs again should not change anything + for (const config of configs) { + await tracker.saveWalletConfig({ config }) + + const imageHash = universal.genericCoderFor(config.version).config.imageHashOf(config) + const getConfig = await tracker.configOfImageHash({ imageHash }) + + expect(normalize(getConfig)).to.deep.equal(normalize(config)) + } + }) + + it.skip('Should combine two different v2 configurations', async () => { + const config1 = utils.configs.random.genRandomV2Config(undefined, undefined, 25, 15, true) + const config2 = utils.configs.random.genRandomV2Config(undefined, undefined, 2, 1, false) + + const ih1 = v2.config.imageHash(config1) + const ih2 = v2.config.imageHash(config2) + + const emptyConfig = { + version: 2, + threshold: ethers.BigNumber.from(2), + checkpoint: ethers.BigNumber.from(0), + tree: { + left: { nodeHash: v2.config.hashNode(config1.tree) }, + right: { nodeHash: v2.config.hashNode(config2.tree) } + } + } + + const imageHash = v2.config.imageHash(emptyConfig) + + await tracker.saveWalletConfig({ config: emptyConfig }) + expect(normalize(await tracker.configOfImageHash({ imageHash }))).to.deep.equal(normalize(emptyConfig)) + + // Add the first config + // should reveal the left branch + await tracker.saveWalletConfig({ config: config1 }) + + // The deduped tracker may cache the result a bit, so if we see a window + // we apply a small delay + if ((tracker as any).window) { + await new Promise((resolve) => setTimeout(resolve, 100)) + } + + expect(normalize(await tracker.configOfImageHash({ imageHash: ih1 }))).to.deep.equal(normalize(config1)) + expect(normalize(await tracker.configOfImageHash({ imageHash }))).to.deep.equal( + normalize({ + version: 2, + threshold: ethers.BigNumber.from(2), + checkpoint: ethers.BigNumber.from(0), + tree: { + left: config1.tree, + right: { nodeHash: v2.config.hashNode(config2.tree) } + } + }) + ) + + // Add the second config + // should reveal the whole tree + await tracker.saveWalletConfig({ config: config2 }) + + if ((tracker as any).window) { + await new Promise((resolve) => setTimeout(resolve, 100)) + } + + expect(normalize(await tracker.configOfImageHash({ imageHash: ih2 }))).to.deep.equal(normalize(config2)) + expect(normalize(await tracker.configOfImageHash({ imageHash }))).to.deep.equal( + normalize({ + version: 2, + threshold: ethers.BigNumber.from(2), + checkpoint: ethers.BigNumber.from(0), + tree: { + left: config1.tree, + right: config2.tree + } + }) + ) + }) + + it('Should return undefined for unknown imageHash', async () => { + const imageHash = ethers.utils.hexlify(ethers.utils.randomBytes(32)) + expect(await tracker.configOfImageHash({ imageHash })).to.be.undefined + }) + + it('Should handle the same request multiple times', async () => { + const config = utils.configs.random.genRandomV1Config() + const imageHash = universal.genericCoderFor(config.version).config.imageHashOf(config) + + await Promise.all(new Array(10).fill(0).map(async () => tracker.saveWalletConfig({ config }))) + const results = await Promise.all(new Array(10).fill(0).map(async () => tracker.configOfImageHash({ imageHash }))) + + expect(results).to.deep.equal(new Array(10).fill(config)) + }) + }) + + describe('Counterfactual address', () => { + it('Should set and get address', async () => { + const context = randomContext() + const config = utils.configs.random.genRandomV1Config() + const imageHash = universal.genericCoderFor(config.version).config.imageHashOf(config) + + const wallet = commons.context.addressOf(context, imageHash) + await tracker.saveCounterfactualWallet({ config, context: [context] }) + const res = await tracker.imageHashOfCounterfactualWallet({ wallet }) + + expect(res).to.deep.equal({ imageHash, context }) + }) + + it('Should set address for multiple configs', async () => { + const contexts = new Array(5).fill(0).map(() => randomContext()) + const config = utils.configs.random.genRandomV1Config() + const imageHash = universal.genericCoderFor(config.version).config.imageHashOf(config) + + const wallets = contexts.map(c => commons.context.addressOf(c, imageHash)) + await tracker.saveCounterfactualWallet({ config, context: contexts }) + + for (let i = 0; i < wallets.length; i++) { + const res = await tracker.imageHashOfCounterfactualWallet({ wallet: wallets[i] }) + expect(res).to.deep.equal({ imageHash, context: contexts[i] }) + } + }) + + it('Should return undefined for unknown wallet', async () => { + const wallet = ethers.Wallet.createRandom().address + expect(await tracker.imageHashOfCounterfactualWallet({ wallet })).to.be.undefined + }) + + it('Should handle the same request multiple times', async () => { + const context = randomContext() + const config = utils.configs.random.genRandomV1Config() + const imageHash = universal.genericCoderFor(config.version).config.imageHashOf(config) + + const wallet = commons.context.addressOf(context, imageHash) + await Promise.all(new Array(10).fill(0).map(async () => tracker.saveCounterfactualWallet({ config, context: [context] }))) + + const results = await Promise.all(new Array(10).fill(0).map(async () => tracker.imageHashOfCounterfactualWallet({ wallet }))) + expect(results).to.deep.equal(new Array(10).fill({ imageHash, context })) + }) + }) + + describe('Chained configurations', () => { + let context: commons.context.WalletContext + + before(async () => { + context = await utils.context.deploySequenceContexts(provider.getSigner(0)).then((c) => c[2]) + }) + + it('Should return return empty chained configuration if config is not known', async () => { + const imageHash = ethers.utils.hexlify(ethers.utils.randomBytes(32)) + const res = await tracker.loadPresignedConfiguration({ wallet: ethers.Wallet.createRandom().address, fromImageHash: imageHash }) + expect(res).to.deep.equal([]) + }) + + it('Should return no chained configuration if no presigned transactions', async () => { + const config = utils.configs.random.genRandomV2Config() + const imageHash = v2.config.imageHash(config) + await tracker.saveWalletConfig({ config }) + const res = await tracker.loadPresignedConfiguration({ wallet: ethers.Wallet.createRandom().address, fromImageHash: imageHash }) + expect(res).to.deep.equal([]) + }) + + it('Should return single presigned step', async () => { + const signer = ethers.Wallet.createRandom() + const config = { version: 2, threshold: 1, checkpoint: 0, tree: { address: signer.address, weight: 1 } } + const imageHash = v2.config.imageHash(config) + const address = commons.context.addressOf(context, imageHash) + const wallet = new Wallet({ config, chainId: 0, coders: v2.coders, address, context, orchestrator: new Orchestrator([signer]) }) + + const nextConfig = utils.configs.random.genRandomV2Config() + const nextImageHash = v2.config.imageHash(nextConfig) + + const digest = v2.chained.hashSetImageHash(nextImageHash) + const signature = await wallet.signDigest(digest) + + await tracker.saveWalletConfig({ config }) + await tracker.saveWalletConfig({ config: nextConfig }) + await tracker.savePresignedConfiguration({ wallet: address, nextConfig, signature }) + + const res = await tracker.loadPresignedConfiguration({ wallet: address, fromImageHash: imageHash }) + expect(res.length).to.equal(1) + expect(res[0].nextImageHash).to.equal(nextImageHash) + expect(res[0].wallet).to.equal(wallet.address) + expect(res[0].signature).to.equal(signature) + }) + + it('Should return empty for wrong wallet', async () => { + const signer = ethers.Wallet.createRandom() + const config = { version: 2, threshold: 1, checkpoint: 0, tree: { address: signer.address, weight: 1 } } + const imageHash = v2.config.imageHash(config) + const address = commons.context.addressOf(context, imageHash) + const wallet = new Wallet({ config, chainId: 0, coders: v2.coders, address, context, orchestrator: new Orchestrator([signer]) }) + + const nextConfig = utils.configs.random.genRandomV2Config() + const nextImageHash = v2.config.imageHash(nextConfig) + + const digest = v2.chained.hashSetImageHash(nextImageHash) + const signature = await wallet.signDigest(digest) + + await tracker.saveWalletConfig({ config }) + await tracker.saveWalletConfig({ config: nextConfig }) + await tracker.savePresignedConfiguration({ wallet: address, nextConfig, signature }) + + const wrongWallet = ethers.Wallet.createRandom().address + const res = await tracker.loadPresignedConfiguration({ wallet: wrongWallet, fromImageHash: imageHash }) + expect(res.length).to.equal(0) + }) + + it('Should return two steps', async () => { + // Step 1 + const signer = ethers.Wallet.createRandom() + const config = { version: 2, threshold: 1, checkpoint: 0, tree: { address: signer.address, weight: 1 } } + const imageHash = v2.config.imageHash(config) + + const address = commons.context.addressOf(context, imageHash) + const wallet1 = new Wallet({ config, chainId: 0, coders: v2.coders, address, context, orchestrator: new Orchestrator([signer]) }) + + const signer2a = ethers.Wallet.createRandom() + const signer2b = ethers.Wallet.createRandom() + const nextConfig1 = { version: 2, threshold: 6, checkpoint: 2, tree: { + right: { + address: signer2a.address, weight: 3 + }, + left: { + address: signer2b.address, weight: 3 + } + } + } + + const nextImageHash1 = v2.config.imageHash(nextConfig1) + + const digest1 = v2.chained.hashSetImageHash(nextImageHash1) + const signature1 = await wallet1.signDigest(digest1) + + // Step 2 + const nextConfig2 = { ...utils.configs.random.genRandomV2Config(), checkpoint: 3 } + const nextImageHash2 = v2.config.imageHash(nextConfig2) + + const digest2 = v2.chained.hashSetImageHash(nextImageHash2) + const wallet2 = new Wallet({ + config: nextConfig1, + chainId: 0, + coders: v2.coders, + address, + context, + orchestrator: new Orchestrator([signer2a, signer2b]) + }) + + const signature2 = await wallet2.signDigest(digest2) + + // Saving only signature2 should lead to empty path + // because there is no route from initial config to config1 + await tracker.saveWalletConfig({ config }) + await tracker.saveWalletConfig({ config: nextConfig1 }) + await tracker.saveWalletConfig({ config: nextConfig2 }) + await tracker.savePresignedConfiguration({ + wallet: address, + nextConfig: nextConfig2, + signature: signature2 + }) + + const route0_2a = await tracker.loadPresignedConfiguration({ + wallet: address, + fromImageHash: imageHash + }) + + expect(route0_2a.length).to.equal(0) + + // But starting from imageHash1 should give us a link + const result1_2a = await tracker.loadPresignedConfiguration({ + wallet: address, + fromImageHash: nextImageHash1 + }) + + expect(result1_2a.length).to.equal(1) + expect(result1_2a[0].nextImageHash).to.equal(nextImageHash2) + expect(result1_2a[0].signature).to.equal(signature2) + expect(result1_2a[0].wallet).to.equal(address) + + // Adding the 0_1 step should give us a full chain to 2 + await tracker.savePresignedConfiguration({ + wallet: address, + nextConfig: nextConfig1, + signature: signature1 + }) + + if ((tracker as any).window) { + await new Promise(resolve => setTimeout(resolve, 100)) + } + + const result0_2b = await tracker.loadPresignedConfiguration({ + wallet: address, + fromImageHash: imageHash + }) + + expect(result0_2b.length).to.equal(2) + expect(result0_2b[0].wallet).to.equal(address) + expect(result0_2b[1].wallet).to.equal(address) + expect(result0_2b[0].nextImageHash).to.equal(nextImageHash1) + expect(result0_2b[1].nextImageHash).to.equal(nextImageHash2) + expect(result0_2b[0].signature).to.equal(signature1) + expect(result0_2b[1].signature).to.equal(signature2) + }) + + it('Should skip step if it uses the same signers', async () => { + const signer1 = ethers.Wallet.createRandom() + const config1 = { version: 2, threshold: 1, checkpoint: 0, tree: { address: signer1.address, weight: 1 } } + const imageHash1 = v2.config.imageHash(config1) + const address = commons.context.addressOf(context, imageHash1) + const wallet = new Wallet({ config: config1, chainId: 0, coders: v2.coders, address, context, orchestrator: new Orchestrator([signer1]) }) + + const signer2 = ethers.Wallet.createRandom() + const config2 = { + version: 2, + threshold: 3, + checkpoint: 1, + tree: { + left: { + address: signer1.address, + weight: 3 + }, + right: { + address: signer2.address, + weight: 4 + } + } + } + + const imageHash2 = v2.config.imageHash(config2) + + const digest1 = v2.chained.hashSetImageHash(imageHash2) + const signature1 = await wallet.signDigest(digest1) + + const config3 = utils.configs.random.genRandomV2Config() + const imageHash3 = v2.config.imageHash(config3) + + const digest2 = v2.chained.hashSetImageHash(imageHash3) + const wallet2 = new Wallet({ + config: config2, + chainId: 0, + coders: v2.coders, + address, + context, + orchestrator: new Orchestrator([signer1, signer2]) + }) + + const signature2 = await wallet2.signDigest(digest2) + + await tracker.saveWalletConfig({ config: config1 }) + await tracker.saveWalletConfig({ config: config2 }) + await tracker.saveWalletConfig({ config: config3 }) + await tracker.savePresignedConfiguration({ wallet: address, nextConfig: config2, signature: signature1 }) + await tracker.savePresignedConfiguration({ wallet: address, nextConfig: config3, signature: signature2 }) + + // Going from 1 to 3 should give us 1 jump + const resa = await tracker.loadPresignedConfiguration({ + wallet: address, + fromImageHash: imageHash1 + }) + + expect(resa.length).to.equal(1) + expect(resa[0].wallet).to.equal(address) + expect(resa[0].nextImageHash).to.equal(imageHash3) + // This is equivalent to having signed the update + // with only signer1 (because that's what we have in imageHash1) + expect(resa[0].signature).to.equal(await wallet.signDigest(digest2)) + + // Unless we ask for the longest path, then we should find + // both jumps + const resb = await tracker.loadPresignedConfiguration({ + wallet: address, + fromImageHash: imageHash1, + longestPath: true + }) + + expect(resb.length).to.equal(2) + expect(resb[0].wallet).to.equal(address) + expect(resb[1].wallet).to.equal(address) + expect(resb[0].nextImageHash).to.equal(imageHash2) + expect(resb[1].nextImageHash).to.equal(imageHash3) + expect(resb[0].signature).to.equal(signature1) + expect(resb[1].signature).to.equal(signature2) + + // Should return wallets of signer1 and signer2 + const wallets1 = await tracker.walletsOfSigner({ signer: signer1.address }) + expect(wallets1.length).to.equal(1) + expect(wallets1[0].wallet).to.equal(address) + + const wallets2 = await tracker.walletsOfSigner({ signer: signer2.address }) + expect(wallets2.length).to.equal(1) + expect(wallets2[0].wallet).to.equal(address) + }) + }) + + describe('Handle witnesses', async () => { + let context: commons.context.WalletContext + + before(async () => { + context = await utils.context.deploySequenceContexts(provider.getSigner(0)).then((c) => c[2]) + }) + + it('Should retrieve no witness for never used signer', async () => { + const signer = ethers.Wallet.createRandom().address + const witness = await tracker.walletsOfSigner({ signer }) + expect(witness.length).to.equal(0) + }) + + it('Should save a witness for a signer', async () => { + const signer = ethers.Wallet.createRandom() + const config = { version: 2, threshold: 1, checkpoint: 0, tree: { address: signer.address, weight: 1 } } + const imageHash = v2.config.imageHash(config) + const address = commons.context.addressOf(context, imageHash) + const wallet = new Wallet({ config, chainId: 1, coders: v2.coders, address, context, orchestrator: new Orchestrator([signer]) }) + + const digest = ethers.utils.hexlify(ethers.utils.randomBytes(32)) + const signature = await wallet.signDigest(digest) + + const decoded = v2.signature.SignatureCoder.decode(signature) + await tracker.saveWitnesses({ + wallet: address, + digest, + chainId: 1, + signatures: [(decoded.decoded.tree as v2.signature.SignatureLeaf).signature] + }) + + const witness = await tracker.walletsOfSigner({ signer: signer.address }) + expect(witness.length).to.equal(1) + expect(witness[0].wallet).to.equal(address) + expect(witness[0].proof.chainId.toNumber()).to.equal(1) + expect(witness[0].proof.digest).to.equal(digest) + expect(witness[0].proof.signature).to.equal((decoded.decoded.tree as v2.signature.SignatureLeaf).signature) + + // Adding a second witness should not change anything + const digest2 = ethers.utils.hexlify(ethers.utils.randomBytes(32)) + const signature2 = await wallet.signDigest(digest2) + const decoded2 = v2.signature.SignatureCoder.decode(signature2) + await tracker.saveWitnesses({ + wallet: address, + digest: digest2, + chainId: 1, + signatures: [(decoded2.decoded.tree as v2.signature.SignatureLeaf).signature] + }) + + const witness2 = await tracker.walletsOfSigner({ signer: signer.address }) + expect(witness2.length).to.equal(1) + + // Adding a witness for a different chain should not change anything + const digest3 = ethers.utils.hexlify(ethers.utils.randomBytes(32)) + const wallet2 = new Wallet({ config, chainId: 2, coders: v2.coders, address, context, orchestrator: new Orchestrator([signer]) }) + const signature3 = await wallet2.signDigest(digest3) + const decoded3 = v2.signature.SignatureCoder.decode(signature3) + await tracker.saveWitnesses({ + wallet: address, + digest: digest3, + chainId: 2, + signatures: [(decoded3.decoded.tree as v2.signature.SignatureLeaf).signature] + }) + + const witness3 = await tracker.walletsOfSigner({ signer: signer.address }) + expect(witness3.length).to.equal(1) + }) + + it('It should save witnesses for multiple wallets', async () => { + const signer = ethers.Wallet.createRandom() + const config = { version: 2, threshold: 1, checkpoint: 0, tree: { address: signer.address, weight: 1 } } + const imageHash = v2.config.imageHash(config) + const address = commons.context.addressOf(context, imageHash) + const wallet = new Wallet({ config, chainId: 1, coders: v2.coders, address, context, orchestrator: new Orchestrator([signer]) }) + + const digest = ethers.utils.hexlify(ethers.utils.randomBytes(32)) + const signature = await wallet.signDigest(digest) + + const decoded = v2.signature.SignatureCoder.decode(signature) + await tracker.saveWitnesses({ + wallet: address, + digest, + chainId: 1, + signatures: [(decoded.decoded.tree as v2.signature.SignatureLeaf).signature] + }) + + const config2 = { version: 2, threshold: 2, checkpoint: 0, tree: { address: signer.address, weight: 2 } } + const imageHash2 = v2.config.imageHash(config2) + const address2 = commons.context.addressOf(context, imageHash2) + const wallet2 = new Wallet({ config: config2, chainId: 1, coders: v2.coders, address: address2, context, orchestrator: new Orchestrator([signer]) }) + + const digest2 = ethers.utils.hexlify(ethers.utils.randomBytes(32)) + const signature2 = await wallet2.signDigest(digest2) + + const decoded2 = v2.signature.SignatureCoder.decode(signature2) + await tracker.saveWitnesses({ + wallet: address2, + digest: digest2, + chainId: 1, + signatures: [(decoded2.decoded.tree as v2.signature.SignatureLeaf).signature] + }) + + const witness = await tracker.walletsOfSigner({ signer: signer.address }) + expect(witness.length).to.equal(2) + + const wallet1Result = witness.find((w) => w.wallet === address) + const wallet2Result = witness.find((w) => w.wallet === address2) + expect(wallet1Result).to.not.be.undefined + expect(wallet2Result).to.not.be.undefined + + expect(wallet1Result?.proof.chainId.toNumber()).to.equal(1) + expect(wallet1Result?.proof.digest).to.equal(digest) + expect(wallet1Result?.proof.signature).to.equal((decoded.decoded.tree as v2.signature.SignatureLeaf).signature) + + expect(wallet2Result?.proof.chainId.toNumber()).to.equal(1) + expect(wallet2Result?.proof.digest).to.equal(digest2) + expect(wallet2Result?.proof.signature).to.equal((decoded2.decoded.tree as v2.signature.SignatureLeaf).signature) + }) + }) + }) + }) + + describe('Multiple config trackers', () => { + let tracker1: trackers.local.LocalConfigTracker + let tracker2: trackers.local.LocalConfigTracker + + let combined: trackers.MultipleTracker + + beforeEach(async () => { + tracker1 = new trackers.local.LocalConfigTracker(provider) + tracker2 = new trackers.local.LocalConfigTracker(provider) + + combined = new trackers.MultipleTracker([tracker1, tracker2]) + }) + + describe('Config', () => { + it('Storing a config should store it in both', async () => { + const config = { + version: 2, + threshold: ethers.BigNumber.from(1), + checkpoint: ethers.BigNumber.from(0), + tree: { + address: ethers.Wallet.createRandom().address, + weight: ethers.BigNumber.from(1) + } + } + + const imageHash = v2.config.imageHash(config) + + await combined.saveWalletConfig({ config }) + + const config1 = await tracker1.configOfImageHash({ imageHash }) + const config2 = await tracker2.configOfImageHash({ imageHash }) + + expect(config1).to.deep.equal(config) + expect(config2).to.deep.equal(config) + }) + + it('Retrieving a config from tracker1, should mirror to tracker2', async () => { + const config = { + version: 2, + threshold: ethers.BigNumber.from(1), + checkpoint: ethers.BigNumber.from(0), + tree: { + address: ethers.Wallet.createRandom().address, + weight: ethers.BigNumber.from(1) + } + } + + const imageHash = v2.config.imageHash(config) + + await tracker1.saveWalletConfig({ config }) + + const config1 = await combined.configOfImageHash({ imageHash }) + + await wait(500) + + const config2 = await tracker2.configOfImageHash({ imageHash }) + + expect(config1).to.deep.equal(config) + expect(config2).to.deep.equal(config) + }) + + it.skip('Should combine 2 different sources', async () => { + const node1 = { + address: ethers.Wallet.createRandom().address, + weight: ethers.BigNumber.from(1) + } + + const node2 = { + address: ethers.Wallet.createRandom().address, + weight: ethers.BigNumber.from(1) + } + + const config1 = { + version: 2, + threshold: ethers.BigNumber.from(1), + checkpoint: ethers.BigNumber.from(1234), + tree: { + left: { + nodeHash: v2.config.hashNode(node1), + }, + right: node2 + } + } + + const config2 = { + version: 2, + threshold: ethers.BigNumber.from(1), + checkpoint: ethers.BigNumber.from(1234), + tree: { + left: node1, + right: { + nodeHash: v2.config.hashNode(node2), + } + } + } + + const configAll = { + version: 2, + threshold: ethers.BigNumber.from(1), + checkpoint: ethers.BigNumber.from(1234), + tree: { + left: node1, + right: node2 + } + } + + await tracker1.saveWalletConfig({ config: config1 }) + await tracker2.saveWalletConfig({ config: config2 }) + + const imageHash = v2.config.imageHash(config2) + const res1 = await combined.configOfImageHash({ imageHash }) + const res2 = await tracker1.configOfImageHash({ imageHash }) + const res3 = await tracker2.configOfImageHash({ imageHash }) + + expect(res1).to.deep.equal(configAll) + expect(res2).to.deep.equal(configAll) + expect(res3).to.deep.equal(configAll) + }) + }) + + describe('Counterfactual addresses', () => { + it('Should store counterfactual address in both', async () => { + const context = randomContext() + const config = utils.configs.random.genRandomV1Config() + const imageHash = universal.genericCoderFor(config.version).config.imageHashOf(config) + + const wallet = commons.context.addressOf(context, imageHash) + await combined.saveCounterfactualWallet({ config, context: [context] }) + + const res1 = await combined.imageHashOfCounterfactualWallet({ wallet }) + const res2 = await tracker1.imageHashOfCounterfactualWallet({ wallet }) + const res3 = await tracker2.imageHashOfCounterfactualWallet({ wallet }) + + expect(res1).to.deep.equal({ imageHash, context }) + expect(res2).to.deep.equal({ imageHash, context }) + expect(res3).to.deep.equal({ imageHash, context }) + }) + + it('Should mirror counterfactual address from tracker1', async () => { + const context = randomContext() + const config = utils.configs.random.genRandomV1Config() + const imageHash = universal.genericCoderFor(config.version).config.imageHashOf(config) + + const wallet = commons.context.addressOf(context, imageHash) + await tracker1.saveCounterfactualWallet({ config, context: [context] }) + + const res1 = await combined.imageHashOfCounterfactualWallet({ wallet }) + + await wait(500) + + const res2 = await tracker1.imageHashOfCounterfactualWallet({ wallet }) + const res3 = await tracker2.imageHashOfCounterfactualWallet({ wallet }) + + expect(res1).to.deep.equal({ imageHash, context }) + expect(res2).to.deep.equal({ imageHash, context }) + expect(res3).to.deep.equal({ imageHash, context }) + }) + }) + + describe('Chained configurations', () => { + let context: commons.context.WalletContext + + before(async () => { + context = await utils.context.deploySequenceContexts(provider.getSigner(0)).then((c) => c[2]) + }) + + it('Should store chained config in both', async () => { + const signer = ethers.Wallet.createRandom() + const config = { version: 2, threshold: 1, checkpoint: 0, tree: { address: signer.address, weight: 1 } } + const imageHash = v2.config.imageHash(config) + const address = commons.context.addressOf(context, imageHash) + const wallet = new Wallet({ config, chainId: 0, coders: v2.coders, address, context, orchestrator: new Orchestrator([signer]) }) + + const nextConfig = utils.configs.random.genRandomV2Config() + const nextImageHash = v2.config.imageHash(nextConfig) + + const digest = v2.chained.hashSetImageHash(nextImageHash) + const signature = await wallet.signDigest(digest) + + await combined.saveWalletConfig({ config }) + await combined.saveWalletConfig({ config: nextConfig }) + await combined.savePresignedConfiguration({ wallet: address, nextConfig, signature }) + + const res2 = await tracker1.loadPresignedConfiguration({ wallet: address, fromImageHash: imageHash }) + const res3 = await tracker2.loadPresignedConfiguration({ wallet: address, fromImageHash: imageHash }) + const res1 = await combined.loadPresignedConfiguration({ wallet: address, fromImageHash: imageHash }) + + expect(res1.length).to.equal(1) + expect(res1[0].nextImageHash).to.equal(nextImageHash) + expect(res1[0].wallet).to.equal(wallet.address) + expect(res1[0].signature).to.equal(signature) + + expect(res2.length).to.equal(1) + expect(res2[0].nextImageHash).to.equal(nextImageHash) + expect(res2[0].wallet).to.equal(wallet.address) + expect(res2[0].signature).to.equal(signature) + + expect(res3.length).to.equal(1) + expect(res3[0].nextImageHash).to.equal(nextImageHash) + expect(res3[0].wallet).to.equal(wallet.address) + expect(res3[0].signature).to.equal(signature) + }) + + it('Should mirror chained config from tracker2', async () => { + const signer = ethers.Wallet.createRandom() + const config = { version: 2, threshold: 1, checkpoint: 0, tree: { address: signer.address, weight: 1 } } + const imageHash = v2.config.imageHash(config) + const address = commons.context.addressOf(context, imageHash) + const wallet = new Wallet({ config, chainId: 0, coders: v2.coders, address, context, orchestrator: new Orchestrator([signer]) }) + + const nextConfig = utils.configs.random.genRandomV2Config() + const nextImageHash = v2.config.imageHash(nextConfig) + + const digest = v2.chained.hashSetImageHash(nextImageHash) + const signature = await wallet.signDigest(digest) + + await tracker2.saveWalletConfig({ config }) + await tracker2.saveWalletConfig({ config: nextConfig }) + await tracker2.savePresignedConfiguration({ wallet: address, nextConfig, signature }) + + const res1 = await combined.loadPresignedConfiguration({ wallet: address, fromImageHash: imageHash }) + + await wait(500) + + const res2 = await tracker1.loadPresignedConfiguration({ wallet: address, fromImageHash: imageHash }) + const res3 = await tracker2.loadPresignedConfiguration({ wallet: address, fromImageHash: imageHash }) + + expect(res1.length).to.equal(1) + expect(res1[0].nextImageHash).to.equal(nextImageHash) + expect(res1[0].wallet).to.equal(wallet.address) + expect(res1[0].signature).to.equal(signature) + + expect(res2.length).to.equal(1) + expect(res2[0].nextImageHash).to.equal(nextImageHash) + expect(res2[0].wallet).to.equal(wallet.address) + expect(res2[0].signature).to.equal(signature) + + expect(res3.length).to.equal(1) + expect(res3[0].nextImageHash).to.equal(nextImageHash) + expect(res3[0].wallet).to.equal(wallet.address) + expect(res3[0].signature).to.equal(signature) + }) + + it('Should return highest checkpoint chain (and then mirror)', async () => { + // Step 1 + const signer = ethers.Wallet.createRandom() + const config = { version: 2, threshold: 1, checkpoint: 0, tree: { address: signer.address, weight: 1 } } + const imageHash = v2.config.imageHash(config) + + const address = commons.context.addressOf(context, imageHash) + const wallet1 = new Wallet({ config, chainId: 0, coders: v2.coders, address, context, orchestrator: new Orchestrator([signer]) }) + + const signer2a = ethers.Wallet.createRandom() + const signer2b = ethers.Wallet.createRandom() + const nextConfig1 = { version: 2, threshold: 6, checkpoint: 2, tree: { + right: { + address: signer2a.address, weight: 3 + }, + left: { + address: signer2b.address, weight: 3 + } + } + } + + const nextImageHash1 = v2.config.imageHash(nextConfig1) + + const digest1 = v2.chained.hashSetImageHash(nextImageHash1) + const signature1 = await wallet1.signDigest(digest1) + + // Step 2 + const nextConfig2 = { ...utils.configs.random.genRandomV2Config(), checkpoint: 3 } + const nextImageHash2 = v2.config.imageHash(nextConfig2) + + const digest2 = v2.chained.hashSetImageHash(nextImageHash2) + const wallet2 = new Wallet({ + config: nextConfig1, + chainId: 0, + coders: v2.coders, + address, + context, + orchestrator: new Orchestrator([signer2a, signer2b]) + }) + + const signature2 = await wallet2.signDigest(digest2) + + // Saving only signature1 on tracker 1 + await tracker1.saveWalletConfig({ config }) + await tracker1.saveWalletConfig({ config: nextConfig1 }) + await tracker1.savePresignedConfiguration({ + wallet: address, + nextConfig: nextConfig1, + signature: signature1 + }) + + // Saving both signatures on tracker 2 + await tracker2.saveWalletConfig({ config }) + await tracker2.saveWalletConfig({ config: nextConfig1 }) + await tracker2.saveWalletConfig({ config: nextConfig2 }) + await tracker2.savePresignedConfiguration({ + wallet: address, + nextConfig: nextConfig1, + signature: signature1 + }) + await tracker2.savePresignedConfiguration({ + wallet: address, + nextConfig: nextConfig2, + signature: signature2 + }) + + // Now the combined tracker should return the highest checkpoint + const res1 = await combined.loadPresignedConfiguration({ wallet: address, fromImageHash: imageHash }) + + await wait(500) + + const res2 = await tracker1.loadPresignedConfiguration({ wallet: address, fromImageHash: imageHash }) + const res3 = await tracker2.loadPresignedConfiguration({ wallet: address, fromImageHash: imageHash }) + + expect(res1.length).to.equal(2) + expect(res1[0].wallet).to.equal(address) + expect(res1[1].wallet).to.equal(address) + expect(res1[0].nextImageHash).to.equal(nextImageHash1) + expect(res1[1].nextImageHash).to.equal(nextImageHash2) + expect(res1[0].signature).to.equal(signature1) + expect(res1[1].signature).to.equal(signature2) + + expect(res2).to.deep.equal(res1) + expect(res3).to.deep.equal(res1) + }) + }) + }) +}) + +function normalize(value: any): any { + switch (typeof value) { + case 'object': + if (ethers.BigNumber.isBigNumber(value)) { + return value.toString() + } + return Object.fromEntries(Object.entries(value).map(([key, value]) => [key, normalize(value)])) + case 'number': + return `${value}` + default: + return value + } +} diff --git a/packages/signhub/package.json b/packages/signhub/package.json new file mode 100644 index 000000000..c3e1a2097 --- /dev/null +++ b/packages/signhub/package.json @@ -0,0 +1,28 @@ +{ + "name": "@0xsequence/signhub", + "version": "0.43.7", + "description": "orchestrates a series of signers, provides visibility into the signing process, and to the signers themselves", + "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/signhub", + "source": "src/index.ts", + "main": "dist/0xsequence-signhub.cjs.js", + "module": "dist/0xsequence-signhub.esm.js", + "author": "Horizon Blockchain Games", + "license": "Apache-2.0", + "scripts": { + "test": "yarn test:file tests/**/*.spec.ts", + "test:file": "TS_NODE_PROJECT=../../tsconfig.test.json mocha -r ts-node/register --timeout 30000", + "test:coverage": "nyc yarn test" + }, + "dependencies": { + "ethers": "^5.5.2" + }, + "peerDependencies": {}, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.2", + "nyc": "^15.1.0" + }, + "files": [ + "src", + "dist" + ] +} diff --git a/packages/signhub/src/index.ts b/packages/signhub/src/index.ts new file mode 100644 index 000000000..c8deaee70 --- /dev/null +++ b/packages/signhub/src/index.ts @@ -0,0 +1,3 @@ + +export * as signers from './signers' +export * from './orchestrator' diff --git a/packages/signhub/src/orchestrator.ts b/packages/signhub/src/orchestrator.ts new file mode 100644 index 000000000..7700620bb --- /dev/null +++ b/packages/signhub/src/orchestrator.ts @@ -0,0 +1,165 @@ +import { ethers } from "ethers" +import { isSapientSigner, SapientSigner } from "./signers/signer" +import { SignerWrapper } from "./signers/wrapper" + +export type Status = { + ended: boolean, + message: ethers.BytesLike, + signers: { [signer: string]: SignerStatus }, +} + +export type SignerStatusPending = { + situation?: string +} + +export type SignerStatusRejected = { + rejected: true, + error?: string +} + +export type SignerStatusSigned = { + signature: ethers.BytesLike, + suffix: ethers.BytesLike +} + +export type SignerStatus = SignerStatusPending | SignerStatusRejected | SignerStatusSigned + +export function isSignerStatusRejected(status: SignerStatus): status is SignerStatusRejected { + return (status as SignerStatusRejected).rejected +} + +export function isSignerStatusSigned(status: SignerStatus): status is SignerStatusSigned { + return (status as SignerStatusSigned).signature !== undefined +} + +export function isSignerStatusPending(status: SignerStatus): status is SignerStatusPending { + return !isSignerStatusRejected(status) && !isSignerStatusSigned(status) +} + +export const InitialSituation = "Initial" + +/** + * It orchestrates the signing of a single digest by multiple signers. + * It can provide internal visibility of the signing process, and it also + * provides the internal signers with additional information about the + * message being signed. + */ +export class Orchestrator { + private observers: ((status: Status, metadata: Object) => void)[] = [] + private signers: SapientSigner[] = [] + + private count = 0 + + constructor(signers: (ethers.Signer | SapientSigner)[], public tag: string = Orchestrator.randomTag()) { + this.setSigners(signers) + } + + private static randomTag(): string { + return `default-${ethers.utils.hexlify(ethers.utils.randomBytes(8)).slice(2)}` + } + + private pullId(): string { + return `${this.tag}-${this.count++}` + } + + setSigners(signers: (ethers.Signer | SapientSigner)[]) { + this.signers = signers.map((s) => isSapientSigner(s) ? s : new SignerWrapper(s)) + } + + async getSigners(): Promise { + return Promise.all(this.signers.map(async (s) => s.getAddress())) + } + + subscribe(observer: (status: Status, metadata: Object) => void): () => void { + this.observers.push(observer) + return () => { this.observers = this.observers.filter((o) => o !== observer) } + } + + private async notifyObservers(id: string, status: Status, metadata: Object) { + await Promise.all([ + ...this.signers.map(async (signer) => signer.notifyStatusChange(id, status, metadata)), + ...this.observers.map(async (observer) => observer(status, metadata)) + ]) + } + + signMessage(args: { + candidates?: string[], + message: ethers.BytesLike, + metadata?: Object, + callback?: ( + status: Status, + onNewMetadata: (metadata: Object) => void + ) => boolean + }): Promise { + const id = this.pullId() + + return new Promise(async (resolve) => { + const { message, metadata, callback, candidates } = args + const status: Status = { ended: false, message, signers: {} } + let lastMetadata = metadata ?? {} + + const onNewMetadata = (newMetadata: Object) => { + lastMetadata = newMetadata + this.notifyObservers(id, status, lastMetadata) + } + + const onStatusUpdate = () => { + try { + this.notifyObservers(id, status, lastMetadata) + + const pending = Object.entries(status.signers).filter(([_, s]) => isSignerStatusPending(s)) + if ((callback && callback(status, onNewMetadata)) || pending.length === 0) { + status.ended = true + resolve(status) + this.notifyObservers(id, status, lastMetadata) + return + } + } catch (e) { + console.error("Error while notifying observers", e) + } + } + + // we only call signers that are found in `candidates` + // if `candidates` is undefined, we call all signers + let signers = this.signers + if (candidates) { + const addresses = await Promise.all(this.signers.map(async (s) => s.getAddress())) + signers = this.signers.filter((_, i) => candidates.includes(addresses[i])) + } + + // build callbacks object + const accepted = await Promise.allSettled(signers.map(async (s) => { + const saddr = await s.getAddress() + status.signers[saddr] = { situation: InitialSituation } + return s.requestSignature(id, message, metadata ?? {}, { + onSignature: (signature) => { + const suffix = s.suffix() + status.signers[saddr] = { signature, suffix } + onStatusUpdate() + }, + onRejection: (error) => { + status.signers[saddr] = { rejected: true, error } + onStatusUpdate() + }, + onStatus: (situation) => { + status.signers[saddr] = { situation } + onStatusUpdate() + } + }) + })) + + for (let i = 0; i < accepted.length; i++) { + const signer = this.signers[i] + const promise = accepted[i] + + if (promise.status === "rejected" || promise.value === false) { + const prejected = promise as PromiseRejectedResult + console.warn(`Signer ${await signer.getAddress()} rejected the request ${prejected.reason}`) + status.signers[await signer.getAddress()] = { rejected: true, error: prejected.reason.toString() } + } + } + + onStatusUpdate() + }) + } +} diff --git a/packages/signhub/src/signers/index.ts b/packages/signhub/src/signers/index.ts new file mode 100644 index 000000000..0bfbaf26e --- /dev/null +++ b/packages/signhub/src/signers/index.ts @@ -0,0 +1,3 @@ + +export * from './signer' +export * from './wrapper' diff --git a/packages/signhub/src/signers/signer.ts b/packages/signhub/src/signers/signer.ts new file mode 100644 index 000000000..28c7749d3 --- /dev/null +++ b/packages/signhub/src/signers/signer.ts @@ -0,0 +1,29 @@ +import { ethers } from "ethers" +import { Status } from "../orchestrator" + +export interface SapientSigner { + getAddress(): Promise + + requestSignature( + id: string, + message: ethers.BytesLike, + metadata: Object, + callbacks: { + onSignature: (signature: ethers.BytesLike) => void, + onRejection: (error: string) => void, + onStatus: (situation: string) => void + } + ): Promise + + notifyStatusChange( + id: string, + status: Status, + metadata: Object + ): void + + suffix(): ethers.BytesLike +} + +export function isSapientSigner(signer: ethers.Signer | SapientSigner): signer is SapientSigner { + return (signer as SapientSigner).requestSignature !== undefined && (signer as SapientSigner).notifyStatusChange !== undefined +} diff --git a/packages/signhub/src/signers/wrapper.ts b/packages/signhub/src/signers/wrapper.ts new file mode 100644 index 000000000..6f9dbbe57 --- /dev/null +++ b/packages/signhub/src/signers/wrapper.ts @@ -0,0 +1,32 @@ + +import { ethers } from 'ethers' +import { Status } from '../orchestrator' +import { SapientSigner } from './signer' + +export class SignerWrapper implements SapientSigner { + constructor(public signer: ethers.Signer, public eoa: boolean = true) {} + + getAddress(): Promise { + return this.signer.getAddress() + } + + async requestSignature( + _id: string, + message: ethers.BytesLike, + _metadata: Object, + callbacks: { + onSignature: (signature: ethers.BytesLike) => void; + onRejection: (error: string) => void; + onStatus: (situation: string) => void + } + ): Promise { + callbacks.onSignature(await this.signer.signMessage(message)) + return true + } + + notifyStatusChange(_i: string, _s: Status, _m: Object): void {} + + suffix(): ethers.BytesLike { + return [ 2 ] + } +} diff --git a/packages/signhub/tests/orchestrator.spec.ts b/packages/signhub/tests/orchestrator.spec.ts new file mode 100644 index 000000000..3cc8c602c --- /dev/null +++ b/packages/signhub/tests/orchestrator.spec.ts @@ -0,0 +1,451 @@ + +import * as chai from 'chai' +import { ethers } from 'ethers' +import { isSignerStatusPending, isSignerStatusRejected, isSignerStatusSigned, Orchestrator, Status } from '../src' +import { SapientSigner } from '../src/signers' + +const { expect } = chai + +describe('Orchestrator', () => { + it('Should call all signers', async () => { + const signers = [ + ethers.Wallet.createRandom(), + ethers.Wallet.createRandom(), + ethers.Wallet.createRandom() + ] + + const orchestrator = new Orchestrator(signers) + const signature = await orchestrator.signMessage({ message: '0x1234' }) + + expect(Object.keys(signature.signers)).to.have.lengthOf(signers.length) + + for (const signer of signers) { + expect(signature.signers).to.have.property(signer.address) + } + }) + + it('Should call callback with status updates', async () => { + const signers = [ + ethers.Wallet.createRandom(), + ethers.Wallet.createRandom(), + ethers.Wallet.createRandom() + ] + + const orchestrator = new Orchestrator(signers) + + let callbackCallsA = 0 + orchestrator.subscribe((status, metadata) => { + // Status should have all signers + let numErrors = 0 + let numSignatures = 0 + let numPending = 0 + expect(Object.keys(status.signers)).to.have.lengthOf(signers.length, 'Should have all signers') + for (const signer of signers) { + expect(status.signers).to.have.property(signer.address) + const signerStatus = status.signers[signer.address] + + if (isSignerStatusRejected(signerStatus)) { + numErrors++ + } + + if (isSignerStatusPending(signerStatus)) { + numPending++ + } + + if (isSignerStatusSigned(signerStatus)) { + numSignatures++ + } + } + + callbackCallsA++ + + expect(numErrors).to.be.equal(0, 'No errors should be present') + expect(numSignatures).to.be.equal(Math.max(callbackCallsA, 3), 'Should have max 3 signatures') + expect(numPending).to.be.equal(Math.min(signers.length - callbackCallsA, 0), 'Should have 0 pending') + }) + + let callbackCallsB = 0 + await orchestrator.signMessage({ + message: '0x1234', + callback: () => { + callbackCallsB++ + return false + } + }) + + // 3 updates + 1 final + expect(callbackCallsA).to.be.equal(4) + + // only the 3 updates + expect(callbackCallsB).to.be.equal(3) + }) + + it('getSigners should return all signers', async () => { + const signers = new Array(10).fill(0).map(() => ethers.Wallet.createRandom()) + const orchestrator = new Orchestrator(signers) + const result = await orchestrator.getSigners() + expect(result).to.have.lengthOf(signers.length) + for (const signer of signers) { + expect(result).to.include(signer.address) + } + }) + + it('setSigners should update the signers', async () => { + const signers = new Array(10).fill(0).map(() => ethers.Wallet.createRandom()) + const orchestrator = new Orchestrator(signers) + + const newSigners = new Array(22).fill(0).map(() => ethers.Wallet.createRandom()) + orchestrator.setSigners(newSigners) + const result = await orchestrator.getSigners() + expect(result).to.have.lengthOf(newSigners.length) + for (const signer of newSigners) { + expect(result).to.include(signer.address) + } + }) + + it('exception on signer should be interpreted as error', async () => { + const brokenSignerEOA = ethers.Wallet.createRandom() + const brokenSigner: SapientSigner = { + getAddress: async function (): Promise { + return brokenSignerEOA.address + }, + requestSignature: async function ( + id: string, + message: ethers.utils.BytesLike, + metadata: Object, + callbacks: { onSignature: (signature: ethers.utils.BytesLike) => void; onRejection: (error: string) => void; onStatus: (situation: string) => void } + ): Promise { + throw new Error('This is a broken signer.') + }, + notifyStatusChange: function (id: string, status: Status): void {}, + suffix: function () { + return [ 2 ] + } + } + + const signers = [ + ethers.Wallet.createRandom(), + brokenSigner, + ethers.Wallet.createRandom() + ] + + const orchestrator = new Orchestrator(signers) + + let callbackCallsA = 0 + orchestrator.subscribe(async (status) => { + // Status should have all signers + let numErrors = 0 + let numSignatures = 0 + let numPending = 0 + + expect(Object.keys(status.signers)).to.have.lengthOf(signers.length) + + for (const signer of signers) { + expect(status.signers).to.have.property(await signer.getAddress()) + const signerStatus = status.signers[await signer.getAddress()] + + if (isSignerStatusRejected(signerStatus)) { + numErrors++ + } + + if (isSignerStatusPending(signerStatus)) { + numPending++ + } + + if (isSignerStatusSigned(signerStatus)) { + numSignatures++ + } + } + + callbackCallsA++ + + expect(numErrors).to.be.equal(1) + expect(numSignatures).to.be.equal(Math.max(callbackCallsA, 3)) + expect(numPending).to.be.equal(Math.min(signers.length - callbackCallsA, 0)) + }) + + const signature = await orchestrator.signMessage({ message: '0x1234' }) + expect(Object.keys(signature.signers)).to.have.lengthOf(signers.length) + + for (const signer of signers) { + const address = await signer.getAddress() + const status = signature.signers[address] + + if (address === await brokenSigner.getAddress()) { + if (isSignerStatusRejected(status)) { + expect(status.error).to.contain('This is a broken signer.') + } else { + expect.fail('Signer should be rejected') + } + } else { + expect(isSignerStatusSigned(status)).to.be.true + } + } + }) + + it('Should manually reject a request', async () => { + const rejectSignerEOA = ethers.Wallet.createRandom() + const rejectSigner: SapientSigner = { + getAddress: async function (): Promise { + return rejectSignerEOA.address + }, + requestSignature: async function ( + id: string, + message: ethers.utils.BytesLike, + metadata: Object, + callbacks: { onSignature: (signature: ethers.utils.BytesLike) => void; onRejection: (error: string) => void; onStatus: (situation: string) => void } + ): Promise { + callbacks.onRejection('This is a rejected signer.') + return true + }, + notifyStatusChange: function (id: string, status: Status): void {}, + suffix: function () { + return [ 2 ] + } + } + + const signers = [ + ethers.Wallet.createRandom(), + rejectSigner + ] + + const orchestrator = new Orchestrator(signers) + + let callbackCallsA = 0 + orchestrator.subscribe(() => { + callbackCallsA++ + }) + + const signature = await orchestrator.signMessage({ message: '0x1234' }) + expect(Object.keys(signature.signers)).to.have.lengthOf(signers.length) + + for (const signer of signers) { + const address = await signer.getAddress() + const status = signature.signers[address] + + if (address === await rejectSigner.getAddress()) { + if (isSignerStatusRejected(status)) { + expect(status.error).to.contain('This is a rejected signer.') + } else { + expect.fail('Signer should be rejected') + } + } else { + expect(isSignerStatusSigned(status)).to.be.true + } + } + }) + + it('Should pass the correct message to the signer', async () => { + const ogMessage = ethers.utils.randomBytes(99) + const signer: SapientSigner = { + getAddress: async function (): Promise { + return '0x1234' + }, + requestSignature: async function ( + id: string, + message: ethers.utils.BytesLike, + metadata: Object, + callbacks: { onSignature: (signature: ethers.utils.BytesLike) => void; onRejection: (error: string) => void; onStatus: (situation: string) => void } + ): Promise { + expect(message).to.be.equal(ogMessage) + callbacks.onSignature('0x5678') + return true + }, + notifyStatusChange: function (id: string, status: Status): void {}, + suffix: function () { + return [ 2 ] + } + } + + const orchestrator = new Orchestrator([signer]) + const signature = await orchestrator.signMessage({ message: ogMessage }) + + expect((signature.signers['0x1234'] as any).signature).to.be.equal('0x5678') + }) + + it('Should pass metadata to signer', async () => { + const ogMessage = ethers.utils.randomBytes(99) + const signer: SapientSigner = { + getAddress: async function (): Promise { + return '0x1234' + }, + requestSignature: async function ( + id: string, + message: ethers.utils.BytesLike, + metadata: Object, + callbacks: { onSignature: (signature: ethers.utils.BytesLike) => void; onRejection: (error: string) => void; onStatus: (situation: string) => void } + ): Promise { + expect(metadata).to.be.deep.equal({ test: 'test' }) + callbacks.onSignature('0x5678') + return true + }, + notifyStatusChange: function (id: string, status: Status): void {}, + suffix: function () { + return [ 2 ] + } + } + + const orchestrator = new Orchestrator([signer]) + const signature = await orchestrator.signMessage({ message: ogMessage, metadata: { test: 'test' }}) + + expect((signature.signers['0x1234'] as any).signature).to.be.equal('0x5678') + }) + + it('Should pass updated metadata to signer', async () => { + const ogMessage = ethers.utils.randomBytes(99) + + let firstCall = true + let errorOnNotify: any = undefined + + const signer1: SapientSigner = { + getAddress: async function (): Promise { + return '0x1234' + }, + requestSignature: async function ( + id: string, + message: ethers.utils.BytesLike, + metadata: Object, + callbacks: { onSignature: (signature: ethers.utils.BytesLike) => void; onRejection: (error: string) => void; onStatus: (situation: string) => void } + ): Promise { + expect(metadata).to.be.deep.equal({ test: 'test' }) + callbacks.onSignature('0x5678') + return true + }, + notifyStatusChange: function (id: string, status: Status, metadata: Object): void { + try { + if (firstCall) { + expect(metadata).to.be.deep.equal({ test: 'test' }) + } else { + expect(metadata).to.be.deep.equal({ hello: 'world' }) + } + } catch (e) { + errorOnNotify = e + } + }, + suffix: function () { + return [ 2 ] + } + } + + const signer2: SapientSigner = { + getAddress: async function (): Promise { + return '0x5678' + }, + requestSignature: async function ( + id: string, + message: ethers.utils.BytesLike, + metadata: Object, + callbacks: { onSignature: (signature: ethers.utils.BytesLike) => void; onRejection: (error: string) => void; onStatus: (situation: string) => void } + ): Promise { + expect(metadata).to.be.deep.equal({ test: 'test' }) + callbacks.onSignature('0x9012') + return true + }, + notifyStatusChange: function (id: string, status: Status, metadata: Object): void { + try { + if (firstCall) { + expect(metadata).to.be.deep.equal({ test: 'test' }) + } else { + expect(metadata).to.be.deep.equal({ hello: 'world' }) + } + } catch (e) { + errorOnNotify = e + } + }, + suffix: function () { + return [ 2 ] + } + } + + const orchestrator = new Orchestrator([signer1, signer2]) + const signature = await orchestrator.signMessage({ + message: ogMessage, + metadata: { test: 'test' }, + callback: (s: Status, onNewMetadata: (metadata: Object) => void) => { + if (firstCall) { + firstCall = false + onNewMetadata({ hello: 'world' }) + return false + } + + return true + } + }) + + expect((signature.signers['0x1234'] as any).signature).to.be.equal('0x5678') + expect((signature.signers['0x5678'] as any).signature).to.be.equal('0x9012') + if (errorOnNotify) throw errorOnNotify + }) + + it('Should generate distinct and incremental ids', async () => { + const ogMessage = ethers.utils.randomBytes(99) + const signer: SapientSigner = { + getAddress: async function (): Promise { + return '0x1234' + }, + requestSignature: async function ( + id: string, + message: ethers.utils.BytesLike, + metadata: any, + callbacks: { + onSignature: (signature: ethers.utils.BytesLike) => void; + onRejection: (error: string) => void; + onStatus: (situation: string) => void + } + ): Promise { + if (metadata.tag === 'test1') { + expect(id).to.be.equal('test-0') + } + if (metadata.tag === 'test2') { + expect(id).to.be.equal('test-1') + } + if (metadata.tag === 'test3') { + expect(id).to.be.equal('test-2') + } + callbacks.onSignature('0x5678') + return true + }, + notifyStatusChange: function (id: string, status: Status): void {}, + suffix: function () { + return [ 2 ] + } + } + + const orchestrator = new Orchestrator([signer], 'test') + const res1 = await orchestrator.signMessage({ message: ogMessage, metadata: { tag: 'test1' }}) + const res2 = await orchestrator.signMessage({ message: ogMessage, metadata: { tag: 'test2' }}) + const res3 = await orchestrator.signMessage({ message: ogMessage, metadata: { tag: 'test3' }}) + + expect((res1.signers['0x1234'] as any).signature).to.be.equal('0x5678') + expect((res2.signers['0x1234'] as any).signature).to.be.equal('0x5678') + expect((res3.signers['0x1234'] as any).signature).to.be.equal('0x5678') + }) + + it('Should auto-generate random tag', () => { + const orchestrator1 = new Orchestrator([]) + const orchestrator2 = new Orchestrator([]) + + expect(orchestrator1.tag).to.not.be.equal(orchestrator2.tag) + }) + + it('Should only sign with candidates', async () => { + const message = ethers.utils.randomBytes(99) + + const signer1 = ethers.Wallet.createRandom() + const signer2 = ethers.Wallet.createRandom() + const signer3 = ethers.Wallet.createRandom() + const signer4 = ethers.Wallet.createRandom() + + const orchestrator = new Orchestrator([signer1, signer2, signer3, signer4]) + + const result = await orchestrator.signMessage({ + message: message, + candidates: [signer1.address, signer3.address] + }) + + expect(result.signers[signer1.address]).to.not.be.undefined + expect(result.signers[signer2.address]).to.be.undefined + expect(result.signers[signer3.address]).to.not.be.undefined + expect(result.signers[signer4.address]).to.be.undefined + }) +}) diff --git a/packages/simulator/package.json b/packages/simulator/package.json index 178d6f842..123e29ff4 100644 --- a/packages/simulator/package.json +++ b/packages/simulator/package.json @@ -17,13 +17,15 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@0xsequence/transactions": "^0.43.34", - "@0xsequence/wallet-contracts": "1.10.0" + "@0xsequence/core": "workspace:*", + "@0xsequence/wallet-contracts": "^1.10.0" }, "peerDependencies": { "ethers": ">=5.5 < 6" }, "devDependencies": { + "@0xsequence/signhub": "workspace:*", + "@0xsequence/tests": "workspace:*", "ethers": "^5.7.2" }, "files": [ diff --git a/packages/simulator/src/simulate.ts b/packages/simulator/src/simulate.ts index bbb3df584..e27ba5415 100644 --- a/packages/simulator/src/simulate.ts +++ b/packages/simulator/src/simulate.ts @@ -1,6 +1,6 @@ import { BigNumber, providers, utils } from 'ethers' -import { sequenceTxAbiEncode, Transaction } from '@0xsequence/transactions' import { gethCall } from './geth-call' +import { commons } from '@0xsequence/core' const simulatorArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/modules/MainModuleGasEstimation.sol/MainModuleGasEstimation.json') const simulatorInterface = new utils.Interface(simulatorArtifact.abi) @@ -9,10 +9,10 @@ const simulatorBytecode = simulatorArtifact.deployedBytecode export async function simulate( provider: providers.JsonRpcProvider, wallet: string, - transactions: Transaction[], + transactions: commons.transaction.Transaction[], block?: providers.BlockTag ): Promise { - const encodedTransactions = sequenceTxAbiEncode(transactions) + const encodedTransactions = commons.transaction.sequenceTxAbiEncode(transactions) const data = simulatorInterface.encodeFunctionData('simulateExecute', [encodedTransactions]) const transaction = { to: wallet, data } const overrides = { [wallet]: { code: simulatorBytecode } } diff --git a/packages/simulator/tests/sequence-simulator.spec.ts b/packages/simulator/tests/sequence-simulator.spec.ts index d44a24300..f61d16b17 100644 --- a/packages/simulator/tests/sequence-simulator.spec.ts +++ b/packages/simulator/tests/sequence-simulator.spec.ts @@ -1,105 +1,53 @@ import { CallReceiverMock, HookCallerMock } from '@0xsequence/wallet-contracts' - -import { Transaction } from '@0xsequence/transactions' - import { LocalRelayer } from '@0xsequence/relayer' - -import { WalletContext, NetworkConfig } from '@0xsequence/network' -import { ethers, Signer as AbstractSigner, providers } from 'ethers' - +import { ethers } from 'ethers' import { configureLogger } from '@0xsequence/utils' import chaiAsPromised from 'chai-as-promised' import * as chai from 'chai' const CallReceiverMockArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/mocks/CallReceiverMock.sol/CallReceiverMock.json') -const HookCallerMockArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/mocks/HookCallerMock.sol/HookCallerMock.json') const { expect } = chai.use(chaiAsPromised) configureLogger({ logLevel: 'DEBUG', silence: false }) -import { Wallet } from '@0xsequence/wallet' -import { deployWalletContext } from '@0xsequence/wallet/tests/utils/deploy-wallet-context' +import { SequenceOrchestratorWrapper, Wallet, WalletV2 } from '@0xsequence/wallet' import { simulate } from '../src' import { encodeData } from '@0xsequence/wallet/tests/utils' +import { context } from '@0xsequence/tests' +import { commons, v2 } from '@0xsequence/core' +import { Orchestrator } from '@0xsequence/signhub' -type EthereumInstance = { - chainId: number - provider: providers.JsonRpcProvider - signer: AbstractSigner -} describe('Wallet integration', function () { - let ethnode: EthereumInstance + let contexts: Awaited> + let provider: ethers.providers.JsonRpcProvider + let signers: ethers.Signer[] let relayer: LocalRelayer let callReceiver: CallReceiverMock - let hookCaller: HookCallerMock - - let context: WalletContext - let networks: NetworkConfig[] before(async () => { - // Provider from hardhat without a server instance const url = 'http://127.0.0.1:10045/' - const provider = new providers.JsonRpcProvider(url) - - ethnode = { - chainId: (await provider.getNetwork()).chainId, - provider: provider, - signer: provider.getSigner() - } + provider = new ethers.providers.JsonRpcProvider(url) - networks = [ - { - name: 'local', - chainId: ethnode.chainId, - provider: ethnode.provider, - isDefaultChain: true, - isAuthChain: true - } - ] + signers = new Array(8).fill(0).map((_, i) => provider.getSigner(i)) - // Deploy Sequence env - const [factory, mainModule, mainModuleUpgradable, guestModule, sequenceUtils, requireFreshSigner] = await deployWalletContext( - ethnode.signer - ) - - // Create fixed context obj - context = { - factory: factory.address, - mainModule: mainModule.address, - mainModuleUpgradable: mainModuleUpgradable.address, - guestModule: guestModule.address, - sequenceUtils: sequenceUtils.address, - libs: { - requireFreshSigner: requireFreshSigner.address - } - } + contexts = await context.deploySequenceContexts(signers[0]) + relayer = new LocalRelayer(signers[0]) // Deploy call receiver mock callReceiver = (await new ethers.ContractFactory( CallReceiverMockArtifact.abi, CallReceiverMockArtifact.bytecode, - ethnode.signer - ).deploy()) as CallReceiverMock - - // Deploy hook caller mock - hookCaller = (await new ethers.ContractFactory( - HookCallerMockArtifact.abi, - HookCallerMockArtifact.bytecode, - ethnode.signer - ).deploy()) as HookCallerMock + signers[0] + ).deploy({ gasLimit: 1000000 })) as CallReceiverMock // Deploy local relayer - relayer = new LocalRelayer({ signer: ethnode.signer }) + relayer = new LocalRelayer({ signer: signers[0] }) }) - function sleep(ms: number) { - return new Promise(resolve => setTimeout(resolve, ms)) - } - beforeEach(async () => { await callReceiver.setRevertFlag(false) await callReceiver.testCall(0, []) @@ -110,23 +58,46 @@ describe('Wallet integration', function () { { name: 'single signer wallet', getWallet: async () => { - const pk = ethers.utils.randomBytes(32) - const wallet = await Wallet.singleOwner(pk, context) - return wallet.connect(ethnode.provider, relayer) + // const pk = ethers.utils.randomBytes(32) + // const wallet = await Wallet.singleOwner(pk, context) + // return wallet.connect(ethnode.provider, relayer) + const signer = ethers.Wallet.createRandom() + const config = v2.config.ConfigCoder.fromSimple({ + threshold: 1, + checkpoint: 0, + signers: [{ weight: 1, address: signer.address }] + }) + + return Wallet.newWallet({ + context: contexts[2], + coders: v2.coders, + config, + provider, + relayer, + orchestrator: new Orchestrator([signer]), + chainId: provider.network.chainId + }) } }, { name: 'multiple signers wallet', getWallet: async () => { const signers = new Array(4).fill(0).map(() => ethers.Wallet.createRandom()) - - const config = { + const config = v2.config.ConfigCoder.fromSimple({ threshold: 3, + checkpoint: 0, signers: signers.map(s => ({ weight: 1, address: s.address })) - } + }) - const wallet = new Wallet({ context, config }, ...signers.slice(0, 3)) - return wallet.connect(ethnode.provider, relayer) + return Wallet.newWallet({ + context: contexts[2], + coders: v2.coders, + config, + provider, + relayer, + orchestrator: new Orchestrator(signers.slice(0, 3)), + chainId: provider.network.chainId + }) } }, { @@ -134,45 +105,73 @@ describe('Wallet integration', function () { getWallet: async () => { const signers = new Array(111).fill(0).map(() => ethers.Wallet.createRandom()) - const config = { - threshold: 11, + const config = v2.config.ConfigCoder.fromSimple({ + threshold: 77, + checkpoint: 0, signers: signers.map(s => ({ weight: 1, address: s.address })) - } + }) - const wallet = new Wallet({ context, config }, ...signers.slice(0, 12)) - return wallet.connect(ethnode.provider, relayer) + return Wallet.newWallet({ + context: contexts[2], + coders: v2.coders, + config, + provider, + relayer, + orchestrator: new Orchestrator(signers.slice(0, 77)), + chainId: provider.network.chainId + }) } }, { name: 'nested wallet', getWallet: async () => { - const EOAsigners = new Array(2).fill(0).map(() => ethers.Wallet.createRandom()) - - const NestedSigners = await Promise.all( + const EOASigners = new Array(3).fill(0).map(() => ethers.Wallet.createRandom()) + const nestedSigners = await Promise.all( new Array(2).fill(0).map(async () => { const signers = new Array(3).fill(0).map(() => ethers.Wallet.createRandom()) - const config = { + const config = v2.config.ConfigCoder.fromSimple({ threshold: 2, + checkpoint: 0, signers: signers.map(s => ({ weight: 1, address: s.address })) - } - const wallet = new Wallet({ context: context, config: config }, ...signers.slice(0, 2)).connect( - ethnode.provider, - relayer - ) - await relayer.deployWallet(wallet.config, wallet.context) - return wallet.connect(ethnode.provider, relayer) + }) + + const wallet = Wallet.newWallet({ + context: contexts[2], + coders: v2.coders, + config, + provider, + relayer, + orchestrator: new Orchestrator(signers.slice(0, 2)), + chainId: provider.network.chainId + }) + + await wallet.deploy() + + return wallet }) ) - const signers = [...NestedSigners, ...EOAsigners] - - const config = { - threshold: 3, - signers: signers.map(s => ({ weight: 1, address: s.address })) - } + const config = v2.config.ConfigCoder.fromSimple({ + threshold: 2, + checkpoint: 0, + signers: [ + ...EOASigners.map(s => ({ weight: 1, address: s.address })), + ...nestedSigners.map(s => ({ weight: 1, address: s.address })) + ] + }) - const wallet = new Wallet({ context, config }, ...signers) - return wallet.connect(ethnode.provider, relayer) + return Wallet.newWallet({ + context: contexts[2], + coders: v2.coders, + config, + provider, + relayer, + orchestrator: new Orchestrator([ + ...EOASigners.slice(0, 2), + ...nestedSigners.slice(0, 1).map((s) => new SequenceOrchestratorWrapper(s)) + ]), + chainId: provider.network.chainId + }) } }, { @@ -181,33 +180,42 @@ describe('Wallet integration', function () { const signersA = new Array(5).fill(0).map(() => ethers.Wallet.createRandom()) const signersB = new Array(6).fill(0).map(() => ethers.Wallet.createRandom()) - const signers = [...signersA, ...signersB] - - const config = { + const config = v2.config.ConfigCoder.fromSimple({ threshold: 5, - signers: signers.map((s, i) => ({ weight: i <= signersA.length ? 1 : 10, address: s.address })) - } + checkpoint: 0, + signers: [ + ...signersA.map(s => ({ weight: 1, address: s.address })), + ...signersB.map(s => ({ weight: 10, address: s.address })) + ] + }) - const wallet = new Wallet({ context, config }, ...signersA) - return wallet.connect(ethnode.provider, relayer) + return Wallet.newWallet({ + context: contexts[2], + coders: v2.coders, + config, + provider, + relayer, + orchestrator: new Orchestrator(signersA), + chainId: provider.network.chainId + }) } } ] options.map(o => { describe(`with ${o.name}`, () => { - let wallet: Wallet + let wallet: WalletV2 beforeEach(async () => { wallet = await o.getWallet() }) describe('with deployed wallet', () => { - let txs: Transaction[] + let txs: commons.transaction.Transaction[] beforeEach(async () => { await callReceiver.testCall(0, []) - await relayer.deployWallet(wallet.config, wallet.context) + await wallet.deploy() }) describe('a single transaction', () => { @@ -219,14 +227,13 @@ describe('Wallet integration', function () { gasLimit: 0, to: callReceiver.address, value: ethers.constants.Zero, - data: await encodeData(callReceiver, 'testCall', 14442, '0x112233'), - nonce: 0 + data: await encodeData(callReceiver, 'testCall', 14442, '0x112233') } ] }) it('should use estimated gas for a single transaction', async () => { - const results = await simulate(ethnode.provider, wallet.address, txs) + const results = await simulate(provider, wallet.address, txs) expect(results).to.have.lengthOf(txs.length) expect(results.every(result => result.executed)).to.be.true @@ -237,7 +244,7 @@ describe('Wallet integration', function () { it('should use estimated gas for a single failing transaction', async () => { await callReceiver.setRevertFlag(true) - const results = await simulate(ethnode.provider, wallet.address, txs) + const results = await simulate(provider, wallet.address, txs) expect(results).to.have.lengthOf(txs.length) expect(results.every(result => result.executed)).to.be.true @@ -260,8 +267,7 @@ describe('Wallet integration', function () { gasLimit: 0, to: callReceiver.address, value: ethers.constants.Zero, - data: await encodeData(callReceiver, 'setRevertFlag', true), - nonce: 0 + data: await encodeData(callReceiver, 'setRevertFlag', true) }, { delegateCall: false, @@ -269,14 +275,13 @@ describe('Wallet integration', function () { gasLimit: 0, to: callReceiver.address, value: ethers.constants.Zero, - data: await encodeData(callReceiver, 'testCall', 2, valB), - nonce: 0 + data: await encodeData(callReceiver, 'testCall', 2, valB) } ] }) it('should use estimated gas for a batch of transactions', async () => { - const results = await simulate(ethnode.provider, wallet.address, txs) + const results = await simulate(provider, wallet.address, txs) expect(results).to.have.lengthOf(txs.length) expect(results[0].executed).to.be.true diff --git a/packages/tests/package.json b/packages/tests/package.json new file mode 100644 index 000000000..a665b2b4c --- /dev/null +++ b/packages/tests/package.json @@ -0,0 +1,29 @@ +{ + "name": "@0xsequence/tests", + "version": "0.43.7", + "description": "test tools for sequence.js", + "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/tests", + "source": "src/index.ts", + "main": "dist/0xsequence-tests.cjs.js", + "module": "dist/0xsequence-tests.esm.js", + "author": "Horizon Blockchain Games", + "license": "Apache-2.0", + "scripts": { + "test": "echo 'no tests for test tools'" + }, + "dependencies": { + "@0xsequence/core": "workspace:*" + }, + "peerDependencies": { + "ethers": ">=5.5" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "ethers": "^5.7.2", + "web3": "^1.8.1" + }, + "files": [ + "src", + "dist" + ] +} diff --git a/packages/tests/src/builds/artifact.ts b/packages/tests/src/builds/artifact.ts new file mode 100644 index 000000000..734dd7c32 --- /dev/null +++ b/packages/tests/src/builds/artifact.ts @@ -0,0 +1,9 @@ +import { ethers } from "ethers" + +export type Artifact = { + contractName: string; + sourceName: string; + abi: ethers.ContractInterface; + bytecode: string; + deployedBytecode: string; +} diff --git a/packages/tests/src/builds/index.ts b/packages/tests/src/builds/index.ts new file mode 100644 index 000000000..2412753bb --- /dev/null +++ b/packages/tests/src/builds/index.ts @@ -0,0 +1,5 @@ + +export * as v1 from './v1' +export * as v2 from './v2' + +export * from './artifact' diff --git a/packages/tests/src/builds/v1/artifacts/Factory.ts b/packages/tests/src/builds/v1/artifacts/Factory.ts new file mode 100644 index 000000000..71c0283d1 --- /dev/null +++ b/packages/tests/src/builds/v1/artifacts/Factory.ts @@ -0,0 +1,35 @@ +export const factory = { + "_format": "hh-sol-artifact-1", + "contractName": "Factory", + "sourceName": "contracts/Factory.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_mainModule", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_salt", + "type": "bytes32" + } + ], + "name": "deploy", + "outputs": [ + { + "internalType": "address", + "name": "_contract", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b506101c8806100206000396000f3fe60806040526004361061001e5760003560e01c806332c02a1414610023575b600080fd5b61005c6004803603604081101561003957600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135169060200135610085565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b60008060405180606001604052806028815260200161016b602891398473ffffffffffffffffffffffffffffffffffffffff166040516020018083805190602001908083835b6020831061010857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016100cb565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0180199092169116179052920193845250604080518085038152938201905282519294508693508401905034f594935050505056fe603a600e3d39601a805130553df3363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3a26469706673582212209b0bce93afab3297b9ebf4e58fa642ef123d74bcbd3bdb4e48b662eb12b430ca64736f6c63430007060033", + "deployedBytecode": "0x60806040526004361061001e5760003560e01c806332c02a1414610023575b600080fd5b61005c6004803603604081101561003957600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135169060200135610085565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b60008060405180606001604052806028815260200161016b602891398473ffffffffffffffffffffffffffffffffffffffff166040516020018083805190602001908083835b6020831061010857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016100cb565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0180199092169116179052920193845250604080518085038152938201905282519294508693508401905034f594935050505056fe603a600e3d39601a805130553df3363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3a26469706673582212209b0bce93afab3297b9ebf4e58fa642ef123d74bcbd3bdb4e48b662eb12b430ca64736f6c63430007060033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/packages/tests/src/builds/v1/artifacts/GuestModule.ts b/packages/tests/src/builds/v1/artifacts/GuestModule.ts new file mode 100644 index 000000000..0ebccb59b --- /dev/null +++ b/packages/tests/src/builds/v1/artifacts/GuestModule.ts @@ -0,0 +1,293 @@ +export const guestModule = { + "_format": "hh-sol-artifact-1", + "contractName": "GuestModule", + "sourceName": "contracts/modules/GuestModule.sol", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_contract", + "type": "address" + } + ], + "name": "CreatedContract", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_space", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_newNonce", + "type": "uint256" + } + ], + "name": "NonceChange", + "type": "event" + }, + { + "anonymous": true, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "_tx", + "type": "bytes32" + } + ], + "name": "TxExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "_tx", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "_reason", + "type": "bytes" + } + ], + "name": "TxFailed", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_code", + "type": "bytes" + } + ], + "name": "createContract", + "outputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "delegateCall", + "type": "bool" + }, + { + "internalType": "bool", + "name": "revertOnError", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct IModuleCalls.Transaction[]", + "name": "_txs", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "execute", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_signatures", + "type": "bytes" + } + ], + "name": "isValidSignature", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_signatures", + "type": "bytes" + } + ], + "name": "isValidSignature", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_space", + "type": "uint256" + } + ], + "name": "readNonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "delegateCall", + "type": "bool" + }, + { + "internalType": "bool", + "name": "revertOnError", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct IModuleCalls.Transaction[]", + "name": "_txs", + "type": "tuple[]" + } + ], + "name": "selfExecute", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_interfaceID", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b50611ddc806100206000396000f3fe60806040526004361061007b5760003560e01c80637a9a16281161004e5780637a9a1628146101255780638c3f55631461014557806390042baf14610172578063affed0e0146101925761007b565b806301ffc9a7146100805780631626ba7e146100b657806320c13b0b146100e357806361c2926c14610103575b600080fd5b34801561008c57600080fd5b506100a061009b366004611677565b6101a7565b6040516100ad91906118be565b60405180910390f35b3480156100c257600080fd5b506100d66100d136600461162d565b6101ba565b6040516100ad91906118eb565b3480156100ef57600080fd5b506100d66100fe3660046116b7565b610233565b34801561010f57600080fd5b5061012361011e366004611590565b61028d565b005b34801561013157600080fd5b506101236101403660046115c3565b6102ce565b34801561015157600080fd5b50610165610160366004611753565b6102f6565b6040516100ad91906118c9565b610185610180366004611720565b610322565b6040516100ad919061189d565b34801561019e57600080fd5b506101656103d6565b60006101b2826103e7565b90505b919050565b60006102046101c885610444565b84848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506104a492505050565b1561022c57507f1626ba7e000000000000000000000000000000000000000000000000000000005b9392505050565b600061025d6101c88686604051808383808284376040519201829003909120935061044492505050565b1561028557507f20c13b0b000000000000000000000000000000000000000000000000000000005b949350505050565b60006102be826040516020016102a39190611a19565b60405160208183030381529060405280519060200120610444565b90506102ca818361069c565b5050565b60006102e4846040516020016102a39190611975565b90506102f0818561069c565b50505050565b60006101b27f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e83610817565b600033301461037c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180611d806027913960400191505060405180910390fd5b81516020830134f06040805173ffffffffffffffffffffffffffffffffffffffff8316815290519192507fa506ad4e7f05eceba62a023c3219e5bd98a615f4fa87e2afb08a2da5cf62bf0c919081900360200190a1919050565b60006103e260006102f6565b905090565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f90042baf00000000000000000000000000000000000000000000000000000000141561043b575060016101b5565b6101b282610844565b604080517f19010000000000000000000000000000000000000000000000000000000000006020808301919091524660228301523060601b6042830152605680830194909452825180830390940184526076909101909152815191012090565b60008060006104b2846108a1565b909250905061ffff821660005b855183101561067957600080806104d6898761090f565b975060ff918216945016915060018314156104fe576104f58987610990565b96509050610622565b8261052a57606061050f8a88610a08565b9750905061051d8b82610ab9565b9150828501945050610622565b60028314156105d15761053d8987610990565b96509050600061054d8a88610e43565b975061ffff16905060606105628b8984610eb4565b985090506105718c8483610fa3565b6105c6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526032815260200180611c0b6032913960400191505060405180910390fd5b505092810192610622565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180611b28602c913960400191505060405180910390fd5b848282604051602001808481526020018381526020018273ffffffffffffffffffffffffffffffffffffffff16815260200193505050506040516020818303038152906040528051906020012094505050506104bf565b8361ffff1681101580156106915750610691826111eb565b979650505050505050565b60005b81518110156108125760008282815181106106b657fe5b6020026020010151905060006060826000015115610709576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610700906119bc565b60405180910390fd5b82604001515a1015610747576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161070090611918565b826060015173ffffffffffffffffffffffffffffffffffffffff168360800151846040015160001461077d57846040015161077f565b5a5b908560a001516040516107929190611881565b600060405180830381858888f193505050503d80600081146107d0576040519150601f19603f3d011682016040523d82523d6000602084013e6107d5565b606091505b50909250905081156107fc57856040516107ef91906118c9565b60405180910390a0610807565b6108078387836111f1565b50505060010161069f565b505050565b60408051602080820194909452808201929092528051808303820181526060909201905280519101205490565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f389901c7000000000000000000000000000000000000000000000000000000001415610898575060016101b5565b6101b282611241565b6020810151815160f09190911c9060029081111561090a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180611b776027913960400191505060405180910390fd5b915091565b8082016020015160f881901c9060f01c60ff166002830183811161092f57fe5b8451811115610989576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180611cdb6026913960400191505060405180910390fd5b9250925092565b8082016020015160601c601482018281116109a757fe5b8351811115610a01576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180611b546023913960400191505060405180910390fd5b9250929050565b604080516042808252608082019092526060916000919060208201818036833701905050915082840160200180516020840152602081015160408401526022810151604284015250604283019050828111610a5f57fe5b8351811115610a01576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180611c7c6023913960400191505060405180910390fd5b60008151604214610b15576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a815260200180611aee603a913960400191505060405180910390fd5b600082600184510381518110610b2757fe5b602001015160f81c60f81b60f81c60ff169050600083604081518110610b4957fe5b016020015160f81c90506000610b5f85826112c9565b90506000610b6e8660206112c9565b90507f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0811115610be9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180611ab1603d913960400191505060405180910390fd5b8260ff16601b14158015610c0157508260ff16601c14155b15610c57576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180611b9e603d913960400191505060405180910390fd5b6001841415610ccb5760018784848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015610cba573d6000803e3d6000fd5b505050602060405103519450610dcd565b6002841415610d7c5760018760405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c018281526020019150506040516020818303038152906040528051906020012084848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015610cba573d6000803e3d6000fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180611c9f603c913960400191505060405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8516610e39576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526030815260200180611bdb6030913960400191505060405180910390fd5b5050505092915050565b8082016020015160f01c60028201828111610e5a57fe5b8351811115610a01576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180611d226022913960400191505060405180910390fd5b606060008267ffffffffffffffff81118015610ecf57600080fd5b506040519080825280601f01601f191660200182016040528015610efa576020820181803683370190505b509150838501602001600060205b85811015610f2157908201518482015260208101610f08565b8486016020018051939092015190850152525082820183811015610f4157fe5b8451811115610f9b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180611d016021913960400191505060405180910390fd5b935093915050565b60008082600184510381518110610fb657fe5b016020015160f81c90506001811480610fcf5750600281145b15611013578373ffffffffffffffffffffffffffffffffffffffff16610ff58685610ab9565b73ffffffffffffffffffffffffffffffffffffffff161491506111e3565b60038114156111925782517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018452604080517f1626ba7e000000000000000000000000000000000000000000000000000000008152600481018881526024820192835286516044830152865173ffffffffffffffffffffffffffffffffffffffff891693631626ba7e938b938a9390929160640190602085019080838360005b838110156110cd5781810151838201526020016110b5565b50505050905090810190601f1680156110fa5780820380516001836020036101000a031916815260200191505b50935050505060206040518083038186803b15801561111857600080fd5b505afa15801561112c573d6000803e3d6000fd5b505050506040513d602081101561114257600080fd5b50519084527fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e000000000000000000000000000000000000000000000000000000001491506111e3565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f815260200180611c3d603f913960400191505060405180910390fd5b509392505050565b50600190565b82602001511561120357805160208201fd5b7f3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd782826040516112349291906118d2565b60405180910390a1505050565b60007fffffffff00000000000000000000000000000000000000000000000000000000821615806112b357507fffffffff0000000000000000000000000000000000000000000000000000000082167f36e7817500000000000000000000000000000000000000000000000000000000145b156112c0575060016101b5565b6101b282611331565b60008160200183511015611328576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180611d44603c913960400191505060405180910390fd5b50016020015190565b7fffffffff0000000000000000000000000000000000000000000000000000000081167f01ffc9a70000000000000000000000000000000000000000000000000000000014919050565b803573ffffffffffffffffffffffffffffffffffffffff811681146101b557600080fd5b600082601f8301126113af578081fd5b8135602067ffffffffffffffff808311156113c657fe5b6113d38283850201611a60565b83815282810190868401865b868110156114af578135890160c0807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0838e0301121561141d57898afd5b604080518281018181108a8211171561143257fe5b825261143f848b016114bd565b815261144c8285016114bd565b8a820152606080850135838301526080925061146983860161137b565b9082015260a08481013583830152928401359289841115611488578c8dfd5b6114968f8c8688010161150d565b90820152875250505092850192908501906001016113df565b509098975050505050505050565b803580151581146101b557600080fd5b60008083601f8401126114de578182fd5b50813567ffffffffffffffff8111156114f5578182fd5b602083019150836020828501011115610a0157600080fd5b600082601f83011261151d578081fd5b813567ffffffffffffffff81111561153157fe5b61156260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601611a60565b818152846020838601011115611576578283fd5b816020850160208301379081016020019190915292915050565b6000602082840312156115a1578081fd5b813567ffffffffffffffff8111156115b7578182fd5b6102858482850161139f565b6000806000606084860312156115d7578182fd5b833567ffffffffffffffff808211156115ee578384fd5b6115fa8783880161139f565b9450602086013593506040860135915080821115611616578283fd5b506116238682870161150d565b9150509250925092565b600080600060408486031215611641578283fd5b83359250602084013567ffffffffffffffff81111561165e578283fd5b61166a868287016114cd565b9497909650939450505050565b600060208284031215611688578081fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461022c578182fd5b600080600080604085870312156116cc578081fd5b843567ffffffffffffffff808211156116e3578283fd5b6116ef888389016114cd565b90965094506020870135915080821115611707578283fd5b50611714878288016114cd565b95989497509550505050565b600060208284031215611731578081fd5b813567ffffffffffffffff811115611747578182fd5b6102858482850161150d565b600060208284031215611764578081fd5b5035919050565b60008282518085526020808601955080818302840101818601855b8481101561182a578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00189528151805115158452848101511515858501526040808201519085015260608082015173ffffffffffffffffffffffffffffffffffffffff16908501526080808201519085015260a09081015160c09185018290529061181681860183611837565b9a86019a9450505090830190600101611786565b5090979650505050505050565b6000815180845261184f816020860160208601611a84565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60008251611893818460208701611a84565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b901515815260200190565b90815260200190565b6000838252604060208301526102856040830184611837565b7fffffffff0000000000000000000000000000000000000000000000000000000091909116815260200190565b60208082526029908201527f47756573744d6f64756c65235f6578656375746547756573743a204e4f545f4560408201527f4e4f5547485f4741530000000000000000000000000000000000000000000000606082015260800190565b600060408252600660408301527f67756573743a000000000000000000000000000000000000000000000000000060608301526080602083015261022c608083018461176b565b60208082526033908201527f47756573744d6f64756c65235f6578656375746547756573743a2064656c656760408201527f61746543616c6c206e6f7420616c6c6f77656400000000000000000000000000606082015260800190565b600060408252600560408301527f73656c663a00000000000000000000000000000000000000000000000000000060608301526080602083015261022c608083018461176b565b60405181810167ffffffffffffffff81118282101715611a7c57fe5b604052919050565b60005b83811015611a9f578181015183820152602001611a87565b838111156102f0575050600091015256fe5369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202773272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265206c656e6774684d6f64756c6541757468235f7369676e617475726556616c69646174696f6e20494e56414c49445f464c41474c696242797465732372656164416464726573733a204f55545f4f465f424f554e44534c696242797465732372656164466972737455696e7431363a204f55545f4f465f424f554e44535369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202776272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20494e56414c49445f5349474e45524d6f64756c6541757468235f7369676e617475726556616c69646174696f6e3a20494e56414c49445f5349474e41545552455369676e617475726556616c696461746f7223697356616c69645369676e61747572653a20554e535550504f525445445f5349474e41545552455f545950454c696242797465732372656164427974657336363a204f55545f4f465f424f554e44535369676e617475726556616c696461746f72237265636f7665725369676e65723a20554e535550504f525445445f5349474e41545552455f545950454c69624279746573237265616455696e743855696e74383a204f55545f4f465f424f554e44534c69624279746573237265616442797465733a204f55545f4f465f424f554e44534c69624279746573237265616455696e7431363a204f55545f4f465f424f554e44534c696242797465732372656164427974657333323a20475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f52455155495245444d6f64756c6553656c6641757468236f6e6c7953656c663a204e4f545f415554484f52495a4544a2646970667358221220f5a1de0b650baa2ee828e8766bc6dbd0c74da0cc4735a143852d24f868e4b62464736f6c63430007060033", + "deployedBytecode": "0x60806040526004361061007b5760003560e01c80637a9a16281161004e5780637a9a1628146101255780638c3f55631461014557806390042baf14610172578063affed0e0146101925761007b565b806301ffc9a7146100805780631626ba7e146100b657806320c13b0b146100e357806361c2926c14610103575b600080fd5b34801561008c57600080fd5b506100a061009b366004611677565b6101a7565b6040516100ad91906118be565b60405180910390f35b3480156100c257600080fd5b506100d66100d136600461162d565b6101ba565b6040516100ad91906118eb565b3480156100ef57600080fd5b506100d66100fe3660046116b7565b610233565b34801561010f57600080fd5b5061012361011e366004611590565b61028d565b005b34801561013157600080fd5b506101236101403660046115c3565b6102ce565b34801561015157600080fd5b50610165610160366004611753565b6102f6565b6040516100ad91906118c9565b610185610180366004611720565b610322565b6040516100ad919061189d565b34801561019e57600080fd5b506101656103d6565b60006101b2826103e7565b90505b919050565b60006102046101c885610444565b84848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506104a492505050565b1561022c57507f1626ba7e000000000000000000000000000000000000000000000000000000005b9392505050565b600061025d6101c88686604051808383808284376040519201829003909120935061044492505050565b1561028557507f20c13b0b000000000000000000000000000000000000000000000000000000005b949350505050565b60006102be826040516020016102a39190611a19565b60405160208183030381529060405280519060200120610444565b90506102ca818361069c565b5050565b60006102e4846040516020016102a39190611975565b90506102f0818561069c565b50505050565b60006101b27f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e83610817565b600033301461037c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180611d806027913960400191505060405180910390fd5b81516020830134f06040805173ffffffffffffffffffffffffffffffffffffffff8316815290519192507fa506ad4e7f05eceba62a023c3219e5bd98a615f4fa87e2afb08a2da5cf62bf0c919081900360200190a1919050565b60006103e260006102f6565b905090565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f90042baf00000000000000000000000000000000000000000000000000000000141561043b575060016101b5565b6101b282610844565b604080517f19010000000000000000000000000000000000000000000000000000000000006020808301919091524660228301523060601b6042830152605680830194909452825180830390940184526076909101909152815191012090565b60008060006104b2846108a1565b909250905061ffff821660005b855183101561067957600080806104d6898761090f565b975060ff918216945016915060018314156104fe576104f58987610990565b96509050610622565b8261052a57606061050f8a88610a08565b9750905061051d8b82610ab9565b9150828501945050610622565b60028314156105d15761053d8987610990565b96509050600061054d8a88610e43565b975061ffff16905060606105628b8984610eb4565b985090506105718c8483610fa3565b6105c6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526032815260200180611c0b6032913960400191505060405180910390fd5b505092810192610622565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180611b28602c913960400191505060405180910390fd5b848282604051602001808481526020018381526020018273ffffffffffffffffffffffffffffffffffffffff16815260200193505050506040516020818303038152906040528051906020012094505050506104bf565b8361ffff1681101580156106915750610691826111eb565b979650505050505050565b60005b81518110156108125760008282815181106106b657fe5b6020026020010151905060006060826000015115610709576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610700906119bc565b60405180910390fd5b82604001515a1015610747576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161070090611918565b826060015173ffffffffffffffffffffffffffffffffffffffff168360800151846040015160001461077d57846040015161077f565b5a5b908560a001516040516107929190611881565b600060405180830381858888f193505050503d80600081146107d0576040519150601f19603f3d011682016040523d82523d6000602084013e6107d5565b606091505b50909250905081156107fc57856040516107ef91906118c9565b60405180910390a0610807565b6108078387836111f1565b50505060010161069f565b505050565b60408051602080820194909452808201929092528051808303820181526060909201905280519101205490565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f389901c7000000000000000000000000000000000000000000000000000000001415610898575060016101b5565b6101b282611241565b6020810151815160f09190911c9060029081111561090a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180611b776027913960400191505060405180910390fd5b915091565b8082016020015160f881901c9060f01c60ff166002830183811161092f57fe5b8451811115610989576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180611cdb6026913960400191505060405180910390fd5b9250925092565b8082016020015160601c601482018281116109a757fe5b8351811115610a01576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180611b546023913960400191505060405180910390fd5b9250929050565b604080516042808252608082019092526060916000919060208201818036833701905050915082840160200180516020840152602081015160408401526022810151604284015250604283019050828111610a5f57fe5b8351811115610a01576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180611c7c6023913960400191505060405180910390fd5b60008151604214610b15576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a815260200180611aee603a913960400191505060405180910390fd5b600082600184510381518110610b2757fe5b602001015160f81c60f81b60f81c60ff169050600083604081518110610b4957fe5b016020015160f81c90506000610b5f85826112c9565b90506000610b6e8660206112c9565b90507f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0811115610be9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180611ab1603d913960400191505060405180910390fd5b8260ff16601b14158015610c0157508260ff16601c14155b15610c57576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180611b9e603d913960400191505060405180910390fd5b6001841415610ccb5760018784848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015610cba573d6000803e3d6000fd5b505050602060405103519450610dcd565b6002841415610d7c5760018760405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c018281526020019150506040516020818303038152906040528051906020012084848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015610cba573d6000803e3d6000fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180611c9f603c913960400191505060405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8516610e39576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526030815260200180611bdb6030913960400191505060405180910390fd5b5050505092915050565b8082016020015160f01c60028201828111610e5a57fe5b8351811115610a01576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180611d226022913960400191505060405180910390fd5b606060008267ffffffffffffffff81118015610ecf57600080fd5b506040519080825280601f01601f191660200182016040528015610efa576020820181803683370190505b509150838501602001600060205b85811015610f2157908201518482015260208101610f08565b8486016020018051939092015190850152525082820183811015610f4157fe5b8451811115610f9b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180611d016021913960400191505060405180910390fd5b935093915050565b60008082600184510381518110610fb657fe5b016020015160f81c90506001811480610fcf5750600281145b15611013578373ffffffffffffffffffffffffffffffffffffffff16610ff58685610ab9565b73ffffffffffffffffffffffffffffffffffffffff161491506111e3565b60038114156111925782517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018452604080517f1626ba7e000000000000000000000000000000000000000000000000000000008152600481018881526024820192835286516044830152865173ffffffffffffffffffffffffffffffffffffffff891693631626ba7e938b938a9390929160640190602085019080838360005b838110156110cd5781810151838201526020016110b5565b50505050905090810190601f1680156110fa5780820380516001836020036101000a031916815260200191505b50935050505060206040518083038186803b15801561111857600080fd5b505afa15801561112c573d6000803e3d6000fd5b505050506040513d602081101561114257600080fd5b50519084527fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e000000000000000000000000000000000000000000000000000000001491506111e3565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f815260200180611c3d603f913960400191505060405180910390fd5b509392505050565b50600190565b82602001511561120357805160208201fd5b7f3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd782826040516112349291906118d2565b60405180910390a1505050565b60007fffffffff00000000000000000000000000000000000000000000000000000000821615806112b357507fffffffff0000000000000000000000000000000000000000000000000000000082167f36e7817500000000000000000000000000000000000000000000000000000000145b156112c0575060016101b5565b6101b282611331565b60008160200183511015611328576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180611d44603c913960400191505060405180910390fd5b50016020015190565b7fffffffff0000000000000000000000000000000000000000000000000000000081167f01ffc9a70000000000000000000000000000000000000000000000000000000014919050565b803573ffffffffffffffffffffffffffffffffffffffff811681146101b557600080fd5b600082601f8301126113af578081fd5b8135602067ffffffffffffffff808311156113c657fe5b6113d38283850201611a60565b83815282810190868401865b868110156114af578135890160c0807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0838e0301121561141d57898afd5b604080518281018181108a8211171561143257fe5b825261143f848b016114bd565b815261144c8285016114bd565b8a820152606080850135838301526080925061146983860161137b565b9082015260a08481013583830152928401359289841115611488578c8dfd5b6114968f8c8688010161150d565b90820152875250505092850192908501906001016113df565b509098975050505050505050565b803580151581146101b557600080fd5b60008083601f8401126114de578182fd5b50813567ffffffffffffffff8111156114f5578182fd5b602083019150836020828501011115610a0157600080fd5b600082601f83011261151d578081fd5b813567ffffffffffffffff81111561153157fe5b61156260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601611a60565b818152846020838601011115611576578283fd5b816020850160208301379081016020019190915292915050565b6000602082840312156115a1578081fd5b813567ffffffffffffffff8111156115b7578182fd5b6102858482850161139f565b6000806000606084860312156115d7578182fd5b833567ffffffffffffffff808211156115ee578384fd5b6115fa8783880161139f565b9450602086013593506040860135915080821115611616578283fd5b506116238682870161150d565b9150509250925092565b600080600060408486031215611641578283fd5b83359250602084013567ffffffffffffffff81111561165e578283fd5b61166a868287016114cd565b9497909650939450505050565b600060208284031215611688578081fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461022c578182fd5b600080600080604085870312156116cc578081fd5b843567ffffffffffffffff808211156116e3578283fd5b6116ef888389016114cd565b90965094506020870135915080821115611707578283fd5b50611714878288016114cd565b95989497509550505050565b600060208284031215611731578081fd5b813567ffffffffffffffff811115611747578182fd5b6102858482850161150d565b600060208284031215611764578081fd5b5035919050565b60008282518085526020808601955080818302840101818601855b8481101561182a578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00189528151805115158452848101511515858501526040808201519085015260608082015173ffffffffffffffffffffffffffffffffffffffff16908501526080808201519085015260a09081015160c09185018290529061181681860183611837565b9a86019a9450505090830190600101611786565b5090979650505050505050565b6000815180845261184f816020860160208601611a84565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60008251611893818460208701611a84565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b901515815260200190565b90815260200190565b6000838252604060208301526102856040830184611837565b7fffffffff0000000000000000000000000000000000000000000000000000000091909116815260200190565b60208082526029908201527f47756573744d6f64756c65235f6578656375746547756573743a204e4f545f4560408201527f4e4f5547485f4741530000000000000000000000000000000000000000000000606082015260800190565b600060408252600660408301527f67756573743a000000000000000000000000000000000000000000000000000060608301526080602083015261022c608083018461176b565b60208082526033908201527f47756573744d6f64756c65235f6578656375746547756573743a2064656c656760408201527f61746543616c6c206e6f7420616c6c6f77656400000000000000000000000000606082015260800190565b600060408252600560408301527f73656c663a00000000000000000000000000000000000000000000000000000060608301526080602083015261022c608083018461176b565b60405181810167ffffffffffffffff81118282101715611a7c57fe5b604052919050565b60005b83811015611a9f578181015183820152602001611a87565b838111156102f0575050600091015256fe5369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202773272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265206c656e6774684d6f64756c6541757468235f7369676e617475726556616c69646174696f6e20494e56414c49445f464c41474c696242797465732372656164416464726573733a204f55545f4f465f424f554e44534c696242797465732372656164466972737455696e7431363a204f55545f4f465f424f554e44535369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202776272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20494e56414c49445f5349474e45524d6f64756c6541757468235f7369676e617475726556616c69646174696f6e3a20494e56414c49445f5349474e41545552455369676e617475726556616c696461746f7223697356616c69645369676e61747572653a20554e535550504f525445445f5349474e41545552455f545950454c696242797465732372656164427974657336363a204f55545f4f465f424f554e44535369676e617475726556616c696461746f72237265636f7665725369676e65723a20554e535550504f525445445f5349474e41545552455f545950454c69624279746573237265616455696e743855696e74383a204f55545f4f465f424f554e44534c69624279746573237265616442797465733a204f55545f4f465f424f554e44534c69624279746573237265616455696e7431363a204f55545f4f465f424f554e44534c696242797465732372656164427974657333323a20475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f52455155495245444d6f64756c6553656c6641757468236f6e6c7953656c663a204e4f545f415554484f52495a4544a2646970667358221220f5a1de0b650baa2ee828e8766bc6dbd0c74da0cc4735a143852d24f868e4b62464736f6c63430007060033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/packages/tests/src/builds/v1/artifacts/MainModule.ts b/packages/tests/src/builds/v1/artifacts/MainModule.ts new file mode 100644 index 000000000..055fa3af1 --- /dev/null +++ b/packages/tests/src/builds/v1/artifacts/MainModule.ts @@ -0,0 +1,526 @@ +export const mainModule = { + "_format": "hh-sol-artifact-1", + "contractName": "MainModule", + "sourceName": "contracts/modules/MainModule.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_factory", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_contract", + "type": "address" + } + ], + "name": "CreatedContract", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "ImplementationUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_space", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_newNonce", + "type": "uint256" + } + ], + "name": "NonceChange", + "type": "event" + }, + { + "anonymous": true, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "_tx", + "type": "bytes32" + } + ], + "name": "TxExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "_tx", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "_reason", + "type": "bytes" + } + ], + "name": "TxFailed", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "FACTORY", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "INIT_CODE_HASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_signature", + "type": "bytes4" + }, + { + "internalType": "address", + "name": "_implementation", + "type": "address" + } + ], + "name": "addHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_code", + "type": "bytes" + } + ], + "name": "createContract", + "outputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "delegateCall", + "type": "bool" + }, + { + "internalType": "bool", + "name": "revertOnError", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct IModuleCalls.Transaction[]", + "name": "_txs", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "_nonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "execute", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_signatures", + "type": "bytes" + } + ], + "name": "isValidSignature", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_signatures", + "type": "bytes" + } + ], + "name": "isValidSignature", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155BatchReceived", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC721Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_signature", + "type": "bytes4" + } + ], + "name": "readHook", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_space", + "type": "uint256" + } + ], + "name": "readNonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_signature", + "type": "bytes4" + } + ], + "name": "removeHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "delegateCall", + "type": "bool" + }, + { + "internalType": "bool", + "name": "revertOnError", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct IModuleCalls.Transaction[]", + "name": "_txs", + "type": "tuple[]" + } + ], + "name": "selfExecute", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_interfaceID", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_implementation", + "type": "address" + } + ], + "name": "updateImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "bytecode": "0x60c06040523480156200001157600080fd5b5060405162002d6338038062002d638339810160408190526200003491620000e2565b80600060405180606001604052806028815260200162002d3b60289139306001600160a01b03166040516020018083805190602001908083835b602083106200008f5780518252601f1990920191602091820191016200006e565b51815160209384036101000a60001901801990921691161790529201938452506040805180850381529382019052825192019190912060805250505060601b6001600160601b03191660a0525062000112565b600060208284031215620000f4578081fd5b81516001600160a01b03811681146200010b578182fd5b9392505050565b60805160a05160601c612bf862000143600039806106d55280611baa5250806106b15280611bdb5250612bf86000f3fe6080604052600436106101125760003560e01c80634fcf3eca116100a557806390042baf11610074578063b93ea7ad11610059578063b93ea7ad146103c5578063bc197c81146103e5578063f23a6e611461040557610119565b806390042baf1461039d578063affed0e0146103b057610119565b80634fcf3eca1461031d57806361c2926c1461033d5780637a9a16281461035d5780638c3f55631461037d57610119565b80631a9b2337116100e15780631a9b23371461029957806320c13b0b146102c6578063257671f5146102e65780632dd310001461030857610119565b806301ffc9a7146101f4578063025b22bc1461022a578063150b7a021461024c5780631626ba7e1461027957610119565b3661011957005b60006101486000357fffffffff0000000000000000000000000000000000000000000000000000000016610425565b905073ffffffffffffffffffffffffffffffffffffffff8116156101f1576000808273ffffffffffffffffffffffffffffffffffffffff166000366040518083838082843760405192019450600093509091505080830381855af49150503d80600081146101d2576040519150601f19603f3d011682016040523d82523d6000602084013e6101d7565b606091505b5091509150816101e957805160208201fd5b805160208201f35b50005b34801561020057600080fd5b5061021461020f366004612401565b61047b565b6040516102219190612633565b60405180910390f35b34801561023657600080fd5b5061024a610245366004612166565b610486565b005b34801561025857600080fd5b5061026c610267366004612237565b6105a7565b6040516102219190612660565b34801561028557600080fd5b5061026c6102943660046123b7565b6105d1565b3480156102a557600080fd5b506102b96102b4366004612401565b61064a565b6040516102219190612612565b3480156102d257600080fd5b5061026c6102e136600461244d565b610655565b3480156102f257600080fd5b506102fb6106af565b604051610221919061263e565b34801561031457600080fd5b506102b96106d3565b34801561032957600080fd5b5061024a610338366004612401565b6106f7565b34801561034957600080fd5b5061024a61035836600461231a565b6107d5565b34801561036957600080fd5b5061024a61037836600461234d565b61086e565b34801561038957600080fd5b506102fb6103983660046124e9565b6108ea565b6102b96103ab3660046124b6565b610916565b3480156103bc57600080fd5b506102fb6109ca565b3480156103d157600080fd5b5061024a6103e036600461241b565b6109db565b3480156103f157600080fd5b5061026c610400366004612180565b610ab4565b34801561041157600080fd5b5061026c6104203660046122a4565b610ae1565b60006104737fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1207fffffffff000000000000000000000000000000000000000000000000000000008416610b0c565b90505b919050565b600061047382610b39565b3330146104de576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612b9c6027913960400191505060405180910390fd5b6104fd8173ffffffffffffffffffffffffffffffffffffffff16610b96565b610552576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526039815260200180612a826039913960400191505060405180910390fd5b61055b81610b9c565b6040805173ffffffffffffffffffffffffffffffffffffffff8316815290517f310ba5f1d2ed074b51e2eccd052a47ae9ab7c6b800d1fca3db3999d6a592ca039181900360200190a150565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b600061061b6105df85610ba0565b84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610c0092505050565b1561064357507f1626ba7e000000000000000000000000000000000000000000000000000000005b9392505050565b600061047382610425565b600061067f6105df86866040518083838082843760405192018290039091209350610ba092505050565b156106a757507f20c13b0b000000000000000000000000000000000000000000000000000000005b949350505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b33301461074f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612b9c6027913960400191505060405180910390fd5b600061075a82610425565b73ffffffffffffffffffffffffffffffffffffffff1614156107c7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b8152602001806128e0602b913960400191505060405180910390fd5b6107d2816000610df8565b50565b33301461082d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612b9c6027913960400191505060405180910390fd5b600061085e82604051602001610843919061277e565b60405160208183030381529060405280519060200120610ba0565b905061086a8183610e5b565b5050565b6108778261102a565b600061088f83856040516020016108439291906127c5565b905061089b8183610c00565b6108da576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108d190612721565b60405180910390fd5b6108e48185610e5b565b50505050565b60006104737f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e83610b0c565b6000333014610970576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612b9c6027913960400191505060405180910390fd5b81516020830134f06040805173ffffffffffffffffffffffffffffffffffffffff8316815290519192507fa506ad4e7f05eceba62a023c3219e5bd98a615f4fa87e2afb08a2da5cf62bf0c919081900360200190a1919050565b60006109d660006108ea565b905090565b333014610a33576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612b9c6027913960400191505060405180910390fd5b6000610a3e83610425565b73ffffffffffffffffffffffffffffffffffffffff1614610aaa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c8152602001806129f4602c913960400191505060405180910390fd5b61086a8282610df8565b7fbc197c810000000000000000000000000000000000000000000000000000000098975050505050505050565b7ff23a6e61000000000000000000000000000000000000000000000000000000009695505050505050565b60408051602080820194909452808201929092528051808303820181526060909201905280519101205490565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f90042baf000000000000000000000000000000000000000000000000000000001415610b8d57506001610476565b610473826110ce565b3b151590565b3055565b604080517f19010000000000000000000000000000000000000000000000000000000000006020808301919091524660228301523060601b6042830152605680830194909452825180830390940184526076909101909152815191012090565b6000806000610c0e8461120f565b909250905061ffff821660005b8551831015610dd55760008080610c32898761127d565b975060ff91821694501691506001831415610c5a57610c5189876112fe565b96509050610d7e565b82610c86576060610c6b8a88611376565b97509050610c798b82611427565b9150828501945050610d7e565b6002831415610d2d57610c9989876112fe565b965090506000610ca98a886117b1565b975061ffff1690506060610cbe8b8984611822565b98509050610ccd8c8483611911565b610d22576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260328152602001806129c26032913960400191505060405180910390fd5b505092810192610d7e565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c8152602001806128b4602c913960400191505060405180910390fd5b848282604051602001808481526020018381526020018273ffffffffffffffffffffffffffffffffffffffff1681526020019350505050604051602081830303815290604052805190602001209450505050610c1b565b8361ffff168110158015610ded5750610ded82611b59565b979650505050505050565b61086a7fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1207fffffffff00000000000000000000000000000000000000000000000000000000841673ffffffffffffffffffffffffffffffffffffffff8416611c37565b60005b8151811015611025576000828281518110610e7557fe5b602002602001015190506000606082604001515a1015610ec1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108d1906126c4565b825115610f5957826060015173ffffffffffffffffffffffffffffffffffffffff168360400151600014610ef9578360400151610efb565b5a5b8460a00151604051610f0d91906125f6565b6000604051808303818686f4925050503d8060008114610f49576040519150601f19603f3d011682016040523d82523d6000602084013e610f4e565b606091505b509092509050610fee565b826060015173ffffffffffffffffffffffffffffffffffffffff1683608001518460400151600014610f8f578460400151610f91565b5a5b908560a00151604051610fa491906125f6565b600060405180830381858888f193505050503d8060008114610fe2576040519150601f19603f3d011682016040523d82523d6000602084013e610fe7565b606091505b5090925090505b811561100f5785604051611002919061263e565b60405180910390a061101a565b61101a838783611c65565b505050600101610e5e565b505050565b60008061103683611cb5565b915091506000611045836108ea565b9050808214611080576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108d19061268d565b6001820161108e8482611cce565b7f1f180c27086c7a39ea2a7b25239d1ab92348f07ca7bb59d1438fcf527568f88184826040516110bf9291906127de565b60405180910390a15050505050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fec6aba5000000000000000000000000000000000000000000000000000000000148061116157507fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e000000000000000000000000000000000000000000000000000000000145b806111ad57507fffffffff0000000000000000000000000000000000000000000000000000000082167f150b7a0200000000000000000000000000000000000000000000000000000000145b806111f957507fffffffff0000000000000000000000000000000000000000000000000000000082167fc0ee0b8a00000000000000000000000000000000000000000000000000000000145b1561120657506001610476565b61047382611cf9565b6020810151815160f09190911c90600290811115611278576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061292e6027913960400191505060405180910390fd5b915091565b8082016020015160f881901c9060f01c60ff166002830183811161129d57fe5b84518111156112f7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180612af76026913960400191505060405180910390fd5b9250925092565b8082016020015160601c6014820182811161131557fe5b835181111561136f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602381526020018061290b6023913960400191505060405180910390fd5b9250929050565b6040805160428082526080820190925260609160009190602082018180368337019050509150828401602001805160208401526020810151604084015260228101516042840152506042830190508281116113cd57fe5b835181111561136f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180612a5f6023913960400191505060405180910390fd5b60008151604214611483576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a81526020018061287a603a913960400191505060405180910390fd5b60008260018451038151811061149557fe5b602001015160f81c60f81b60f81c60ff1690506000836040815181106114b757fe5b016020015160f81c905060006114cd8582611d56565b905060006114dc866020611d56565b90507f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0811115611557576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d81526020018061283d603d913960400191505060405180910390fd5b8260ff16601b1415801561156f57508260ff16601c14155b156115c5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180612955603d913960400191505060405180910390fd5b60018414156116395760018784848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611628573d6000803e3d6000fd5b50505060206040510351945061173b565b60028414156116ea5760018760405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c018281526020019150506040516020818303038152906040528051906020012084848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611628573d6000803e3d6000fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180612abb603c913960400191505060405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff85166117a7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260308152602001806129926030913960400191505060405180910390fd5b5050505092915050565b8082016020015160f01c600282018281116117c857fe5b835181111561136f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180612b3e6022913960400191505060405180910390fd5b606060008267ffffffffffffffff8111801561183d57600080fd5b506040519080825280601f01601f191660200182016040528015611868576020820181803683370190505b509150838501602001600060205b8581101561188f57908201518482015260208101611876565b84860160200180519390920151908501525250828201838110156118af57fe5b8451811115611909576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180612b1d6021913960400191505060405180910390fd5b935093915050565b6000808260018451038151811061192457fe5b016020015160f81c9050600181148061193d5750600281145b15611981578373ffffffffffffffffffffffffffffffffffffffff166119638685611427565b73ffffffffffffffffffffffffffffffffffffffff16149150611b51565b6003811415611b005782517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018452604080517f1626ba7e000000000000000000000000000000000000000000000000000000008152600481018881526024820192835286516044830152865173ffffffffffffffffffffffffffffffffffffffff891693631626ba7e938b938a9390929160640190602085019080838360005b83811015611a3b578181015183820152602001611a23565b50505050905090810190601f168015611a685780820380516001836020036101000a031916815260200191505b50935050505060206040518083038186803b158015611a8657600080fd5b505afa158015611a9a573d6000803e3d6000fd5b505050506040513d6020811015611ab057600080fd5b50519084527fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e00000000000000000000000000000000000000000000000000000000149150611b51565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f815260200180612a20603f913960400191505060405180910390fd5b509392505050565b604080517fff000000000000000000000000000000000000000000000000000000000000006020808301919091527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000060601b166021830152603582018490527f0000000000000000000000000000000000000000000000000000000000000000605580840191909152835180840390910181526075909201909252805191012073ffffffffffffffffffffffffffffffffffffffff163014919050565b6040805160208082019590955280820193909352805180840382018152606090930190528151919092012055565b826020015115611c7757805160208201fd5b7f3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd78282604051611ca8929190612647565b60405180910390a1505050565b606081901c916bffffffffffffffffffffffff90911690565b61086a7f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e8383611c37565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f025b22bc000000000000000000000000000000000000000000000000000000001415611d4d57506001610476565b61047382611dbe565b60008160200183511015611db5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180612b60603c913960400191505060405180910390fd5b50016020015190565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f389901c7000000000000000000000000000000000000000000000000000000001415611e1257506001610476565b6104738260007fffffffff0000000000000000000000000000000000000000000000000000000082161580611e8857507fffffffff0000000000000000000000000000000000000000000000000000000082167f36e7817500000000000000000000000000000000000000000000000000000000145b15611e9557506001610476565b7f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831614610473565b803573ffffffffffffffffffffffffffffffffffffffff8116811461047657600080fd5b600082601f830112611f13578081fd5b8135602067ffffffffffffffff80831115611f2a57fe5b611f3782838502016127ec565b83815282810190868401865b86811015612013578135890160c0807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0838e03011215611f8157898afd5b604080518281018181108a82111715611f9657fe5b8252611fa3848b01612063565b8152611fb0828501612063565b8a8201526060808501358383015260809250611fcd838601611edf565b9082015260a08481013583830152928401359289841115611fec578c8dfd5b611ffa8f8c868801016120e3565b9082015287525050509285019290850190600101611f43565b509098975050505050505050565b60008083601f840112612032578182fd5b50813567ffffffffffffffff811115612049578182fd5b602083019150836020808302850101111561136f57600080fd5b8035801515811461047657600080fd5b80357fffffffff000000000000000000000000000000000000000000000000000000008116811461047657600080fd5b60008083601f8401126120b4578182fd5b50813567ffffffffffffffff8111156120cb578182fd5b60208301915083602082850101111561136f57600080fd5b600082601f8301126120f3578081fd5b813567ffffffffffffffff81111561210757fe5b61213860207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016127ec565b81815284602083860101111561214c578283fd5b816020850160208301379081016020019190915292915050565b600060208284031215612177578081fd5b61064382611edf565b60008060008060008060008060a0898b03121561219b578384fd5b6121a489611edf565b97506121b260208a01611edf565b9650604089013567ffffffffffffffff808211156121ce578586fd5b6121da8c838d01612021565b909850965060608b01359150808211156121f2578586fd5b6121fe8c838d01612021565b909650945060808b0135915080821115612216578384fd5b506122238b828c016120a3565b999c989b5096995094979396929594505050565b60008060008060006080868803121561224e578081fd5b61225786611edf565b945061226560208701611edf565b935060408601359250606086013567ffffffffffffffff811115612287578182fd5b612293888289016120a3565b969995985093965092949392505050565b60008060008060008060a087890312156122bc578182fd5b6122c587611edf565b95506122d360208801611edf565b94506040870135935060608701359250608087013567ffffffffffffffff8111156122fc578283fd5b61230889828a016120a3565b979a9699509497509295939492505050565b60006020828403121561232b578081fd5b813567ffffffffffffffff811115612341578182fd5b6106a784828501611f03565b600080600060608486031215612361578283fd5b833567ffffffffffffffff80821115612378578485fd5b61238487838801611f03565b94506020860135935060408601359150808211156123a0578283fd5b506123ad868287016120e3565b9150509250925092565b6000806000604084860312156123cb578283fd5b83359250602084013567ffffffffffffffff8111156123e8578283fd5b6123f4868287016120a3565b9497909650939450505050565b600060208284031215612412578081fd5b61064382612073565b6000806040838503121561242d578182fd5b61243683612073565b915061244460208401611edf565b90509250929050565b60008060008060408587031215612462578182fd5b843567ffffffffffffffff80821115612479578384fd5b612485888389016120a3565b9096509450602087013591508082111561249d578384fd5b506124aa878288016120a3565b95989497509550505050565b6000602082840312156124c7578081fd5b813567ffffffffffffffff8111156124dd578182fd5b6106a7848285016120e3565b6000602082840312156124fa578081fd5b5035919050565b6000815180845260208085018081965082840281019150828601855b8581101561259f5782840389528151805115158552858101511515868601526040808201519086015260608082015173ffffffffffffffffffffffffffffffffffffffff16908601526080808201519086015260a09081015160c09186018290529061258b818701836125ac565b9a87019a955050509084019060010161251d565b5091979650505050505050565b600081518084526125c4816020860160208601612810565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60008251612608818460208701612810565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b901515815260200190565b90815260200190565b6000838252604060208301526106a760408301846125ac565b7fffffffff0000000000000000000000000000000000000000000000000000000091909116815260200190565b6020808252601f908201527f4d61696e4d6f64756c65235f617574683a20494e56414c49445f4e4f4e434500604082015260600190565b60208082526024908201527f4d6f64756c6543616c6c73235f657865637574653a204e4f545f454e4f55474860408201527f5f47415300000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526026908201527f4d6f64756c6543616c6c7323657865637574653a20494e56414c49445f53494760408201527f4e41545552450000000000000000000000000000000000000000000000000000606082015260800190565b600060408252600560408301527f73656c663a0000000000000000000000000000000000000000000000000000006060830152608060208301526106436080830184612501565b6000838252604060208301526106a76040830184612501565b918252602082015260400190565b60405181810167ffffffffffffffff8111828210171561280857fe5b604052919050565b60005b8381101561282b578181015183820152602001612813565b838111156108e4575050600091015256fe5369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202773272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265206c656e6774684d6f64756c6541757468235f7369676e617475726556616c69646174696f6e20494e56414c49445f464c41474d6f64756c65486f6f6b732372656d6f7665486f6f6b3a20484f4f4b5f4e4f545f524547495354455245444c696242797465732372656164416464726573733a204f55545f4f465f424f554e44534c696242797465732372656164466972737455696e7431363a204f55545f4f465f424f554e44535369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202776272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20494e56414c49445f5349474e45524d6f64756c6541757468235f7369676e617475726556616c69646174696f6e3a20494e56414c49445f5349474e41545552454d6f64756c65486f6f6b7323616464486f6f6b3a20484f4f4b5f414c52454144595f524547495354455245445369676e617475726556616c696461746f7223697356616c69645369676e61747572653a20554e535550504f525445445f5349474e41545552455f545950454c696242797465732372656164427974657336363a204f55545f4f465f424f554e44534d6f64756c6555706461746523757064617465496d706c656d656e746174696f6e3a20494e56414c49445f494d504c454d454e544154494f4e5369676e617475726556616c696461746f72237265636f7665725369676e65723a20554e535550504f525445445f5349474e41545552455f545950454c69624279746573237265616455696e743855696e74383a204f55545f4f465f424f554e44534c69624279746573237265616442797465733a204f55545f4f465f424f554e44534c69624279746573237265616455696e7431363a204f55545f4f465f424f554e44534c696242797465732372656164427974657333323a20475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f52455155495245444d6f64756c6553656c6641757468236f6e6c7953656c663a204e4f545f415554484f52495a4544a2646970667358221220b34deca9dd75815e4ef8a9279e45750ec5554b22c673e160bdba849d80f5888564736f6c63430007060033603a600e3d39601a805130553df3363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3", + "deployedBytecode": "0x6080604052600436106101125760003560e01c80634fcf3eca116100a557806390042baf11610074578063b93ea7ad11610059578063b93ea7ad146103c5578063bc197c81146103e5578063f23a6e611461040557610119565b806390042baf1461039d578063affed0e0146103b057610119565b80634fcf3eca1461031d57806361c2926c1461033d5780637a9a16281461035d5780638c3f55631461037d57610119565b80631a9b2337116100e15780631a9b23371461029957806320c13b0b146102c6578063257671f5146102e65780632dd310001461030857610119565b806301ffc9a7146101f4578063025b22bc1461022a578063150b7a021461024c5780631626ba7e1461027957610119565b3661011957005b60006101486000357fffffffff0000000000000000000000000000000000000000000000000000000016610425565b905073ffffffffffffffffffffffffffffffffffffffff8116156101f1576000808273ffffffffffffffffffffffffffffffffffffffff166000366040518083838082843760405192019450600093509091505080830381855af49150503d80600081146101d2576040519150601f19603f3d011682016040523d82523d6000602084013e6101d7565b606091505b5091509150816101e957805160208201fd5b805160208201f35b50005b34801561020057600080fd5b5061021461020f366004612401565b61047b565b6040516102219190612633565b60405180910390f35b34801561023657600080fd5b5061024a610245366004612166565b610486565b005b34801561025857600080fd5b5061026c610267366004612237565b6105a7565b6040516102219190612660565b34801561028557600080fd5b5061026c6102943660046123b7565b6105d1565b3480156102a557600080fd5b506102b96102b4366004612401565b61064a565b6040516102219190612612565b3480156102d257600080fd5b5061026c6102e136600461244d565b610655565b3480156102f257600080fd5b506102fb6106af565b604051610221919061263e565b34801561031457600080fd5b506102b96106d3565b34801561032957600080fd5b5061024a610338366004612401565b6106f7565b34801561034957600080fd5b5061024a61035836600461231a565b6107d5565b34801561036957600080fd5b5061024a61037836600461234d565b61086e565b34801561038957600080fd5b506102fb6103983660046124e9565b6108ea565b6102b96103ab3660046124b6565b610916565b3480156103bc57600080fd5b506102fb6109ca565b3480156103d157600080fd5b5061024a6103e036600461241b565b6109db565b3480156103f157600080fd5b5061026c610400366004612180565b610ab4565b34801561041157600080fd5b5061026c6104203660046122a4565b610ae1565b60006104737fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1207fffffffff000000000000000000000000000000000000000000000000000000008416610b0c565b90505b919050565b600061047382610b39565b3330146104de576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612b9c6027913960400191505060405180910390fd5b6104fd8173ffffffffffffffffffffffffffffffffffffffff16610b96565b610552576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526039815260200180612a826039913960400191505060405180910390fd5b61055b81610b9c565b6040805173ffffffffffffffffffffffffffffffffffffffff8316815290517f310ba5f1d2ed074b51e2eccd052a47ae9ab7c6b800d1fca3db3999d6a592ca039181900360200190a150565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b600061061b6105df85610ba0565b84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610c0092505050565b1561064357507f1626ba7e000000000000000000000000000000000000000000000000000000005b9392505050565b600061047382610425565b600061067f6105df86866040518083838082843760405192018290039091209350610ba092505050565b156106a757507f20c13b0b000000000000000000000000000000000000000000000000000000005b949350505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b33301461074f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612b9c6027913960400191505060405180910390fd5b600061075a82610425565b73ffffffffffffffffffffffffffffffffffffffff1614156107c7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b8152602001806128e0602b913960400191505060405180910390fd5b6107d2816000610df8565b50565b33301461082d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612b9c6027913960400191505060405180910390fd5b600061085e82604051602001610843919061277e565b60405160208183030381529060405280519060200120610ba0565b905061086a8183610e5b565b5050565b6108778261102a565b600061088f83856040516020016108439291906127c5565b905061089b8183610c00565b6108da576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108d190612721565b60405180910390fd5b6108e48185610e5b565b50505050565b60006104737f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e83610b0c565b6000333014610970576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612b9c6027913960400191505060405180910390fd5b81516020830134f06040805173ffffffffffffffffffffffffffffffffffffffff8316815290519192507fa506ad4e7f05eceba62a023c3219e5bd98a615f4fa87e2afb08a2da5cf62bf0c919081900360200190a1919050565b60006109d660006108ea565b905090565b333014610a33576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612b9c6027913960400191505060405180910390fd5b6000610a3e83610425565b73ffffffffffffffffffffffffffffffffffffffff1614610aaa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c8152602001806129f4602c913960400191505060405180910390fd5b61086a8282610df8565b7fbc197c810000000000000000000000000000000000000000000000000000000098975050505050505050565b7ff23a6e61000000000000000000000000000000000000000000000000000000009695505050505050565b60408051602080820194909452808201929092528051808303820181526060909201905280519101205490565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f90042baf000000000000000000000000000000000000000000000000000000001415610b8d57506001610476565b610473826110ce565b3b151590565b3055565b604080517f19010000000000000000000000000000000000000000000000000000000000006020808301919091524660228301523060601b6042830152605680830194909452825180830390940184526076909101909152815191012090565b6000806000610c0e8461120f565b909250905061ffff821660005b8551831015610dd55760008080610c32898761127d565b975060ff91821694501691506001831415610c5a57610c5189876112fe565b96509050610d7e565b82610c86576060610c6b8a88611376565b97509050610c798b82611427565b9150828501945050610d7e565b6002831415610d2d57610c9989876112fe565b965090506000610ca98a886117b1565b975061ffff1690506060610cbe8b8984611822565b98509050610ccd8c8483611911565b610d22576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260328152602001806129c26032913960400191505060405180910390fd5b505092810192610d7e565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c8152602001806128b4602c913960400191505060405180910390fd5b848282604051602001808481526020018381526020018273ffffffffffffffffffffffffffffffffffffffff1681526020019350505050604051602081830303815290604052805190602001209450505050610c1b565b8361ffff168110158015610ded5750610ded82611b59565b979650505050505050565b61086a7fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1207fffffffff00000000000000000000000000000000000000000000000000000000841673ffffffffffffffffffffffffffffffffffffffff8416611c37565b60005b8151811015611025576000828281518110610e7557fe5b602002602001015190506000606082604001515a1015610ec1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108d1906126c4565b825115610f5957826060015173ffffffffffffffffffffffffffffffffffffffff168360400151600014610ef9578360400151610efb565b5a5b8460a00151604051610f0d91906125f6565b6000604051808303818686f4925050503d8060008114610f49576040519150601f19603f3d011682016040523d82523d6000602084013e610f4e565b606091505b509092509050610fee565b826060015173ffffffffffffffffffffffffffffffffffffffff1683608001518460400151600014610f8f578460400151610f91565b5a5b908560a00151604051610fa491906125f6565b600060405180830381858888f193505050503d8060008114610fe2576040519150601f19603f3d011682016040523d82523d6000602084013e610fe7565b606091505b5090925090505b811561100f5785604051611002919061263e565b60405180910390a061101a565b61101a838783611c65565b505050600101610e5e565b505050565b60008061103683611cb5565b915091506000611045836108ea565b9050808214611080576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108d19061268d565b6001820161108e8482611cce565b7f1f180c27086c7a39ea2a7b25239d1ab92348f07ca7bb59d1438fcf527568f88184826040516110bf9291906127de565b60405180910390a15050505050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fec6aba5000000000000000000000000000000000000000000000000000000000148061116157507fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e000000000000000000000000000000000000000000000000000000000145b806111ad57507fffffffff0000000000000000000000000000000000000000000000000000000082167f150b7a0200000000000000000000000000000000000000000000000000000000145b806111f957507fffffffff0000000000000000000000000000000000000000000000000000000082167fc0ee0b8a00000000000000000000000000000000000000000000000000000000145b1561120657506001610476565b61047382611cf9565b6020810151815160f09190911c90600290811115611278576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061292e6027913960400191505060405180910390fd5b915091565b8082016020015160f881901c9060f01c60ff166002830183811161129d57fe5b84518111156112f7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180612af76026913960400191505060405180910390fd5b9250925092565b8082016020015160601c6014820182811161131557fe5b835181111561136f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602381526020018061290b6023913960400191505060405180910390fd5b9250929050565b6040805160428082526080820190925260609160009190602082018180368337019050509150828401602001805160208401526020810151604084015260228101516042840152506042830190508281116113cd57fe5b835181111561136f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180612a5f6023913960400191505060405180910390fd5b60008151604214611483576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a81526020018061287a603a913960400191505060405180910390fd5b60008260018451038151811061149557fe5b602001015160f81c60f81b60f81c60ff1690506000836040815181106114b757fe5b016020015160f81c905060006114cd8582611d56565b905060006114dc866020611d56565b90507f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0811115611557576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d81526020018061283d603d913960400191505060405180910390fd5b8260ff16601b1415801561156f57508260ff16601c14155b156115c5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180612955603d913960400191505060405180910390fd5b60018414156116395760018784848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611628573d6000803e3d6000fd5b50505060206040510351945061173b565b60028414156116ea5760018760405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c018281526020019150506040516020818303038152906040528051906020012084848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611628573d6000803e3d6000fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180612abb603c913960400191505060405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff85166117a7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260308152602001806129926030913960400191505060405180910390fd5b5050505092915050565b8082016020015160f01c600282018281116117c857fe5b835181111561136f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180612b3e6022913960400191505060405180910390fd5b606060008267ffffffffffffffff8111801561183d57600080fd5b506040519080825280601f01601f191660200182016040528015611868576020820181803683370190505b509150838501602001600060205b8581101561188f57908201518482015260208101611876565b84860160200180519390920151908501525250828201838110156118af57fe5b8451811115611909576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180612b1d6021913960400191505060405180910390fd5b935093915050565b6000808260018451038151811061192457fe5b016020015160f81c9050600181148061193d5750600281145b15611981578373ffffffffffffffffffffffffffffffffffffffff166119638685611427565b73ffffffffffffffffffffffffffffffffffffffff16149150611b51565b6003811415611b005782517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018452604080517f1626ba7e000000000000000000000000000000000000000000000000000000008152600481018881526024820192835286516044830152865173ffffffffffffffffffffffffffffffffffffffff891693631626ba7e938b938a9390929160640190602085019080838360005b83811015611a3b578181015183820152602001611a23565b50505050905090810190601f168015611a685780820380516001836020036101000a031916815260200191505b50935050505060206040518083038186803b158015611a8657600080fd5b505afa158015611a9a573d6000803e3d6000fd5b505050506040513d6020811015611ab057600080fd5b50519084527fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e00000000000000000000000000000000000000000000000000000000149150611b51565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f815260200180612a20603f913960400191505060405180910390fd5b509392505050565b604080517fff000000000000000000000000000000000000000000000000000000000000006020808301919091527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000060601b166021830152603582018490527f0000000000000000000000000000000000000000000000000000000000000000605580840191909152835180840390910181526075909201909252805191012073ffffffffffffffffffffffffffffffffffffffff163014919050565b6040805160208082019590955280820193909352805180840382018152606090930190528151919092012055565b826020015115611c7757805160208201fd5b7f3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd78282604051611ca8929190612647565b60405180910390a1505050565b606081901c916bffffffffffffffffffffffff90911690565b61086a7f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e8383611c37565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f025b22bc000000000000000000000000000000000000000000000000000000001415611d4d57506001610476565b61047382611dbe565b60008160200183511015611db5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180612b60603c913960400191505060405180910390fd5b50016020015190565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f389901c7000000000000000000000000000000000000000000000000000000001415611e1257506001610476565b6104738260007fffffffff0000000000000000000000000000000000000000000000000000000082161580611e8857507fffffffff0000000000000000000000000000000000000000000000000000000082167f36e7817500000000000000000000000000000000000000000000000000000000145b15611e9557506001610476565b7f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831614610473565b803573ffffffffffffffffffffffffffffffffffffffff8116811461047657600080fd5b600082601f830112611f13578081fd5b8135602067ffffffffffffffff80831115611f2a57fe5b611f3782838502016127ec565b83815282810190868401865b86811015612013578135890160c0807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0838e03011215611f8157898afd5b604080518281018181108a82111715611f9657fe5b8252611fa3848b01612063565b8152611fb0828501612063565b8a8201526060808501358383015260809250611fcd838601611edf565b9082015260a08481013583830152928401359289841115611fec578c8dfd5b611ffa8f8c868801016120e3565b9082015287525050509285019290850190600101611f43565b509098975050505050505050565b60008083601f840112612032578182fd5b50813567ffffffffffffffff811115612049578182fd5b602083019150836020808302850101111561136f57600080fd5b8035801515811461047657600080fd5b80357fffffffff000000000000000000000000000000000000000000000000000000008116811461047657600080fd5b60008083601f8401126120b4578182fd5b50813567ffffffffffffffff8111156120cb578182fd5b60208301915083602082850101111561136f57600080fd5b600082601f8301126120f3578081fd5b813567ffffffffffffffff81111561210757fe5b61213860207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016127ec565b81815284602083860101111561214c578283fd5b816020850160208301379081016020019190915292915050565b600060208284031215612177578081fd5b61064382611edf565b60008060008060008060008060a0898b03121561219b578384fd5b6121a489611edf565b97506121b260208a01611edf565b9650604089013567ffffffffffffffff808211156121ce578586fd5b6121da8c838d01612021565b909850965060608b01359150808211156121f2578586fd5b6121fe8c838d01612021565b909650945060808b0135915080821115612216578384fd5b506122238b828c016120a3565b999c989b5096995094979396929594505050565b60008060008060006080868803121561224e578081fd5b61225786611edf565b945061226560208701611edf565b935060408601359250606086013567ffffffffffffffff811115612287578182fd5b612293888289016120a3565b969995985093965092949392505050565b60008060008060008060a087890312156122bc578182fd5b6122c587611edf565b95506122d360208801611edf565b94506040870135935060608701359250608087013567ffffffffffffffff8111156122fc578283fd5b61230889828a016120a3565b979a9699509497509295939492505050565b60006020828403121561232b578081fd5b813567ffffffffffffffff811115612341578182fd5b6106a784828501611f03565b600080600060608486031215612361578283fd5b833567ffffffffffffffff80821115612378578485fd5b61238487838801611f03565b94506020860135935060408601359150808211156123a0578283fd5b506123ad868287016120e3565b9150509250925092565b6000806000604084860312156123cb578283fd5b83359250602084013567ffffffffffffffff8111156123e8578283fd5b6123f4868287016120a3565b9497909650939450505050565b600060208284031215612412578081fd5b61064382612073565b6000806040838503121561242d578182fd5b61243683612073565b915061244460208401611edf565b90509250929050565b60008060008060408587031215612462578182fd5b843567ffffffffffffffff80821115612479578384fd5b612485888389016120a3565b9096509450602087013591508082111561249d578384fd5b506124aa878288016120a3565b95989497509550505050565b6000602082840312156124c7578081fd5b813567ffffffffffffffff8111156124dd578182fd5b6106a7848285016120e3565b6000602082840312156124fa578081fd5b5035919050565b6000815180845260208085018081965082840281019150828601855b8581101561259f5782840389528151805115158552858101511515868601526040808201519086015260608082015173ffffffffffffffffffffffffffffffffffffffff16908601526080808201519086015260a09081015160c09186018290529061258b818701836125ac565b9a87019a955050509084019060010161251d565b5091979650505050505050565b600081518084526125c4816020860160208601612810565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60008251612608818460208701612810565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b901515815260200190565b90815260200190565b6000838252604060208301526106a760408301846125ac565b7fffffffff0000000000000000000000000000000000000000000000000000000091909116815260200190565b6020808252601f908201527f4d61696e4d6f64756c65235f617574683a20494e56414c49445f4e4f4e434500604082015260600190565b60208082526024908201527f4d6f64756c6543616c6c73235f657865637574653a204e4f545f454e4f55474860408201527f5f47415300000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526026908201527f4d6f64756c6543616c6c7323657865637574653a20494e56414c49445f53494760408201527f4e41545552450000000000000000000000000000000000000000000000000000606082015260800190565b600060408252600560408301527f73656c663a0000000000000000000000000000000000000000000000000000006060830152608060208301526106436080830184612501565b6000838252604060208301526106a76040830184612501565b918252602082015260400190565b60405181810167ffffffffffffffff8111828210171561280857fe5b604052919050565b60005b8381101561282b578181015183820152602001612813565b838111156108e4575050600091015256fe5369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202773272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265206c656e6774684d6f64756c6541757468235f7369676e617475726556616c69646174696f6e20494e56414c49445f464c41474d6f64756c65486f6f6b732372656d6f7665486f6f6b3a20484f4f4b5f4e4f545f524547495354455245444c696242797465732372656164416464726573733a204f55545f4f465f424f554e44534c696242797465732372656164466972737455696e7431363a204f55545f4f465f424f554e44535369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202776272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20494e56414c49445f5349474e45524d6f64756c6541757468235f7369676e617475726556616c69646174696f6e3a20494e56414c49445f5349474e41545552454d6f64756c65486f6f6b7323616464486f6f6b3a20484f4f4b5f414c52454144595f524547495354455245445369676e617475726556616c696461746f7223697356616c69645369676e61747572653a20554e535550504f525445445f5349474e41545552455f545950454c696242797465732372656164427974657336363a204f55545f4f465f424f554e44534d6f64756c6555706461746523757064617465496d706c656d656e746174696f6e3a20494e56414c49445f494d504c454d454e544154494f4e5369676e617475726556616c696461746f72237265636f7665725369676e65723a20554e535550504f525445445f5349474e41545552455f545950454c69624279746573237265616455696e743855696e74383a204f55545f4f465f424f554e44534c69624279746573237265616442797465733a204f55545f4f465f424f554e44534c69624279746573237265616455696e7431363a204f55545f4f465f424f554e44534c696242797465732372656164427974657333323a20475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f52455155495245444d6f64756c6553656c6641757468236f6e6c7953656c663a204e4f545f415554484f52495a4544a2646970667358221220b34deca9dd75815e4ef8a9279e45750ec5554b22c673e160bdba849d80f5888564736f6c63430007060033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/packages/tests/src/builds/v1/artifacts/MainModuleUpgradable.ts b/packages/tests/src/builds/v1/artifacts/MainModuleUpgradable.ts new file mode 100644 index 000000000..f392f077d --- /dev/null +++ b/packages/tests/src/builds/v1/artifacts/MainModuleUpgradable.ts @@ -0,0 +1,528 @@ +export const mainModuleUpgradable = { + "_format": "hh-sol-artifact-1", + "contractName": "MainModuleUpgradable", + "sourceName": "contracts/modules/MainModuleUpgradable.sol", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_contract", + "type": "address" + } + ], + "name": "CreatedContract", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "newImageHash", + "type": "bytes32" + } + ], + "name": "ImageHashUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "ImplementationUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_space", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_newNonce", + "type": "uint256" + } + ], + "name": "NonceChange", + "type": "event" + }, + { + "anonymous": true, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "_tx", + "type": "bytes32" + } + ], + "name": "TxExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "_tx", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "_reason", + "type": "bytes" + } + ], + "name": "TxFailed", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_signature", + "type": "bytes4" + }, + { + "internalType": "address", + "name": "_implementation", + "type": "address" + } + ], + "name": "addHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_code", + "type": "bytes" + } + ], + "name": "createContract", + "outputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "delegateCall", + "type": "bool" + }, + { + "internalType": "bool", + "name": "revertOnError", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct IModuleCalls.Transaction[]", + "name": "_txs", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "_nonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "execute", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "imageHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_signatures", + "type": "bytes" + } + ], + "name": "isValidSignature", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_signatures", + "type": "bytes" + } + ], + "name": "isValidSignature", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155BatchReceived", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC721Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_signature", + "type": "bytes4" + } + ], + "name": "readHook", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_space", + "type": "uint256" + } + ], + "name": "readNonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_signature", + "type": "bytes4" + } + ], + "name": "removeHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "delegateCall", + "type": "bool" + }, + { + "internalType": "bool", + "name": "revertOnError", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct IModuleCalls.Transaction[]", + "name": "_txs", + "type": "tuple[]" + } + ], + "name": "selfExecute", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_interfaceID", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_imageHash", + "type": "bytes32" + } + ], + "name": "updateImageHash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_implementation", + "type": "address" + } + ], + "name": "updateImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b50612ce7806100206000396000f3fe6080604052600436106101125760003560e01c806351605d80116100a557806390042baf11610074578063b93ea7ad11610059578063b93ea7ad146103d0578063bc197c81146103f0578063f23a6e611461041057610119565b806390042baf146103a8578063affed0e0146103bb57610119565b806351605d801461032657806361c2926c146103485780637a9a1628146103685780638c3f55631461038857610119565b80631a9b2337116100e15780631a9b23371461029957806320c13b0b146102c657806329561426146102e65780634fcf3eca1461030657610119565b806301ffc9a7146101f4578063025b22bc1461022a578063150b7a021461024c5780631626ba7e1461027957610119565b3661011957005b60006101486000357fffffffff0000000000000000000000000000000000000000000000000000000016610430565b905073ffffffffffffffffffffffffffffffffffffffff8116156101f1576000808273ffffffffffffffffffffffffffffffffffffffff166000366040518083838082843760405192019450600093509091505080830381855af49150503d80600081146101d2576040519150601f19603f3d011682016040523d82523d6000602084013e6101d7565b606091505b5091509150816101e957805160208201fd5b805160208201f35b50005b34801561020057600080fd5b5061021461020f3660046124d4565b610486565b60405161022191906126eb565b60405180910390f35b34801561023657600080fd5b5061024a610245366004612221565b610491565b005b34801561025857600080fd5b5061026c6102673660046122f2565b6105b2565b6040516102219190612718565b34801561028557600080fd5b5061026c61029436600461248a565b6105dc565b3480156102a557600080fd5b506102b96102b43660046124d4565b610655565b60405161022191906126ca565b3480156102d257600080fd5b5061026c6102e1366004612520565b610660565b3480156102f257600080fd5b5061024a610301366004612472565b6106ba565b34801561031257600080fd5b5061024a6103213660046124d4565b6107c8565b34801561033257600080fd5b5061033b6108a6565b60405161022191906126f6565b34801561035457600080fd5b5061024a6103633660046123d5565b6108d6565b34801561037457600080fd5b5061024a610383366004612408565b61096f565b34801561039457600080fd5b5061033b6103a3366004612472565b6109eb565b6102b96103b6366004612589565b610a17565b3480156103c757600080fd5b5061033b610acb565b3480156103dc57600080fd5b5061024a6103eb3660046124ee565b610ad7565b3480156103fc57600080fd5b5061026c61040b36600461223b565b610bb0565b34801561041c57600080fd5b5061026c61042b36600461235f565b610bdd565b600061047e7fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1207fffffffff000000000000000000000000000000000000000000000000000000008416610c08565b90505b919050565b600061047e82610c35565b3330146104e9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612c8b6027913960400191505060405180910390fd5b6105088173ffffffffffffffffffffffffffffffffffffffff16610c92565b61055d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526039815260200180612b716039913960400191505060405180910390fd5b61056681610c98565b6040805173ffffffffffffffffffffffffffffffffffffffff8316815290517f310ba5f1d2ed074b51e2eccd052a47ae9ab7c6b800d1fca3db3999d6a592ca039181900360200190a150565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b60006106266105ea85610c9c565b84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610cfc92505050565b1561064e57507f1626ba7e000000000000000000000000000000000000000000000000000000005b9392505050565b600061047e82610430565b600061068a6105ea86866040518083838082843760405192018290039091209350610c9c92505050565b156106b257507f20c13b0b000000000000000000000000000000000000000000000000000000005b949350505050565b333014610712576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612c8b6027913960400191505060405180910390fd5b80610768576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260378152602001806129986037913960400191505060405180910390fd5b6107927fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf882610ef4565b6040805182815290517f307ed6bd941ee9fc80f369c94af5fa11e25bab5102a6140191756c5474a30bfa9181900360200190a150565b333014610820576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612c8b6027913960400191505060405180910390fd5b600061082b82610430565b73ffffffffffffffffffffffffffffffffffffffff161415610898576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b8152602001806129cf602b913960400191505060405180910390fd5b6108a3816000610ef8565b50565b60006108d17fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8610f5b565b905090565b33301461092e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612c8b6027913960400191505060405180910390fd5b600061095f826040516020016109449190612836565b60405160208183030381529060405280519060200120610c9c565b905061096b8183610f5f565b5050565b6109788261112e565b6000610990838560405160200161094492919061287d565b905061099c8183610cfc565b6109db576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109d2906127d9565b60405180910390fd5b6109e58185610f5f565b50505050565b600061047e7f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e83610c08565b6000333014610a71576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612c8b6027913960400191505060405180910390fd5b81516020830134f06040805173ffffffffffffffffffffffffffffffffffffffff8316815290519192507fa506ad4e7f05eceba62a023c3219e5bd98a615f4fa87e2afb08a2da5cf62bf0c919081900360200190a1919050565b60006108d160006109eb565b333014610b2f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612c8b6027913960400191505060405180910390fd5b6000610b3a83610430565b73ffffffffffffffffffffffffffffffffffffffff1614610ba6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180612ae3602c913960400191505060405180910390fd5b61096b8282610ef8565b7fbc197c810000000000000000000000000000000000000000000000000000000098975050505050505050565b7ff23a6e61000000000000000000000000000000000000000000000000000000009695505050505050565b60408051602080820194909452808201929092528051808303820181526060909201905280519101205490565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f90042baf000000000000000000000000000000000000000000000000000000001415610c8957506001610481565b61047e826111d2565b3b151590565b3055565b604080517f19010000000000000000000000000000000000000000000000000000000000006020808301919091524660228301523060601b6042830152605680830194909452825180830390940184526076909101909152815191012090565b6000806000610d0a84611313565b909250905061ffff821660005b8551831015610ed15760008080610d2e8987611381565b975060ff91821694501691506001831415610d5657610d4d8987611402565b96509050610e7a565b82610d82576060610d678a8861147a565b97509050610d758b8261152b565b9150828501945050610e7a565b6002831415610e2957610d958987611402565b965090506000610da58a886118b5565b975061ffff1690506060610dba8b8984611926565b98509050610dc98c8483611a15565b610e1e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526032815260200180612ab16032913960400191505060405180910390fd5b505092810192610e7a565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c81526020018061296c602c913960400191505060405180910390fd5b848282604051602001808481526020018381526020018273ffffffffffffffffffffffffffffffffffffffff1681526020019350505050604051602081830303815290604052805190602001209450505050610d17565b8361ffff168110158015610ee95750610ee982611c5d565b979650505050505050565b9055565b61096b7fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1207fffffffff00000000000000000000000000000000000000000000000000000000841673ffffffffffffffffffffffffffffffffffffffff8416611c9a565b5490565b60005b8151811015611129576000828281518110610f7957fe5b602002602001015190506000606082604001515a1015610fc5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109d29061277c565b82511561105d57826060015173ffffffffffffffffffffffffffffffffffffffff168360400151600014610ffd578360400151610fff565b5a5b8460a0015160405161101191906126ae565b6000604051808303818686f4925050503d806000811461104d576040519150601f19603f3d011682016040523d82523d6000602084013e611052565b606091505b5090925090506110f2565b826060015173ffffffffffffffffffffffffffffffffffffffff1683608001518460400151600014611093578460400151611095565b5a5b908560a001516040516110a891906126ae565b600060405180830381858888f193505050503d80600081146110e6576040519150601f19603f3d011682016040523d82523d6000602084013e6110eb565b606091505b5090925090505b8115611113578560405161110691906126f6565b60405180910390a061111e565b61111e838783611cc8565b505050600101610f62565b505050565b60008061113a83611d18565b915091506000611149836109eb565b9050808214611184576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109d290612745565b600182016111928482611d31565b7f1f180c27086c7a39ea2a7b25239d1ab92348f07ca7bb59d1438fcf527568f88184826040516111c3929190612896565b60405180910390a15050505050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fec6aba5000000000000000000000000000000000000000000000000000000000148061126557507fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e000000000000000000000000000000000000000000000000000000000145b806112b157507fffffffff0000000000000000000000000000000000000000000000000000000082167f150b7a0200000000000000000000000000000000000000000000000000000000145b806112fd57507fffffffff0000000000000000000000000000000000000000000000000000000082167fc0ee0b8a00000000000000000000000000000000000000000000000000000000145b1561130a57506001610481565b61047e82611d5c565b6020810151815160f09190911c9060029081111561137c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612a1d6027913960400191505060405180910390fd5b915091565b8082016020015160f881901c9060f01c60ff16600283018381116113a157fe5b84518111156113fb576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180612be66026913960400191505060405180910390fd5b9250925092565b8082016020015160601c6014820182811161141957fe5b8351811115611473576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806129fa6023913960400191505060405180910390fd5b9250929050565b6040805160428082526080820190925260609160009190602082018180368337019050509150828401602001805160208401526020810151604084015260228101516042840152506042830190508281116114d157fe5b8351811115611473576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180612b4e6023913960400191505060405180910390fd5b60008151604214611587576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a815260200180612932603a913960400191505060405180910390fd5b60008260018451038151811061159957fe5b602001015160f81c60f81b60f81c60ff1690506000836040815181106115bb57fe5b016020015160f81c905060006115d18582611db9565b905060006115e0866020611db9565b90507f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a081111561165b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d8152602001806128f5603d913960400191505060405180910390fd5b8260ff16601b1415801561167357508260ff16601c14155b156116c9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180612a44603d913960400191505060405180910390fd5b600184141561173d5760018784848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa15801561172c573d6000803e3d6000fd5b50505060206040510351945061183f565b60028414156117ee5760018760405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c018281526020019150506040516020818303038152906040528051906020012084848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa15801561172c573d6000803e3d6000fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180612baa603c913960400191505060405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff85166118ab576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526030815260200180612a816030913960400191505060405180910390fd5b5050505092915050565b8082016020015160f01c600282018281116118cc57fe5b8351811115611473576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180612c2d6022913960400191505060405180910390fd5b606060008267ffffffffffffffff8111801561194157600080fd5b506040519080825280601f01601f19166020018201604052801561196c576020820181803683370190505b509150838501602001600060205b858110156119935790820151848201526020810161197a565b84860160200180519390920151908501525250828201838110156119b357fe5b8451811115611a0d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180612c0c6021913960400191505060405180910390fd5b935093915050565b60008082600184510381518110611a2857fe5b016020015160f81c90506001811480611a415750600281145b15611a85578373ffffffffffffffffffffffffffffffffffffffff16611a67868561152b565b73ffffffffffffffffffffffffffffffffffffffff16149150611c55565b6003811415611c045782517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018452604080517f1626ba7e000000000000000000000000000000000000000000000000000000008152600481018881526024820192835286516044830152865173ffffffffffffffffffffffffffffffffffffffff891693631626ba7e938b938a9390929160640190602085019080838360005b83811015611b3f578181015183820152602001611b27565b50505050905090810190601f168015611b6c5780820380516001836020036101000a031916815260200191505b50935050505060206040518083038186803b158015611b8a57600080fd5b505afa158015611b9e573d6000803e3d6000fd5b505050506040513d6020811015611bb457600080fd5b50519084527fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e00000000000000000000000000000000000000000000000000000000149150611c55565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f815260200180612b0f603f913960400191505060405180910390fd5b509392505050565b6000811580159061047e5750611c927fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8610f5b565b909114919050565b6040805160208082019590955280820193909352805180840382018152606090930190528151919092012055565b826020015115611cda57805160208201fd5b7f3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd78282604051611d0b9291906126ff565b60405180910390a1505050565b606081901c916bffffffffffffffffffffffff90911690565b61096b7f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e8383611c9a565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f025b22bc000000000000000000000000000000000000000000000000000000001415611db057506001610481565b61047e82611e21565b60008160200183511015611e18576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180612c4f603c913960400191505060405180910390fd5b50016020015190565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f389901c7000000000000000000000000000000000000000000000000000000001415611e7557506001610481565b61047e8260007fffffffff0000000000000000000000000000000000000000000000000000000082167f783649a6000000000000000000000000000000000000000000000000000000001415611ecd57506001610481565b61047e8260007fffffffff0000000000000000000000000000000000000000000000000000000082161580611f4357507fffffffff0000000000000000000000000000000000000000000000000000000082167f36e7817500000000000000000000000000000000000000000000000000000000145b15611f5057506001610481565b7f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083161461047e565b803573ffffffffffffffffffffffffffffffffffffffff8116811461048157600080fd5b600082601f830112611fce578081fd5b8135602067ffffffffffffffff80831115611fe557fe5b611ff282838502016128a4565b83815282810190868401865b868110156120ce578135890160c0807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0838e0301121561203c57898afd5b604080518281018181108a8211171561205157fe5b825261205e848b0161211e565b815261206b82850161211e565b8a8201526060808501358383015260809250612088838601611f9a565b9082015260a084810135838301529284013592898411156120a7578c8dfd5b6120b58f8c8688010161219e565b9082015287525050509285019290850190600101611ffe565b509098975050505050505050565b60008083601f8401126120ed578182fd5b50813567ffffffffffffffff811115612104578182fd5b602083019150836020808302850101111561147357600080fd5b8035801515811461048157600080fd5b80357fffffffff000000000000000000000000000000000000000000000000000000008116811461048157600080fd5b60008083601f84011261216f578182fd5b50813567ffffffffffffffff811115612186578182fd5b60208301915083602082850101111561147357600080fd5b600082601f8301126121ae578081fd5b813567ffffffffffffffff8111156121c257fe5b6121f360207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016128a4565b818152846020838601011115612207578283fd5b816020850160208301379081016020019190915292915050565b600060208284031215612232578081fd5b61064e82611f9a565b60008060008060008060008060a0898b031215612256578384fd5b61225f89611f9a565b975061226d60208a01611f9a565b9650604089013567ffffffffffffffff80821115612289578586fd5b6122958c838d016120dc565b909850965060608b01359150808211156122ad578586fd5b6122b98c838d016120dc565b909650945060808b01359150808211156122d1578384fd5b506122de8b828c0161215e565b999c989b5096995094979396929594505050565b600080600080600060808688031215612309578081fd5b61231286611f9a565b945061232060208701611f9a565b935060408601359250606086013567ffffffffffffffff811115612342578182fd5b61234e8882890161215e565b969995985093965092949392505050565b60008060008060008060a08789031215612377578182fd5b61238087611f9a565b955061238e60208801611f9a565b94506040870135935060608701359250608087013567ffffffffffffffff8111156123b7578283fd5b6123c389828a0161215e565b979a9699509497509295939492505050565b6000602082840312156123e6578081fd5b813567ffffffffffffffff8111156123fc578182fd5b6106b284828501611fbe565b60008060006060848603121561241c578283fd5b833567ffffffffffffffff80821115612433578485fd5b61243f87838801611fbe565b945060208601359350604086013591508082111561245b578283fd5b506124688682870161219e565b9150509250925092565b600060208284031215612483578081fd5b5035919050565b60008060006040848603121561249e578283fd5b83359250602084013567ffffffffffffffff8111156124bb578283fd5b6124c78682870161215e565b9497909650939450505050565b6000602082840312156124e5578081fd5b61064e8261212e565b60008060408385031215612500578182fd5b6125098361212e565b915061251760208401611f9a565b90509250929050565b60008060008060408587031215612535578182fd5b843567ffffffffffffffff8082111561254c578384fd5b6125588883890161215e565b90965094506020870135915080821115612570578384fd5b5061257d8782880161215e565b95989497509550505050565b60006020828403121561259a578081fd5b813567ffffffffffffffff8111156125b0578182fd5b6106b28482850161219e565b6000815180845260208085019450848183028601828601855b858110156126575783830389528151805115158452858101511515868501526040808201519085015260608082015173ffffffffffffffffffffffffffffffffffffffff16908501526080808201519085015260a09081015160c09185018290529061264381860183612664565b9a87019a94505050908401906001016125d5565b5090979650505050505050565b6000815180845261267c8160208601602086016128c8565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600082516126c08184602087016128c8565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b901515815260200190565b90815260200190565b6000838252604060208301526106b26040830184612664565b7fffffffff0000000000000000000000000000000000000000000000000000000091909116815260200190565b6020808252601f908201527f4d61696e4d6f64756c65235f617574683a20494e56414c49445f4e4f4e434500604082015260600190565b60208082526024908201527f4d6f64756c6543616c6c73235f657865637574653a204e4f545f454e4f55474860408201527f5f47415300000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526026908201527f4d6f64756c6543616c6c7323657865637574653a20494e56414c49445f53494760408201527f4e41545552450000000000000000000000000000000000000000000000000000606082015260800190565b600060408252600560408301527f73656c663a00000000000000000000000000000000000000000000000000000060608301526080602083015261064e60808301846125bc565b6000838252604060208301526106b260408301846125bc565b918252602082015260400190565b60405181810167ffffffffffffffff811182821017156128c057fe5b604052919050565b60005b838110156128e35781810151838201526020016128cb565b838111156109e5575050600091015256fe5369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202773272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265206c656e6774684d6f64756c6541757468235f7369676e617475726556616c69646174696f6e20494e56414c49445f464c41474d6f64756c654175746855706772616461626c6523757064617465496d6167654861736820494e56414c49445f494d4147455f484153484d6f64756c65486f6f6b732372656d6f7665486f6f6b3a20484f4f4b5f4e4f545f524547495354455245444c696242797465732372656164416464726573733a204f55545f4f465f424f554e44534c696242797465732372656164466972737455696e7431363a204f55545f4f465f424f554e44535369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202776272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20494e56414c49445f5349474e45524d6f64756c6541757468235f7369676e617475726556616c69646174696f6e3a20494e56414c49445f5349474e41545552454d6f64756c65486f6f6b7323616464486f6f6b3a20484f4f4b5f414c52454144595f524547495354455245445369676e617475726556616c696461746f7223697356616c69645369676e61747572653a20554e535550504f525445445f5349474e41545552455f545950454c696242797465732372656164427974657336363a204f55545f4f465f424f554e44534d6f64756c6555706461746523757064617465496d706c656d656e746174696f6e3a20494e56414c49445f494d504c454d454e544154494f4e5369676e617475726556616c696461746f72237265636f7665725369676e65723a20554e535550504f525445445f5349474e41545552455f545950454c69624279746573237265616455696e743855696e74383a204f55545f4f465f424f554e44534c69624279746573237265616442797465733a204f55545f4f465f424f554e44534c69624279746573237265616455696e7431363a204f55545f4f465f424f554e44534c696242797465732372656164427974657333323a20475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f52455155495245444d6f64756c6553656c6641757468236f6e6c7953656c663a204e4f545f415554484f52495a4544a2646970667358221220aebb8d931ef86555b6441c416b208bb9fe8fe0974c5733ebbccce548296c37ce64736f6c63430007060033", + "deployedBytecode": "0x6080604052600436106101125760003560e01c806351605d80116100a557806390042baf11610074578063b93ea7ad11610059578063b93ea7ad146103d0578063bc197c81146103f0578063f23a6e611461041057610119565b806390042baf146103a8578063affed0e0146103bb57610119565b806351605d801461032657806361c2926c146103485780637a9a1628146103685780638c3f55631461038857610119565b80631a9b2337116100e15780631a9b23371461029957806320c13b0b146102c657806329561426146102e65780634fcf3eca1461030657610119565b806301ffc9a7146101f4578063025b22bc1461022a578063150b7a021461024c5780631626ba7e1461027957610119565b3661011957005b60006101486000357fffffffff0000000000000000000000000000000000000000000000000000000016610430565b905073ffffffffffffffffffffffffffffffffffffffff8116156101f1576000808273ffffffffffffffffffffffffffffffffffffffff166000366040518083838082843760405192019450600093509091505080830381855af49150503d80600081146101d2576040519150601f19603f3d011682016040523d82523d6000602084013e6101d7565b606091505b5091509150816101e957805160208201fd5b805160208201f35b50005b34801561020057600080fd5b5061021461020f3660046124d4565b610486565b60405161022191906126eb565b60405180910390f35b34801561023657600080fd5b5061024a610245366004612221565b610491565b005b34801561025857600080fd5b5061026c6102673660046122f2565b6105b2565b6040516102219190612718565b34801561028557600080fd5b5061026c61029436600461248a565b6105dc565b3480156102a557600080fd5b506102b96102b43660046124d4565b610655565b60405161022191906126ca565b3480156102d257600080fd5b5061026c6102e1366004612520565b610660565b3480156102f257600080fd5b5061024a610301366004612472565b6106ba565b34801561031257600080fd5b5061024a6103213660046124d4565b6107c8565b34801561033257600080fd5b5061033b6108a6565b60405161022191906126f6565b34801561035457600080fd5b5061024a6103633660046123d5565b6108d6565b34801561037457600080fd5b5061024a610383366004612408565b61096f565b34801561039457600080fd5b5061033b6103a3366004612472565b6109eb565b6102b96103b6366004612589565b610a17565b3480156103c757600080fd5b5061033b610acb565b3480156103dc57600080fd5b5061024a6103eb3660046124ee565b610ad7565b3480156103fc57600080fd5b5061026c61040b36600461223b565b610bb0565b34801561041c57600080fd5b5061026c61042b36600461235f565b610bdd565b600061047e7fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1207fffffffff000000000000000000000000000000000000000000000000000000008416610c08565b90505b919050565b600061047e82610c35565b3330146104e9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612c8b6027913960400191505060405180910390fd5b6105088173ffffffffffffffffffffffffffffffffffffffff16610c92565b61055d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526039815260200180612b716039913960400191505060405180910390fd5b61056681610c98565b6040805173ffffffffffffffffffffffffffffffffffffffff8316815290517f310ba5f1d2ed074b51e2eccd052a47ae9ab7c6b800d1fca3db3999d6a592ca039181900360200190a150565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b60006106266105ea85610c9c565b84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610cfc92505050565b1561064e57507f1626ba7e000000000000000000000000000000000000000000000000000000005b9392505050565b600061047e82610430565b600061068a6105ea86866040518083838082843760405192018290039091209350610c9c92505050565b156106b257507f20c13b0b000000000000000000000000000000000000000000000000000000005b949350505050565b333014610712576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612c8b6027913960400191505060405180910390fd5b80610768576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260378152602001806129986037913960400191505060405180910390fd5b6107927fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf882610ef4565b6040805182815290517f307ed6bd941ee9fc80f369c94af5fa11e25bab5102a6140191756c5474a30bfa9181900360200190a150565b333014610820576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612c8b6027913960400191505060405180910390fd5b600061082b82610430565b73ffffffffffffffffffffffffffffffffffffffff161415610898576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b8152602001806129cf602b913960400191505060405180910390fd5b6108a3816000610ef8565b50565b60006108d17fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8610f5b565b905090565b33301461092e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612c8b6027913960400191505060405180910390fd5b600061095f826040516020016109449190612836565b60405160208183030381529060405280519060200120610c9c565b905061096b8183610f5f565b5050565b6109788261112e565b6000610990838560405160200161094492919061287d565b905061099c8183610cfc565b6109db576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109d2906127d9565b60405180910390fd5b6109e58185610f5f565b50505050565b600061047e7f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e83610c08565b6000333014610a71576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612c8b6027913960400191505060405180910390fd5b81516020830134f06040805173ffffffffffffffffffffffffffffffffffffffff8316815290519192507fa506ad4e7f05eceba62a023c3219e5bd98a615f4fa87e2afb08a2da5cf62bf0c919081900360200190a1919050565b60006108d160006109eb565b333014610b2f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612c8b6027913960400191505060405180910390fd5b6000610b3a83610430565b73ffffffffffffffffffffffffffffffffffffffff1614610ba6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180612ae3602c913960400191505060405180910390fd5b61096b8282610ef8565b7fbc197c810000000000000000000000000000000000000000000000000000000098975050505050505050565b7ff23a6e61000000000000000000000000000000000000000000000000000000009695505050505050565b60408051602080820194909452808201929092528051808303820181526060909201905280519101205490565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f90042baf000000000000000000000000000000000000000000000000000000001415610c8957506001610481565b61047e826111d2565b3b151590565b3055565b604080517f19010000000000000000000000000000000000000000000000000000000000006020808301919091524660228301523060601b6042830152605680830194909452825180830390940184526076909101909152815191012090565b6000806000610d0a84611313565b909250905061ffff821660005b8551831015610ed15760008080610d2e8987611381565b975060ff91821694501691506001831415610d5657610d4d8987611402565b96509050610e7a565b82610d82576060610d678a8861147a565b97509050610d758b8261152b565b9150828501945050610e7a565b6002831415610e2957610d958987611402565b965090506000610da58a886118b5565b975061ffff1690506060610dba8b8984611926565b98509050610dc98c8483611a15565b610e1e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526032815260200180612ab16032913960400191505060405180910390fd5b505092810192610e7a565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c81526020018061296c602c913960400191505060405180910390fd5b848282604051602001808481526020018381526020018273ffffffffffffffffffffffffffffffffffffffff1681526020019350505050604051602081830303815290604052805190602001209450505050610d17565b8361ffff168110158015610ee95750610ee982611c5d565b979650505050505050565b9055565b61096b7fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1207fffffffff00000000000000000000000000000000000000000000000000000000841673ffffffffffffffffffffffffffffffffffffffff8416611c9a565b5490565b60005b8151811015611129576000828281518110610f7957fe5b602002602001015190506000606082604001515a1015610fc5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109d29061277c565b82511561105d57826060015173ffffffffffffffffffffffffffffffffffffffff168360400151600014610ffd578360400151610fff565b5a5b8460a0015160405161101191906126ae565b6000604051808303818686f4925050503d806000811461104d576040519150601f19603f3d011682016040523d82523d6000602084013e611052565b606091505b5090925090506110f2565b826060015173ffffffffffffffffffffffffffffffffffffffff1683608001518460400151600014611093578460400151611095565b5a5b908560a001516040516110a891906126ae565b600060405180830381858888f193505050503d80600081146110e6576040519150601f19603f3d011682016040523d82523d6000602084013e6110eb565b606091505b5090925090505b8115611113578560405161110691906126f6565b60405180910390a061111e565b61111e838783611cc8565b505050600101610f62565b505050565b60008061113a83611d18565b915091506000611149836109eb565b9050808214611184576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109d290612745565b600182016111928482611d31565b7f1f180c27086c7a39ea2a7b25239d1ab92348f07ca7bb59d1438fcf527568f88184826040516111c3929190612896565b60405180910390a15050505050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fec6aba5000000000000000000000000000000000000000000000000000000000148061126557507fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e000000000000000000000000000000000000000000000000000000000145b806112b157507fffffffff0000000000000000000000000000000000000000000000000000000082167f150b7a0200000000000000000000000000000000000000000000000000000000145b806112fd57507fffffffff0000000000000000000000000000000000000000000000000000000082167fc0ee0b8a00000000000000000000000000000000000000000000000000000000145b1561130a57506001610481565b61047e82611d5c565b6020810151815160f09190911c9060029081111561137c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612a1d6027913960400191505060405180910390fd5b915091565b8082016020015160f881901c9060f01c60ff16600283018381116113a157fe5b84518111156113fb576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180612be66026913960400191505060405180910390fd5b9250925092565b8082016020015160601c6014820182811161141957fe5b8351811115611473576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806129fa6023913960400191505060405180910390fd5b9250929050565b6040805160428082526080820190925260609160009190602082018180368337019050509150828401602001805160208401526020810151604084015260228101516042840152506042830190508281116114d157fe5b8351811115611473576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180612b4e6023913960400191505060405180910390fd5b60008151604214611587576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a815260200180612932603a913960400191505060405180910390fd5b60008260018451038151811061159957fe5b602001015160f81c60f81b60f81c60ff1690506000836040815181106115bb57fe5b016020015160f81c905060006115d18582611db9565b905060006115e0866020611db9565b90507f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a081111561165b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d8152602001806128f5603d913960400191505060405180910390fd5b8260ff16601b1415801561167357508260ff16601c14155b156116c9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180612a44603d913960400191505060405180910390fd5b600184141561173d5760018784848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa15801561172c573d6000803e3d6000fd5b50505060206040510351945061183f565b60028414156117ee5760018760405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c018281526020019150506040516020818303038152906040528051906020012084848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa15801561172c573d6000803e3d6000fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180612baa603c913960400191505060405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff85166118ab576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526030815260200180612a816030913960400191505060405180910390fd5b5050505092915050565b8082016020015160f01c600282018281116118cc57fe5b8351811115611473576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180612c2d6022913960400191505060405180910390fd5b606060008267ffffffffffffffff8111801561194157600080fd5b506040519080825280601f01601f19166020018201604052801561196c576020820181803683370190505b509150838501602001600060205b858110156119935790820151848201526020810161197a565b84860160200180519390920151908501525250828201838110156119b357fe5b8451811115611a0d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180612c0c6021913960400191505060405180910390fd5b935093915050565b60008082600184510381518110611a2857fe5b016020015160f81c90506001811480611a415750600281145b15611a85578373ffffffffffffffffffffffffffffffffffffffff16611a67868561152b565b73ffffffffffffffffffffffffffffffffffffffff16149150611c55565b6003811415611c045782517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018452604080517f1626ba7e000000000000000000000000000000000000000000000000000000008152600481018881526024820192835286516044830152865173ffffffffffffffffffffffffffffffffffffffff891693631626ba7e938b938a9390929160640190602085019080838360005b83811015611b3f578181015183820152602001611b27565b50505050905090810190601f168015611b6c5780820380516001836020036101000a031916815260200191505b50935050505060206040518083038186803b158015611b8a57600080fd5b505afa158015611b9e573d6000803e3d6000fd5b505050506040513d6020811015611bb457600080fd5b50519084527fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e00000000000000000000000000000000000000000000000000000000149150611c55565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f815260200180612b0f603f913960400191505060405180910390fd5b509392505050565b6000811580159061047e5750611c927fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8610f5b565b909114919050565b6040805160208082019590955280820193909352805180840382018152606090930190528151919092012055565b826020015115611cda57805160208201fd5b7f3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd78282604051611d0b9291906126ff565b60405180910390a1505050565b606081901c916bffffffffffffffffffffffff90911690565b61096b7f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e8383611c9a565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f025b22bc000000000000000000000000000000000000000000000000000000001415611db057506001610481565b61047e82611e21565b60008160200183511015611e18576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180612c4f603c913960400191505060405180910390fd5b50016020015190565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f389901c7000000000000000000000000000000000000000000000000000000001415611e7557506001610481565b61047e8260007fffffffff0000000000000000000000000000000000000000000000000000000082167f783649a6000000000000000000000000000000000000000000000000000000001415611ecd57506001610481565b61047e8260007fffffffff0000000000000000000000000000000000000000000000000000000082161580611f4357507fffffffff0000000000000000000000000000000000000000000000000000000082167f36e7817500000000000000000000000000000000000000000000000000000000145b15611f5057506001610481565b7f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083161461047e565b803573ffffffffffffffffffffffffffffffffffffffff8116811461048157600080fd5b600082601f830112611fce578081fd5b8135602067ffffffffffffffff80831115611fe557fe5b611ff282838502016128a4565b83815282810190868401865b868110156120ce578135890160c0807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0838e0301121561203c57898afd5b604080518281018181108a8211171561205157fe5b825261205e848b0161211e565b815261206b82850161211e565b8a8201526060808501358383015260809250612088838601611f9a565b9082015260a084810135838301529284013592898411156120a7578c8dfd5b6120b58f8c8688010161219e565b9082015287525050509285019290850190600101611ffe565b509098975050505050505050565b60008083601f8401126120ed578182fd5b50813567ffffffffffffffff811115612104578182fd5b602083019150836020808302850101111561147357600080fd5b8035801515811461048157600080fd5b80357fffffffff000000000000000000000000000000000000000000000000000000008116811461048157600080fd5b60008083601f84011261216f578182fd5b50813567ffffffffffffffff811115612186578182fd5b60208301915083602082850101111561147357600080fd5b600082601f8301126121ae578081fd5b813567ffffffffffffffff8111156121c257fe5b6121f360207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016128a4565b818152846020838601011115612207578283fd5b816020850160208301379081016020019190915292915050565b600060208284031215612232578081fd5b61064e82611f9a565b60008060008060008060008060a0898b031215612256578384fd5b61225f89611f9a565b975061226d60208a01611f9a565b9650604089013567ffffffffffffffff80821115612289578586fd5b6122958c838d016120dc565b909850965060608b01359150808211156122ad578586fd5b6122b98c838d016120dc565b909650945060808b01359150808211156122d1578384fd5b506122de8b828c0161215e565b999c989b5096995094979396929594505050565b600080600080600060808688031215612309578081fd5b61231286611f9a565b945061232060208701611f9a565b935060408601359250606086013567ffffffffffffffff811115612342578182fd5b61234e8882890161215e565b969995985093965092949392505050565b60008060008060008060a08789031215612377578182fd5b61238087611f9a565b955061238e60208801611f9a565b94506040870135935060608701359250608087013567ffffffffffffffff8111156123b7578283fd5b6123c389828a0161215e565b979a9699509497509295939492505050565b6000602082840312156123e6578081fd5b813567ffffffffffffffff8111156123fc578182fd5b6106b284828501611fbe565b60008060006060848603121561241c578283fd5b833567ffffffffffffffff80821115612433578485fd5b61243f87838801611fbe565b945060208601359350604086013591508082111561245b578283fd5b506124688682870161219e565b9150509250925092565b600060208284031215612483578081fd5b5035919050565b60008060006040848603121561249e578283fd5b83359250602084013567ffffffffffffffff8111156124bb578283fd5b6124c78682870161215e565b9497909650939450505050565b6000602082840312156124e5578081fd5b61064e8261212e565b60008060408385031215612500578182fd5b6125098361212e565b915061251760208401611f9a565b90509250929050565b60008060008060408587031215612535578182fd5b843567ffffffffffffffff8082111561254c578384fd5b6125588883890161215e565b90965094506020870135915080821115612570578384fd5b5061257d8782880161215e565b95989497509550505050565b60006020828403121561259a578081fd5b813567ffffffffffffffff8111156125b0578182fd5b6106b28482850161219e565b6000815180845260208085019450848183028601828601855b858110156126575783830389528151805115158452858101511515868501526040808201519085015260608082015173ffffffffffffffffffffffffffffffffffffffff16908501526080808201519085015260a09081015160c09185018290529061264381860183612664565b9a87019a94505050908401906001016125d5565b5090979650505050505050565b6000815180845261267c8160208601602086016128c8565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600082516126c08184602087016128c8565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b901515815260200190565b90815260200190565b6000838252604060208301526106b26040830184612664565b7fffffffff0000000000000000000000000000000000000000000000000000000091909116815260200190565b6020808252601f908201527f4d61696e4d6f64756c65235f617574683a20494e56414c49445f4e4f4e434500604082015260600190565b60208082526024908201527f4d6f64756c6543616c6c73235f657865637574653a204e4f545f454e4f55474860408201527f5f47415300000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526026908201527f4d6f64756c6543616c6c7323657865637574653a20494e56414c49445f53494760408201527f4e41545552450000000000000000000000000000000000000000000000000000606082015260800190565b600060408252600560408301527f73656c663a00000000000000000000000000000000000000000000000000000060608301526080602083015261064e60808301846125bc565b6000838252604060208301526106b260408301846125bc565b918252602082015260400190565b60405181810167ffffffffffffffff811182821017156128c057fe5b604052919050565b60005b838110156128e35781810151838201526020016128cb565b838111156109e5575050600091015256fe5369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202773272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265206c656e6774684d6f64756c6541757468235f7369676e617475726556616c69646174696f6e20494e56414c49445f464c41474d6f64756c654175746855706772616461626c6523757064617465496d6167654861736820494e56414c49445f494d4147455f484153484d6f64756c65486f6f6b732372656d6f7665486f6f6b3a20484f4f4b5f4e4f545f524547495354455245444c696242797465732372656164416464726573733a204f55545f4f465f424f554e44534c696242797465732372656164466972737455696e7431363a204f55545f4f465f424f554e44535369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202776272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20494e56414c49445f5349474e45524d6f64756c6541757468235f7369676e617475726556616c69646174696f6e3a20494e56414c49445f5349474e41545552454d6f64756c65486f6f6b7323616464486f6f6b3a20484f4f4b5f414c52454144595f524547495354455245445369676e617475726556616c696461746f7223697356616c69645369676e61747572653a20554e535550504f525445445f5349474e41545552455f545950454c696242797465732372656164427974657336363a204f55545f4f465f424f554e44534d6f64756c6555706461746523757064617465496d706c656d656e746174696f6e3a20494e56414c49445f494d504c454d454e544154494f4e5369676e617475726556616c696461746f72237265636f7665725369676e65723a20554e535550504f525445445f5349474e41545552455f545950454c69624279746573237265616455696e743855696e74383a204f55545f4f465f424f554e44534c69624279746573237265616442797465733a204f55545f4f465f424f554e44534c69624279746573237265616455696e7431363a204f55545f4f465f424f554e44534c696242797465732372656164427974657333323a20475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f52455155495245444d6f64756c6553656c6641757468236f6e6c7953656c663a204e4f545f415554484f52495a4544a2646970667358221220aebb8d931ef86555b6441c416b208bb9fe8fe0974c5733ebbccce548296c37ce64736f6c63430007060033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/packages/tests/src/builds/v1/artifacts/MultiCallUtils.ts b/packages/tests/src/builds/v1/artifacts/MultiCallUtils.ts new file mode 100644 index 000000000..4b2bf4f4d --- /dev/null +++ b/packages/tests/src/builds/v1/artifacts/MultiCallUtils.ts @@ -0,0 +1,279 @@ +export const multiCallUtils = { + "_format": "hh-sol-artifact-1", + "contractName": "MultiCallUtils", + "sourceName": "contracts/modules/utils/MultiCallUtils.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_addr", + "type": "address" + } + ], + "name": "callBalanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "callBlockNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_i", + "type": "uint256" + } + ], + "name": "callBlockhash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "callChainId", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addr", + "type": "address" + } + ], + "name": "callCode", + "outputs": [ + { + "internalType": "bytes", + "name": "code", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addr", + "type": "address" + } + ], + "name": "callCodeHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "codeHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addr", + "type": "address" + } + ], + "name": "callCodeSize", + "outputs": [ + { + "internalType": "uint256", + "name": "size", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "callCoinbase", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "callDifficulty", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "callGasLeft", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "callGasLimit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "callGasPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "callOrigin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "callTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "delegateCall", + "type": "bool" + }, + { + "internalType": "bool", + "name": "revertOnError", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct IModuleCalls.Transaction[]", + "name": "_txs", + "type": "tuple[]" + } + ], + "name": "multiCall", + "outputs": [ + { + "internalType": "bool[]", + "name": "_successes", + "type": "bool[]" + }, + { + "internalType": "bytes[]", + "name": "_results", + "type": "bytes[]" + } + ], + "stateMutability": "payable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b50610aac806100206000396000f3fe6080604052600436106100e85760003560e01c8063c272d5c31161008a578063d5b5337f11610059578063d5b5337f14610230578063e90f13e71461021b578063f209883a14610250578063ffd7d74114610265576100e8565b8063c272d5c3146101b9578063c39f2d5c146101ce578063c66764e1146101ee578063d1db39071461021b576100e8565b8063543196eb116100c6578063543196eb1461014d578063984395bc1461016d57806398f9fbc41461018f578063aeea5fb5146101a4576100e8565b80630fdecfac146100ed57806343d9c9351461011857806348acd29f1461012d575b600080fd5b3480156100f957600080fd5b50610102610286565b60405161010f91906108ef565b60405180910390f35b34801561012457600080fd5b5061010261028a565b34801561013957600080fd5b50610102610148366004610649565b610292565b34801561015957600080fd5b50610102610168366004610649565b6102b0565b34801561017957600080fd5b506101826102b4565b60405161010f9190610828565b34801561019b57600080fd5b506101826102b8565b3480156101b057600080fd5b506101026102bc565b3480156101c557600080fd5b506101026102c0565b3480156101da57600080fd5b506101026101e9366004610649565b6102c4565b3480156101fa57600080fd5b5061020e610209366004610649565b6102c8565b60405161010f91906108f8565b34801561022757600080fd5b5061010261030d565b34801561023c57600080fd5b5061010261024b3660046107aa565b610311565b34801561025c57600080fd5b50610102610315565b61027861027336600461066a565b610319565b60405161010f929190610849565b4690565b60005a905090565b73ffffffffffffffffffffffffffffffffffffffff8116315b919050565b3f90565b3290565b4190565b4490565b3a90565b3b90565b60408051603f833b9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092528181529080600060208401853c50919050565b4590565b4090565b4290565b606080825167ffffffffffffffff8111801561033457600080fd5b5060405190808252806020026020018201604052801561035e578160200160208202803683370190505b509150825167ffffffffffffffff8111801561037957600080fd5b506040519080825280602002602001820160405280156103ad57816020015b60608152602001906001900390816103985790505b50905060005b835181101561058c5760008482815181106103ca57fe5b60200260200101519050806000015115610419576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610410906109c5565b60405180910390fd5b80604001515a1015610457576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161041090610968565b806060015173ffffffffffffffffffffffffffffffffffffffff168160800151826040015160001461048d57826040015161048f565b5a5b908360a001516040516104a2919061080c565b600060405180830381858888f193505050503d80600081146104e0576040519150601f19603f3d011682016040523d82523d6000602084013e6104e5565b606091505b508584815181106104f257fe5b6020026020010185858151811061050557fe5b602002602001018290528215151515815250505083828151811061052557fe5b60200260200101518061054d575084828151811061053f57fe5b602002602001015160200151155b610583576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104109061090b565b506001016103b3565b50915091565b803573ffffffffffffffffffffffffffffffffffffffff811681146102ab57600080fd5b803580151581146102ab57600080fd5b600082601f8301126105d6578081fd5b813567ffffffffffffffff8111156105ea57fe5b61061b60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601610a22565b81815284602083860101111561062f578283fd5b816020850160208301379081016020019190915292915050565b60006020828403121561065a578081fd5b61066382610592565b9392505050565b6000602080838503121561067c578182fd5b823567ffffffffffffffff80821115610693578384fd5b818501915085601f8301126106a6578384fd5b8135818111156106b257fe5b6106bf8485830201610a22565b81815284810190848601875b8481101561079b578135870160c0807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0838f03011215610709578a8bfd5b604080518281018181108b8211171561071e57fe5b825261072b848d016105b6565b81526107388285016105b6565b8c8201526060808501358383015260809250610755838601610592565b9082015260a084013582820152918301359189831115610773578c8dfd5b6107818f8d858701016105c6565b60a0820152875250505092870192908701906001016106cb565b50909998505050505050505050565b6000602082840312156107bb578081fd5b5035919050565b600081518084526107da816020860160208601610a46565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000825161081e818460208701610a46565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b604080825283519082018190526000906020906060840190828701845b82811015610884578151151584529284019290840190600101610866565b5050508381038285015284518082528282019080840283018401878501865b8381101561079b577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08684030185526108dd8383516107c2565b948701949250908601906001016108a3565b90815260200190565b60006020825261066360208301846107c2565b60208082526027908201527f4d756c746943616c6c5574696c73236d756c746943616c6c3a2043414c4c5f5260408201527f4556455254454400000000000000000000000000000000000000000000000000606082015260800190565b60208082526028908201527f4d756c746943616c6c5574696c73236d756c746943616c6c3a204e4f545f454e60408201527f4f5547485f474153000000000000000000000000000000000000000000000000606082015260800190565b60208082526032908201527f4d756c746943616c6c5574696c73236d756c746943616c6c3a2064656c65676160408201527f746543616c6c206e6f7420616c6c6f7765640000000000000000000000000000606082015260800190565b60405181810167ffffffffffffffff81118282101715610a3e57fe5b604052919050565b60005b83811015610a61578181015183820152602001610a49565b83811115610a70576000848401525b5050505056fea26469706673582212209bcbc4408d83c4567da8d51b96a29d3d2cf56395e5ac84eee40917a48945daaf64736f6c63430007060033", + "deployedBytecode": "0x6080604052600436106100e85760003560e01c8063c272d5c31161008a578063d5b5337f11610059578063d5b5337f14610230578063e90f13e71461021b578063f209883a14610250578063ffd7d74114610265576100e8565b8063c272d5c3146101b9578063c39f2d5c146101ce578063c66764e1146101ee578063d1db39071461021b576100e8565b8063543196eb116100c6578063543196eb1461014d578063984395bc1461016d57806398f9fbc41461018f578063aeea5fb5146101a4576100e8565b80630fdecfac146100ed57806343d9c9351461011857806348acd29f1461012d575b600080fd5b3480156100f957600080fd5b50610102610286565b60405161010f91906108ef565b60405180910390f35b34801561012457600080fd5b5061010261028a565b34801561013957600080fd5b50610102610148366004610649565b610292565b34801561015957600080fd5b50610102610168366004610649565b6102b0565b34801561017957600080fd5b506101826102b4565b60405161010f9190610828565b34801561019b57600080fd5b506101826102b8565b3480156101b057600080fd5b506101026102bc565b3480156101c557600080fd5b506101026102c0565b3480156101da57600080fd5b506101026101e9366004610649565b6102c4565b3480156101fa57600080fd5b5061020e610209366004610649565b6102c8565b60405161010f91906108f8565b34801561022757600080fd5b5061010261030d565b34801561023c57600080fd5b5061010261024b3660046107aa565b610311565b34801561025c57600080fd5b50610102610315565b61027861027336600461066a565b610319565b60405161010f929190610849565b4690565b60005a905090565b73ffffffffffffffffffffffffffffffffffffffff8116315b919050565b3f90565b3290565b4190565b4490565b3a90565b3b90565b60408051603f833b9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092528181529080600060208401853c50919050565b4590565b4090565b4290565b606080825167ffffffffffffffff8111801561033457600080fd5b5060405190808252806020026020018201604052801561035e578160200160208202803683370190505b509150825167ffffffffffffffff8111801561037957600080fd5b506040519080825280602002602001820160405280156103ad57816020015b60608152602001906001900390816103985790505b50905060005b835181101561058c5760008482815181106103ca57fe5b60200260200101519050806000015115610419576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610410906109c5565b60405180910390fd5b80604001515a1015610457576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161041090610968565b806060015173ffffffffffffffffffffffffffffffffffffffff168160800151826040015160001461048d57826040015161048f565b5a5b908360a001516040516104a2919061080c565b600060405180830381858888f193505050503d80600081146104e0576040519150601f19603f3d011682016040523d82523d6000602084013e6104e5565b606091505b508584815181106104f257fe5b6020026020010185858151811061050557fe5b602002602001018290528215151515815250505083828151811061052557fe5b60200260200101518061054d575084828151811061053f57fe5b602002602001015160200151155b610583576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016104109061090b565b506001016103b3565b50915091565b803573ffffffffffffffffffffffffffffffffffffffff811681146102ab57600080fd5b803580151581146102ab57600080fd5b600082601f8301126105d6578081fd5b813567ffffffffffffffff8111156105ea57fe5b61061b60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601610a22565b81815284602083860101111561062f578283fd5b816020850160208301379081016020019190915292915050565b60006020828403121561065a578081fd5b61066382610592565b9392505050565b6000602080838503121561067c578182fd5b823567ffffffffffffffff80821115610693578384fd5b818501915085601f8301126106a6578384fd5b8135818111156106b257fe5b6106bf8485830201610a22565b81815284810190848601875b8481101561079b578135870160c0807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0838f03011215610709578a8bfd5b604080518281018181108b8211171561071e57fe5b825261072b848d016105b6565b81526107388285016105b6565b8c8201526060808501358383015260809250610755838601610592565b9082015260a084013582820152918301359189831115610773578c8dfd5b6107818f8d858701016105c6565b60a0820152875250505092870192908701906001016106cb565b50909998505050505050505050565b6000602082840312156107bb578081fd5b5035919050565b600081518084526107da816020860160208601610a46565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000825161081e818460208701610a46565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b604080825283519082018190526000906020906060840190828701845b82811015610884578151151584529284019290840190600101610866565b5050508381038285015284518082528282019080840283018401878501865b8381101561079b577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08684030185526108dd8383516107c2565b948701949250908601906001016108a3565b90815260200190565b60006020825261066360208301846107c2565b60208082526027908201527f4d756c746943616c6c5574696c73236d756c746943616c6c3a2043414c4c5f5260408201527f4556455254454400000000000000000000000000000000000000000000000000606082015260800190565b60208082526028908201527f4d756c746943616c6c5574696c73236d756c746943616c6c3a204e4f545f454e60408201527f4f5547485f474153000000000000000000000000000000000000000000000000606082015260800190565b60208082526032908201527f4d756c746943616c6c5574696c73236d756c746943616c6c3a2064656c65676160408201527f746543616c6c206e6f7420616c6c6f7765640000000000000000000000000000606082015260800190565b60405181810167ffffffffffffffff81118282101715610a3e57fe5b604052919050565b60005b83811015610a61578181015183820152602001610a49565b83811115610a70576000848401525b5050505056fea26469706673582212209bcbc4408d83c4567da8d51b96a29d3d2cf56395e5ac84eee40917a48945daaf64736f6c63430007060033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/packages/tests/src/builds/v1/artifacts/SequenceUtils.ts b/packages/tests/src/builds/v1/artifacts/SequenceUtils.ts new file mode 100644 index 000000000..aff74762f --- /dev/null +++ b/packages/tests/src/builds/v1/artifacts/SequenceUtils.ts @@ -0,0 +1,525 @@ +export const sequenceUtils = { + "_format": "hh-sol-artifact-1", + "contractName": "SequenceUtils", + "sourceName": "contracts/modules/utils/SequenceUtils.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_factory", + "type": "address" + }, + { + "internalType": "address", + "name": "_mainModule", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_wallet", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "_imageHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "_signers", + "type": "bytes" + } + ], + "name": "RequiredConfig", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_wallet", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_signer", + "type": "address" + } + ], + "name": "RequiredSigner", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addr", + "type": "address" + } + ], + "name": "callBalanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "callBlockNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_i", + "type": "uint256" + } + ], + "name": "callBlockhash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "callChainId", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addr", + "type": "address" + } + ], + "name": "callCode", + "outputs": [ + { + "internalType": "bytes", + "name": "code", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addr", + "type": "address" + } + ], + "name": "callCodeHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "codeHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_addr", + "type": "address" + } + ], + "name": "callCodeSize", + "outputs": [ + { + "internalType": "uint256", + "name": "size", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "callCoinbase", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "callDifficulty", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "callGasLeft", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "callGasLimit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "callGasPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "callOrigin", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "callTimestamp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "knownImageHashes", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "lastImageHashUpdate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "lastSignerUpdate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "lastWalletUpdate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "delegateCall", + "type": "bool" + }, + { + "internalType": "bool", + "name": "revertOnError", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct IModuleCalls.Transaction[]", + "name": "_txs", + "type": "tuple[]" + } + ], + "name": "multiCall", + "outputs": [ + { + "internalType": "bool[]", + "name": "_successes", + "type": "bool[]" + }, + { + "internalType": "bytes[]", + "name": "_results", + "type": "bytes[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_wallet", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_threshold", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "weight", + "type": "uint256" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + } + ], + "internalType": "struct RequireUtils.Member[]", + "name": "_members", + "type": "tuple[]" + }, + { + "internalType": "bool", + "name": "_index", + "type": "bool" + } + ], + "name": "publishConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_sizeMembers", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "_index", + "type": "bool" + } + ], + "name": "publishInitialSigners", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_wallet", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_nonce", + "type": "uint256" + } + ], + "name": "requireMinNonce", + "outputs": [], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_expiration", + "type": "uint256" + } + ], + "name": "requireNonExpired", + "outputs": [], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x60c06040523480156200001157600080fd5b5060405162002ad638038062002ad68339810160408190526200003491620000cd565b8181816001600160a01b031660a0816001600160a01b031660601b8152505060405180606001604052806028815260200162002aae60289139816001600160a01b03166040516020016200008a92919062000104565b60408051601f198184030181529190528051602090910120608052506200014692505050565b80516001600160a01b0381168114620000c857600080fd5b919050565b60008060408385031215620000e0578182fd5b620000eb83620000b0565b9150620000fb60208401620000b0565b90509250929050565b60008351815b818110156200012657602081870181015185830152016200010a565b81811115620001355782828501525b509190910191825250602001919050565b60805160a05160601c61293762000177600039806106515280610b1b5250806106755280610b3f52506129376000f3fe6080604052600436106101805760003560e01c806398f9fbc4116100d6578063d1db39071161007f578063e90f13e711610059578063e90f13e714610395578063f209883a146103ea578063ffd7d741146103ff57610180565b8063d1db390714610395578063d5b5337f146103aa578063e717aba9146103ca57610180565b8063c272d5c3116100b0578063c272d5c314610333578063c39f2d5c14610348578063c66764e11461036857610180565b806398f9fbc4146102e9578063aeea5fb5146102fe578063b472f0a21461031357610180565b806348acd29f116101385780637ae99638116101125780637ae99638146102875780637f29d538146102a7578063984395bc146102c757610180565b806348acd29f14610227578063543196eb146102475780637082503b1461026757610180565b80631cd05dc4116101695780631cd05dc4146101d057806343d9c935146101f057806344d466c21461020557610180565b80630fdecfac146101855780631551f0ab146101b0575b600080fd5b34801561019157600080fd5b5061019a610420565b6040516101a79190612190565b60405180910390f35b3480156101bc57600080fd5b5061019a6101cb366004611e76565b610424565b3480156101dc57600080fd5b5061019a6101eb366004611bea565b610436565b3480156101fc57600080fd5b5061019a610448565b34801561021157600080fd5b50610225610220366004611ca4565b610450565b005b34801561023357600080fd5b5061019a610242366004611bea565b61080a565b34801561025357600080fd5b5061019a610262366004611bea565b610828565b34801561027357600080fd5b50610225610282366004611c0b565b61082c565b34801561029357600080fd5b5061019a6102a2366004611bea565b610cb0565b3480156102b357600080fd5b506102256102c2366004611e76565b610cc2565b3480156102d357600080fd5b506102dc610cfe565b6040516101a79190612000565b3480156102f557600080fd5b506102dc610d02565b34801561030a57600080fd5b5061019a610d06565b34801561031f57600080fd5b5061022561032e366004611c7b565b610d0a565b34801561033f57600080fd5b5061019a610de8565b34801561035457600080fd5b5061019a610363366004611bea565b610dec565b34801561037457600080fd5b50610388610383366004611bea565b610df0565b6040516101a791906121c5565b3480156103a157600080fd5b5061019a610e35565b3480156103b657600080fd5b5061019a6103c5366004611e76565b610e39565b3480156103d657600080fd5b5061019a6103e5366004611bea565b610e3d565b3480156103f657600080fd5b5061019a610e4f565b61041261040d366004611d34565b610e53565b6040516101a7929190612021565b4690565b60036020526000908152604090205481565b60006020819052908152604090205481565b60005a905090565b8360005b838110156104e9578185858381811061046957fe5b9050604002016000013586868481811061047f57fe5b90506040020160200160208101906104979190611bea565b6040516020016104a993929190612199565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101209150600101610454565b506000808773ffffffffffffffffffffffffffffffffffffffff166351605d8060e01b60405160200161051c9190611f54565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529082905261055491611f81565b6000604051808303816000865af19150503d8060008114610591576040519150601f19603f3d011682016040523d82523d6000602084013e610596565b606091505b50915091508180156105a9575080516020145b1561060e576000818060200190518101906105c49190611e8e565b9050838114610608576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff90612543565b60405180910390fd5b50610732565b60405173ffffffffffffffffffffffffffffffffffffffff89169061069d907fff00000000000000000000000000000000000000000000000000000000000000907f00000000000000000000000000000000000000000000000000000000000000009087907f000000000000000000000000000000000000000000000000000000000000000090602001611ef0565b6040516020818303038152906040528051906020012060001c73ffffffffffffffffffffffffffffffffffffffff1614610703576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff906125a0565b83156107325773ffffffffffffffffffffffffffffffffffffffff881660009081526002602052604090208390555b828873ffffffffffffffffffffffffffffffffffffffff167fb502b7446ca079086188acf3abef47c2f464f2ee9a72fcdf05ffcb74dcc17cee89898960405160200161077f9291906120c7565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526107b89291612623565b60405180910390a383156108005773ffffffffffffffffffffffffffffffffffffffff8816600090815260016020908152604080832043908190558684526003909252909120555b5050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116315b919050565b3f90565b600080610838846110c3565b9150915060008046905080898960405160200161085793929190611f9d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012091505061ffff831660008767ffffffffffffffff811180156108ae57600080fd5b506040519080825280602002602001820160405280156108e857816020015b6108d5611b1c565b8152602001906001900390816108cd5790505b50905060005b8751851015610a9f57600080806109058b89611131565b995060ff9182169450169150600183141561092d576109248b896111b2565b98509050610a20565b8261095f57606061093e8c8a61122a565b9950905061094c88826112db565b91506109598f838d611665565b50610a20565b60028314156109ee576109728b896111b2565b9850905060006109828c8a6116f3565b995061ffff16905060606109978d8b84611764565b9a5090506109a6898483611853565b6109dc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff9061242c565b50506109e98e828c611665565b610a20565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff906121d8565b60405180604001604052808381526020018273ffffffffffffffffffffffffffffffffffffffff16815250858581518110610a5757fe5b60200260200101819052508380600101945050858282604051602001610a7f93929190612199565b6040516020818303038152906040528051906020012095505050506108ee565b888114610ad8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff906124e6565b60405173ffffffffffffffffffffffffffffffffffffffff8c1690610b67907fff00000000000000000000000000000000000000000000000000000000000000907f00000000000000000000000000000000000000000000000000000000000000009087907f000000000000000000000000000000000000000000000000000000000000000090602001611ef0565b6040516020818303038152906040528051906020012060001c73ffffffffffffffffffffffffffffffffffffffff1614610bcd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff906123a9565b828b73ffffffffffffffffffffffffffffffffffffffff167fb502b7446ca079086188acf3abef47c2f464f2ee9a72fcdf05ffcb74dcc17cee8885604051602001610c18919061212b565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815290829052610c5192916125fe565b60405180910390a38615610ca35773ffffffffffffffffffffffffffffffffffffffff8b1660008181526001602090815260408083204390819055878452600383528184205592825260029052208390555b5050505050505050505050565b60026020526000908152604090205481565b804210610cfb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff9061234c565b50565b3290565b4190565b4490565b600080610d1683611a9b565b9150915060008473ffffffffffffffffffffffffffffffffffffffff16638c3f5563846040518263ffffffff1660e01b8152600401610d559190612190565b60206040518083038186803b158015610d6d57600080fd5b505afa158015610d81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610da59190611e8e565b905081811015610de1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff906122ef565b5050505050565b3a90565b3b90565b60408051603f833b9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092528181529080600060208401853c50919050565b4590565b4090565b60016020526000908152604090205481565b4290565b606080825167ffffffffffffffff81118015610e6e57600080fd5b50604051908082528060200260200182016040528015610e98578160200160208202803683370190505b509150825167ffffffffffffffff81118015610eb357600080fd5b50604051908082528060200260200182016040528015610ee757816020015b6060815260200190600190039081610ed25790505b50905060005b83518110156110bd576000848281518110610f0457fe5b60200260200101519050806000015115610f4a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff90612489565b80604001515a1015610f88576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff90612292565b806060015173ffffffffffffffffffffffffffffffffffffffff1681608001518260400151600014610fbe578260400151610fc0565b5a5b908360a00151604051610fd39190611f81565b600060405180830381858888f193505050503d8060008114611011576040519150601f19603f3d011682016040523d82523d6000602084013e611016565b606091505b5085848151811061102357fe5b6020026020010185858151811061103657fe5b602002602001018290528215151515815250505083828151811061105657fe5b60200260200101518061107e575084828151811061107057fe5b602002602001015160200151155b6110b4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff90612235565b50600101610eed565b50915091565b6020810151815160f09190911c9060029081111561112c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061272b6027913960400191505060405180910390fd5b915091565b8082016020015160f881901c9060f01c60ff166002830183811161115157fe5b84518111156111ab576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602681526020018061285d6026913960400191505060405180910390fd5b9250925092565b8082016020015160601c601482018281116111c957fe5b8351811115611223576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806127086023913960400191505060405180910390fd5b9250929050565b60408051604280825260808201909252606091600091906020820181803683370190505091508284016020018051602084015260208101516040840152602281015160428401525060428301905082811161128157fe5b8351811115611223576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806127fe6023913960400191505060405180910390fd5b60008151604214611337576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a8152602001806126ce603a913960400191505060405180910390fd5b60008260018451038151811061134957fe5b602001015160f81c60f81b60f81c60ff16905060008360408151811061136b57fe5b016020015160f81c905060006113818582611ab4565b90506000611390866020611ab4565b90507f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a081111561140b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180612691603d913960400191505060405180910390fd5b8260ff16601b1415801561142357508260ff16601c14155b15611479576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180612752603d913960400191505060405180910390fd5b60018414156114ed5760018784848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156114dc573d6000803e3d6000fd5b5050506020604051035194506115ef565b600284141561159e5760018760405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c018281526020019150506040516020818303038152906040528051906020012084848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156114dc573d6000803e3d6000fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180612821603c913960400191505060405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff851661165b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603081526020018061278f6030913960400191505060405180910390fd5b5050505092915050565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f600ba597427f042bcd559a0d06fa1732cc104d6dd43cbe8845b5a0e804b2b39f60405160405180910390a380156116ee5773ffffffffffffffffffffffffffffffffffffffff821660009081526020819052604090204390555b505050565b8082016020015160f01c6002820182811161170a57fe5b8351811115611223576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806128a46022913960400191505060405180910390fd5b606060008267ffffffffffffffff8111801561177f57600080fd5b506040519080825280601f01601f1916602001820160405280156117aa576020820181803683370190505b509150838501602001600060205b858110156117d1579082015184820152602081016117b8565b84860160200180519390920151908501525250828201838110156117f157fe5b845181111561184b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806128836021913960400191505060405180910390fd5b935093915050565b6000808260018451038151811061186657fe5b016020015160f81c9050600181148061187f5750600281145b156118c3578373ffffffffffffffffffffffffffffffffffffffff166118a586856112db565b73ffffffffffffffffffffffffffffffffffffffff16149150611a93565b6003811415611a425782517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018452604080517f1626ba7e000000000000000000000000000000000000000000000000000000008152600481018881526024820192835286516044830152865173ffffffffffffffffffffffffffffffffffffffff891693631626ba7e938b938a9390929160640190602085019080838360005b8381101561197d578181015183820152602001611965565b50505050905090810190601f1680156119aa5780820380516001836020036101000a031916815260200191505b50935050505060206040518083038186803b1580156119c857600080fd5b505afa1580156119dc573d6000803e3d6000fd5b505050506040513d60208110156119f257600080fd5b50519084527fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e00000000000000000000000000000000000000000000000000000000149150611a93565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f8152602001806127bf603f913960400191505060405180910390fd5b509392505050565b606081901c916bffffffffffffffffffffffff90911690565b60008160200183511015611b13576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c8152602001806128c6603c913960400191505060405180910390fd5b50016020015190565b604080518082019091526000808252602082015290565b803573ffffffffffffffffffffffffffffffffffffffff8116811461082357600080fd5b8035801515811461082357600080fd5b600082601f830112611b77578081fd5b813567ffffffffffffffff811115611b8b57fe5b611bbc60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161263c565b818152846020838601011115611bd0578283fd5b816020850160208301379081016020019190915292915050565b600060208284031215611bfb578081fd5b611c0482611b33565b9392505050565b600080600080600060a08688031215611c22578081fd5b611c2b86611b33565b94506020860135935060408601359250606086013567ffffffffffffffff811115611c54578182fd5b611c6088828901611b67565b925050611c6f60808701611b57565b90509295509295909350565b60008060408385031215611c8d578182fd5b611c9683611b33565b946020939093013593505050565b600080600080600060808688031215611cbb578081fd5b611cc486611b33565b945060208601359350604086013567ffffffffffffffff80821115611ce7578283fd5b818801915088601f830112611cfa578283fd5b813581811115611d08578384fd5b896020604083028501011115611d1c578384fd5b602083019550809450505050611c6f60608701611b57565b60006020808385031215611d46578182fd5b823567ffffffffffffffff80821115611d5d578384fd5b818501915085601f830112611d70578384fd5b813581811115611d7c57fe5b611d89848583020161263c565b81815284810190848601875b84811015611e67578135870160c0807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0838f03011215611dd3578a8bfd5b604080518281018181108b82111715611de857fe5b8252611df5848d01611b57565b8152611e02828501611b57565b8c82015260608085013583830152611e1c60808601611b33565b9082015260a08481013560808301529284013592915089831115611e3e578c8dfd5b611e4c8f8d85870101611b67565b91810191909152865250509287019290870190600101611d95565b50909998505050505050505050565b600060208284031215611e87578081fd5b5035919050565b600060208284031215611e9f578081fd5b5051919050565b60008151808452611ebe816020860160208601612660565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b7fff0000000000000000000000000000000000000000000000000000000000000094909416845260609290921b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660018401526015830152603582015260550190565b7fffffffff0000000000000000000000000000000000000000000000000000000091909116815260040190565b60008251611f93818460208701612660565b9190910192915050565b7f19010000000000000000000000000000000000000000000000000000000000008152600281019390935260609190911b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166022830152603682015260560190565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b604080825283519082018190526000906020906060840190828701845b8281101561205c57815115158452928401929084019060010161203e565b5050508381038285015284518082528282019080840283018401878501865b83811015611e67577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08684030185526120b5838351611ea6565b9487019492509086019060010161207b565b6020808252818101839052600090604080840186845b8781101561211e578135835273ffffffffffffffffffffffffffffffffffffffff612109868401611b33565b168386015291830191908301906001016120dd565b5090979650505050505050565b602080825282518282018190526000919060409081850190868401855b828110156121835781518051855286015173ffffffffffffffffffffffffffffffffffffffff16868501529284019290850190600101612148565b5091979650505050505050565b90815260200190565b928352602083019190915273ffffffffffffffffffffffffffffffffffffffff16604082015260600190565b600060208252611c046020830184611ea6565b6020808252603a908201527f526571756972655574696c73237075626c697368496e697469616c5369676e6560408201527f72733a20494e56414c49445f5349474e41545552455f464c4147000000000000606082015260800190565b60208082526027908201527f4d756c746943616c6c5574696c73236d756c746943616c6c3a2043414c4c5f5260408201527f4556455254454400000000000000000000000000000000000000000000000000606082015260800190565b60208082526028908201527f4d756c746943616c6c5574696c73236d756c746943616c6c3a204e4f545f454e60408201527f4f5547485f474153000000000000000000000000000000000000000000000000606082015260800190565b60208082526032908201527f526571756972655574696c7323726571756972654d696e4e6f6e63653a204e4f60408201527f4e43455f42454c4f575f52455155495245440000000000000000000000000000606082015260800190565b60208082526027908201527f526571756972655574696c7323726571756972654e6f6e457870697265643a2060408201527f4558504952454400000000000000000000000000000000000000000000000000606082015260800190565b60208082526048908201527f526571756972655574696c73237075626c697368496e697469616c5369676e6560408201527f72733a20554e45585045435445445f434f554e5445524641435455414c5f494d60608201527f4147455f48415348000000000000000000000000000000000000000000000000608082015260a00190565b60208082526032908201527f4d6f64756c6541757468235f7369676e617475726556616c69646174696f6e3a60408201527f20494e56414c49445f5349474e41545552450000000000000000000000000000606082015260800190565b60208082526032908201527f4d756c746943616c6c5574696c73236d756c746943616c6c3a2064656c65676160408201527f746543616c6c206e6f7420616c6c6f7765640000000000000000000000000000606082015260800190565b60208082526039908201527f526571756972655574696c73237075626c697368496e697469616c5369676e6560408201527f72733a20494e56414c49445f4d454d424552535f434f554e5400000000000000606082015260800190565b60208082526031908201527f526571756972655574696c73237075626c697368436f6e6669673a20554e455860408201527f5045435445445f494d4147455f48415348000000000000000000000000000000606082015260800190565b602080825260409082018190527f526571756972655574696c73237075626c697368436f6e6669673a20554e4558908201527f5045435445445f434f554e5445524641435455414c5f494d4147455f48415348606082015260800190565b600061ffff841682526040602083015261261b6040830184611ea6565b949350505050565b60008382526040602083015261261b6040830184611ea6565b60405181810167ffffffffffffffff8111828210171561265857fe5b604052919050565b60005b8381101561267b578181015183820152602001612663565b8381111561268a576000848401525b5050505056fe5369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202773272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265206c656e6774684c696242797465732372656164416464726573733a204f55545f4f465f424f554e44534c696242797465732372656164466972737455696e7431363a204f55545f4f465f424f554e44535369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202776272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20494e56414c49445f5349474e45525369676e617475726556616c696461746f7223697356616c69645369676e61747572653a20554e535550504f525445445f5349474e41545552455f545950454c696242797465732372656164427974657336363a204f55545f4f465f424f554e44535369676e617475726556616c696461746f72237265636f7665725369676e65723a20554e535550504f525445445f5349474e41545552455f545950454c69624279746573237265616455696e743855696e74383a204f55545f4f465f424f554e44534c69624279746573237265616442797465733a204f55545f4f465f424f554e44534c69624279746573237265616455696e7431363a204f55545f4f465f424f554e44534c696242797465732372656164427974657333323a20475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f5245515549524544a26469706673582212200abb842b6eea58df953f048e3a9aa7589fd3ce15ca086e43b61cdb0c0c42723564736f6c63430007060033603a600e3d39601a805130553df3363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3", + "deployedBytecode": "0x6080604052600436106101805760003560e01c806398f9fbc4116100d6578063d1db39071161007f578063e90f13e711610059578063e90f13e714610395578063f209883a146103ea578063ffd7d741146103ff57610180565b8063d1db390714610395578063d5b5337f146103aa578063e717aba9146103ca57610180565b8063c272d5c3116100b0578063c272d5c314610333578063c39f2d5c14610348578063c66764e11461036857610180565b806398f9fbc4146102e9578063aeea5fb5146102fe578063b472f0a21461031357610180565b806348acd29f116101385780637ae99638116101125780637ae99638146102875780637f29d538146102a7578063984395bc146102c757610180565b806348acd29f14610227578063543196eb146102475780637082503b1461026757610180565b80631cd05dc4116101695780631cd05dc4146101d057806343d9c935146101f057806344d466c21461020557610180565b80630fdecfac146101855780631551f0ab146101b0575b600080fd5b34801561019157600080fd5b5061019a610420565b6040516101a79190612190565b60405180910390f35b3480156101bc57600080fd5b5061019a6101cb366004611e76565b610424565b3480156101dc57600080fd5b5061019a6101eb366004611bea565b610436565b3480156101fc57600080fd5b5061019a610448565b34801561021157600080fd5b50610225610220366004611ca4565b610450565b005b34801561023357600080fd5b5061019a610242366004611bea565b61080a565b34801561025357600080fd5b5061019a610262366004611bea565b610828565b34801561027357600080fd5b50610225610282366004611c0b565b61082c565b34801561029357600080fd5b5061019a6102a2366004611bea565b610cb0565b3480156102b357600080fd5b506102256102c2366004611e76565b610cc2565b3480156102d357600080fd5b506102dc610cfe565b6040516101a79190612000565b3480156102f557600080fd5b506102dc610d02565b34801561030a57600080fd5b5061019a610d06565b34801561031f57600080fd5b5061022561032e366004611c7b565b610d0a565b34801561033f57600080fd5b5061019a610de8565b34801561035457600080fd5b5061019a610363366004611bea565b610dec565b34801561037457600080fd5b50610388610383366004611bea565b610df0565b6040516101a791906121c5565b3480156103a157600080fd5b5061019a610e35565b3480156103b657600080fd5b5061019a6103c5366004611e76565b610e39565b3480156103d657600080fd5b5061019a6103e5366004611bea565b610e3d565b3480156103f657600080fd5b5061019a610e4f565b61041261040d366004611d34565b610e53565b6040516101a7929190612021565b4690565b60036020526000908152604090205481565b60006020819052908152604090205481565b60005a905090565b8360005b838110156104e9578185858381811061046957fe5b9050604002016000013586868481811061047f57fe5b90506040020160200160208101906104979190611bea565b6040516020016104a993929190612199565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101209150600101610454565b506000808773ffffffffffffffffffffffffffffffffffffffff166351605d8060e01b60405160200161051c9190611f54565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529082905261055491611f81565b6000604051808303816000865af19150503d8060008114610591576040519150601f19603f3d011682016040523d82523d6000602084013e610596565b606091505b50915091508180156105a9575080516020145b1561060e576000818060200190518101906105c49190611e8e565b9050838114610608576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff90612543565b60405180910390fd5b50610732565b60405173ffffffffffffffffffffffffffffffffffffffff89169061069d907fff00000000000000000000000000000000000000000000000000000000000000907f00000000000000000000000000000000000000000000000000000000000000009087907f000000000000000000000000000000000000000000000000000000000000000090602001611ef0565b6040516020818303038152906040528051906020012060001c73ffffffffffffffffffffffffffffffffffffffff1614610703576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff906125a0565b83156107325773ffffffffffffffffffffffffffffffffffffffff881660009081526002602052604090208390555b828873ffffffffffffffffffffffffffffffffffffffff167fb502b7446ca079086188acf3abef47c2f464f2ee9a72fcdf05ffcb74dcc17cee89898960405160200161077f9291906120c7565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526107b89291612623565b60405180910390a383156108005773ffffffffffffffffffffffffffffffffffffffff8816600090815260016020908152604080832043908190558684526003909252909120555b5050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116315b919050565b3f90565b600080610838846110c3565b9150915060008046905080898960405160200161085793929190611f9d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012091505061ffff831660008767ffffffffffffffff811180156108ae57600080fd5b506040519080825280602002602001820160405280156108e857816020015b6108d5611b1c565b8152602001906001900390816108cd5790505b50905060005b8751851015610a9f57600080806109058b89611131565b995060ff9182169450169150600183141561092d576109248b896111b2565b98509050610a20565b8261095f57606061093e8c8a61122a565b9950905061094c88826112db565b91506109598f838d611665565b50610a20565b60028314156109ee576109728b896111b2565b9850905060006109828c8a6116f3565b995061ffff16905060606109978d8b84611764565b9a5090506109a6898483611853565b6109dc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff9061242c565b50506109e98e828c611665565b610a20565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff906121d8565b60405180604001604052808381526020018273ffffffffffffffffffffffffffffffffffffffff16815250858581518110610a5757fe5b60200260200101819052508380600101945050858282604051602001610a7f93929190612199565b6040516020818303038152906040528051906020012095505050506108ee565b888114610ad8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff906124e6565b60405173ffffffffffffffffffffffffffffffffffffffff8c1690610b67907fff00000000000000000000000000000000000000000000000000000000000000907f00000000000000000000000000000000000000000000000000000000000000009087907f000000000000000000000000000000000000000000000000000000000000000090602001611ef0565b6040516020818303038152906040528051906020012060001c73ffffffffffffffffffffffffffffffffffffffff1614610bcd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff906123a9565b828b73ffffffffffffffffffffffffffffffffffffffff167fb502b7446ca079086188acf3abef47c2f464f2ee9a72fcdf05ffcb74dcc17cee8885604051602001610c18919061212b565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815290829052610c5192916125fe565b60405180910390a38615610ca35773ffffffffffffffffffffffffffffffffffffffff8b1660008181526001602090815260408083204390819055878452600383528184205592825260029052208390555b5050505050505050505050565b60026020526000908152604090205481565b804210610cfb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff9061234c565b50565b3290565b4190565b4490565b600080610d1683611a9b565b9150915060008473ffffffffffffffffffffffffffffffffffffffff16638c3f5563846040518263ffffffff1660e01b8152600401610d559190612190565b60206040518083038186803b158015610d6d57600080fd5b505afa158015610d81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610da59190611e8e565b905081811015610de1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff906122ef565b5050505050565b3a90565b3b90565b60408051603f833b9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092528181529080600060208401853c50919050565b4590565b4090565b60016020526000908152604090205481565b4290565b606080825167ffffffffffffffff81118015610e6e57600080fd5b50604051908082528060200260200182016040528015610e98578160200160208202803683370190505b509150825167ffffffffffffffff81118015610eb357600080fd5b50604051908082528060200260200182016040528015610ee757816020015b6060815260200190600190039081610ed25790505b50905060005b83518110156110bd576000848281518110610f0457fe5b60200260200101519050806000015115610f4a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff90612489565b80604001515a1015610f88576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff90612292565b806060015173ffffffffffffffffffffffffffffffffffffffff1681608001518260400151600014610fbe578260400151610fc0565b5a5b908360a00151604051610fd39190611f81565b600060405180830381858888f193505050503d8060008114611011576040519150601f19603f3d011682016040523d82523d6000602084013e611016565b606091505b5085848151811061102357fe5b6020026020010185858151811061103657fe5b602002602001018290528215151515815250505083828151811061105657fe5b60200260200101518061107e575084828151811061107057fe5b602002602001015160200151155b6110b4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff90612235565b50600101610eed565b50915091565b6020810151815160f09190911c9060029081111561112c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061272b6027913960400191505060405180910390fd5b915091565b8082016020015160f881901c9060f01c60ff166002830183811161115157fe5b84518111156111ab576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602681526020018061285d6026913960400191505060405180910390fd5b9250925092565b8082016020015160601c601482018281116111c957fe5b8351811115611223576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806127086023913960400191505060405180910390fd5b9250929050565b60408051604280825260808201909252606091600091906020820181803683370190505091508284016020018051602084015260208101516040840152602281015160428401525060428301905082811161128157fe5b8351811115611223576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806127fe6023913960400191505060405180910390fd5b60008151604214611337576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a8152602001806126ce603a913960400191505060405180910390fd5b60008260018451038151811061134957fe5b602001015160f81c60f81b60f81c60ff16905060008360408151811061136b57fe5b016020015160f81c905060006113818582611ab4565b90506000611390866020611ab4565b90507f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a081111561140b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180612691603d913960400191505060405180910390fd5b8260ff16601b1415801561142357508260ff16601c14155b15611479576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180612752603d913960400191505060405180910390fd5b60018414156114ed5760018784848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156114dc573d6000803e3d6000fd5b5050506020604051035194506115ef565b600284141561159e5760018760405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c018281526020019150506040516020818303038152906040528051906020012084848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156114dc573d6000803e3d6000fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180612821603c913960400191505060405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff851661165b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603081526020018061278f6030913960400191505060405180910390fd5b5050505092915050565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f600ba597427f042bcd559a0d06fa1732cc104d6dd43cbe8845b5a0e804b2b39f60405160405180910390a380156116ee5773ffffffffffffffffffffffffffffffffffffffff821660009081526020819052604090204390555b505050565b8082016020015160f01c6002820182811161170a57fe5b8351811115611223576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806128a46022913960400191505060405180910390fd5b606060008267ffffffffffffffff8111801561177f57600080fd5b506040519080825280601f01601f1916602001820160405280156117aa576020820181803683370190505b509150838501602001600060205b858110156117d1579082015184820152602081016117b8565b84860160200180519390920151908501525250828201838110156117f157fe5b845181111561184b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806128836021913960400191505060405180910390fd5b935093915050565b6000808260018451038151811061186657fe5b016020015160f81c9050600181148061187f5750600281145b156118c3578373ffffffffffffffffffffffffffffffffffffffff166118a586856112db565b73ffffffffffffffffffffffffffffffffffffffff16149150611a93565b6003811415611a425782517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018452604080517f1626ba7e000000000000000000000000000000000000000000000000000000008152600481018881526024820192835286516044830152865173ffffffffffffffffffffffffffffffffffffffff891693631626ba7e938b938a9390929160640190602085019080838360005b8381101561197d578181015183820152602001611965565b50505050905090810190601f1680156119aa5780820380516001836020036101000a031916815260200191505b50935050505060206040518083038186803b1580156119c857600080fd5b505afa1580156119dc573d6000803e3d6000fd5b505050506040513d60208110156119f257600080fd5b50519084527fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e00000000000000000000000000000000000000000000000000000000149150611a93565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f8152602001806127bf603f913960400191505060405180910390fd5b509392505050565b606081901c916bffffffffffffffffffffffff90911690565b60008160200183511015611b13576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c8152602001806128c6603c913960400191505060405180910390fd5b50016020015190565b604080518082019091526000808252602082015290565b803573ffffffffffffffffffffffffffffffffffffffff8116811461082357600080fd5b8035801515811461082357600080fd5b600082601f830112611b77578081fd5b813567ffffffffffffffff811115611b8b57fe5b611bbc60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161263c565b818152846020838601011115611bd0578283fd5b816020850160208301379081016020019190915292915050565b600060208284031215611bfb578081fd5b611c0482611b33565b9392505050565b600080600080600060a08688031215611c22578081fd5b611c2b86611b33565b94506020860135935060408601359250606086013567ffffffffffffffff811115611c54578182fd5b611c6088828901611b67565b925050611c6f60808701611b57565b90509295509295909350565b60008060408385031215611c8d578182fd5b611c9683611b33565b946020939093013593505050565b600080600080600060808688031215611cbb578081fd5b611cc486611b33565b945060208601359350604086013567ffffffffffffffff80821115611ce7578283fd5b818801915088601f830112611cfa578283fd5b813581811115611d08578384fd5b896020604083028501011115611d1c578384fd5b602083019550809450505050611c6f60608701611b57565b60006020808385031215611d46578182fd5b823567ffffffffffffffff80821115611d5d578384fd5b818501915085601f830112611d70578384fd5b813581811115611d7c57fe5b611d89848583020161263c565b81815284810190848601875b84811015611e67578135870160c0807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0838f03011215611dd3578a8bfd5b604080518281018181108b82111715611de857fe5b8252611df5848d01611b57565b8152611e02828501611b57565b8c82015260608085013583830152611e1c60808601611b33565b9082015260a08481013560808301529284013592915089831115611e3e578c8dfd5b611e4c8f8d85870101611b67565b91810191909152865250509287019290870190600101611d95565b50909998505050505050505050565b600060208284031215611e87578081fd5b5035919050565b600060208284031215611e9f578081fd5b5051919050565b60008151808452611ebe816020860160208601612660565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b7fff0000000000000000000000000000000000000000000000000000000000000094909416845260609290921b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660018401526015830152603582015260550190565b7fffffffff0000000000000000000000000000000000000000000000000000000091909116815260040190565b60008251611f93818460208701612660565b9190910192915050565b7f19010000000000000000000000000000000000000000000000000000000000008152600281019390935260609190911b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166022830152603682015260560190565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b604080825283519082018190526000906020906060840190828701845b8281101561205c57815115158452928401929084019060010161203e565b5050508381038285015284518082528282019080840283018401878501865b83811015611e67577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08684030185526120b5838351611ea6565b9487019492509086019060010161207b565b6020808252818101839052600090604080840186845b8781101561211e578135835273ffffffffffffffffffffffffffffffffffffffff612109868401611b33565b168386015291830191908301906001016120dd565b5090979650505050505050565b602080825282518282018190526000919060409081850190868401855b828110156121835781518051855286015173ffffffffffffffffffffffffffffffffffffffff16868501529284019290850190600101612148565b5091979650505050505050565b90815260200190565b928352602083019190915273ffffffffffffffffffffffffffffffffffffffff16604082015260600190565b600060208252611c046020830184611ea6565b6020808252603a908201527f526571756972655574696c73237075626c697368496e697469616c5369676e6560408201527f72733a20494e56414c49445f5349474e41545552455f464c4147000000000000606082015260800190565b60208082526027908201527f4d756c746943616c6c5574696c73236d756c746943616c6c3a2043414c4c5f5260408201527f4556455254454400000000000000000000000000000000000000000000000000606082015260800190565b60208082526028908201527f4d756c746943616c6c5574696c73236d756c746943616c6c3a204e4f545f454e60408201527f4f5547485f474153000000000000000000000000000000000000000000000000606082015260800190565b60208082526032908201527f526571756972655574696c7323726571756972654d696e4e6f6e63653a204e4f60408201527f4e43455f42454c4f575f52455155495245440000000000000000000000000000606082015260800190565b60208082526027908201527f526571756972655574696c7323726571756972654e6f6e457870697265643a2060408201527f4558504952454400000000000000000000000000000000000000000000000000606082015260800190565b60208082526048908201527f526571756972655574696c73237075626c697368496e697469616c5369676e6560408201527f72733a20554e45585045435445445f434f554e5445524641435455414c5f494d60608201527f4147455f48415348000000000000000000000000000000000000000000000000608082015260a00190565b60208082526032908201527f4d6f64756c6541757468235f7369676e617475726556616c69646174696f6e3a60408201527f20494e56414c49445f5349474e41545552450000000000000000000000000000606082015260800190565b60208082526032908201527f4d756c746943616c6c5574696c73236d756c746943616c6c3a2064656c65676160408201527f746543616c6c206e6f7420616c6c6f7765640000000000000000000000000000606082015260800190565b60208082526039908201527f526571756972655574696c73237075626c697368496e697469616c5369676e6560408201527f72733a20494e56414c49445f4d454d424552535f434f554e5400000000000000606082015260800190565b60208082526031908201527f526571756972655574696c73237075626c697368436f6e6669673a20554e455860408201527f5045435445445f494d4147455f48415348000000000000000000000000000000606082015260800190565b602080825260409082018190527f526571756972655574696c73237075626c697368436f6e6669673a20554e4558908201527f5045435445445f434f554e5445524641435455414c5f494d4147455f48415348606082015260800190565b600061ffff841682526040602083015261261b6040830184611ea6565b949350505050565b60008382526040602083015261261b6040830184611ea6565b60405181810167ffffffffffffffff8111828210171561265857fe5b604052919050565b60005b8381101561267b578181015183820152602001612663565b8381111561268a576000848401525b5050505056fe5369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202773272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265206c656e6774684c696242797465732372656164416464726573733a204f55545f4f465f424f554e44534c696242797465732372656164466972737455696e7431363a204f55545f4f465f424f554e44535369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202776272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20494e56414c49445f5349474e45525369676e617475726556616c696461746f7223697356616c69645369676e61747572653a20554e535550504f525445445f5349474e41545552455f545950454c696242797465732372656164427974657336363a204f55545f4f465f424f554e44535369676e617475726556616c696461746f72237265636f7665725369676e65723a20554e535550504f525445445f5349474e41545552455f545950454c69624279746573237265616455696e743855696e74383a204f55545f4f465f424f554e44534c69624279746573237265616442797465733a204f55545f4f465f424f554e44534c69624279746573237265616455696e7431363a204f55545f4f465f424f554e44534c696242797465732372656164427974657333323a20475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f5245515549524544a26469706673582212200abb842b6eea58df953f048e3a9aa7589fd3ce15ca086e43b61cdb0c0c42723564736f6c63430007060033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/packages/tests/src/builds/v1/index.ts b/packages/tests/src/builds/v1/index.ts new file mode 100644 index 000000000..5af292e41 --- /dev/null +++ b/packages/tests/src/builds/v1/index.ts @@ -0,0 +1,7 @@ + +export { factory } from './artifacts/Factory' +export { guestModule } from './artifacts/GuestModule' +export { mainModule } from './artifacts/MainModule' +export { mainModuleUpgradable } from './artifacts/MainModuleUpgradable' +export { multiCallUtils } from './artifacts/MultiCallUtils' +export { sequenceUtils } from './artifacts/SequenceUtils' diff --git a/packages/tests/src/builds/v2/artifacts/Factory.ts b/packages/tests/src/builds/v2/artifacts/Factory.ts new file mode 100644 index 000000000..4f481b926 --- /dev/null +++ b/packages/tests/src/builds/v2/artifacts/Factory.ts @@ -0,0 +1,35 @@ +export const factory = { + "_format": "hh-sol-artifact-1", + "contractName": "Factory", + "sourceName": "contracts/Factory.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_mainModule", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_salt", + "type": "bytes32" + } + ], + "name": "deploy", + "outputs": [ + { + "internalType": "address", + "name": "_contract", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b5061019a806100206000396000f3fe60806040526004361061001e5760003560e01c806332c02a1414610023575b600080fd5b6100366100313660046100c5565b61005f565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b60008060405180606001604052806028815260200161013d602891398473ffffffffffffffffffffffffffffffffffffffff166040516020016100a392919061010a565b60405160208183030381529060405290508281516020830134f5949350505050565b600080604083850312156100d857600080fd5b823573ffffffffffffffffffffffffffffffffffffffff811681146100fc57600080fd5b946020939093013593505050565b6000835160005b8181101561012b5760208187018101518583015201610111565b50919091019182525060200191905056fe603a600e3d39601a805130553df3363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3a264697066735822122043a67ce1dd84e0676792a0fadb81e020ae20ed22debbddf46c2790ea0338256464736f6c63430008110033", + "deployedBytecode": "0x60806040526004361061001e5760003560e01c806332c02a1414610023575b600080fd5b6100366100313660046100c5565b61005f565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b60008060405180606001604052806028815260200161013d602891398473ffffffffffffffffffffffffffffffffffffffff166040516020016100a392919061010a565b60405160208183030381529060405290508281516020830134f5949350505050565b600080604083850312156100d857600080fd5b823573ffffffffffffffffffffffffffffffffffffffff811681146100fc57600080fd5b946020939093013593505050565b6000835160005b8181101561012b5760208187018101518583015201610111565b50919091019182525060200191905056fe603a600e3d39601a805130553df3363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3a264697066735822122043a67ce1dd84e0676792a0fadb81e020ae20ed22debbddf46c2790ea0338256464736f6c63430008110033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/packages/tests/src/builds/v2/artifacts/GuestModule.ts b/packages/tests/src/builds/v2/artifacts/GuestModule.ts new file mode 100644 index 000000000..c992703f9 --- /dev/null +++ b/packages/tests/src/builds/v2/artifacts/GuestModule.ts @@ -0,0 +1,626 @@ +export const guestModule = { + "_format": "hh-sol-artifact-1", + "contractName": "GuestModule", + "sourceName": "contracts/modules/GuestModule.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "uint256", + "name": "_space", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_provided", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_current", + "type": "uint256" + } + ], + "name": "BadNonce", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_index", + "type": "uint256" + } + ], + "name": "DelegateCallNotAllowed", + "type": "error" + }, + { + "inputs": [], + "name": "ImageHashIsZero", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_addr", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "InvalidNestedSignature", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "_s", + "type": "bytes32" + } + ], + "name": "InvalidSValue", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "InvalidSignature", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_flag", + "type": "uint256" + } + ], + "name": "InvalidSignatureFlag", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "InvalidSignatureLength", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes1", + "name": "_type", + "type": "bytes1" + } + ], + "name": "InvalidSignatureType", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_v", + "type": "uint256" + } + ], + "name": "InvalidVValue", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "threshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_weight", + "type": "uint256" + } + ], + "name": "LowWeightChainedSignature", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_requested", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_available", + "type": "uint256" + } + ], + "name": "NotEnoughGas", + "type": "error" + }, + { + "inputs": [], + "name": "NotSupported", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_sender", + "type": "address" + }, + { + "internalType": "address", + "name": "_self", + "type": "address" + } + ], + "name": "OnlySelfAuth", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "SignerIsAddress0", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_type", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "_recoverMode", + "type": "bool" + } + ], + "name": "UnsupportedSignatureType", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_current", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_prev", + "type": "uint256" + } + ], + "name": "WrongChainedCheckpointOrder", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_contract", + "type": "address" + } + ], + "name": "CreatedContract", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "newImageHash", + "type": "bytes32" + } + ], + "name": "ImageHashUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_space", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_newNonce", + "type": "uint256" + } + ], + "name": "NonceChange", + "type": "event" + }, + { + "anonymous": true, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "_tx", + "type": "bytes32" + } + ], + "name": "TxExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "_tx", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "_reason", + "type": "bytes" + } + ], + "name": "TxFailed", + "type": "event" + }, + { + "inputs": [], + "name": "SET_IMAGE_HASH_TYPE_HASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_code", + "type": "bytes" + } + ], + "name": "createContract", + "outputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "delegateCall", + "type": "bool" + }, + { + "internalType": "bool", + "name": "revertOnError", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct IModuleCalls.Transaction[]", + "name": "_txs", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "execute", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_signatures", + "type": "bytes" + } + ], + "name": "isValidSignature", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_signatures", + "type": "bytes" + } + ], + "name": "isValidSignature", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_space", + "type": "uint256" + } + ], + "name": "readNonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "delegateCall", + "type": "bool" + }, + { + "internalType": "bool", + "name": "revertOnError", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct IModuleCalls.Transaction[]", + "name": "_txs", + "type": "tuple[]" + } + ], + "name": "selfExecute", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_digest", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "signatureRecovery", + "outputs": [ + { + "internalType": "uint256", + "name": "threshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "weight", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "imageHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "subDigest", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "checkpoint", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_interfaceID", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_imageHash", + "type": "bytes32" + } + ], + "name": "updateImageHash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b5061210b806100206000396000f3fe6080604052600436106100bc5760003560e01c806361c2926c116100745780638c3f55631161004e5780638c3f55631461025357806390042baf14610273578063affed0e0146102ab57600080fd5b806361c2926c146101cb5780637a9a1628146101eb578063853c50681461020b57600080fd5b806320c13b0b116100a557806320c13b0b14610147578063295614261461016757806357c56d6b1461018957600080fd5b806301ffc9a7146100c15780631626ba7e146100f6575b600080fd5b3480156100cd57600080fd5b506100e16100dc3660046117cc565b6102c0565b60405190151581526020015b60405180910390f35b34801561010257600080fd5b50610116610111366004611832565b6102d1565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020016100ed565b34801561015357600080fd5b5061011661016236600461187e565b61031e565b34801561017357600080fd5b506101876101823660046118ea565b610383565b005b34801561019557600080fd5b506101bd7f8713a7c4465f6fbee2b6e9d6646d1d9f83fec929edfc4baf661f3c865bdd04d181565b6040519081526020016100ed565b3480156101d757600080fd5b506101876101e6366004611948565b6103d5565b3480156101f757600080fd5b5061018761020636600461198a565b61041a565b34801561021757600080fd5b5061022b610226366004611832565b610447565b604080519586526020860194909452928401919091526060830152608082015260a0016100ed565b34801561025f57600080fd5b506101bd61026e3660046118ea565b61060f565b610286610281366004611a33565b61063b565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100ed565b3480156102b757600080fd5b506101bd6106d7565b60006102cb826106e8565b92915050565b6000806102df858585610744565b509050801561031157507f1626ba7e000000000000000000000000000000000000000000000000000000009050610317565b50600090505b9392505050565b6000806103438686604051610334929190611b02565b60405180910390208585610744565b509050801561037557507f20c13b0b00000000000000000000000000000000000000000000000000000000905061037b565b50600090505b949350505050565b3330146103c9576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044015b60405180910390fd5b6103d28161077c565b50565b600061040883836040516020016103ed929190611ce0565b604051602081830303815290604052805190602001206107ae565b9050610415818484610833565b505050565b600061043286866040516020016103ed929190611d28565b905061043f818787610833565b505050505050565b6000806000806000808787600081811061046357610463611d70565b909101357fff000000000000000000000000000000000000000000000000000000000000001691508190506104b95761049b896107ae565b92506104a8838989610996565b929850909650945091506106049050565b7fff00000000000000000000000000000000000000000000000000000000000000818116016104f8576104eb896107ae565b92506104a88389896109e7565b7ffe000000000000000000000000000000000000000000000000000000000000007fff0000000000000000000000000000000000000000000000000000000000000082160161054a576104eb89610a13565b7ffd000000000000000000000000000000000000000000000000000000000000007fff000000000000000000000000000000000000000000000000000000000000008216016105ae5761059e898989610a80565b9550955095509550955050610604565b6040517f6085cd820000000000000000000000000000000000000000000000000000000081527fff00000000000000000000000000000000000000000000000000000000000000821660048201526024016103c0565b939792965093509350565b60006102cb7f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e83610bfd565b600033301461067e576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016103c0565b81516020830134f060405173ffffffffffffffffffffffffffffffffffffffff821681529091507fa506ad4e7f05eceba62a023c3219e5bd98a615f4fa87e2afb08a2da5cf62bf0c9060200160405180910390a1919050565b60006106e3600061060f565b905090565b60007f6ffbd451000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083160161073b57506001919050565b6102cb82610c5b565b6000806000806000610757888888610447565b5096509194509250905082821080159061076f575060015b9450505050935093915050565b6040517fa038794000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f190100000000000000000000000000000000000000000000000000000000000060208201524660228201527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b166042820152605681018290526000906076015b604051602081830303815290604052805190602001209050919050565b8060005b8181101561098f573684848381811061085257610852611d70565b90506020028101906108649190611d9f565b90506108736020820182611ddd565b156108ad576040517f230d1ccc000000000000000000000000000000000000000000000000000000008152600481018390526024016103c0565b6040810135805a10156109005782815a6040517f2bb3e3ba0000000000000000000000000000000000000000000000000000000081526004810193909352602483019190915260448201526064016103c0565b600061093a6109156080850160608601611df8565b608085013584156109265784610928565b5a5b61093560a0880188611e13565b610cb7565b905080156109585760405188815260200160405180910390a0610979565b61097961096b6040850160208601611ddd565b89610974610cd4565b610cf3565b505050808061098790611ea7565b915050610837565b5050505050565b60008080806109b1876109ac876006818b611edf565b610d3f565b6000908152873560f01c6020818152604080842084526002909a013560e01c908190529890912090999198509695509350505050565b6000808080610a02876109fd876001818b611edf565b610996565b935093509350935093509350935093565b6040517f190100000000000000000000000000000000000000000000000000000000000060208201526000602282018190527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b1660428301526056820183905290607601610816565b6000808080806004600188013560e81c82610a9b8383611f09565b9050610aad8b61022683868d8f611edf565b939b5091995097509550935087871015610b0557610acd81848b8d611edf565b89896040517fb006aba00000000000000000000000000000000000000000000000000000000081526004016103c09493929190611f1c565b8092505b88831015610bef5760038301928a013560e81c9150610b288383611f09565b90506000610b4a610b38886111d5565b8c8c8790869261022693929190611edf565b939c50919a5098509091505088881015610ba257610b6a82858c8e611edf565b8a8a6040517fb006aba00000000000000000000000000000000000000000000000000000000081526004016103c09493929190611f1c565b848110610be5576040517f37daf62b00000000000000000000000000000000000000000000000000000000815260048101829052602481018690526044016103c0565b9350915081610b09565b505050939792965093509350565b6000808383604051602001610c1c929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012054949350505050565b60007fe4a77bbc000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831601610cae57506001919050565b6102cb82611209565b6000604051828482376000808483898b8af1979650505050505050565b60603d604051915060208201818101604052818352816000823e505090565b8215610d0157805160208201fd5b7f3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd78282604051610d32929190611f43565b60405180910390a1505050565b60008060005b838110156111cc57600181019085013560f81c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101610de657601582019186013560f881901c9060581c73ffffffffffffffffffffffffffffffffffffffff81169074ff000000000000000000000000000000000000000016811785610dcc5780610ddb565b60008681526020829052604090205b955050505050610d45565b80610e7c5760018201918681013560f81c906043016000610e128a610e0d84888c8e611edf565b6112f3565b60ff841697909701969194508491905060a083901b74ff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff82161786610e615780610e70565b60008781526020829052604090205b96505050505050610d45565b60028103610fa4576000808784013560f881901c9060581c73ffffffffffffffffffffffffffffffffffffffff16601586019550909250905060008885013560e81c600386018162ffffff169150809650819250505060008186019050610ef58b848c8c8a908692610ef093929190611edf565b6115b6565b610f3d578a83610f0783898d8f611edf565b6040517f9a9462320000000000000000000000000000000000000000000000000000000081526004016103c09493929190611fb7565b60ff8416979097019694508460a084901b74ff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84161787610f885780610f97565b60008881526020829052604090205b9750505050505050610d45565b60038103610fd757602082019186013583610fbf5780610fce565b60008481526020829052604090205b93505050610d45565b60048103611023576003808301928781013560e81c91908201016000806110048b6109ac85898d8f611edf565b60009889526020526040909720969097019650909350610d4592505050565b6006810361112b5760008287013560f81c60018401935060ff16905060008784013560f01c60028501945061ffff16905060008885013560e81c600386018162ffffff1691508096508192505050600081860190506000806110918d8d8d8b9087926109ac93929190611edf565b939850889390925090508482106110a757988501985b604080517f53657175656e6365206e657374656420636f6e6669673a0a0000000000000000602080830191909152603882018490526058820188905260788083018a905283518084039091018152609890920190925280519101208961110d578061111c565b60008a81526020829052604090205b99505050505050505050610d45565b60058103611197576020820191860135878103611166577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff94505b600061117182611763565b90508461117e578061118d565b60008581526020829052604090205b9450505050610d45565b6040517fb2505f7c000000000000000000000000000000000000000000000000000000008152600481018290526024016103c0565b50935093915050565b7f8713a7c4465f6fbee2b6e9d6646d1d9f83fec929edfc4baf661f3c865bdd04d160009081526020829052604081206102cb565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fac6a444e00000000000000000000000000000000000000000000000000000000148061129c57507fffffffff0000000000000000000000000000000000000000000000000000000082167f36e7817500000000000000000000000000000000000000000000000000000000145b156112a957506001919050565b7f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316146102cb565b6000604282146113335782826040517f2ee17a3d0000000000000000000000000000000000000000000000000000000081526004016103c0929190611ff7565b600061134c61134360018561200b565b85013560f81c90565b60ff169050604084013560f81c843560208601357f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08111156113c0578686826040517fad4aac760000000000000000000000000000000000000000000000000000000081526004016103c09392919061201e565b8260ff16601b141580156113d857508260ff16601c14155b15611415578686846040517fe578897e0000000000000000000000000000000000000000000000000000000081526004016103c093929190612042565b60018403611482576040805160008152602081018083528a905260ff851691810191909152606081018390526080810182905260019060a0015b6020604051602081039080840390855afa158015611471573d6000803e3d6000fd5b50505060206040510351945061155a565b6002840361151f576040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101899052600190605c01604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff861690820152606081018490526080810183905260a00161144f565b86868560016040517f9dfba8520000000000000000000000000000000000000000000000000000000081526004016103c09493929190612069565b73ffffffffffffffffffffffffffffffffffffffff85166115ab5786866040517f6c1719d20000000000000000000000000000000000000000000000000000000081526004016103c0929190611ff7565b505050509392505050565b60008083836115c660018261200b565b8181106115d5576115d5611d70565b919091013560f81c91505060018114806115ef5750600281145b15611634578473ffffffffffffffffffffffffffffffffffffffff166116168786866112f3565b73ffffffffffffffffffffffffffffffffffffffff1614915061175a565b6003810361171f5773ffffffffffffffffffffffffffffffffffffffff8516631626ba7e878660008761166860018261200b565b9261167593929190611edf565b6040518463ffffffff1660e01b815260040161169393929190612095565b602060405180830381865afa1580156116b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116d491906120b8565b7fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e0000000000000000000000000000000000000000000000000000000014915061175a565b83838260006040517f9dfba8520000000000000000000000000000000000000000000000000000000081526004016103c09493929190612069565b50949350505050565b6040517f53657175656e636520737461746963206469676573743a0a0000000000000000602082015260388101829052600090605801610816565b7fffffffff00000000000000000000000000000000000000000000000000000000811681146103d257600080fd5b6000602082840312156117de57600080fd5b81356103178161179e565b60008083601f8401126117fb57600080fd5b50813567ffffffffffffffff81111561181357600080fd5b60208301915083602082850101111561182b57600080fd5b9250929050565b60008060006040848603121561184757600080fd5b83359250602084013567ffffffffffffffff81111561186557600080fd5b611871868287016117e9565b9497909650939450505050565b6000806000806040858703121561189457600080fd5b843567ffffffffffffffff808211156118ac57600080fd5b6118b8888389016117e9565b909650945060208701359150808211156118d157600080fd5b506118de878288016117e9565b95989497509550505050565b6000602082840312156118fc57600080fd5b5035919050565b60008083601f84011261191557600080fd5b50813567ffffffffffffffff81111561192d57600080fd5b6020830191508360208260051b850101111561182b57600080fd5b6000806020838503121561195b57600080fd5b823567ffffffffffffffff81111561197257600080fd5b61197e85828601611903565b90969095509350505050565b6000806000806000606086880312156119a257600080fd5b853567ffffffffffffffff808211156119ba57600080fd5b6119c689838a01611903565b90975095506020880135945060408801359150808211156119e657600080fd5b506119f3888289016117e9565b969995985093965092949392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600060208284031215611a4557600080fd5b813567ffffffffffffffff80821115611a5d57600080fd5b818401915084601f830112611a7157600080fd5b813581811115611a8357611a83611a04565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715611ac957611ac9611a04565b81604052828152876020848701011115611ae257600080fd5b826020860160208301376000928101602001929092525095945050505050565b8183823760009101908152919050565b80358015158114611b2257600080fd5b919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114611b2257600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b818352600060208085019450848460051b86018460005b87811015611cd357838303895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff41883603018112611bea57600080fd5b870160c0611bf782611b12565b15158552611c06878301611b12565b15158588015260408281013590860152606073ffffffffffffffffffffffffffffffffffffffff611c38828501611b27565b16908601526080828101359086015260a080830135368490037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1018112611c7e57600080fd5b90920187810192903567ffffffffffffffff811115611c9c57600080fd5b803603841315611cab57600080fd5b8282880152611cbd8388018286611b4b565b9c89019c96505050928601925050600101611bab565b5090979650505050505050565b60408152600560408201527f73656c663a000000000000000000000000000000000000000000000000000000606082015260806020820152600061037b608083018486611b94565b60408152600660408201527f67756573743a0000000000000000000000000000000000000000000000000000606082015260806020820152600061037b608083018486611b94565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff41833603018112611dd357600080fd5b9190910192915050565b600060208284031215611def57600080fd5b61031782611b12565b600060208284031215611e0a57600080fd5b61031782611b27565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112611e4857600080fd5b83018035915067ffffffffffffffff821115611e6357600080fd5b60200191503681900382131561182b57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203611ed857611ed8611e78565b5060010190565b60008085851115611eef57600080fd5b83861115611efc57600080fd5b5050820193919092039150565b808201808211156102cb576102cb611e78565b606081526000611f30606083018688611b4b565b6020830194909452506040015292915050565b82815260006020604081840152835180604085015260005b81811015611f7757858101830151858201606001528201611f5b565b5060006060828601015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116850101925050509392505050565b84815273ffffffffffffffffffffffffffffffffffffffff84166020820152606060408201526000611fed606083018486611b4b565b9695505050505050565b60208152600061037b602083018486611b4b565b818103818111156102cb576102cb611e78565b604081526000612032604083018587611b4b565b9050826020830152949350505050565b604081526000612056604083018587611b4b565b905060ff83166020830152949350505050565b60608152600061207d606083018688611b4b565b60208301949094525090151560409091015292915050565b8381526040602082015260006120af604083018486611b4b565b95945050505050565b6000602082840312156120ca57600080fd5b81516103178161179e56fea264697066735822122075ce1ed9c453c8c833ec89aa2911db2e9a1e07c0a29fc3ed180acba619d449be64736f6c63430008110033", + "deployedBytecode": "0x6080604052600436106100bc5760003560e01c806361c2926c116100745780638c3f55631161004e5780638c3f55631461025357806390042baf14610273578063affed0e0146102ab57600080fd5b806361c2926c146101cb5780637a9a1628146101eb578063853c50681461020b57600080fd5b806320c13b0b116100a557806320c13b0b14610147578063295614261461016757806357c56d6b1461018957600080fd5b806301ffc9a7146100c15780631626ba7e146100f6575b600080fd5b3480156100cd57600080fd5b506100e16100dc3660046117cc565b6102c0565b60405190151581526020015b60405180910390f35b34801561010257600080fd5b50610116610111366004611832565b6102d1565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020016100ed565b34801561015357600080fd5b5061011661016236600461187e565b61031e565b34801561017357600080fd5b506101876101823660046118ea565b610383565b005b34801561019557600080fd5b506101bd7f8713a7c4465f6fbee2b6e9d6646d1d9f83fec929edfc4baf661f3c865bdd04d181565b6040519081526020016100ed565b3480156101d757600080fd5b506101876101e6366004611948565b6103d5565b3480156101f757600080fd5b5061018761020636600461198a565b61041a565b34801561021757600080fd5b5061022b610226366004611832565b610447565b604080519586526020860194909452928401919091526060830152608082015260a0016100ed565b34801561025f57600080fd5b506101bd61026e3660046118ea565b61060f565b610286610281366004611a33565b61063b565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100ed565b3480156102b757600080fd5b506101bd6106d7565b60006102cb826106e8565b92915050565b6000806102df858585610744565b509050801561031157507f1626ba7e000000000000000000000000000000000000000000000000000000009050610317565b50600090505b9392505050565b6000806103438686604051610334929190611b02565b60405180910390208585610744565b509050801561037557507f20c13b0b00000000000000000000000000000000000000000000000000000000905061037b565b50600090505b949350505050565b3330146103c9576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044015b60405180910390fd5b6103d28161077c565b50565b600061040883836040516020016103ed929190611ce0565b604051602081830303815290604052805190602001206107ae565b9050610415818484610833565b505050565b600061043286866040516020016103ed929190611d28565b905061043f818787610833565b505050505050565b6000806000806000808787600081811061046357610463611d70565b909101357fff000000000000000000000000000000000000000000000000000000000000001691508190506104b95761049b896107ae565b92506104a8838989610996565b929850909650945091506106049050565b7fff00000000000000000000000000000000000000000000000000000000000000818116016104f8576104eb896107ae565b92506104a88389896109e7565b7ffe000000000000000000000000000000000000000000000000000000000000007fff0000000000000000000000000000000000000000000000000000000000000082160161054a576104eb89610a13565b7ffd000000000000000000000000000000000000000000000000000000000000007fff000000000000000000000000000000000000000000000000000000000000008216016105ae5761059e898989610a80565b9550955095509550955050610604565b6040517f6085cd820000000000000000000000000000000000000000000000000000000081527fff00000000000000000000000000000000000000000000000000000000000000821660048201526024016103c0565b939792965093509350565b60006102cb7f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e83610bfd565b600033301461067e576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016103c0565b81516020830134f060405173ffffffffffffffffffffffffffffffffffffffff821681529091507fa506ad4e7f05eceba62a023c3219e5bd98a615f4fa87e2afb08a2da5cf62bf0c9060200160405180910390a1919050565b60006106e3600061060f565b905090565b60007f6ffbd451000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083160161073b57506001919050565b6102cb82610c5b565b6000806000806000610757888888610447565b5096509194509250905082821080159061076f575060015b9450505050935093915050565b6040517fa038794000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f190100000000000000000000000000000000000000000000000000000000000060208201524660228201527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b166042820152605681018290526000906076015b604051602081830303815290604052805190602001209050919050565b8060005b8181101561098f573684848381811061085257610852611d70565b90506020028101906108649190611d9f565b90506108736020820182611ddd565b156108ad576040517f230d1ccc000000000000000000000000000000000000000000000000000000008152600481018390526024016103c0565b6040810135805a10156109005782815a6040517f2bb3e3ba0000000000000000000000000000000000000000000000000000000081526004810193909352602483019190915260448201526064016103c0565b600061093a6109156080850160608601611df8565b608085013584156109265784610928565b5a5b61093560a0880188611e13565b610cb7565b905080156109585760405188815260200160405180910390a0610979565b61097961096b6040850160208601611ddd565b89610974610cd4565b610cf3565b505050808061098790611ea7565b915050610837565b5050505050565b60008080806109b1876109ac876006818b611edf565b610d3f565b6000908152873560f01c6020818152604080842084526002909a013560e01c908190529890912090999198509695509350505050565b6000808080610a02876109fd876001818b611edf565b610996565b935093509350935093509350935093565b6040517f190100000000000000000000000000000000000000000000000000000000000060208201526000602282018190527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b1660428301526056820183905290607601610816565b6000808080806004600188013560e81c82610a9b8383611f09565b9050610aad8b61022683868d8f611edf565b939b5091995097509550935087871015610b0557610acd81848b8d611edf565b89896040517fb006aba00000000000000000000000000000000000000000000000000000000081526004016103c09493929190611f1c565b8092505b88831015610bef5760038301928a013560e81c9150610b288383611f09565b90506000610b4a610b38886111d5565b8c8c8790869261022693929190611edf565b939c50919a5098509091505088881015610ba257610b6a82858c8e611edf565b8a8a6040517fb006aba00000000000000000000000000000000000000000000000000000000081526004016103c09493929190611f1c565b848110610be5576040517f37daf62b00000000000000000000000000000000000000000000000000000000815260048101829052602481018690526044016103c0565b9350915081610b09565b505050939792965093509350565b6000808383604051602001610c1c929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012054949350505050565b60007fe4a77bbc000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831601610cae57506001919050565b6102cb82611209565b6000604051828482376000808483898b8af1979650505050505050565b60603d604051915060208201818101604052818352816000823e505090565b8215610d0157805160208201fd5b7f3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd78282604051610d32929190611f43565b60405180910390a1505050565b60008060005b838110156111cc57600181019085013560f81c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101610de657601582019186013560f881901c9060581c73ffffffffffffffffffffffffffffffffffffffff81169074ff000000000000000000000000000000000000000016811785610dcc5780610ddb565b60008681526020829052604090205b955050505050610d45565b80610e7c5760018201918681013560f81c906043016000610e128a610e0d84888c8e611edf565b6112f3565b60ff841697909701969194508491905060a083901b74ff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff82161786610e615780610e70565b60008781526020829052604090205b96505050505050610d45565b60028103610fa4576000808784013560f881901c9060581c73ffffffffffffffffffffffffffffffffffffffff16601586019550909250905060008885013560e81c600386018162ffffff169150809650819250505060008186019050610ef58b848c8c8a908692610ef093929190611edf565b6115b6565b610f3d578a83610f0783898d8f611edf565b6040517f9a9462320000000000000000000000000000000000000000000000000000000081526004016103c09493929190611fb7565b60ff8416979097019694508460a084901b74ff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84161787610f885780610f97565b60008881526020829052604090205b9750505050505050610d45565b60038103610fd757602082019186013583610fbf5780610fce565b60008481526020829052604090205b93505050610d45565b60048103611023576003808301928781013560e81c91908201016000806110048b6109ac85898d8f611edf565b60009889526020526040909720969097019650909350610d4592505050565b6006810361112b5760008287013560f81c60018401935060ff16905060008784013560f01c60028501945061ffff16905060008885013560e81c600386018162ffffff1691508096508192505050600081860190506000806110918d8d8d8b9087926109ac93929190611edf565b939850889390925090508482106110a757988501985b604080517f53657175656e6365206e657374656420636f6e6669673a0a0000000000000000602080830191909152603882018490526058820188905260788083018a905283518084039091018152609890920190925280519101208961110d578061111c565b60008a81526020829052604090205b99505050505050505050610d45565b60058103611197576020820191860135878103611166577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff94505b600061117182611763565b90508461117e578061118d565b60008581526020829052604090205b9450505050610d45565b6040517fb2505f7c000000000000000000000000000000000000000000000000000000008152600481018290526024016103c0565b50935093915050565b7f8713a7c4465f6fbee2b6e9d6646d1d9f83fec929edfc4baf661f3c865bdd04d160009081526020829052604081206102cb565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fac6a444e00000000000000000000000000000000000000000000000000000000148061129c57507fffffffff0000000000000000000000000000000000000000000000000000000082167f36e7817500000000000000000000000000000000000000000000000000000000145b156112a957506001919050565b7f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316146102cb565b6000604282146113335782826040517f2ee17a3d0000000000000000000000000000000000000000000000000000000081526004016103c0929190611ff7565b600061134c61134360018561200b565b85013560f81c90565b60ff169050604084013560f81c843560208601357f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08111156113c0578686826040517fad4aac760000000000000000000000000000000000000000000000000000000081526004016103c09392919061201e565b8260ff16601b141580156113d857508260ff16601c14155b15611415578686846040517fe578897e0000000000000000000000000000000000000000000000000000000081526004016103c093929190612042565b60018403611482576040805160008152602081018083528a905260ff851691810191909152606081018390526080810182905260019060a0015b6020604051602081039080840390855afa158015611471573d6000803e3d6000fd5b50505060206040510351945061155a565b6002840361151f576040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101899052600190605c01604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff861690820152606081018490526080810183905260a00161144f565b86868560016040517f9dfba8520000000000000000000000000000000000000000000000000000000081526004016103c09493929190612069565b73ffffffffffffffffffffffffffffffffffffffff85166115ab5786866040517f6c1719d20000000000000000000000000000000000000000000000000000000081526004016103c0929190611ff7565b505050509392505050565b60008083836115c660018261200b565b8181106115d5576115d5611d70565b919091013560f81c91505060018114806115ef5750600281145b15611634578473ffffffffffffffffffffffffffffffffffffffff166116168786866112f3565b73ffffffffffffffffffffffffffffffffffffffff1614915061175a565b6003810361171f5773ffffffffffffffffffffffffffffffffffffffff8516631626ba7e878660008761166860018261200b565b9261167593929190611edf565b6040518463ffffffff1660e01b815260040161169393929190612095565b602060405180830381865afa1580156116b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116d491906120b8565b7fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e0000000000000000000000000000000000000000000000000000000014915061175a565b83838260006040517f9dfba8520000000000000000000000000000000000000000000000000000000081526004016103c09493929190612069565b50949350505050565b6040517f53657175656e636520737461746963206469676573743a0a0000000000000000602082015260388101829052600090605801610816565b7fffffffff00000000000000000000000000000000000000000000000000000000811681146103d257600080fd5b6000602082840312156117de57600080fd5b81356103178161179e565b60008083601f8401126117fb57600080fd5b50813567ffffffffffffffff81111561181357600080fd5b60208301915083602082850101111561182b57600080fd5b9250929050565b60008060006040848603121561184757600080fd5b83359250602084013567ffffffffffffffff81111561186557600080fd5b611871868287016117e9565b9497909650939450505050565b6000806000806040858703121561189457600080fd5b843567ffffffffffffffff808211156118ac57600080fd5b6118b8888389016117e9565b909650945060208701359150808211156118d157600080fd5b506118de878288016117e9565b95989497509550505050565b6000602082840312156118fc57600080fd5b5035919050565b60008083601f84011261191557600080fd5b50813567ffffffffffffffff81111561192d57600080fd5b6020830191508360208260051b850101111561182b57600080fd5b6000806020838503121561195b57600080fd5b823567ffffffffffffffff81111561197257600080fd5b61197e85828601611903565b90969095509350505050565b6000806000806000606086880312156119a257600080fd5b853567ffffffffffffffff808211156119ba57600080fd5b6119c689838a01611903565b90975095506020880135945060408801359150808211156119e657600080fd5b506119f3888289016117e9565b969995985093965092949392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600060208284031215611a4557600080fd5b813567ffffffffffffffff80821115611a5d57600080fd5b818401915084601f830112611a7157600080fd5b813581811115611a8357611a83611a04565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715611ac957611ac9611a04565b81604052828152876020848701011115611ae257600080fd5b826020860160208301376000928101602001929092525095945050505050565b8183823760009101908152919050565b80358015158114611b2257600080fd5b919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114611b2257600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b818352600060208085019450848460051b86018460005b87811015611cd357838303895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff41883603018112611bea57600080fd5b870160c0611bf782611b12565b15158552611c06878301611b12565b15158588015260408281013590860152606073ffffffffffffffffffffffffffffffffffffffff611c38828501611b27565b16908601526080828101359086015260a080830135368490037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1018112611c7e57600080fd5b90920187810192903567ffffffffffffffff811115611c9c57600080fd5b803603841315611cab57600080fd5b8282880152611cbd8388018286611b4b565b9c89019c96505050928601925050600101611bab565b5090979650505050505050565b60408152600560408201527f73656c663a000000000000000000000000000000000000000000000000000000606082015260806020820152600061037b608083018486611b94565b60408152600660408201527f67756573743a0000000000000000000000000000000000000000000000000000606082015260806020820152600061037b608083018486611b94565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff41833603018112611dd357600080fd5b9190910192915050565b600060208284031215611def57600080fd5b61031782611b12565b600060208284031215611e0a57600080fd5b61031782611b27565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112611e4857600080fd5b83018035915067ffffffffffffffff821115611e6357600080fd5b60200191503681900382131561182b57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203611ed857611ed8611e78565b5060010190565b60008085851115611eef57600080fd5b83861115611efc57600080fd5b5050820193919092039150565b808201808211156102cb576102cb611e78565b606081526000611f30606083018688611b4b565b6020830194909452506040015292915050565b82815260006020604081840152835180604085015260005b81811015611f7757858101830151858201606001528201611f5b565b5060006060828601015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116850101925050509392505050565b84815273ffffffffffffffffffffffffffffffffffffffff84166020820152606060408201526000611fed606083018486611b4b565b9695505050505050565b60208152600061037b602083018486611b4b565b818103818111156102cb576102cb611e78565b604081526000612032604083018587611b4b565b9050826020830152949350505050565b604081526000612056604083018587611b4b565b905060ff83166020830152949350505050565b60608152600061207d606083018688611b4b565b60208301949094525090151560409091015292915050565b8381526040602082015260006120af604083018486611b4b565b95945050505050565b6000602082840312156120ca57600080fd5b81516103178161179e56fea264697066735822122075ce1ed9c453c8c833ec89aa2911db2e9a1e07c0a29fc3ed180acba619d449be64736f6c63430008110033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/packages/tests/src/builds/v2/artifacts/MainModule.ts b/packages/tests/src/builds/v2/artifacts/MainModule.ts new file mode 100644 index 000000000..bebf9ed61 --- /dev/null +++ b/packages/tests/src/builds/v2/artifacts/MainModule.ts @@ -0,0 +1,1102 @@ +export const mainModule = { + "_format": "hh-sol-artifact-1", + "contractName": "MainModule", + "sourceName": "contracts/modules/MainModule.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_factory", + "type": "address" + }, + { + "internalType": "address", + "name": "_mainModuleUpgradable", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_space", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_provided", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_current", + "type": "uint256" + } + ], + "name": "BadNonce", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_signature", + "type": "bytes4" + } + ], + "name": "HookAlreadyExists", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_signature", + "type": "bytes4" + } + ], + "name": "HookDoesNotExist", + "type": "error" + }, + { + "inputs": [], + "name": "ImageHashIsZero", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_implementation", + "type": "address" + } + ], + "name": "InvalidImplementation", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_addr", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "InvalidNestedSignature", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "_s", + "type": "bytes32" + } + ], + "name": "InvalidSValue", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "InvalidSignature", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_flag", + "type": "uint256" + } + ], + "name": "InvalidSignatureFlag", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "InvalidSignatureLength", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes1", + "name": "_type", + "type": "bytes1" + } + ], + "name": "InvalidSignatureType", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_v", + "type": "uint256" + } + ], + "name": "InvalidVValue", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "threshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_weight", + "type": "uint256" + } + ], + "name": "LowWeightChainedSignature", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_requested", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_available", + "type": "uint256" + } + ], + "name": "NotEnoughGas", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_sender", + "type": "address" + }, + { + "internalType": "address", + "name": "_self", + "type": "address" + } + ], + "name": "OnlySelfAuth", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "SignerIsAddress0", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_type", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "_recoverMode", + "type": "bool" + } + ], + "name": "UnsupportedSignatureType", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_current", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_prev", + "type": "uint256" + } + ], + "name": "WrongChainedCheckpointOrder", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_contract", + "type": "address" + } + ], + "name": "CreatedContract", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + } + ], + "name": "IPFSRootUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "newImageHash", + "type": "bytes32" + } + ], + "name": "ImageHashUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "ImplementationUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_space", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_newNonce", + "type": "uint256" + } + ], + "name": "NonceChange", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "_imageHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_expiration", + "type": "uint256" + } + ], + "name": "SetExtraImageHash", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "_digest", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_expiration", + "type": "uint256" + } + ], + "name": "SetStaticDigest", + "type": "event" + }, + { + "anonymous": true, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "_tx", + "type": "bytes32" + } + ], + "name": "TxExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "_tx", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "_reason", + "type": "bytes" + } + ], + "name": "TxFailed", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "FACTORY", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "INIT_CODE_HASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "SET_IMAGE_HASH_TYPE_HASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "UPGRADEABLE_IMPLEMENTATION", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_signature", + "type": "bytes4" + }, + { + "internalType": "address", + "name": "_implementation", + "type": "address" + } + ], + "name": "addHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "_digests", + "type": "bytes32[]" + } + ], + "name": "addStaticDigests", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "_imageHashes", + "type": "bytes32[]" + } + ], + "name": "clearExtraImageHashes", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_code", + "type": "bytes" + } + ], + "name": "createContract", + "outputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "delegateCall", + "type": "bool" + }, + { + "internalType": "bool", + "name": "revertOnError", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct IModuleCalls.Transaction[]", + "name": "_txs", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "_nonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "execute", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_imageHash", + "type": "bytes32" + } + ], + "name": "extraImageHash", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ipfsRoot", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ipfsRootBytes32", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_signatures", + "type": "bytes" + } + ], + "name": "isValidSignature", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_signatures", + "type": "bytes" + } + ], + "name": "isValidSignature", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155BatchReceived", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC721Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_signature", + "type": "bytes4" + } + ], + "name": "readHook", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_space", + "type": "uint256" + } + ], + "name": "readNonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_signature", + "type": "bytes4" + } + ], + "name": "removeHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "delegateCall", + "type": "bool" + }, + { + "internalType": "bool", + "name": "revertOnError", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct IModuleCalls.Transaction[]", + "name": "_txs", + "type": "tuple[]" + } + ], + "name": "selfExecute", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_imageHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_expiration", + "type": "uint256" + } + ], + "name": "setExtraImageHash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_digest", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_expiration", + "type": "uint256" + } + ], + "name": "setStaticDigest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_digest", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "signatureRecovery", + "outputs": [ + { + "internalType": "uint256", + "name": "threshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "weight", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "imageHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "subDigest", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "checkpoint", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_digest", + "type": "bytes32" + } + ], + "name": "staticDigest", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_interfaceID", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + } + ], + "name": "updateIPFSRoot", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_imageHash", + "type": "bytes32" + } + ], + "name": "updateImageHash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_imageHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_ipfsRoot", + "type": "bytes32" + } + ], + "name": "updateImageHashAndIPFS", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_implementation", + "type": "address" + } + ], + "name": "updateImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "bytecode": "0x60e06040523480156200001157600080fd5b5060405162003b9e38038062003b9e8339810160408190526200003491620000ba565b8181600060405180606001604052806028815260200162003b76602891396040516200006691903090602001620000f2565b60408051601f198184030181529190528051602090910120608052506001600160a01b0391821660a0521660c05250620001269050565b80516001600160a01b0381168114620000b557600080fd5b919050565b60008060408385031215620000ce57600080fd5b620000d9836200009d565b9150620000e9602084016200009d565b90509250929050565b6000835160005b81811015620001155760208187018101518583015201620000f9565b509190910191825250602001919050565b60805160a05160c051613a0b6200016b6000396000818161060b015261171f01526000818161049b0152612ca30152600081816104390152612cd40152613a0b6000f3fe6080604052600436106101dc5760003560e01c806379e472c911610102578063a4ab5f9f11610095578063c71f1f9611610064578063c71f1f961461073f578063d0748f7114610754578063d59f788514610774578063f23a6e6114610794576101e3565b8063a4ab5f9f146106a2578063affed0e0146106c2578063b93ea7ad146106d7578063bc197c81146106f7576101e3565b80638c3f5563116100d15780638c3f55631461062d5780638efa64411461064d57806390042baf1461066f578063a38cef1914610682576101e3565b806379e472c9146105715780637a9a162814610591578063853c5068146105b1578063888eeec6146105f9576101e3565b8063257671f51161017a5780634598154f116101495780634598154f146104dd5780634fcf3eca146104fd57806357c56d6b1461051d57806361c2926c14610551576101e3565b8063257671f51461042757806329561426146104695780632dd310001461048957806341ea0302146104bd576101e3565b8063150b7a02116101b6578063150b7a021461032c5780631626ba7e146103a25780631a9b2337146103c257806320c13b0b14610407576101e3565b806301ffc9a7146102b7578063025b22bc146102ec578063038dbaac1461030c576101e3565b366101e357005b60006102126000357fffffffff00000000000000000000000000000000000000000000000000000000166107da565b905073ffffffffffffffffffffffffffffffffffffffff8116156102b5576000808273ffffffffffffffffffffffffffffffffffffffff1660003660405161025b929190612e69565b600060405180830381855af49150503d8060008114610296576040519150601f19603f3d011682016040523d82523d6000602084013e61029b565b606091505b5091509150816102ad57805160208201fd5b805160208201f35b005b3480156102c357600080fd5b506102d76102d2366004612ea7565b61082e565b60405190151581526020015b60405180910390f35b3480156102f857600080fd5b506102b5610307366004612eed565b610839565b34801561031857600080fd5b506102b5610327366004612f54565b61088b565b34801561033857600080fd5b50610371610347366004612fd8565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020016102e3565b3480156103ae57600080fd5b506103716103bd366004613047565b610996565b3480156103ce57600080fd5b506103e26103dd366004612ea7565b6109e3565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102e3565b34801561041357600080fd5b50610371610422366004613093565b6109ee565b34801561043357600080fd5b5061045b7f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020016102e3565b34801561047557600080fd5b506102b56104843660046130ff565b610a53565b34801561049557600080fd5b506103e27f000000000000000000000000000000000000000000000000000000000000000081565b3480156104c957600080fd5b5061045b6104d83660046130ff565b610a9d565b3480156104e957600080fd5b506102b56104f8366004613118565b610aa8565b34801561050957600080fd5b506102b5610518366004612ea7565b610b6e565b34801561052957600080fd5b5061045b7f8713a7c4465f6fbee2b6e9d6646d1d9f83fec929edfc4baf661f3c865bdd04d181565b34801561055d57600080fd5b506102b561056c366004612f54565b610c9d565b34801561057d57600080fd5b506102b561058c366004613118565b610d23565b34801561059d57600080fd5b506102b56105ac36600461313a565b610de1565b3480156105bd57600080fd5b506105d16105cc366004613047565b610e77565b604080519586526020860194909452928401919091526060830152608082015260a0016102e3565b34801561060557600080fd5b506103e27f000000000000000000000000000000000000000000000000000000000000000081565b34801561063957600080fd5b5061045b6106483660046130ff565b61103f565b34801561065957600080fd5b5061066261106b565b6040516102e39190613211565b6103e261067d366004613253565b6110ec565b34801561068e57600080fd5b506102b561069d3660046130ff565b611188565b3480156106ae57600080fd5b5061045b6106bd3660046130ff565b6111d2565b3480156106ce57600080fd5b5061045b6111dd565b3480156106e357600080fd5b506102b56106f2366004613322565b6111ee565b34801561070357600080fd5b50610371610712366004613357565b7fbc197c810000000000000000000000000000000000000000000000000000000098975050505050505050565b34801561074b57600080fd5b5061045b611337565b34801561076057600080fd5b506102b561076f366004613118565b611361565b34801561078057600080fd5b506102b561078f366004612f54565b6113b4565b3480156107a057600080fd5b506103716107af366004613412565b7ff23a6e61000000000000000000000000000000000000000000000000000000009695505050505050565b60006108287fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1207fffffffff0000000000000000000000000000000000000000000000000000000084166114f7565b92915050565b600061082882611555565b33301461087f576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044015b60405180910390fd5b610888816115b1565b50565b3330146108cc576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b8060005b818110156109905760008484838181106108ec576108ec61348a565b90506020020135905061094c816000604080517f849e7bdc245db17e50b9f43086f1914d70eb4dab6dd89af4d541d53353ad97de602080830191909152818301859052825180830384018152606090920190925280519101208190555050565b807f804f6171d6008d9e16ee3aa0561fec328397f4ba2827a6605db388cfdefa3b0c600060405161097f91815260200190565b60405180910390a2506001016108d0565b50505050565b6000806109a485858561166c565b50905080156109d657507f1626ba7e0000000000000000000000000000000000000000000000000000000090506109dc565b50600090505b9392505050565b6000610828826107da565b600080610a138686604051610a04929190612e69565b6040518091039020858561166c565b5090508015610a4557507f20c13b0b000000000000000000000000000000000000000000000000000000009050610a4b565b50600090505b949350505050565b333014610a94576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b61088881611687565b600061082882611743565b333014610ae9576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b604080517f849e7bdc245db17e50b9f43086f1914d70eb4dab6dd89af4d541d53353ad97de602080830191909152818301859052825180830384018152606083019384905280519101208390559082905282907f804f6171d6008d9e16ee3aa0561fec328397f4ba2827a6605db388cfdefa3b0c906080015b60405180910390a25050565b333014610baf576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b6000610bba826107da565b73ffffffffffffffffffffffffffffffffffffffff1603610c2b576040517f1c3812cc0000000000000000000000000000000000000000000000000000000081527fffffffff0000000000000000000000000000000000000000000000000000000082166004820152602401610876565b604080517fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1206020808301919091527fffffffff00000000000000000000000000000000000000000000000000000000841682840152825180830384018152606090920190925280519101206000905550565b333014610cde576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b6000610d118383604051602001610cf6929190613661565b6040516020818303038152906040528051906020012061176f565b9050610d1e8184846117f4565b505050565b333014610d64576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b604080517f7f25a23abc421d10864063e9a8ae5fd3fbd5116e156f148428b6a3a02ffd9454602080830191909152818301859052825180830384018152606083019384905280519101208390559082905282907f180e56184e3025975e8449fab79ff135cc5c3b3fe517a19bf8f111d69b33d2e290608001610b62565b610dea83611952565b600080610e22858888604051602001610e05939291906136a9565b60405160208183030381529060405280519060200120858561166c565b9150915081610e63578084846040517f8f4a234f000000000000000000000000000000000000000000000000000000008152600401610876939291906136cc565b610e6e8188886117f4565b50505050505050565b60008060008060008087876000818110610e9357610e9361348a565b909101357fff00000000000000000000000000000000000000000000000000000000000000169150819050610ee957610ecb8961176f565b9250610ed8838989611a4f565b929850909650945091506110349050565b7fff0000000000000000000000000000000000000000000000000000000000000081811601610f2857610f1b8961176f565b9250610ed8838989611aa0565b7ffe000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821601610f7a57610f1b89611acc565b7ffd000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821601610fde57610fce898989611b39565b9550955095509550955050611034565b6040517f6085cd820000000000000000000000000000000000000000000000000000000081527fff0000000000000000000000000000000000000000000000000000000000000082166004820152602401610876565b939792965093509350565b60006108287f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e836114f7565b60606110c86110c361107b611337565b6040517f017012200000000000000000000000000000000000000000000000000000000060208201526024810191909152604401604051602081830303815290604052611cb6565b611ecf565b6040516020016110d891906136e6565b604051602081830303815290604052905090565b600033301461112f576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b81516020830134f060405173ffffffffffffffffffffffffffffffffffffffff821681529091507fa506ad4e7f05eceba62a023c3219e5bd98a615f4fa87e2afb08a2da5cf62bf0c9060200160405180910390a1919050565b3330146111c9576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b61088881611ef8565b600061082882611f51565b60006111e9600061103f565b905090565b33301461122f576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b600061123a836107da565b73ffffffffffffffffffffffffffffffffffffffff16146112ab576040517f5b4d6d6a0000000000000000000000000000000000000000000000000000000081527fffffffff0000000000000000000000000000000000000000000000000000000083166004820152602401610876565b604080517fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1206020808301919091527fffffffff000000000000000000000000000000000000000000000000000000008516828401528251808303840181526060909201909252805191012073ffffffffffffffffffffffffffffffffffffffff821690555050565b5050565b60006111e97f0eecac93ced8722d209199364cda3bc33da3bc3a23daef6be49ebd780511d0335490565b3330146113a2576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b6113ab82611687565b61133381611ef8565b3330146113f5576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b8060005b818110156109905760008484838181106114155761141561348a565b905060200201359050611494817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff604080517f7f25a23abc421d10864063e9a8ae5fd3fbd5116e156f148428b6a3a02ffd9454602080830191909152818301859052825180830384018152606090920190925280519101208190555050565b807f180e56184e3025975e8449fab79ff135cc5c3b3fe517a19bf8f111d69b33d2e27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040516114e691815260200190565b60405180910390a2506001016113f9565b6000808383604051602001611516929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012054949350505050565b60007f6ffbd451000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316016115a857506001919050565b61082882611f7d565b73ffffffffffffffffffffffffffffffffffffffff81163b611617576040517f0c76093700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610876565b61161f813055565b60405173ffffffffffffffffffffffffffffffffffffffff821681527f310ba5f1d2ed074b51e2eccd052a47ae9ab7c6b800d1fca3db3999d6a592ca03906020015b60405180910390a150565b60008061167a8585856120be565b915091505b935093915050565b806116be576040517f4294d12700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6116e77fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8829055565b6040518181527f307ed6bd941ee9fc80f369c94af5fa11e25bab5102a6140191756c5474a30bfa9060200160405180910390a16108887f00000000000000000000000000000000000000000000000000000000000000006115b1565b60006108287f7f25a23abc421d10864063e9a8ae5fd3fbd5116e156f148428b6a3a02ffd9454836114f7565b6040517f190100000000000000000000000000000000000000000000000000000000000060208201524660228201527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b166042820152605681018290526000906076015b604051602081830303815290604052805190602001209050919050565b8060005b8181101561194b57368484838181106118135761181361348a565b9050602002810190611825919061372b565b90506040810135805a101561187a5782815a6040517f2bb3e3ba000000000000000000000000000000000000000000000000000000008152600481019390935260248301919091526044820152606401610876565b60006118896020840184613769565b156118c8576118c16118a16080850160608601612eed565b83156118ad57836118af565b5a5b6118bc60a0870187613784565b6120f2565b9050611903565b6119006118db6080850160608601612eed565b608085013584156118ec57846118ee565b5a5b6118fb60a0880188613784565b61210d565b90505b801561191f5760405188815260200160405180910390a0611940565b6119406119326040850160208601613769565b8961193b61212a565b612149565b5050506001016117f8565b5050505050565b606081901c6bffffffffffffffffffffffff821660006119718361103f565b90508181146119bd576040517f9b6514f4000000000000000000000000000000000000000000000000000000008152600481018490526024810183905260448101829052606401610876565b604080517f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e60208083019190915281830186905282518083038401815260609092019092528051910120600183019081905560408051858152602081018390527f1f180c27086c7a39ea2a7b25239d1ab92348f07ca7bb59d1438fcf527568f881910160405180910390a15050505050565b6000808080611a6a87611a65876006818b6137e9565b612195565b6000908152873560f01c6020818152604080842084526002909a013560e01c908190529890912090999198509695509350505050565b6000808080611abb87611ab6876001818b6137e9565b611a4f565b935093509350935093509350935093565b6040517f190100000000000000000000000000000000000000000000000000000000000060208201526000602282018190527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b16604283015260568201839052906076016117d7565b6000808080806004600188013560e81c82611b548383613842565b9050611b668b6105cc83868d8f6137e9565b939b5091995097509550935087871015611bbe57611b8681848b8d6137e9565b89896040517fb006aba00000000000000000000000000000000000000000000000000000000081526004016108769493929190613855565b8092505b88831015611ca85760038301928a013560e81c9150611be18383613842565b90506000611c03611bf18861262b565b8c8c879086926105cc939291906137e9565b939c50919a5098509091505088881015611c5b57611c2382858c8e6137e9565b8a8a6040517fb006aba00000000000000000000000000000000000000000000000000000000081526004016108769493929190613855565b848110611c9e576040517f37daf62b0000000000000000000000000000000000000000000000000000000081526004810182905260248101869052604401610876565b9350915081611bc2565b505050939792965093509350565b8051606090600381901b60006005600483010467ffffffffffffffff811115611ce157611ce1613224565b6040519080825280601f01601f191660200182016040528015611d0b576020820181803683370190505b5090506000806000805b86811015611e1f57888181518110611d2f57611d2f61348a565b01602001516008948501949390931b60f89390931c92909217915b60058410611e17576040805180820190915260208082527f6162636465666768696a6b6c6d6e6f707172737475767778797a323334353637818301527ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb90950194601f85871c16908110611dc057611dc061348a565b602001015160f81c60f81b858381518110611ddd57611ddd61348a565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600190910190611d4a565b600101611d15565b508215611ec3576040518060400160405280602081526020017f6162636465666768696a6b6c6d6e6f707172737475767778797a3233343536378152508360050383901b601f1681518110611e7657611e7661348a565b602001015160f81c60f81b848281518110611e9357611e9361348a565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053505b50919695505050505050565b606081604051602001611ee2919061387c565b6040516020818303038152906040529050919050565b611f217f0eecac93ced8722d209199364cda3bc33da3bc3a23daef6be49ebd780511d033829055565b6040518181527f20d3ef1b5738a9f6d7beae515432206e7a8e2740ca6dcf46a952190ad01bcb5190602001611661565b60006108287f849e7bdc245db17e50b9f43086f1914d70eb4dab6dd89af4d541d53353ad97de836114f7565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fec6aba5000000000000000000000000000000000000000000000000000000000148061201057507fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e000000000000000000000000000000000000000000000000000000000145b8061205c57507fffffffff0000000000000000000000000000000000000000000000000000000082167f150b7a0200000000000000000000000000000000000000000000000000000000145b806120a857507fffffffff0000000000000000000000000000000000000000000000000000000082167fc0ee0b8a00000000000000000000000000000000000000000000000000000000145b156120b557506001919050565b6108288261265f565b600080426120cb86611743565b11915081156120e757816120de866126bb565b9150915061167f565b61167a8585856126f6565b60006040518284823760008084838989f49695505050505050565b6000604051828482376000808483898b8af1979650505050505050565b60603d604051915060208201818101604052818352816000823e505090565b821561215757805160208201fd5b7f3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd782826040516121889291906138c1565b60405180910390a1505050565b60008060005b8381101561262257600181019085013560f81c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810161223c57601582019186013560f881901c9060581c73ffffffffffffffffffffffffffffffffffffffff81169074ff0000000000000000000000000000000000000000168117856122225780612231565b60008681526020829052604090205b95505050505061219b565b806122d25760018201918681013560f81c9060430160006122688a61226384888c8e6137e9565b612734565b60ff841697909701969194508491905060a083901b74ff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff821617866122b757806122c6565b60008781526020829052604090205b9650505050505061219b565b600281036123fa576000808784013560f881901c9060581c73ffffffffffffffffffffffffffffffffffffffff16601586019550909250905060008885013560e81c600386018162ffffff16915080965081925050506000818601905061234b8b848c8c8a908692612346939291906137e9565b6129f7565b612393578a8361235d83898d8f6137e9565b6040517f9a94623200000000000000000000000000000000000000000000000000000000815260040161087694939291906138da565b60ff8416979097019694508460a084901b74ff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff841617876123de57806123ed565b60008881526020829052604090205b975050505050505061219b565b6003810361242d576020820191860135836124155780612424565b60008481526020829052604090205b9350505061219b565b60048103612479576003808301928781013560e81c919082010160008061245a8b611a6585898d8f6137e9565b6000988952602052604090972096909701965090935061219b92505050565b600681036125815760008287013560f81c60018401935060ff16905060008784013560f01c60028501945061ffff16905060008885013560e81c600386018162ffffff1691508096508192505050600081860190506000806124e78d8d8d8b908792611a65939291906137e9565b939850889390925090508482106124fd57988501985b604080517f53657175656e6365206e657374656420636f6e6669673a0a0000000000000000602080830191909152603882018490526058820188905260788083018a90528351808403909101815260989092019092528051910120896125635780612572565b60008a81526020829052604090205b9950505050505050505061219b565b600581036125ed5760208201918601358781036125bc577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff94505b60006125c782612ba4565b9050846125d457806125e3565b60008581526020829052604090205b945050505061219b565b6040517fb2505f7c00000000000000000000000000000000000000000000000000000000815260048101829052602401610876565b50935093915050565b7f8713a7c4465f6fbee2b6e9d6646d1d9f83fec929edfc4baf661f3c865bdd04d16000908152602082905260408120610828565b60007fe4a77bbc000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316016126b257506001919050565b61082882612bdf565b604080517f7f25a23abc421d10864063e9a8ae5fd3fbd5116e156f148428b6a3a02ffd945460208201529081018290526000906060016117d7565b6000806000806000612709888888610e77565b50965091945092509050828210801590612727575061272781612bea565b9450505050935093915050565b6000604282146127745782826040517f2ee17a3d00000000000000000000000000000000000000000000000000000000815260040161087692919061391a565b600061278d61278460018561392e565b85013560f81c90565b60ff169050604084013560f81c843560208601357f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0811115612801578686826040517fad4aac7600000000000000000000000000000000000000000000000000000000815260040161087693929190613941565b8260ff16601b1415801561281957508260ff16601c14155b15612856578686846040517fe578897e00000000000000000000000000000000000000000000000000000000815260040161087693929190613965565b600184036128c3576040805160008152602081018083528a905260ff851691810191909152606081018390526080810182905260019060a0015b6020604051602081039080840390855afa1580156128b2573d6000803e3d6000fd5b50505060206040510351945061299b565b60028403612960576040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101899052600190605c01604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff861690820152606081018490526080810183905260a001612890565b86868560016040517f9dfba852000000000000000000000000000000000000000000000000000000008152600401610876949392919061398c565b73ffffffffffffffffffffffffffffffffffffffff85166129ec5786866040517f6c1719d200000000000000000000000000000000000000000000000000000000815260040161087692919061391a565b505050509392505050565b6000808383612a0760018261392e565b818110612a1657612a1661348a565b919091013560f81c9150506001811480612a305750600281145b15612a75578473ffffffffffffffffffffffffffffffffffffffff16612a57878686612734565b73ffffffffffffffffffffffffffffffffffffffff16149150612b9b565b60038103612b605773ffffffffffffffffffffffffffffffffffffffff8516631626ba7e8786600087612aa960018261392e565b92612ab6939291906137e9565b6040518463ffffffff1660e01b8152600401612ad4939291906136cc565b602060405180830381865afa158015612af1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b1591906139b8565b7fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e00000000000000000000000000000000000000000000000000000000149150612b9b565b83838260006040517f9dfba852000000000000000000000000000000000000000000000000000000008152600401610876949392919061398c565b50949350505050565b6040517f53657175656e636520737461746963206469676573743a0a00000000000000006020820152603881018290526000906058016117d7565b600061082882612bf5565b600061082882612c51565b60007ffda4dd44000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831601612c4857506001919050565b61082882612d7f565b6000612d53826040517fff0000000000000000000000000000000000000000000000000000000000000060208201527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000060601b166021820152603581018290527f000000000000000000000000000000000000000000000000000000000000000060558201526000903090607501604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012073ffffffffffffffffffffffffffffffffffffffff161492915050565b15612d6057506001919050565b6000612d6b83611f51565b905080158015906109dc5750421092915050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fac6a444e000000000000000000000000000000000000000000000000000000001480612e1257507fffffffff0000000000000000000000000000000000000000000000000000000082167f36e7817500000000000000000000000000000000000000000000000000000000145b15612e1f57506001919050565b7f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831614610828565b8183823760009101908152919050565b7fffffffff000000000000000000000000000000000000000000000000000000008116811461088857600080fd5b600060208284031215612eb957600080fd5b81356109dc81612e79565b803573ffffffffffffffffffffffffffffffffffffffff81168114612ee857600080fd5b919050565b600060208284031215612eff57600080fd5b6109dc82612ec4565b60008083601f840112612f1a57600080fd5b50813567ffffffffffffffff811115612f3257600080fd5b6020830191508360208260051b8501011115612f4d57600080fd5b9250929050565b60008060208385031215612f6757600080fd5b823567ffffffffffffffff811115612f7e57600080fd5b612f8a85828601612f08565b90969095509350505050565b60008083601f840112612fa857600080fd5b50813567ffffffffffffffff811115612fc057600080fd5b602083019150836020828501011115612f4d57600080fd5b600080600080600060808688031215612ff057600080fd5b612ff986612ec4565b945061300760208701612ec4565b935060408601359250606086013567ffffffffffffffff81111561302a57600080fd5b61303688828901612f96565b969995985093965092949392505050565b60008060006040848603121561305c57600080fd5b83359250602084013567ffffffffffffffff81111561307a57600080fd5b61308686828701612f96565b9497909650939450505050565b600080600080604085870312156130a957600080fd5b843567ffffffffffffffff808211156130c157600080fd5b6130cd88838901612f96565b909650945060208701359150808211156130e657600080fd5b506130f387828801612f96565b95989497509550505050565b60006020828403121561311157600080fd5b5035919050565b6000806040838503121561312b57600080fd5b50508035926020909101359150565b60008060008060006060868803121561315257600080fd5b853567ffffffffffffffff8082111561316a57600080fd5b61317689838a01612f08565b909750955060208801359450604088013591508082111561319657600080fd5b5061303688828901612f96565b60005b838110156131be5781810151838201526020016131a6565b50506000910152565b600081518084526131df8160208601602086016131a3565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006109dc60208301846131c7565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561326557600080fd5b813567ffffffffffffffff8082111561327d57600080fd5b818401915084601f83011261329157600080fd5b8135818111156132a3576132a3613224565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156132e9576132e9613224565b8160405282815287602084870101111561330257600080fd5b826020860160208301376000928101602001929092525095945050505050565b6000806040838503121561333557600080fd5b823561334081612e79565b915061334e60208401612ec4565b90509250929050565b60008060008060008060008060a0898b03121561337357600080fd5b61337c89612ec4565b975061338a60208a01612ec4565b9650604089013567ffffffffffffffff808211156133a757600080fd5b6133b38c838d01612f08565b909850965060608b01359150808211156133cc57600080fd5b6133d88c838d01612f08565b909650945060808b01359150808211156133f157600080fd5b506133fe8b828c01612f96565b999c989b5096995094979396929594505050565b60008060008060008060a0878903121561342b57600080fd5b61343487612ec4565b955061344260208801612ec4565b94506040870135935060608701359250608087013567ffffffffffffffff81111561346c57600080fd5b61347889828a01612f96565b979a9699509497509295939492505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b80358015158114612ee857600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b81835260006020808501808196508560051b810191508460005b8781101561365457828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4188360301811261356b57600080fd5b870160c0613578826134b9565b151586526135878783016134b9565b15158688015260408281013590870152606073ffffffffffffffffffffffffffffffffffffffff6135b9828501612ec4565b16908701526080828101359087015260a080830135368490037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe10181126135ff57600080fd5b90920187810192903567ffffffffffffffff81111561361d57600080fd5b80360384131561362c57600080fd5b828289015261363e83890182866134c9565b9c89019c9750505092860192505060010161352c565b5091979650505050505050565b60408152600560408201527f73656c663a0000000000000000000000000000000000000000000000000000006060820152608060208201526000610a4b608083018486613512565b8381526040602082015260006136c3604083018486613512565b95945050505050565b8381526040602082015260006136c36040830184866134c9565b7f697066733a2f2f0000000000000000000000000000000000000000000000000081526000825161371e8160078501602087016131a3565b9190910160070192915050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4183360301811261375f57600080fd5b9190910192915050565b60006020828403121561377b57600080fd5b6109dc826134b9565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126137b957600080fd5b83018035915067ffffffffffffffff8211156137d457600080fd5b602001915036819003821315612f4d57600080fd5b600080858511156137f957600080fd5b8386111561380657600080fd5b5050820193919092039150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082018082111561082857610828613813565b6060815260006138696060830186886134c9565b6020830194909452506040015292915050565b7f62000000000000000000000000000000000000000000000000000000000000008152600082516138b48160018501602087016131a3565b9190910160010192915050565b828152604060208201526000610a4b60408301846131c7565b84815273ffffffffffffffffffffffffffffffffffffffff841660208201526060604082015260006139106060830184866134c9565b9695505050505050565b602081526000610a4b6020830184866134c9565b8181038181111561082857610828613813565b6040815260006139556040830185876134c9565b9050826020830152949350505050565b6040815260006139796040830185876134c9565b905060ff83166020830152949350505050565b6060815260006139a06060830186886134c9565b60208301949094525090151560409091015292915050565b6000602082840312156139ca57600080fd5b81516109dc81612e7956fea2646970667358221220e6905b82ca2ea91a0c6cc4a371ce0a85eb88794fb3bc7734ed5414f524c47c4264736f6c63430008110033603a600e3d39601a805130553df3363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3", + "deployedBytecode": "0x6080604052600436106101dc5760003560e01c806379e472c911610102578063a4ab5f9f11610095578063c71f1f9611610064578063c71f1f961461073f578063d0748f7114610754578063d59f788514610774578063f23a6e6114610794576101e3565b8063a4ab5f9f146106a2578063affed0e0146106c2578063b93ea7ad146106d7578063bc197c81146106f7576101e3565b80638c3f5563116100d15780638c3f55631461062d5780638efa64411461064d57806390042baf1461066f578063a38cef1914610682576101e3565b806379e472c9146105715780637a9a162814610591578063853c5068146105b1578063888eeec6146105f9576101e3565b8063257671f51161017a5780634598154f116101495780634598154f146104dd5780634fcf3eca146104fd57806357c56d6b1461051d57806361c2926c14610551576101e3565b8063257671f51461042757806329561426146104695780632dd310001461048957806341ea0302146104bd576101e3565b8063150b7a02116101b6578063150b7a021461032c5780631626ba7e146103a25780631a9b2337146103c257806320c13b0b14610407576101e3565b806301ffc9a7146102b7578063025b22bc146102ec578063038dbaac1461030c576101e3565b366101e357005b60006102126000357fffffffff00000000000000000000000000000000000000000000000000000000166107da565b905073ffffffffffffffffffffffffffffffffffffffff8116156102b5576000808273ffffffffffffffffffffffffffffffffffffffff1660003660405161025b929190612e69565b600060405180830381855af49150503d8060008114610296576040519150601f19603f3d011682016040523d82523d6000602084013e61029b565b606091505b5091509150816102ad57805160208201fd5b805160208201f35b005b3480156102c357600080fd5b506102d76102d2366004612ea7565b61082e565b60405190151581526020015b60405180910390f35b3480156102f857600080fd5b506102b5610307366004612eed565b610839565b34801561031857600080fd5b506102b5610327366004612f54565b61088b565b34801561033857600080fd5b50610371610347366004612fd8565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020016102e3565b3480156103ae57600080fd5b506103716103bd366004613047565b610996565b3480156103ce57600080fd5b506103e26103dd366004612ea7565b6109e3565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102e3565b34801561041357600080fd5b50610371610422366004613093565b6109ee565b34801561043357600080fd5b5061045b7f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020016102e3565b34801561047557600080fd5b506102b56104843660046130ff565b610a53565b34801561049557600080fd5b506103e27f000000000000000000000000000000000000000000000000000000000000000081565b3480156104c957600080fd5b5061045b6104d83660046130ff565b610a9d565b3480156104e957600080fd5b506102b56104f8366004613118565b610aa8565b34801561050957600080fd5b506102b5610518366004612ea7565b610b6e565b34801561052957600080fd5b5061045b7f8713a7c4465f6fbee2b6e9d6646d1d9f83fec929edfc4baf661f3c865bdd04d181565b34801561055d57600080fd5b506102b561056c366004612f54565b610c9d565b34801561057d57600080fd5b506102b561058c366004613118565b610d23565b34801561059d57600080fd5b506102b56105ac36600461313a565b610de1565b3480156105bd57600080fd5b506105d16105cc366004613047565b610e77565b604080519586526020860194909452928401919091526060830152608082015260a0016102e3565b34801561060557600080fd5b506103e27f000000000000000000000000000000000000000000000000000000000000000081565b34801561063957600080fd5b5061045b6106483660046130ff565b61103f565b34801561065957600080fd5b5061066261106b565b6040516102e39190613211565b6103e261067d366004613253565b6110ec565b34801561068e57600080fd5b506102b561069d3660046130ff565b611188565b3480156106ae57600080fd5b5061045b6106bd3660046130ff565b6111d2565b3480156106ce57600080fd5b5061045b6111dd565b3480156106e357600080fd5b506102b56106f2366004613322565b6111ee565b34801561070357600080fd5b50610371610712366004613357565b7fbc197c810000000000000000000000000000000000000000000000000000000098975050505050505050565b34801561074b57600080fd5b5061045b611337565b34801561076057600080fd5b506102b561076f366004613118565b611361565b34801561078057600080fd5b506102b561078f366004612f54565b6113b4565b3480156107a057600080fd5b506103716107af366004613412565b7ff23a6e61000000000000000000000000000000000000000000000000000000009695505050505050565b60006108287fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1207fffffffff0000000000000000000000000000000000000000000000000000000084166114f7565b92915050565b600061082882611555565b33301461087f576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044015b60405180910390fd5b610888816115b1565b50565b3330146108cc576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b8060005b818110156109905760008484838181106108ec576108ec61348a565b90506020020135905061094c816000604080517f849e7bdc245db17e50b9f43086f1914d70eb4dab6dd89af4d541d53353ad97de602080830191909152818301859052825180830384018152606090920190925280519101208190555050565b807f804f6171d6008d9e16ee3aa0561fec328397f4ba2827a6605db388cfdefa3b0c600060405161097f91815260200190565b60405180910390a2506001016108d0565b50505050565b6000806109a485858561166c565b50905080156109d657507f1626ba7e0000000000000000000000000000000000000000000000000000000090506109dc565b50600090505b9392505050565b6000610828826107da565b600080610a138686604051610a04929190612e69565b6040518091039020858561166c565b5090508015610a4557507f20c13b0b000000000000000000000000000000000000000000000000000000009050610a4b565b50600090505b949350505050565b333014610a94576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b61088881611687565b600061082882611743565b333014610ae9576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b604080517f849e7bdc245db17e50b9f43086f1914d70eb4dab6dd89af4d541d53353ad97de602080830191909152818301859052825180830384018152606083019384905280519101208390559082905282907f804f6171d6008d9e16ee3aa0561fec328397f4ba2827a6605db388cfdefa3b0c906080015b60405180910390a25050565b333014610baf576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b6000610bba826107da565b73ffffffffffffffffffffffffffffffffffffffff1603610c2b576040517f1c3812cc0000000000000000000000000000000000000000000000000000000081527fffffffff0000000000000000000000000000000000000000000000000000000082166004820152602401610876565b604080517fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1206020808301919091527fffffffff00000000000000000000000000000000000000000000000000000000841682840152825180830384018152606090920190925280519101206000905550565b333014610cde576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b6000610d118383604051602001610cf6929190613661565b6040516020818303038152906040528051906020012061176f565b9050610d1e8184846117f4565b505050565b333014610d64576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b604080517f7f25a23abc421d10864063e9a8ae5fd3fbd5116e156f148428b6a3a02ffd9454602080830191909152818301859052825180830384018152606083019384905280519101208390559082905282907f180e56184e3025975e8449fab79ff135cc5c3b3fe517a19bf8f111d69b33d2e290608001610b62565b610dea83611952565b600080610e22858888604051602001610e05939291906136a9565b60405160208183030381529060405280519060200120858561166c565b9150915081610e63578084846040517f8f4a234f000000000000000000000000000000000000000000000000000000008152600401610876939291906136cc565b610e6e8188886117f4565b50505050505050565b60008060008060008087876000818110610e9357610e9361348a565b909101357fff00000000000000000000000000000000000000000000000000000000000000169150819050610ee957610ecb8961176f565b9250610ed8838989611a4f565b929850909650945091506110349050565b7fff0000000000000000000000000000000000000000000000000000000000000081811601610f2857610f1b8961176f565b9250610ed8838989611aa0565b7ffe000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821601610f7a57610f1b89611acc565b7ffd000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821601610fde57610fce898989611b39565b9550955095509550955050611034565b6040517f6085cd820000000000000000000000000000000000000000000000000000000081527fff0000000000000000000000000000000000000000000000000000000000000082166004820152602401610876565b939792965093509350565b60006108287f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e836114f7565b60606110c86110c361107b611337565b6040517f017012200000000000000000000000000000000000000000000000000000000060208201526024810191909152604401604051602081830303815290604052611cb6565b611ecf565b6040516020016110d891906136e6565b604051602081830303815290604052905090565b600033301461112f576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b81516020830134f060405173ffffffffffffffffffffffffffffffffffffffff821681529091507fa506ad4e7f05eceba62a023c3219e5bd98a615f4fa87e2afb08a2da5cf62bf0c9060200160405180910390a1919050565b3330146111c9576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b61088881611ef8565b600061082882611f51565b60006111e9600061103f565b905090565b33301461122f576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b600061123a836107da565b73ffffffffffffffffffffffffffffffffffffffff16146112ab576040517f5b4d6d6a0000000000000000000000000000000000000000000000000000000081527fffffffff0000000000000000000000000000000000000000000000000000000083166004820152602401610876565b604080517fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1206020808301919091527fffffffff000000000000000000000000000000000000000000000000000000008516828401528251808303840181526060909201909252805191012073ffffffffffffffffffffffffffffffffffffffff821690555050565b5050565b60006111e97f0eecac93ced8722d209199364cda3bc33da3bc3a23daef6be49ebd780511d0335490565b3330146113a2576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b6113ab82611687565b61133381611ef8565b3330146113f5576040517fe1258894000000000000000000000000000000000000000000000000000000008152336004820152306024820152604401610876565b8060005b818110156109905760008484838181106114155761141561348a565b905060200201359050611494817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff604080517f7f25a23abc421d10864063e9a8ae5fd3fbd5116e156f148428b6a3a02ffd9454602080830191909152818301859052825180830384018152606090920190925280519101208190555050565b807f180e56184e3025975e8449fab79ff135cc5c3b3fe517a19bf8f111d69b33d2e27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040516114e691815260200190565b60405180910390a2506001016113f9565b6000808383604051602001611516929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012054949350505050565b60007f6ffbd451000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316016115a857506001919050565b61082882611f7d565b73ffffffffffffffffffffffffffffffffffffffff81163b611617576040517f0c76093700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602401610876565b61161f813055565b60405173ffffffffffffffffffffffffffffffffffffffff821681527f310ba5f1d2ed074b51e2eccd052a47ae9ab7c6b800d1fca3db3999d6a592ca03906020015b60405180910390a150565b60008061167a8585856120be565b915091505b935093915050565b806116be576040517f4294d12700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6116e77fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8829055565b6040518181527f307ed6bd941ee9fc80f369c94af5fa11e25bab5102a6140191756c5474a30bfa9060200160405180910390a16108887f00000000000000000000000000000000000000000000000000000000000000006115b1565b60006108287f7f25a23abc421d10864063e9a8ae5fd3fbd5116e156f148428b6a3a02ffd9454836114f7565b6040517f190100000000000000000000000000000000000000000000000000000000000060208201524660228201527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b166042820152605681018290526000906076015b604051602081830303815290604052805190602001209050919050565b8060005b8181101561194b57368484838181106118135761181361348a565b9050602002810190611825919061372b565b90506040810135805a101561187a5782815a6040517f2bb3e3ba000000000000000000000000000000000000000000000000000000008152600481019390935260248301919091526044820152606401610876565b60006118896020840184613769565b156118c8576118c16118a16080850160608601612eed565b83156118ad57836118af565b5a5b6118bc60a0870187613784565b6120f2565b9050611903565b6119006118db6080850160608601612eed565b608085013584156118ec57846118ee565b5a5b6118fb60a0880188613784565b61210d565b90505b801561191f5760405188815260200160405180910390a0611940565b6119406119326040850160208601613769565b8961193b61212a565b612149565b5050506001016117f8565b5050505050565b606081901c6bffffffffffffffffffffffff821660006119718361103f565b90508181146119bd576040517f9b6514f4000000000000000000000000000000000000000000000000000000008152600481018490526024810183905260448101829052606401610876565b604080517f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e60208083019190915281830186905282518083038401815260609092019092528051910120600183019081905560408051858152602081018390527f1f180c27086c7a39ea2a7b25239d1ab92348f07ca7bb59d1438fcf527568f881910160405180910390a15050505050565b6000808080611a6a87611a65876006818b6137e9565b612195565b6000908152873560f01c6020818152604080842084526002909a013560e01c908190529890912090999198509695509350505050565b6000808080611abb87611ab6876001818b6137e9565b611a4f565b935093509350935093509350935093565b6040517f190100000000000000000000000000000000000000000000000000000000000060208201526000602282018190527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b16604283015260568201839052906076016117d7565b6000808080806004600188013560e81c82611b548383613842565b9050611b668b6105cc83868d8f6137e9565b939b5091995097509550935087871015611bbe57611b8681848b8d6137e9565b89896040517fb006aba00000000000000000000000000000000000000000000000000000000081526004016108769493929190613855565b8092505b88831015611ca85760038301928a013560e81c9150611be18383613842565b90506000611c03611bf18861262b565b8c8c879086926105cc939291906137e9565b939c50919a5098509091505088881015611c5b57611c2382858c8e6137e9565b8a8a6040517fb006aba00000000000000000000000000000000000000000000000000000000081526004016108769493929190613855565b848110611c9e576040517f37daf62b0000000000000000000000000000000000000000000000000000000081526004810182905260248101869052604401610876565b9350915081611bc2565b505050939792965093509350565b8051606090600381901b60006005600483010467ffffffffffffffff811115611ce157611ce1613224565b6040519080825280601f01601f191660200182016040528015611d0b576020820181803683370190505b5090506000806000805b86811015611e1f57888181518110611d2f57611d2f61348a565b01602001516008948501949390931b60f89390931c92909217915b60058410611e17576040805180820190915260208082527f6162636465666768696a6b6c6d6e6f707172737475767778797a323334353637818301527ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb90950194601f85871c16908110611dc057611dc061348a565b602001015160f81c60f81b858381518110611ddd57611ddd61348a565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600190910190611d4a565b600101611d15565b508215611ec3576040518060400160405280602081526020017f6162636465666768696a6b6c6d6e6f707172737475767778797a3233343536378152508360050383901b601f1681518110611e7657611e7661348a565b602001015160f81c60f81b848281518110611e9357611e9361348a565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053505b50919695505050505050565b606081604051602001611ee2919061387c565b6040516020818303038152906040529050919050565b611f217f0eecac93ced8722d209199364cda3bc33da3bc3a23daef6be49ebd780511d033829055565b6040518181527f20d3ef1b5738a9f6d7beae515432206e7a8e2740ca6dcf46a952190ad01bcb5190602001611661565b60006108287f849e7bdc245db17e50b9f43086f1914d70eb4dab6dd89af4d541d53353ad97de836114f7565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fec6aba5000000000000000000000000000000000000000000000000000000000148061201057507fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e000000000000000000000000000000000000000000000000000000000145b8061205c57507fffffffff0000000000000000000000000000000000000000000000000000000082167f150b7a0200000000000000000000000000000000000000000000000000000000145b806120a857507fffffffff0000000000000000000000000000000000000000000000000000000082167fc0ee0b8a00000000000000000000000000000000000000000000000000000000145b156120b557506001919050565b6108288261265f565b600080426120cb86611743565b11915081156120e757816120de866126bb565b9150915061167f565b61167a8585856126f6565b60006040518284823760008084838989f49695505050505050565b6000604051828482376000808483898b8af1979650505050505050565b60603d604051915060208201818101604052818352816000823e505090565b821561215757805160208201fd5b7f3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd782826040516121889291906138c1565b60405180910390a1505050565b60008060005b8381101561262257600181019085013560f81c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810161223c57601582019186013560f881901c9060581c73ffffffffffffffffffffffffffffffffffffffff81169074ff0000000000000000000000000000000000000000168117856122225780612231565b60008681526020829052604090205b95505050505061219b565b806122d25760018201918681013560f81c9060430160006122688a61226384888c8e6137e9565b612734565b60ff841697909701969194508491905060a083901b74ff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff821617866122b757806122c6565b60008781526020829052604090205b9650505050505061219b565b600281036123fa576000808784013560f881901c9060581c73ffffffffffffffffffffffffffffffffffffffff16601586019550909250905060008885013560e81c600386018162ffffff16915080965081925050506000818601905061234b8b848c8c8a908692612346939291906137e9565b6129f7565b612393578a8361235d83898d8f6137e9565b6040517f9a94623200000000000000000000000000000000000000000000000000000000815260040161087694939291906138da565b60ff8416979097019694508460a084901b74ff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff841617876123de57806123ed565b60008881526020829052604090205b975050505050505061219b565b6003810361242d576020820191860135836124155780612424565b60008481526020829052604090205b9350505061219b565b60048103612479576003808301928781013560e81c919082010160008061245a8b611a6585898d8f6137e9565b6000988952602052604090972096909701965090935061219b92505050565b600681036125815760008287013560f81c60018401935060ff16905060008784013560f01c60028501945061ffff16905060008885013560e81c600386018162ffffff1691508096508192505050600081860190506000806124e78d8d8d8b908792611a65939291906137e9565b939850889390925090508482106124fd57988501985b604080517f53657175656e6365206e657374656420636f6e6669673a0a0000000000000000602080830191909152603882018490526058820188905260788083018a90528351808403909101815260989092019092528051910120896125635780612572565b60008a81526020829052604090205b9950505050505050505061219b565b600581036125ed5760208201918601358781036125bc577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff94505b60006125c782612ba4565b9050846125d457806125e3565b60008581526020829052604090205b945050505061219b565b6040517fb2505f7c00000000000000000000000000000000000000000000000000000000815260048101829052602401610876565b50935093915050565b7f8713a7c4465f6fbee2b6e9d6646d1d9f83fec929edfc4baf661f3c865bdd04d16000908152602082905260408120610828565b60007fe4a77bbc000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316016126b257506001919050565b61082882612bdf565b604080517f7f25a23abc421d10864063e9a8ae5fd3fbd5116e156f148428b6a3a02ffd945460208201529081018290526000906060016117d7565b6000806000806000612709888888610e77565b50965091945092509050828210801590612727575061272781612bea565b9450505050935093915050565b6000604282146127745782826040517f2ee17a3d00000000000000000000000000000000000000000000000000000000815260040161087692919061391a565b600061278d61278460018561392e565b85013560f81c90565b60ff169050604084013560f81c843560208601357f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0811115612801578686826040517fad4aac7600000000000000000000000000000000000000000000000000000000815260040161087693929190613941565b8260ff16601b1415801561281957508260ff16601c14155b15612856578686846040517fe578897e00000000000000000000000000000000000000000000000000000000815260040161087693929190613965565b600184036128c3576040805160008152602081018083528a905260ff851691810191909152606081018390526080810182905260019060a0015b6020604051602081039080840390855afa1580156128b2573d6000803e3d6000fd5b50505060206040510351945061299b565b60028403612960576040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101899052600190605c01604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff861690820152606081018490526080810183905260a001612890565b86868560016040517f9dfba852000000000000000000000000000000000000000000000000000000008152600401610876949392919061398c565b73ffffffffffffffffffffffffffffffffffffffff85166129ec5786866040517f6c1719d200000000000000000000000000000000000000000000000000000000815260040161087692919061391a565b505050509392505050565b6000808383612a0760018261392e565b818110612a1657612a1661348a565b919091013560f81c9150506001811480612a305750600281145b15612a75578473ffffffffffffffffffffffffffffffffffffffff16612a57878686612734565b73ffffffffffffffffffffffffffffffffffffffff16149150612b9b565b60038103612b605773ffffffffffffffffffffffffffffffffffffffff8516631626ba7e8786600087612aa960018261392e565b92612ab6939291906137e9565b6040518463ffffffff1660e01b8152600401612ad4939291906136cc565b602060405180830381865afa158015612af1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b1591906139b8565b7fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e00000000000000000000000000000000000000000000000000000000149150612b9b565b83838260006040517f9dfba852000000000000000000000000000000000000000000000000000000008152600401610876949392919061398c565b50949350505050565b6040517f53657175656e636520737461746963206469676573743a0a00000000000000006020820152603881018290526000906058016117d7565b600061082882612bf5565b600061082882612c51565b60007ffda4dd44000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831601612c4857506001919050565b61082882612d7f565b6000612d53826040517fff0000000000000000000000000000000000000000000000000000000000000060208201527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000060601b166021820152603581018290527f000000000000000000000000000000000000000000000000000000000000000060558201526000903090607501604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012073ffffffffffffffffffffffffffffffffffffffff161492915050565b15612d6057506001919050565b6000612d6b83611f51565b905080158015906109dc5750421092915050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fac6a444e000000000000000000000000000000000000000000000000000000001480612e1257507fffffffff0000000000000000000000000000000000000000000000000000000082167f36e7817500000000000000000000000000000000000000000000000000000000145b15612e1f57506001919050565b7f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831614610828565b8183823760009101908152919050565b7fffffffff000000000000000000000000000000000000000000000000000000008116811461088857600080fd5b600060208284031215612eb957600080fd5b81356109dc81612e79565b803573ffffffffffffffffffffffffffffffffffffffff81168114612ee857600080fd5b919050565b600060208284031215612eff57600080fd5b6109dc82612ec4565b60008083601f840112612f1a57600080fd5b50813567ffffffffffffffff811115612f3257600080fd5b6020830191508360208260051b8501011115612f4d57600080fd5b9250929050565b60008060208385031215612f6757600080fd5b823567ffffffffffffffff811115612f7e57600080fd5b612f8a85828601612f08565b90969095509350505050565b60008083601f840112612fa857600080fd5b50813567ffffffffffffffff811115612fc057600080fd5b602083019150836020828501011115612f4d57600080fd5b600080600080600060808688031215612ff057600080fd5b612ff986612ec4565b945061300760208701612ec4565b935060408601359250606086013567ffffffffffffffff81111561302a57600080fd5b61303688828901612f96565b969995985093965092949392505050565b60008060006040848603121561305c57600080fd5b83359250602084013567ffffffffffffffff81111561307a57600080fd5b61308686828701612f96565b9497909650939450505050565b600080600080604085870312156130a957600080fd5b843567ffffffffffffffff808211156130c157600080fd5b6130cd88838901612f96565b909650945060208701359150808211156130e657600080fd5b506130f387828801612f96565b95989497509550505050565b60006020828403121561311157600080fd5b5035919050565b6000806040838503121561312b57600080fd5b50508035926020909101359150565b60008060008060006060868803121561315257600080fd5b853567ffffffffffffffff8082111561316a57600080fd5b61317689838a01612f08565b909750955060208801359450604088013591508082111561319657600080fd5b5061303688828901612f96565b60005b838110156131be5781810151838201526020016131a6565b50506000910152565b600081518084526131df8160208601602086016131a3565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006109dc60208301846131c7565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561326557600080fd5b813567ffffffffffffffff8082111561327d57600080fd5b818401915084601f83011261329157600080fd5b8135818111156132a3576132a3613224565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156132e9576132e9613224565b8160405282815287602084870101111561330257600080fd5b826020860160208301376000928101602001929092525095945050505050565b6000806040838503121561333557600080fd5b823561334081612e79565b915061334e60208401612ec4565b90509250929050565b60008060008060008060008060a0898b03121561337357600080fd5b61337c89612ec4565b975061338a60208a01612ec4565b9650604089013567ffffffffffffffff808211156133a757600080fd5b6133b38c838d01612f08565b909850965060608b01359150808211156133cc57600080fd5b6133d88c838d01612f08565b909650945060808b01359150808211156133f157600080fd5b506133fe8b828c01612f96565b999c989b5096995094979396929594505050565b60008060008060008060a0878903121561342b57600080fd5b61343487612ec4565b955061344260208801612ec4565b94506040870135935060608701359250608087013567ffffffffffffffff81111561346c57600080fd5b61347889828a01612f96565b979a9699509497509295939492505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b80358015158114612ee857600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b81835260006020808501808196508560051b810191508460005b8781101561365457828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4188360301811261356b57600080fd5b870160c0613578826134b9565b151586526135878783016134b9565b15158688015260408281013590870152606073ffffffffffffffffffffffffffffffffffffffff6135b9828501612ec4565b16908701526080828101359087015260a080830135368490037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe10181126135ff57600080fd5b90920187810192903567ffffffffffffffff81111561361d57600080fd5b80360384131561362c57600080fd5b828289015261363e83890182866134c9565b9c89019c9750505092860192505060010161352c565b5091979650505050505050565b60408152600560408201527f73656c663a0000000000000000000000000000000000000000000000000000006060820152608060208201526000610a4b608083018486613512565b8381526040602082015260006136c3604083018486613512565b95945050505050565b8381526040602082015260006136c36040830184866134c9565b7f697066733a2f2f0000000000000000000000000000000000000000000000000081526000825161371e8160078501602087016131a3565b9190910160070192915050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4183360301811261375f57600080fd5b9190910192915050565b60006020828403121561377b57600080fd5b6109dc826134b9565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126137b957600080fd5b83018035915067ffffffffffffffff8211156137d457600080fd5b602001915036819003821315612f4d57600080fd5b600080858511156137f957600080fd5b8386111561380657600080fd5b5050820193919092039150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082018082111561082857610828613813565b6060815260006138696060830186886134c9565b6020830194909452506040015292915050565b7f62000000000000000000000000000000000000000000000000000000000000008152600082516138b48160018501602087016131a3565b9190910160010192915050565b828152604060208201526000610a4b60408301846131c7565b84815273ffffffffffffffffffffffffffffffffffffffff841660208201526060604082015260006139106060830184866134c9565b9695505050505050565b602081526000610a4b6020830184866134c9565b8181038181111561082857610828613813565b6040815260006139556040830185876134c9565b9050826020830152949350505050565b6040815260006139796040830185876134c9565b905060ff83166020830152949350505050565b6060815260006139a06060830186886134c9565b60208301949094525090151560409091015292915050565b6000602082840312156139ca57600080fd5b81516109dc81612e7956fea2646970667358221220e6905b82ca2ea91a0c6cc4a371ce0a85eb88794fb3bc7734ed5414f524c47c4264736f6c63430008110033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/packages/tests/src/builds/v2/artifacts/MainModuleUpgradable.ts b/packages/tests/src/builds/v2/artifacts/MainModuleUpgradable.ts new file mode 100644 index 000000000..a90bdbcd6 --- /dev/null +++ b/packages/tests/src/builds/v2/artifacts/MainModuleUpgradable.ts @@ -0,0 +1,1060 @@ +export const mainModuleUpgradable = { + "_format": "hh-sol-artifact-1", + "contractName": "MainModuleUpgradable", + "sourceName": "contracts/modules/MainModuleUpgradable.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "uint256", + "name": "_space", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_provided", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_current", + "type": "uint256" + } + ], + "name": "BadNonce", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_signature", + "type": "bytes4" + } + ], + "name": "HookAlreadyExists", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_signature", + "type": "bytes4" + } + ], + "name": "HookDoesNotExist", + "type": "error" + }, + { + "inputs": [], + "name": "ImageHashIsZero", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_implementation", + "type": "address" + } + ], + "name": "InvalidImplementation", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "_addr", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "InvalidNestedSignature", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + }, + { + "internalType": "bytes32", + "name": "_s", + "type": "bytes32" + } + ], + "name": "InvalidSValue", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "InvalidSignature", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_flag", + "type": "uint256" + } + ], + "name": "InvalidSignatureFlag", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "InvalidSignatureLength", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes1", + "name": "_type", + "type": "bytes1" + } + ], + "name": "InvalidSignatureType", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_v", + "type": "uint256" + } + ], + "name": "InvalidVValue", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "threshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_weight", + "type": "uint256" + } + ], + "name": "LowWeightChainedSignature", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_requested", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_available", + "type": "uint256" + } + ], + "name": "NotEnoughGas", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_sender", + "type": "address" + }, + { + "internalType": "address", + "name": "_self", + "type": "address" + } + ], + "name": "OnlySelfAuth", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "SignerIsAddress0", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "_type", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "_recoverMode", + "type": "bool" + } + ], + "name": "UnsupportedSignatureType", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_current", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_prev", + "type": "uint256" + } + ], + "name": "WrongChainedCheckpointOrder", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_contract", + "type": "address" + } + ], + "name": "CreatedContract", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + } + ], + "name": "IPFSRootUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "newImageHash", + "type": "bytes32" + } + ], + "name": "ImageHashUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "ImplementationUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "_space", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_newNonce", + "type": "uint256" + } + ], + "name": "NonceChange", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "_imageHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_expiration", + "type": "uint256" + } + ], + "name": "SetExtraImageHash", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "_digest", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_expiration", + "type": "uint256" + } + ], + "name": "SetStaticDigest", + "type": "event" + }, + { + "anonymous": true, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "_tx", + "type": "bytes32" + } + ], + "name": "TxExecuted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "_tx", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "_reason", + "type": "bytes" + } + ], + "name": "TxFailed", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "SET_IMAGE_HASH_TYPE_HASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_signature", + "type": "bytes4" + }, + { + "internalType": "address", + "name": "_implementation", + "type": "address" + } + ], + "name": "addHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "_digests", + "type": "bytes32[]" + } + ], + "name": "addStaticDigests", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "_imageHashes", + "type": "bytes32[]" + } + ], + "name": "clearExtraImageHashes", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_code", + "type": "bytes" + } + ], + "name": "createContract", + "outputs": [ + { + "internalType": "address", + "name": "addr", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "delegateCall", + "type": "bool" + }, + { + "internalType": "bool", + "name": "revertOnError", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct IModuleCalls.Transaction[]", + "name": "_txs", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "_nonce", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "execute", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_imageHash", + "type": "bytes32" + } + ], + "name": "extraImageHash", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "imageHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ipfsRoot", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ipfsRootBytes32", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_signatures", + "type": "bytes" + } + ], + "name": "isValidSignature", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "_signatures", + "type": "bytes" + } + ], + "name": "isValidSignature", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155BatchReceived", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC721Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_signature", + "type": "bytes4" + } + ], + "name": "readHook", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_space", + "type": "uint256" + } + ], + "name": "readNonce", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_signature", + "type": "bytes4" + } + ], + "name": "removeHook", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bool", + "name": "delegateCall", + "type": "bool" + }, + { + "internalType": "bool", + "name": "revertOnError", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "internalType": "struct IModuleCalls.Transaction[]", + "name": "_txs", + "type": "tuple[]" + } + ], + "name": "selfExecute", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_imageHash", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_expiration", + "type": "uint256" + } + ], + "name": "setExtraImageHash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_digest", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_expiration", + "type": "uint256" + } + ], + "name": "setStaticDigest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_digest", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "signatureRecovery", + "outputs": [ + { + "internalType": "uint256", + "name": "threshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "weight", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "imageHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "subDigest", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "checkpoint", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_digest", + "type": "bytes32" + } + ], + "name": "staticDigest", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_interfaceID", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + } + ], + "name": "updateIPFSRoot", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_imageHash", + "type": "bytes32" + } + ], + "name": "updateImageHash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_imageHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_ipfsRoot", + "type": "bytes32" + } + ], + "name": "updateImageHashAndIPFS", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_implementation", + "type": "address" + } + ], + "name": "updateImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b506138f9806100206000396000f3fe6080604052600436106101c65760003560e01c806379e472c9116100f7578063a4ab5f9f11610095578063c71f1f9611610064578063c71f1f96146106a2578063d0748f71146106b7578063d59f7885146106d7578063f23a6e61146106f7576101cd565b8063a4ab5f9f14610605578063affed0e014610625578063b93ea7ad1461063a578063bc197c811461065a576101cd565b80638c3f5563116100d15780638c3f5563146105905780638efa6441146105b057806390042baf146105d2578063a38cef19146105e5576101cd565b806379e472c9146105085780637a9a162814610528578063853c506814610548576101cd565b806329561426116101645780634fcf3eca1161013e5780634fcf3eca1461047f57806351605d801461049f57806357c56d6b146104b457806361c2926c146104e8576101cd565b8063295614261461041157806341ea0302146104315780634598154f1461045f576101cd565b8063150b7a02116101a0578063150b7a02146103165780631626ba7e1461038c5780631a9b2337146103ac57806320c13b0b146103f1576101cd565b806301ffc9a7146102a1578063025b22bc146102d6578063038dbaac146102f6576101cd565b366101cd57005b60006101fc6000357fffffffff000000000000000000000000000000000000000000000000000000001661073d565b905073ffffffffffffffffffffffffffffffffffffffff81161561029f576000808273ffffffffffffffffffffffffffffffffffffffff16600036604051610245929190612d57565b600060405180830381855af49150503d8060008114610280576040519150601f19603f3d011682016040523d82523d6000602084013e610285565b606091505b50915091508161029757805160208201fd5b805160208201f35b005b3480156102ad57600080fd5b506102c16102bc366004612d95565b610791565b60405190151581526020015b60405180910390f35b3480156102e257600080fd5b5061029f6102f1366004612ddb565b61079c565b34801561030257600080fd5b5061029f610311366004612e42565b6107ee565b34801561032257600080fd5b5061035b610331366004612ec6565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020016102cd565b34801561039857600080fd5b5061035b6103a7366004612f35565b6108f9565b3480156103b857600080fd5b506103cc6103c7366004612d95565b610946565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102cd565b3480156103fd57600080fd5b5061035b61040c366004612f81565b610951565b34801561041d57600080fd5b5061029f61042c366004612fed565b6109b6565b34801561043d57600080fd5b5061045161044c366004612fed565b610a00565b6040519081526020016102cd565b34801561046b57600080fd5b5061029f61047a366004613006565b610a0b565b34801561048b57600080fd5b5061029f61049a366004612d95565b610ad1565b3480156104ab57600080fd5b50610451610c00565b3480156104c057600080fd5b506104517f8713a7c4465f6fbee2b6e9d6646d1d9f83fec929edfc4baf661f3c865bdd04d181565b3480156104f457600080fd5b5061029f610503366004612e42565b610c2f565b34801561051457600080fd5b5061029f610523366004613006565b610cb5565b34801561053457600080fd5b5061029f610543366004613028565b610d73565b34801561055457600080fd5b50610568610563366004612f35565b610e09565b604080519586526020860194909452928401919091526060830152608082015260a0016102cd565b34801561059c57600080fd5b506104516105ab366004612fed565b610fd1565b3480156105bc57600080fd5b506105c5610ffd565b6040516102cd91906130ff565b6103cc6105e0366004613141565b61107e565b3480156105f157600080fd5b5061029f610600366004612fed565b61111a565b34801561061157600080fd5b50610451610620366004612fed565b611164565b34801561063157600080fd5b5061045161116f565b34801561064657600080fd5b5061029f610655366004613210565b61117b565b34801561066657600080fd5b5061035b610675366004613245565b7fbc197c810000000000000000000000000000000000000000000000000000000098975050505050505050565b3480156106ae57600080fd5b506104516112c4565b3480156106c357600080fd5b5061029f6106d2366004613006565b6112ee565b3480156106e357600080fd5b5061029f6106f2366004612e42565b611341565b34801561070357600080fd5b5061035b610712366004613300565b7ff23a6e61000000000000000000000000000000000000000000000000000000009695505050505050565b600061078b7fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1207fffffffff000000000000000000000000000000000000000000000000000000008416611484565b92915050565b600061078b826114e2565b3330146107e2576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044015b60405180910390fd5b6107eb8161153e565b50565b33301461082f576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b8060005b818110156108f357600084848381811061084f5761084f613378565b9050602002013590506108af816000604080517f849e7bdc245db17e50b9f43086f1914d70eb4dab6dd89af4d541d53353ad97de602080830191909152818301859052825180830384018152606090920190925280519101208190555050565b807f804f6171d6008d9e16ee3aa0561fec328397f4ba2827a6605db388cfdefa3b0c60006040516108e291815260200190565b60405180910390a250600101610833565b50505050565b6000806109078585856115f9565b509050801561093957507f1626ba7e00000000000000000000000000000000000000000000000000000000905061093f565b50600090505b9392505050565b600061078b8261073d565b6000806109768686604051610967929190612d57565b604051809103902085856115f9565b50905080156109a857507f20c13b0b0000000000000000000000000000000000000000000000000000000090506109ae565b50600090505b949350505050565b3330146109f7576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b6107eb81611614565b600061078b826116a4565b333014610a4c576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b604080517f849e7bdc245db17e50b9f43086f1914d70eb4dab6dd89af4d541d53353ad97de602080830191909152818301859052825180830384018152606083019384905280519101208390559082905282907f804f6171d6008d9e16ee3aa0561fec328397f4ba2827a6605db388cfdefa3b0c906080015b60405180910390a25050565b333014610b12576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b6000610b1d8261073d565b73ffffffffffffffffffffffffffffffffffffffff1603610b8e576040517f1c3812cc0000000000000000000000000000000000000000000000000000000081527fffffffff00000000000000000000000000000000000000000000000000000000821660048201526024016107d9565b604080517fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1206020808301919091527fffffffff00000000000000000000000000000000000000000000000000000000841682840152825180830384018152606090920190925280519101206000905550565b6000610c2a7fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf85490565b905090565b333014610c70576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b6000610ca38383604051602001610c8892919061354f565b604051602081830303815290604052805190602001206116d0565b9050610cb0818484611755565b505050565b333014610cf6576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b604080517f7f25a23abc421d10864063e9a8ae5fd3fbd5116e156f148428b6a3a02ffd9454602080830191909152818301859052825180830384018152606083019384905280519101208390559082905282907f180e56184e3025975e8449fab79ff135cc5c3b3fe517a19bf8f111d69b33d2e290608001610ac5565b610d7c836118b3565b600080610db4858888604051602001610d9793929190613597565b6040516020818303038152906040528051906020012085856115f9565b9150915081610df5578084846040517f8f4a234f0000000000000000000000000000000000000000000000000000000081526004016107d9939291906135ba565b610e00818888611755565b50505050505050565b60008060008060008087876000818110610e2557610e25613378565b909101357fff00000000000000000000000000000000000000000000000000000000000000169150819050610e7b57610e5d896116d0565b9250610e6a8389896119b0565b92985090965094509150610fc69050565b7fff0000000000000000000000000000000000000000000000000000000000000081811601610eba57610ead896116d0565b9250610e6a838989611a01565b7ffe000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821601610f0c57610ead89611a2d565b7ffd000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821601610f7057610f60898989611a9a565b9550955095509550955050610fc6565b6040517f6085cd820000000000000000000000000000000000000000000000000000000081527fff00000000000000000000000000000000000000000000000000000000000000821660048201526024016107d9565b939792965093509350565b600061078b7f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e83611484565b606061105a61105561100d6112c4565b6040517f017012200000000000000000000000000000000000000000000000000000000060208201526024810191909152604401604051602081830303815290604052611c17565b611e30565b60405160200161106a91906135d4565b604051602081830303815290604052905090565b60003330146110c1576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b81516020830134f060405173ffffffffffffffffffffffffffffffffffffffff821681529091507fa506ad4e7f05eceba62a023c3219e5bd98a615f4fa87e2afb08a2da5cf62bf0c9060200160405180910390a1919050565b33301461115b576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b6107eb81611e59565b600061078b82611eb2565b6000610c2a6000610fd1565b3330146111bc576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b60006111c78361073d565b73ffffffffffffffffffffffffffffffffffffffff1614611238576040517f5b4d6d6a0000000000000000000000000000000000000000000000000000000081527fffffffff00000000000000000000000000000000000000000000000000000000831660048201526024016107d9565b604080517fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1206020808301919091527fffffffff000000000000000000000000000000000000000000000000000000008516828401528251808303840181526060909201909252805191012073ffffffffffffffffffffffffffffffffffffffff821690555050565b5050565b6000610c2a7f0eecac93ced8722d209199364cda3bc33da3bc3a23daef6be49ebd780511d0335490565b33301461132f576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b61133882611614565b6112c081611e59565b333014611382576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b8060005b818110156108f35760008484838181106113a2576113a2613378565b905060200201359050611421817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff604080517f7f25a23abc421d10864063e9a8ae5fd3fbd5116e156f148428b6a3a02ffd9454602080830191909152818301859052825180830384018152606090920190925280519101208190555050565b807f180e56184e3025975e8449fab79ff135cc5c3b3fe517a19bf8f111d69b33d2e27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60405161147391815260200190565b60405180910390a250600101611386565b60008083836040516020016114a3929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012054949350505050565b60007f6ffbd451000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083160161153557506001919050565b61078b82611ede565b73ffffffffffffffffffffffffffffffffffffffff81163b6115a4576040517f0c76093700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016107d9565b6115ac813055565b60405173ffffffffffffffffffffffffffffffffffffffff821681527f310ba5f1d2ed074b51e2eccd052a47ae9ab7c6b800d1fca3db3999d6a592ca03906020015b60405180910390a150565b60008061160785858561201f565b915091505b935093915050565b8061164b576040517f4294d12700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6116747fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8829055565b6040518181527f307ed6bd941ee9fc80f369c94af5fa11e25bab5102a6140191756c5474a30bfa906020016115ee565b600061078b7f7f25a23abc421d10864063e9a8ae5fd3fbd5116e156f148428b6a3a02ffd945483611484565b6040517f190100000000000000000000000000000000000000000000000000000000000060208201524660228201527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b166042820152605681018290526000906076015b604051602081830303815290604052805190602001209050919050565b8060005b818110156118ac573684848381811061177457611774613378565b90506020028101906117869190613619565b90506040810135805a10156117db5782815a6040517f2bb3e3ba0000000000000000000000000000000000000000000000000000000081526004810193909352602483019190915260448201526064016107d9565b60006117ea6020840184613657565b15611829576118226118026080850160608601612ddb565b831561180e5783611810565b5a5b61181d60a0870187613672565b612053565b9050611864565b61186161183c6080850160608601612ddb565b6080850135841561184d578461184f565b5a5b61185c60a0880188613672565b61206e565b90505b80156118805760405188815260200160405180910390a06118a1565b6118a16118936040850160208601613657565b8961189c61208b565b6120aa565b505050600101611759565b5050505050565b606081901c6bffffffffffffffffffffffff821660006118d283610fd1565b905081811461191e576040517f9b6514f40000000000000000000000000000000000000000000000000000000081526004810184905260248101839052604481018290526064016107d9565b604080517f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e60208083019190915281830186905282518083038401815260609092019092528051910120600183019081905560408051858152602081018390527f1f180c27086c7a39ea2a7b25239d1ab92348f07ca7bb59d1438fcf527568f881910160405180910390a15050505050565b60008080806119cb876119c6876006818b6136d7565b6120f6565b6000908152873560f01c6020818152604080842084526002909a013560e01c908190529890912090999198509695509350505050565b6000808080611a1c87611a17876001818b6136d7565b6119b0565b935093509350935093509350935093565b6040517f190100000000000000000000000000000000000000000000000000000000000060208201526000602282018190527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b1660428301526056820183905290607601611738565b6000808080806004600188013560e81c82611ab58383613730565b9050611ac78b61056383868d8f6136d7565b939b5091995097509550935087871015611b1f57611ae781848b8d6136d7565b89896040517fb006aba00000000000000000000000000000000000000000000000000000000081526004016107d99493929190613743565b8092505b88831015611c095760038301928a013560e81c9150611b428383613730565b90506000611b64611b528861258c565b8c8c87908692610563939291906136d7565b939c50919a5098509091505088881015611bbc57611b8482858c8e6136d7565b8a8a6040517fb006aba00000000000000000000000000000000000000000000000000000000081526004016107d99493929190613743565b848110611bff576040517f37daf62b00000000000000000000000000000000000000000000000000000000815260048101829052602481018690526044016107d9565b9350915081611b23565b505050939792965093509350565b8051606090600381901b60006005600483010467ffffffffffffffff811115611c4257611c42613112565b6040519080825280601f01601f191660200182016040528015611c6c576020820181803683370190505b5090506000806000805b86811015611d8057888181518110611c9057611c90613378565b01602001516008948501949390931b60f89390931c92909217915b60058410611d78576040805180820190915260208082527f6162636465666768696a6b6c6d6e6f707172737475767778797a323334353637818301527ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb90950194601f85871c16908110611d2157611d21613378565b602001015160f81c60f81b858381518110611d3e57611d3e613378565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600190910190611cab565b600101611c76565b508215611e24576040518060400160405280602081526020017f6162636465666768696a6b6c6d6e6f707172737475767778797a3233343536378152508360050383901b601f1681518110611dd757611dd7613378565b602001015160f81c60f81b848281518110611df457611df4613378565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053505b50919695505050505050565b606081604051602001611e43919061376a565b6040516020818303038152906040529050919050565b611e827f0eecac93ced8722d209199364cda3bc33da3bc3a23daef6be49ebd780511d033829055565b6040518181527f20d3ef1b5738a9f6d7beae515432206e7a8e2740ca6dcf46a952190ad01bcb51906020016115ee565b600061078b7f849e7bdc245db17e50b9f43086f1914d70eb4dab6dd89af4d541d53353ad97de83611484565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fec6aba50000000000000000000000000000000000000000000000000000000001480611f7157507fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e000000000000000000000000000000000000000000000000000000000145b80611fbd57507fffffffff0000000000000000000000000000000000000000000000000000000082167f150b7a0200000000000000000000000000000000000000000000000000000000145b8061200957507fffffffff0000000000000000000000000000000000000000000000000000000082167fc0ee0b8a00000000000000000000000000000000000000000000000000000000145b1561201657506001919050565b61078b826125c0565b6000804261202c866116a4565b1191508115612048578161203f8661261c565b9150915061160c565b611607858585612657565b60006040518284823760008084838989f49695505050505050565b6000604051828482376000808483898b8af1979650505050505050565b60603d604051915060208201818101604052818352816000823e505090565b82156120b857805160208201fd5b7f3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd782826040516120e99291906137af565b60405180910390a1505050565b60008060005b8381101561258357600181019085013560f81c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810161219d57601582019186013560f881901c9060581c73ffffffffffffffffffffffffffffffffffffffff81169074ff0000000000000000000000000000000000000000168117856121835780612192565b60008681526020829052604090205b9550505050506120fc565b806122335760018201918681013560f81c9060430160006121c98a6121c484888c8e6136d7565b612695565b60ff841697909701969194508491905060a083901b74ff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff821617866122185780612227565b60008781526020829052604090205b965050505050506120fc565b6002810361235b576000808784013560f881901c9060581c73ffffffffffffffffffffffffffffffffffffffff16601586019550909250905060008885013560e81c600386018162ffffff1691508096508192505050600081860190506122ac8b848c8c8a9086926122a7939291906136d7565b612958565b6122f4578a836122be83898d8f6136d7565b6040517f9a9462320000000000000000000000000000000000000000000000000000000081526004016107d994939291906137c8565b60ff8416979097019694508460a084901b74ff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8416178761233f578061234e565b60008881526020829052604090205b97505050505050506120fc565b6003810361238e576020820191860135836123765780612385565b60008481526020829052604090205b935050506120fc565b600481036123da576003808301928781013560e81c91908201016000806123bb8b6119c685898d8f6136d7565b600098895260205260409097209690970196509093506120fc92505050565b600681036124e25760008287013560f81c60018401935060ff16905060008784013560f01c60028501945061ffff16905060008885013560e81c600386018162ffffff1691508096508192505050600081860190506000806124488d8d8d8b9087926119c6939291906136d7565b9398508893909250905084821061245e57988501985b604080517f53657175656e6365206e657374656420636f6e6669673a0a0000000000000000602080830191909152603882018490526058820188905260788083018a90528351808403909101815260989092019092528051910120896124c457806124d3565b60008a81526020829052604090205b995050505050505050506120fc565b6005810361254e57602082019186013587810361251d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff94505b600061252882612b05565b9050846125355780612544565b60008581526020829052604090205b94505050506120fc565b6040517fb2505f7c000000000000000000000000000000000000000000000000000000008152600481018290526024016107d9565b50935093915050565b7f8713a7c4465f6fbee2b6e9d6646d1d9f83fec929edfc4baf661f3c865bdd04d1600090815260208290526040812061078b565b60007ffda4dd44000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083160161261357506001919050565b61078b82612b40565b604080517f7f25a23abc421d10864063e9a8ae5fd3fbd5116e156f148428b6a3a02ffd94546020820152908101829052600090606001611738565b600080600080600061266a888888610e09565b50965091945092509050828210801590612688575061268881612b9c565b9450505050935093915050565b6000604282146126d55782826040517f2ee17a3d0000000000000000000000000000000000000000000000000000000081526004016107d9929190613808565b60006126ee6126e560018561381c565b85013560f81c90565b60ff169050604084013560f81c843560208601357f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0811115612762578686826040517fad4aac760000000000000000000000000000000000000000000000000000000081526004016107d99392919061382f565b8260ff16601b1415801561277a57508260ff16601c14155b156127b7578686846040517fe578897e0000000000000000000000000000000000000000000000000000000081526004016107d993929190613853565b60018403612824576040805160008152602081018083528a905260ff851691810191909152606081018390526080810182905260019060a0015b6020604051602081039080840390855afa158015612813573d6000803e3d6000fd5b5050506020604051035194506128fc565b600284036128c1576040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101899052600190605c01604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff861690820152606081018490526080810183905260a0016127f1565b86868560016040517f9dfba8520000000000000000000000000000000000000000000000000000000081526004016107d9949392919061387a565b73ffffffffffffffffffffffffffffffffffffffff851661294d5786866040517f6c1719d20000000000000000000000000000000000000000000000000000000081526004016107d9929190613808565b505050509392505050565b600080838361296860018261381c565b81811061297757612977613378565b919091013560f81c91505060018114806129915750600281145b156129d6578473ffffffffffffffffffffffffffffffffffffffff166129b8878686612695565b73ffffffffffffffffffffffffffffffffffffffff16149150612afc565b60038103612ac15773ffffffffffffffffffffffffffffffffffffffff8516631626ba7e8786600087612a0a60018261381c565b92612a17939291906136d7565b6040518463ffffffff1660e01b8152600401612a35939291906135ba565b602060405180830381865afa158015612a52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a7691906138a6565b7fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e00000000000000000000000000000000000000000000000000000000149150612afc565b83838260006040517f9dfba8520000000000000000000000000000000000000000000000000000000081526004016107d9949392919061387a565b50949350505050565b6040517f53657175656e636520737461746963206469676573743a0a0000000000000000602082015260388101829052600090605801611738565b60007fe4a77bbc000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831601612b9357506001919050565b61078b82612ba7565b600061078b82612c03565b60007fae9fa280000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831601612bfa57506001919050565b61078b82612c3a565b6000612c0e82612d24565b15612c1b57506001919050565b6000612c2683611eb2565b9050801580159061093f5750421092915050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fac6a444e000000000000000000000000000000000000000000000000000000001480612ccd57507fffffffff0000000000000000000000000000000000000000000000000000000082167f36e7817500000000000000000000000000000000000000000000000000000000145b15612cda57506001919050565b7f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083161461078b565b6000811580159061078b5750507fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8541490565b8183823760009101908152919050565b7fffffffff00000000000000000000000000000000000000000000000000000000811681146107eb57600080fd5b600060208284031215612da757600080fd5b813561093f81612d67565b803573ffffffffffffffffffffffffffffffffffffffff81168114612dd657600080fd5b919050565b600060208284031215612ded57600080fd5b61093f82612db2565b60008083601f840112612e0857600080fd5b50813567ffffffffffffffff811115612e2057600080fd5b6020830191508360208260051b8501011115612e3b57600080fd5b9250929050565b60008060208385031215612e5557600080fd5b823567ffffffffffffffff811115612e6c57600080fd5b612e7885828601612df6565b90969095509350505050565b60008083601f840112612e9657600080fd5b50813567ffffffffffffffff811115612eae57600080fd5b602083019150836020828501011115612e3b57600080fd5b600080600080600060808688031215612ede57600080fd5b612ee786612db2565b9450612ef560208701612db2565b935060408601359250606086013567ffffffffffffffff811115612f1857600080fd5b612f2488828901612e84565b969995985093965092949392505050565b600080600060408486031215612f4a57600080fd5b83359250602084013567ffffffffffffffff811115612f6857600080fd5b612f7486828701612e84565b9497909650939450505050565b60008060008060408587031215612f9757600080fd5b843567ffffffffffffffff80821115612faf57600080fd5b612fbb88838901612e84565b90965094506020870135915080821115612fd457600080fd5b50612fe187828801612e84565b95989497509550505050565b600060208284031215612fff57600080fd5b5035919050565b6000806040838503121561301957600080fd5b50508035926020909101359150565b60008060008060006060868803121561304057600080fd5b853567ffffffffffffffff8082111561305857600080fd5b61306489838a01612df6565b909750955060208801359450604088013591508082111561308457600080fd5b50612f2488828901612e84565b60005b838110156130ac578181015183820152602001613094565b50506000910152565b600081518084526130cd816020860160208601613091565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061093f60208301846130b5565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561315357600080fd5b813567ffffffffffffffff8082111561316b57600080fd5b818401915084601f83011261317f57600080fd5b81358181111561319157613191613112565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156131d7576131d7613112565b816040528281528760208487010111156131f057600080fd5b826020860160208301376000928101602001929092525095945050505050565b6000806040838503121561322357600080fd5b823561322e81612d67565b915061323c60208401612db2565b90509250929050565b60008060008060008060008060a0898b03121561326157600080fd5b61326a89612db2565b975061327860208a01612db2565b9650604089013567ffffffffffffffff8082111561329557600080fd5b6132a18c838d01612df6565b909850965060608b01359150808211156132ba57600080fd5b6132c68c838d01612df6565b909650945060808b01359150808211156132df57600080fd5b506132ec8b828c01612e84565b999c989b5096995094979396929594505050565b60008060008060008060a0878903121561331957600080fd5b61332287612db2565b955061333060208801612db2565b94506040870135935060608701359250608087013567ffffffffffffffff81111561335a57600080fd5b61336689828a01612e84565b979a9699509497509295939492505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b80358015158114612dd657600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b81835260006020808501808196508560051b810191508460005b8781101561354257828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4188360301811261345957600080fd5b870160c0613466826133a7565b151586526134758783016133a7565b15158688015260408281013590870152606073ffffffffffffffffffffffffffffffffffffffff6134a7828501612db2565b16908701526080828101359087015260a080830135368490037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe10181126134ed57600080fd5b90920187810192903567ffffffffffffffff81111561350b57600080fd5b80360384131561351a57600080fd5b828289015261352c83890182866133b7565b9c89019c9750505092860192505060010161341a565b5091979650505050505050565b60408152600560408201527f73656c663a00000000000000000000000000000000000000000000000000000060608201526080602082015260006109ae608083018486613400565b8381526040602082015260006135b1604083018486613400565b95945050505050565b8381526040602082015260006135b16040830184866133b7565b7f697066733a2f2f0000000000000000000000000000000000000000000000000081526000825161360c816007850160208701613091565b9190910160070192915050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4183360301811261364d57600080fd5b9190910192915050565b60006020828403121561366957600080fd5b61093f826133a7565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126136a757600080fd5b83018035915067ffffffffffffffff8211156136c257600080fd5b602001915036819003821315612e3b57600080fd5b600080858511156136e757600080fd5b838611156136f457600080fd5b5050820193919092039150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082018082111561078b5761078b613701565b6060815260006137576060830186886133b7565b6020830194909452506040015292915050565b7f62000000000000000000000000000000000000000000000000000000000000008152600082516137a2816001850160208701613091565b9190910160010192915050565b8281526040602082015260006109ae60408301846130b5565b84815273ffffffffffffffffffffffffffffffffffffffff841660208201526060604082015260006137fe6060830184866133b7565b9695505050505050565b6020815260006109ae6020830184866133b7565b8181038181111561078b5761078b613701565b6040815260006138436040830185876133b7565b9050826020830152949350505050565b6040815260006138676040830185876133b7565b905060ff83166020830152949350505050565b60608152600061388e6060830186886133b7565b60208301949094525090151560409091015292915050565b6000602082840312156138b857600080fd5b815161093f81612d6756fea264697066735822122030f6a03eecf061513999472455e58728f2693e3a3541e4333a309b089861d90064736f6c63430008110033", + "deployedBytecode": "0x6080604052600436106101c65760003560e01c806379e472c9116100f7578063a4ab5f9f11610095578063c71f1f9611610064578063c71f1f96146106a2578063d0748f71146106b7578063d59f7885146106d7578063f23a6e61146106f7576101cd565b8063a4ab5f9f14610605578063affed0e014610625578063b93ea7ad1461063a578063bc197c811461065a576101cd565b80638c3f5563116100d15780638c3f5563146105905780638efa6441146105b057806390042baf146105d2578063a38cef19146105e5576101cd565b806379e472c9146105085780637a9a162814610528578063853c506814610548576101cd565b806329561426116101645780634fcf3eca1161013e5780634fcf3eca1461047f57806351605d801461049f57806357c56d6b146104b457806361c2926c146104e8576101cd565b8063295614261461041157806341ea0302146104315780634598154f1461045f576101cd565b8063150b7a02116101a0578063150b7a02146103165780631626ba7e1461038c5780631a9b2337146103ac57806320c13b0b146103f1576101cd565b806301ffc9a7146102a1578063025b22bc146102d6578063038dbaac146102f6576101cd565b366101cd57005b60006101fc6000357fffffffff000000000000000000000000000000000000000000000000000000001661073d565b905073ffffffffffffffffffffffffffffffffffffffff81161561029f576000808273ffffffffffffffffffffffffffffffffffffffff16600036604051610245929190612d57565b600060405180830381855af49150503d8060008114610280576040519150601f19603f3d011682016040523d82523d6000602084013e610285565b606091505b50915091508161029757805160208201fd5b805160208201f35b005b3480156102ad57600080fd5b506102c16102bc366004612d95565b610791565b60405190151581526020015b60405180910390f35b3480156102e257600080fd5b5061029f6102f1366004612ddb565b61079c565b34801561030257600080fd5b5061029f610311366004612e42565b6107ee565b34801561032257600080fd5b5061035b610331366004612ec6565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020016102cd565b34801561039857600080fd5b5061035b6103a7366004612f35565b6108f9565b3480156103b857600080fd5b506103cc6103c7366004612d95565b610946565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016102cd565b3480156103fd57600080fd5b5061035b61040c366004612f81565b610951565b34801561041d57600080fd5b5061029f61042c366004612fed565b6109b6565b34801561043d57600080fd5b5061045161044c366004612fed565b610a00565b6040519081526020016102cd565b34801561046b57600080fd5b5061029f61047a366004613006565b610a0b565b34801561048b57600080fd5b5061029f61049a366004612d95565b610ad1565b3480156104ab57600080fd5b50610451610c00565b3480156104c057600080fd5b506104517f8713a7c4465f6fbee2b6e9d6646d1d9f83fec929edfc4baf661f3c865bdd04d181565b3480156104f457600080fd5b5061029f610503366004612e42565b610c2f565b34801561051457600080fd5b5061029f610523366004613006565b610cb5565b34801561053457600080fd5b5061029f610543366004613028565b610d73565b34801561055457600080fd5b50610568610563366004612f35565b610e09565b604080519586526020860194909452928401919091526060830152608082015260a0016102cd565b34801561059c57600080fd5b506104516105ab366004612fed565b610fd1565b3480156105bc57600080fd5b506105c5610ffd565b6040516102cd91906130ff565b6103cc6105e0366004613141565b61107e565b3480156105f157600080fd5b5061029f610600366004612fed565b61111a565b34801561061157600080fd5b50610451610620366004612fed565b611164565b34801561063157600080fd5b5061045161116f565b34801561064657600080fd5b5061029f610655366004613210565b61117b565b34801561066657600080fd5b5061035b610675366004613245565b7fbc197c810000000000000000000000000000000000000000000000000000000098975050505050505050565b3480156106ae57600080fd5b506104516112c4565b3480156106c357600080fd5b5061029f6106d2366004613006565b6112ee565b3480156106e357600080fd5b5061029f6106f2366004612e42565b611341565b34801561070357600080fd5b5061035b610712366004613300565b7ff23a6e61000000000000000000000000000000000000000000000000000000009695505050505050565b600061078b7fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1207fffffffff000000000000000000000000000000000000000000000000000000008416611484565b92915050565b600061078b826114e2565b3330146107e2576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044015b60405180910390fd5b6107eb8161153e565b50565b33301461082f576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b8060005b818110156108f357600084848381811061084f5761084f613378565b9050602002013590506108af816000604080517f849e7bdc245db17e50b9f43086f1914d70eb4dab6dd89af4d541d53353ad97de602080830191909152818301859052825180830384018152606090920190925280519101208190555050565b807f804f6171d6008d9e16ee3aa0561fec328397f4ba2827a6605db388cfdefa3b0c60006040516108e291815260200190565b60405180910390a250600101610833565b50505050565b6000806109078585856115f9565b509050801561093957507f1626ba7e00000000000000000000000000000000000000000000000000000000905061093f565b50600090505b9392505050565b600061078b8261073d565b6000806109768686604051610967929190612d57565b604051809103902085856115f9565b50905080156109a857507f20c13b0b0000000000000000000000000000000000000000000000000000000090506109ae565b50600090505b949350505050565b3330146109f7576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b6107eb81611614565b600061078b826116a4565b333014610a4c576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b604080517f849e7bdc245db17e50b9f43086f1914d70eb4dab6dd89af4d541d53353ad97de602080830191909152818301859052825180830384018152606083019384905280519101208390559082905282907f804f6171d6008d9e16ee3aa0561fec328397f4ba2827a6605db388cfdefa3b0c906080015b60405180910390a25050565b333014610b12576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b6000610b1d8261073d565b73ffffffffffffffffffffffffffffffffffffffff1603610b8e576040517f1c3812cc0000000000000000000000000000000000000000000000000000000081527fffffffff00000000000000000000000000000000000000000000000000000000821660048201526024016107d9565b604080517fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1206020808301919091527fffffffff00000000000000000000000000000000000000000000000000000000841682840152825180830384018152606090920190925280519101206000905550565b6000610c2a7fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf85490565b905090565b333014610c70576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b6000610ca38383604051602001610c8892919061354f565b604051602081830303815290604052805190602001206116d0565b9050610cb0818484611755565b505050565b333014610cf6576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b604080517f7f25a23abc421d10864063e9a8ae5fd3fbd5116e156f148428b6a3a02ffd9454602080830191909152818301859052825180830384018152606083019384905280519101208390559082905282907f180e56184e3025975e8449fab79ff135cc5c3b3fe517a19bf8f111d69b33d2e290608001610ac5565b610d7c836118b3565b600080610db4858888604051602001610d9793929190613597565b6040516020818303038152906040528051906020012085856115f9565b9150915081610df5578084846040517f8f4a234f0000000000000000000000000000000000000000000000000000000081526004016107d9939291906135ba565b610e00818888611755565b50505050505050565b60008060008060008087876000818110610e2557610e25613378565b909101357fff00000000000000000000000000000000000000000000000000000000000000169150819050610e7b57610e5d896116d0565b9250610e6a8389896119b0565b92985090965094509150610fc69050565b7fff0000000000000000000000000000000000000000000000000000000000000081811601610eba57610ead896116d0565b9250610e6a838989611a01565b7ffe000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821601610f0c57610ead89611a2d565b7ffd000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821601610f7057610f60898989611a9a565b9550955095509550955050610fc6565b6040517f6085cd820000000000000000000000000000000000000000000000000000000081527fff00000000000000000000000000000000000000000000000000000000000000821660048201526024016107d9565b939792965093509350565b600061078b7f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e83611484565b606061105a61105561100d6112c4565b6040517f017012200000000000000000000000000000000000000000000000000000000060208201526024810191909152604401604051602081830303815290604052611c17565b611e30565b60405160200161106a91906135d4565b604051602081830303815290604052905090565b60003330146110c1576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b81516020830134f060405173ffffffffffffffffffffffffffffffffffffffff821681529091507fa506ad4e7f05eceba62a023c3219e5bd98a615f4fa87e2afb08a2da5cf62bf0c9060200160405180910390a1919050565b33301461115b576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b6107eb81611e59565b600061078b82611eb2565b6000610c2a6000610fd1565b3330146111bc576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b60006111c78361073d565b73ffffffffffffffffffffffffffffffffffffffff1614611238576040517f5b4d6d6a0000000000000000000000000000000000000000000000000000000081527fffffffff00000000000000000000000000000000000000000000000000000000831660048201526024016107d9565b604080517fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1206020808301919091527fffffffff000000000000000000000000000000000000000000000000000000008516828401528251808303840181526060909201909252805191012073ffffffffffffffffffffffffffffffffffffffff821690555050565b5050565b6000610c2a7f0eecac93ced8722d209199364cda3bc33da3bc3a23daef6be49ebd780511d0335490565b33301461132f576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b61133882611614565b6112c081611e59565b333014611382576040517fe12588940000000000000000000000000000000000000000000000000000000081523360048201523060248201526044016107d9565b8060005b818110156108f35760008484838181106113a2576113a2613378565b905060200201359050611421817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff604080517f7f25a23abc421d10864063e9a8ae5fd3fbd5116e156f148428b6a3a02ffd9454602080830191909152818301859052825180830384018152606090920190925280519101208190555050565b807f180e56184e3025975e8449fab79ff135cc5c3b3fe517a19bf8f111d69b33d2e27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60405161147391815260200190565b60405180910390a250600101611386565b60008083836040516020016114a3929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012054949350505050565b60007f6ffbd451000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083160161153557506001919050565b61078b82611ede565b73ffffffffffffffffffffffffffffffffffffffff81163b6115a4576040517f0c76093700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016107d9565b6115ac813055565b60405173ffffffffffffffffffffffffffffffffffffffff821681527f310ba5f1d2ed074b51e2eccd052a47ae9ab7c6b800d1fca3db3999d6a592ca03906020015b60405180910390a150565b60008061160785858561201f565b915091505b935093915050565b8061164b576040517f4294d12700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6116747fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8829055565b6040518181527f307ed6bd941ee9fc80f369c94af5fa11e25bab5102a6140191756c5474a30bfa906020016115ee565b600061078b7f7f25a23abc421d10864063e9a8ae5fd3fbd5116e156f148428b6a3a02ffd945483611484565b6040517f190100000000000000000000000000000000000000000000000000000000000060208201524660228201527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b166042820152605681018290526000906076015b604051602081830303815290604052805190602001209050919050565b8060005b818110156118ac573684848381811061177457611774613378565b90506020028101906117869190613619565b90506040810135805a10156117db5782815a6040517f2bb3e3ba0000000000000000000000000000000000000000000000000000000081526004810193909352602483019190915260448201526064016107d9565b60006117ea6020840184613657565b15611829576118226118026080850160608601612ddb565b831561180e5783611810565b5a5b61181d60a0870187613672565b612053565b9050611864565b61186161183c6080850160608601612ddb565b6080850135841561184d578461184f565b5a5b61185c60a0880188613672565b61206e565b90505b80156118805760405188815260200160405180910390a06118a1565b6118a16118936040850160208601613657565b8961189c61208b565b6120aa565b505050600101611759565b5050505050565b606081901c6bffffffffffffffffffffffff821660006118d283610fd1565b905081811461191e576040517f9b6514f40000000000000000000000000000000000000000000000000000000081526004810184905260248101839052604481018290526064016107d9565b604080517f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e60208083019190915281830186905282518083038401815260609092019092528051910120600183019081905560408051858152602081018390527f1f180c27086c7a39ea2a7b25239d1ab92348f07ca7bb59d1438fcf527568f881910160405180910390a15050505050565b60008080806119cb876119c6876006818b6136d7565b6120f6565b6000908152873560f01c6020818152604080842084526002909a013560e01c908190529890912090999198509695509350505050565b6000808080611a1c87611a17876001818b6136d7565b6119b0565b935093509350935093509350935093565b6040517f190100000000000000000000000000000000000000000000000000000000000060208201526000602282018190527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b1660428301526056820183905290607601611738565b6000808080806004600188013560e81c82611ab58383613730565b9050611ac78b61056383868d8f6136d7565b939b5091995097509550935087871015611b1f57611ae781848b8d6136d7565b89896040517fb006aba00000000000000000000000000000000000000000000000000000000081526004016107d99493929190613743565b8092505b88831015611c095760038301928a013560e81c9150611b428383613730565b90506000611b64611b528861258c565b8c8c87908692610563939291906136d7565b939c50919a5098509091505088881015611bbc57611b8482858c8e6136d7565b8a8a6040517fb006aba00000000000000000000000000000000000000000000000000000000081526004016107d99493929190613743565b848110611bff576040517f37daf62b00000000000000000000000000000000000000000000000000000000815260048101829052602481018690526044016107d9565b9350915081611b23565b505050939792965093509350565b8051606090600381901b60006005600483010467ffffffffffffffff811115611c4257611c42613112565b6040519080825280601f01601f191660200182016040528015611c6c576020820181803683370190505b5090506000806000805b86811015611d8057888181518110611c9057611c90613378565b01602001516008948501949390931b60f89390931c92909217915b60058410611d78576040805180820190915260208082527f6162636465666768696a6b6c6d6e6f707172737475767778797a323334353637818301527ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb90950194601f85871c16908110611d2157611d21613378565b602001015160f81c60f81b858381518110611d3e57611d3e613378565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600190910190611cab565b600101611c76565b508215611e24576040518060400160405280602081526020017f6162636465666768696a6b6c6d6e6f707172737475767778797a3233343536378152508360050383901b601f1681518110611dd757611dd7613378565b602001015160f81c60f81b848281518110611df457611df4613378565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053505b50919695505050505050565b606081604051602001611e43919061376a565b6040516020818303038152906040529050919050565b611e827f0eecac93ced8722d209199364cda3bc33da3bc3a23daef6be49ebd780511d033829055565b6040518181527f20d3ef1b5738a9f6d7beae515432206e7a8e2740ca6dcf46a952190ad01bcb51906020016115ee565b600061078b7f849e7bdc245db17e50b9f43086f1914d70eb4dab6dd89af4d541d53353ad97de83611484565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fec6aba50000000000000000000000000000000000000000000000000000000001480611f7157507fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e000000000000000000000000000000000000000000000000000000000145b80611fbd57507fffffffff0000000000000000000000000000000000000000000000000000000082167f150b7a0200000000000000000000000000000000000000000000000000000000145b8061200957507fffffffff0000000000000000000000000000000000000000000000000000000082167fc0ee0b8a00000000000000000000000000000000000000000000000000000000145b1561201657506001919050565b61078b826125c0565b6000804261202c866116a4565b1191508115612048578161203f8661261c565b9150915061160c565b611607858585612657565b60006040518284823760008084838989f49695505050505050565b6000604051828482376000808483898b8af1979650505050505050565b60603d604051915060208201818101604052818352816000823e505090565b82156120b857805160208201fd5b7f3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd782826040516120e99291906137af565b60405180910390a1505050565b60008060005b8381101561258357600181019085013560f81c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810161219d57601582019186013560f881901c9060581c73ffffffffffffffffffffffffffffffffffffffff81169074ff0000000000000000000000000000000000000000168117856121835780612192565b60008681526020829052604090205b9550505050506120fc565b806122335760018201918681013560f81c9060430160006121c98a6121c484888c8e6136d7565b612695565b60ff841697909701969194508491905060a083901b74ff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff821617866122185780612227565b60008781526020829052604090205b965050505050506120fc565b6002810361235b576000808784013560f881901c9060581c73ffffffffffffffffffffffffffffffffffffffff16601586019550909250905060008885013560e81c600386018162ffffff1691508096508192505050600081860190506122ac8b848c8c8a9086926122a7939291906136d7565b612958565b6122f4578a836122be83898d8f6136d7565b6040517f9a9462320000000000000000000000000000000000000000000000000000000081526004016107d994939291906137c8565b60ff8416979097019694508460a084901b74ff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8416178761233f578061234e565b60008881526020829052604090205b97505050505050506120fc565b6003810361238e576020820191860135836123765780612385565b60008481526020829052604090205b935050506120fc565b600481036123da576003808301928781013560e81c91908201016000806123bb8b6119c685898d8f6136d7565b600098895260205260409097209690970196509093506120fc92505050565b600681036124e25760008287013560f81c60018401935060ff16905060008784013560f01c60028501945061ffff16905060008885013560e81c600386018162ffffff1691508096508192505050600081860190506000806124488d8d8d8b9087926119c6939291906136d7565b9398508893909250905084821061245e57988501985b604080517f53657175656e6365206e657374656420636f6e6669673a0a0000000000000000602080830191909152603882018490526058820188905260788083018a90528351808403909101815260989092019092528051910120896124c457806124d3565b60008a81526020829052604090205b995050505050505050506120fc565b6005810361254e57602082019186013587810361251d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff94505b600061252882612b05565b9050846125355780612544565b60008581526020829052604090205b94505050506120fc565b6040517fb2505f7c000000000000000000000000000000000000000000000000000000008152600481018290526024016107d9565b50935093915050565b7f8713a7c4465f6fbee2b6e9d6646d1d9f83fec929edfc4baf661f3c865bdd04d1600090815260208290526040812061078b565b60007ffda4dd44000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083160161261357506001919050565b61078b82612b40565b604080517f7f25a23abc421d10864063e9a8ae5fd3fbd5116e156f148428b6a3a02ffd94546020820152908101829052600090606001611738565b600080600080600061266a888888610e09565b50965091945092509050828210801590612688575061268881612b9c565b9450505050935093915050565b6000604282146126d55782826040517f2ee17a3d0000000000000000000000000000000000000000000000000000000081526004016107d9929190613808565b60006126ee6126e560018561381c565b85013560f81c90565b60ff169050604084013560f81c843560208601357f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0811115612762578686826040517fad4aac760000000000000000000000000000000000000000000000000000000081526004016107d99392919061382f565b8260ff16601b1415801561277a57508260ff16601c14155b156127b7578686846040517fe578897e0000000000000000000000000000000000000000000000000000000081526004016107d993929190613853565b60018403612824576040805160008152602081018083528a905260ff851691810191909152606081018390526080810182905260019060a0015b6020604051602081039080840390855afa158015612813573d6000803e3d6000fd5b5050506020604051035194506128fc565b600284036128c1576040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101899052600190605c01604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600084529083018083525260ff861690820152606081018490526080810183905260a0016127f1565b86868560016040517f9dfba8520000000000000000000000000000000000000000000000000000000081526004016107d9949392919061387a565b73ffffffffffffffffffffffffffffffffffffffff851661294d5786866040517f6c1719d20000000000000000000000000000000000000000000000000000000081526004016107d9929190613808565b505050509392505050565b600080838361296860018261381c565b81811061297757612977613378565b919091013560f81c91505060018114806129915750600281145b156129d6578473ffffffffffffffffffffffffffffffffffffffff166129b8878686612695565b73ffffffffffffffffffffffffffffffffffffffff16149150612afc565b60038103612ac15773ffffffffffffffffffffffffffffffffffffffff8516631626ba7e8786600087612a0a60018261381c565b92612a17939291906136d7565b6040518463ffffffff1660e01b8152600401612a35939291906135ba565b602060405180830381865afa158015612a52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a7691906138a6565b7fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e00000000000000000000000000000000000000000000000000000000149150612afc565b83838260006040517f9dfba8520000000000000000000000000000000000000000000000000000000081526004016107d9949392919061387a565b50949350505050565b6040517f53657175656e636520737461746963206469676573743a0a0000000000000000602082015260388101829052600090605801611738565b60007fe4a77bbc000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831601612b9357506001919050565b61078b82612ba7565b600061078b82612c03565b60007fae9fa280000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831601612bfa57506001919050565b61078b82612c3a565b6000612c0e82612d24565b15612c1b57506001919050565b6000612c2683611eb2565b9050801580159061093f5750421092915050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fac6a444e000000000000000000000000000000000000000000000000000000001480612ccd57507fffffffff0000000000000000000000000000000000000000000000000000000082167f36e7817500000000000000000000000000000000000000000000000000000000145b15612cda57506001919050565b7f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083161461078b565b6000811580159061078b5750507fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8541490565b8183823760009101908152919050565b7fffffffff00000000000000000000000000000000000000000000000000000000811681146107eb57600080fd5b600060208284031215612da757600080fd5b813561093f81612d67565b803573ffffffffffffffffffffffffffffffffffffffff81168114612dd657600080fd5b919050565b600060208284031215612ded57600080fd5b61093f82612db2565b60008083601f840112612e0857600080fd5b50813567ffffffffffffffff811115612e2057600080fd5b6020830191508360208260051b8501011115612e3b57600080fd5b9250929050565b60008060208385031215612e5557600080fd5b823567ffffffffffffffff811115612e6c57600080fd5b612e7885828601612df6565b90969095509350505050565b60008083601f840112612e9657600080fd5b50813567ffffffffffffffff811115612eae57600080fd5b602083019150836020828501011115612e3b57600080fd5b600080600080600060808688031215612ede57600080fd5b612ee786612db2565b9450612ef560208701612db2565b935060408601359250606086013567ffffffffffffffff811115612f1857600080fd5b612f2488828901612e84565b969995985093965092949392505050565b600080600060408486031215612f4a57600080fd5b83359250602084013567ffffffffffffffff811115612f6857600080fd5b612f7486828701612e84565b9497909650939450505050565b60008060008060408587031215612f9757600080fd5b843567ffffffffffffffff80821115612faf57600080fd5b612fbb88838901612e84565b90965094506020870135915080821115612fd457600080fd5b50612fe187828801612e84565b95989497509550505050565b600060208284031215612fff57600080fd5b5035919050565b6000806040838503121561301957600080fd5b50508035926020909101359150565b60008060008060006060868803121561304057600080fd5b853567ffffffffffffffff8082111561305857600080fd5b61306489838a01612df6565b909750955060208801359450604088013591508082111561308457600080fd5b50612f2488828901612e84565b60005b838110156130ac578181015183820152602001613094565b50506000910152565b600081518084526130cd816020860160208601613091565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061093f60208301846130b5565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561315357600080fd5b813567ffffffffffffffff8082111561316b57600080fd5b818401915084601f83011261317f57600080fd5b81358181111561319157613191613112565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156131d7576131d7613112565b816040528281528760208487010111156131f057600080fd5b826020860160208301376000928101602001929092525095945050505050565b6000806040838503121561322357600080fd5b823561322e81612d67565b915061323c60208401612db2565b90509250929050565b60008060008060008060008060a0898b03121561326157600080fd5b61326a89612db2565b975061327860208a01612db2565b9650604089013567ffffffffffffffff8082111561329557600080fd5b6132a18c838d01612df6565b909850965060608b01359150808211156132ba57600080fd5b6132c68c838d01612df6565b909650945060808b01359150808211156132df57600080fd5b506132ec8b828c01612e84565b999c989b5096995094979396929594505050565b60008060008060008060a0878903121561331957600080fd5b61332287612db2565b955061333060208801612db2565b94506040870135935060608701359250608087013567ffffffffffffffff81111561335a57600080fd5b61336689828a01612e84565b979a9699509497509295939492505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b80358015158114612dd657600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b81835260006020808501808196508560051b810191508460005b8781101561354257828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4188360301811261345957600080fd5b870160c0613466826133a7565b151586526134758783016133a7565b15158688015260408281013590870152606073ffffffffffffffffffffffffffffffffffffffff6134a7828501612db2565b16908701526080828101359087015260a080830135368490037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe10181126134ed57600080fd5b90920187810192903567ffffffffffffffff81111561350b57600080fd5b80360384131561351a57600080fd5b828289015261352c83890182866133b7565b9c89019c9750505092860192505060010161341a565b5091979650505050505050565b60408152600560408201527f73656c663a00000000000000000000000000000000000000000000000000000060608201526080602082015260006109ae608083018486613400565b8381526040602082015260006135b1604083018486613400565b95945050505050565b8381526040602082015260006135b16040830184866133b7565b7f697066733a2f2f0000000000000000000000000000000000000000000000000081526000825161360c816007850160208701613091565b9190910160070192915050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4183360301811261364d57600080fd5b9190910192915050565b60006020828403121561366957600080fd5b61093f826133a7565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126136a757600080fd5b83018035915067ffffffffffffffff8211156136c257600080fd5b602001915036819003821315612e3b57600080fd5b600080858511156136e757600080fd5b838611156136f457600080fd5b5050820193919092039150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082018082111561078b5761078b613701565b6060815260006137576060830186886133b7565b6020830194909452506040015292915050565b7f62000000000000000000000000000000000000000000000000000000000000008152600082516137a2816001850160208701613091565b9190910160010192915050565b8281526040602082015260006109ae60408301846130b5565b84815273ffffffffffffffffffffffffffffffffffffffff841660208201526060604082015260006137fe6060830184866133b7565b9695505050505050565b6020815260006109ae6020830184866133b7565b8181038181111561078b5761078b613701565b6040815260006138436040830185876133b7565b9050826020830152949350505050565b6040815260006138676040830185876133b7565b905060ff83166020830152949350505050565b60608152600061388e6060830186886133b7565b60208301949094525090151560409091015292915050565b6000602082840312156138b857600080fd5b815161093f81612d6756fea264697066735822122030f6a03eecf061513999472455e58728f2693e3a3541e4333a309b089861d90064736f6c63430008110033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/packages/tests/src/builds/v2/artifacts/UniversalSigValidator.ts b/packages/tests/src/builds/v2/artifacts/UniversalSigValidator.ts new file mode 100644 index 000000000..9499da195 --- /dev/null +++ b/packages/tests/src/builds/v2/artifacts/UniversalSigValidator.ts @@ -0,0 +1,188 @@ +export const universalSigValidator = { + "_format": "hh-sol-artifact-1", + "contractName": "UniversalSigValidator", + "sourceName": "contracts/EIP6492.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "bytes", + "name": "error", + "type": "bytes" + } + ], + "name": "ERC1271Revert", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "error", + "type": "bytes" + } + ], + "name": "ERC6492DeployFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_signer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "isValidSig", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_signer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "allowSideEffects", + "type": "bool" + }, + { + "internalType": "bool", + "name": "deployAlreadyDeployed", + "type": "bool" + } + ], + "name": "isValidSigImpl", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_signer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "isValidSigNoThrow", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_signer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "isValidSigWithSideEffects", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_signer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_hash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_signature", + "type": "bytes" + } + ], + "name": "isValidSigWithSideEffectsNoThrow", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b50610fbc806100206000396000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c806376be4cea1161005057806376be4cea146100a65780638f068430146100b957806398ef1ed8146100cc57600080fd5b80631c6453271461006c5780633d787b6314610093575b600080fd5b61007f61007a366004610ad4565b6100df565b604051901515815260200160405180910390f35b61007f6100a1366004610ad4565b61023d565b61007f6100b4366004610b3e565b61031e565b61007f6100c7366004610ad4565b6108e1565b61007f6100da366004610ad4565b61096e565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea9061012890889088908890889088908190600401610bc3565b6020604051808303816000875af1925050508015610181575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261017e91810190610c45565b60015b610232573d8080156101af576040519150601f19603f3d011682016040523d82523d6000602084013e6101b4565b606091505b508051600181900361022757816000815181106101d3576101d3610c69565b6020910101517fff00000000000000000000000000000000000000000000000000000000000000167f0100000000000000000000000000000000000000000000000000000000000000149250610235915050565b600092505050610235565b90505b949350505050565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea906102879088908890889088906001908990600401610bc3565b6020604051808303816000875af19250505080156102e0575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526102dd91810190610c45565b60015b610232573d80801561030e576040519150601f19603f3d011682016040523d82523d6000602084013e610313565b606091505b506000915050610235565b600073ffffffffffffffffffffffffffffffffffffffff87163b6060827f64926492649264926492649264926492649264926492649264926492649264928888610369602082610c98565b610375928b9290610cd8565b61037e91610d02565b1490508015610484576000606089828a610399602082610c98565b926103a693929190610cd8565b8101906103b39190610e18565b955090925090508415806103c45750865b1561047d576000808373ffffffffffffffffffffffffffffffffffffffff16836040516103f19190610eb2565b6000604051808303816000865af19150503d806000811461042e576040519150601f19603f3d011682016040523d82523d6000602084013e610433565b606091505b50915091508161047a57806040517f9d0d6e2d0000000000000000000000000000000000000000000000000000000081526004016104719190610f18565b60405180910390fd5b50505b50506104be565b87878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509294505050505b80806104ca5750600083115b156106bb576040517f1626ba7e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8b1690631626ba7e90610523908c908690600401610f2b565b602060405180830381865afa92505050801561057a575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261057791810190610f44565b60015b61060f573d8080156105a8576040519150601f19603f3d011682016040523d82523d6000602084013e6105ad565b606091505b50851580156105bc5750600084115b156105db576105d08b8b8b8b8b600161031e565b9450505050506108d7565b806040517f6f2a95990000000000000000000000000000000000000000000000000000000081526004016104719190610f18565b7fffffffff0000000000000000000000000000000000000000000000000000000081167f1626ba7e000000000000000000000000000000000000000000000000000000001480158161065f575086155b801561066b5750600085115b1561068b5761067f8c8c8c8c8c600161031e565b955050505050506108d7565b841580156106965750825b80156106a0575087155b156106af57806000526001601ffd5b94506108d79350505050565b6041871461074b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f5369676e617475726556616c696461746f72237265636f7665725369676e657260448201527f3a20696e76616c6964207369676e6174757265206c656e6774680000000000006064820152608401610471565b600061075a6020828a8c610cd8565b61076391610d02565b90506000610775604060208b8d610cd8565b61077e91610d02565b905060008a8a604081811061079557610795610c69565b919091013560f81c915050601b81148015906107b557508060ff16601c14155b15610842576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f5369676e617475726556616c696461746f723a20696e76616c6964207369676e60448201527f617475726520762076616c7565000000000000000000000000000000000000006064820152608401610471565b6040805160008152602081018083528e905260ff831691810191909152606081018490526080810183905273ffffffffffffffffffffffffffffffffffffffff8e169060019060a0016020604051602081039080840390855afa1580156108ad573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff161496505050505050505b9695505050505050565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea9061092b9088908890889088906001908990600401610bc3565b6020604051808303816000875af115801561094a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102329190610c45565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea906109b790889088908890889088908190600401610bc3565b6020604051808303816000875af1925050508015610a10575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252610a0d91810190610c45565b60015b610232573d808015610a3e576040519150601f19603f3d011682016040523d82523d6000602084013e610a43565b606091505b5080516001819003610a6257816000815181106101d3576101d3610c69565b8082fd5b73ffffffffffffffffffffffffffffffffffffffff81168114610a8857600080fd5b50565b60008083601f840112610a9d57600080fd5b50813567ffffffffffffffff811115610ab557600080fd5b602083019150836020828501011115610acd57600080fd5b9250929050565b60008060008060608587031215610aea57600080fd5b8435610af581610a66565b935060208501359250604085013567ffffffffffffffff811115610b1857600080fd5b610b2487828801610a8b565b95989497509550505050565b8015158114610a8857600080fd5b60008060008060008060a08789031215610b5757600080fd5b8635610b6281610a66565b955060208701359450604087013567ffffffffffffffff811115610b8557600080fd5b610b9189828a01610a8b565b9095509350506060870135610ba581610b30565b91506080870135610bb581610b30565b809150509295509295509295565b73ffffffffffffffffffffffffffffffffffffffff8716815285602082015260a060408201528360a0820152838560c0830137600060c085830181019190915292151560608201529015156080820152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016909101019392505050565b600060208284031215610c5757600080fd5b8151610c6281610b30565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b81810381811115610cd2577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b92915050565b60008085851115610ce857600080fd5b83861115610cf557600080fd5b5050820193919092039150565b80356020831015610cd2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602084900360031b1b1692915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f830112610d7e57600080fd5b813567ffffffffffffffff80821115610d9957610d99610d3e565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715610ddf57610ddf610d3e565b81604052838152866020858801011115610df857600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080600060608486031215610e2d57600080fd5b8335610e3881610a66565b9250602084013567ffffffffffffffff80821115610e5557600080fd5b610e6187838801610d6d565b93506040860135915080821115610e7757600080fd5b50610e8486828701610d6d565b9150509250925092565b60005b83811015610ea9578181015183820152602001610e91565b50506000910152565b60008251610ec4818460208701610e8e565b9190910192915050565b60008151808452610ee6816020860160208601610e8e565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610c626020830184610ece565b8281526040602082015260006102356040830184610ece565b600060208284031215610f5657600080fd5b81517fffffffff0000000000000000000000000000000000000000000000000000000081168114610c6257600080fdfea26469706673582212201a72aed4b15ffb05b6502997a9bb655992e06590bd26b336dfbb153d7ff6f34b64736f6c63430008120033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100675760003560e01c806376be4cea1161005057806376be4cea146100a65780638f068430146100b957806398ef1ed8146100cc57600080fd5b80631c6453271461006c5780633d787b6314610093575b600080fd5b61007f61007a366004610ad4565b6100df565b604051901515815260200160405180910390f35b61007f6100a1366004610ad4565b61023d565b61007f6100b4366004610b3e565b61031e565b61007f6100c7366004610ad4565b6108e1565b61007f6100da366004610ad4565b61096e565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea9061012890889088908890889088908190600401610bc3565b6020604051808303816000875af1925050508015610181575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261017e91810190610c45565b60015b610232573d8080156101af576040519150601f19603f3d011682016040523d82523d6000602084013e6101b4565b606091505b508051600181900361022757816000815181106101d3576101d3610c69565b6020910101517fff00000000000000000000000000000000000000000000000000000000000000167f0100000000000000000000000000000000000000000000000000000000000000149250610235915050565b600092505050610235565b90505b949350505050565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea906102879088908890889088906001908990600401610bc3565b6020604051808303816000875af19250505080156102e0575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526102dd91810190610c45565b60015b610232573d80801561030e576040519150601f19603f3d011682016040523d82523d6000602084013e610313565b606091505b506000915050610235565b600073ffffffffffffffffffffffffffffffffffffffff87163b6060827f64926492649264926492649264926492649264926492649264926492649264928888610369602082610c98565b610375928b9290610cd8565b61037e91610d02565b1490508015610484576000606089828a610399602082610c98565b926103a693929190610cd8565b8101906103b39190610e18565b955090925090508415806103c45750865b1561047d576000808373ffffffffffffffffffffffffffffffffffffffff16836040516103f19190610eb2565b6000604051808303816000865af19150503d806000811461042e576040519150601f19603f3d011682016040523d82523d6000602084013e610433565b606091505b50915091508161047a57806040517f9d0d6e2d0000000000000000000000000000000000000000000000000000000081526004016104719190610f18565b60405180910390fd5b50505b50506104be565b87878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509294505050505b80806104ca5750600083115b156106bb576040517f1626ba7e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8b1690631626ba7e90610523908c908690600401610f2b565b602060405180830381865afa92505050801561057a575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261057791810190610f44565b60015b61060f573d8080156105a8576040519150601f19603f3d011682016040523d82523d6000602084013e6105ad565b606091505b50851580156105bc5750600084115b156105db576105d08b8b8b8b8b600161031e565b9450505050506108d7565b806040517f6f2a95990000000000000000000000000000000000000000000000000000000081526004016104719190610f18565b7fffffffff0000000000000000000000000000000000000000000000000000000081167f1626ba7e000000000000000000000000000000000000000000000000000000001480158161065f575086155b801561066b5750600085115b1561068b5761067f8c8c8c8c8c600161031e565b955050505050506108d7565b841580156106965750825b80156106a0575087155b156106af57806000526001601ffd5b94506108d79350505050565b6041871461074b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f5369676e617475726556616c696461746f72237265636f7665725369676e657260448201527f3a20696e76616c6964207369676e6174757265206c656e6774680000000000006064820152608401610471565b600061075a6020828a8c610cd8565b61076391610d02565b90506000610775604060208b8d610cd8565b61077e91610d02565b905060008a8a604081811061079557610795610c69565b919091013560f81c915050601b81148015906107b557508060ff16601c14155b15610842576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f5369676e617475726556616c696461746f723a20696e76616c6964207369676e60448201527f617475726520762076616c7565000000000000000000000000000000000000006064820152608401610471565b6040805160008152602081018083528e905260ff831691810191909152606081018490526080810183905273ffffffffffffffffffffffffffffffffffffffff8e169060019060a0016020604051602081039080840390855afa1580156108ad573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff161496505050505050505b9695505050505050565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea9061092b9088908890889088906001908990600401610bc3565b6020604051808303816000875af115801561094a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102329190610c45565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea906109b790889088908890889088908190600401610bc3565b6020604051808303816000875af1925050508015610a10575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252610a0d91810190610c45565b60015b610232573d808015610a3e576040519150601f19603f3d011682016040523d82523d6000602084013e610a43565b606091505b5080516001819003610a6257816000815181106101d3576101d3610c69565b8082fd5b73ffffffffffffffffffffffffffffffffffffffff81168114610a8857600080fd5b50565b60008083601f840112610a9d57600080fd5b50813567ffffffffffffffff811115610ab557600080fd5b602083019150836020828501011115610acd57600080fd5b9250929050565b60008060008060608587031215610aea57600080fd5b8435610af581610a66565b935060208501359250604085013567ffffffffffffffff811115610b1857600080fd5b610b2487828801610a8b565b95989497509550505050565b8015158114610a8857600080fd5b60008060008060008060a08789031215610b5757600080fd5b8635610b6281610a66565b955060208701359450604087013567ffffffffffffffff811115610b8557600080fd5b610b9189828a01610a8b565b9095509350506060870135610ba581610b30565b91506080870135610bb581610b30565b809150509295509295509295565b73ffffffffffffffffffffffffffffffffffffffff8716815285602082015260a060408201528360a0820152838560c0830137600060c085830181019190915292151560608201529015156080820152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016909101019392505050565b600060208284031215610c5757600080fd5b8151610c6281610b30565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b81810381811115610cd2577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b92915050565b60008085851115610ce857600080fd5b83861115610cf557600080fd5b5050820193919092039150565b80356020831015610cd2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602084900360031b1b1692915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f830112610d7e57600080fd5b813567ffffffffffffffff80821115610d9957610d99610d3e565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715610ddf57610ddf610d3e565b81604052838152866020858801011115610df857600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080600060608486031215610e2d57600080fd5b8335610e3881610a66565b9250602084013567ffffffffffffffff80821115610e5557600080fd5b610e6187838801610d6d565b93506040860135915080821115610e7757600080fd5b50610e8486828701610d6d565b9150509250925092565b60005b83811015610ea9578181015183820152602001610e91565b50506000910152565b60008251610ec4818460208701610e8e565b9190910192915050565b60008151808452610ee6816020860160208601610e8e565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610c626020830184610ece565b8281526040602082015260006102356040830184610ece565b600060208284031215610f5657600080fd5b81517fffffffff0000000000000000000000000000000000000000000000000000000081168114610c6257600080fdfea26469706673582212201a72aed4b15ffb05b6502997a9bb655992e06590bd26b336dfbb153d7ff6f34b64736f6c63430008120033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/packages/tests/src/builds/v2/index.ts b/packages/tests/src/builds/v2/index.ts new file mode 100644 index 000000000..baf2a4670 --- /dev/null +++ b/packages/tests/src/builds/v2/index.ts @@ -0,0 +1,6 @@ + +export { factory } from './artifacts/Factory' +export { guestModule } from './artifacts/GuestModule' +export { mainModule } from './artifacts/MainModule' +export { mainModuleUpgradable } from './artifacts/MainModuleUpgradable' +export { universalSigValidator } from './artifacts/UniversalSigValidator' diff --git a/packages/tests/src/configs/index.ts b/packages/tests/src/configs/index.ts new file mode 100644 index 000000000..5a5991894 --- /dev/null +++ b/packages/tests/src/configs/index.ts @@ -0,0 +1,2 @@ + +export * as random from './random' diff --git a/packages/tests/src/configs/random.ts b/packages/tests/src/configs/random.ts new file mode 100644 index 000000000..cc8ce959e --- /dev/null +++ b/packages/tests/src/configs/random.ts @@ -0,0 +1,48 @@ +import { v1, v2 } from "@0xsequence/core" +import { ethers } from "ethers" +import { maxForBits, randomBigNumber, randomBool } from "../utils" + +export function genRandomV1Config( + threshold: ethers.BigNumberish = randomBigNumber(0, maxForBits(16)), + numSigners: ethers.BigNumberish = randomBigNumber(1, 24) +): v1.config.WalletConfig { + const signers: v1.config.AddressMember[] = [] + + for (let i = ethers.constants.Zero; i.lt(numSigners); i = i.add(1)) { + signers.push({ + address: ethers.Wallet.createRandom().address, + weight: randomBigNumber(0, maxForBits(8)) + }) + } + + return { version: 1, threshold, signers } +} + +export function genRandomV2Config( + threshold: ethers.BigNumberish = randomBigNumber(0, maxForBits(16)), + checkpoint: ethers.BigNumberish = randomBigNumber(0, maxForBits(32)), + numSigners: ethers.BigNumberish = randomBigNumber(1, 24), + numSubdigests: ethers.BigNumberish = randomBigNumber(0, 24), + useMerkleTopology: boolean = randomBool() +): v2.config.WalletConfig { + const signers: v2.config.SignerLeaf[] = [] + for (let i = ethers.constants.Zero; i.lt(numSigners); i = i.add(1)) { + signers.push({ + address: ethers.Wallet.createRandom().address, + weight: randomBigNumber(0, maxForBits(8)) + }) + } + + const subdigests: v2.config.SubdigestLeaf[] = [] + for (let i = ethers.constants.Zero; i.lt(numSubdigests); i = i.add(1)) { + subdigests.push({ + subdigest: ethers.utils.hexlify(ethers.utils.randomBytes(32)) + }) + } + + const topologyBuilder = useMerkleTopology ? v2.config.merkleTopologyBuilder : v2.config.legacyTopologyBuilder + const tree = topologyBuilder([...signers, ...subdigests]) + + return { version: 2, threshold, checkpoint, tree } +} + diff --git a/packages/tests/src/context/index.ts b/packages/tests/src/context/index.ts new file mode 100644 index 000000000..cb3525475 --- /dev/null +++ b/packages/tests/src/context/index.ts @@ -0,0 +1,13 @@ +import { ethers } from "ethers" + +import { deployV1Context } from "./v1" +import { deployV2Context } from "./v2" + +export async function deploySequenceContexts(signer: ethers.Signer) { + const v1 = await deployV1Context(signer) + const v2 = await deployV2Context(signer) + return { 1: v1, 2: v2 } +} + +export * as v1 from './v1' +export * as v2 from './v2' diff --git a/packages/tests/src/context/v1.ts b/packages/tests/src/context/v1.ts new file mode 100644 index 000000000..7461c99b1 --- /dev/null +++ b/packages/tests/src/context/v1.ts @@ -0,0 +1,101 @@ +import { ethers } from "ethers" +import { v1 } from '../builds' +import { deployContract } from "../singletonFactory" +import { isContract } from "../utils" + +// These are the Sequence v1 contracts +// we use them if they are available +const predefinedAddresses = { + factory: '0xf9D09D634Fb818b05149329C1dcCFAeA53639d96', + mainModule: '0xd01F11855bCcb95f88D7A48492F66410d4637313', + mainModuleUpgradable: '0x7EFE6cE415956c5f80C6530cC6cc81b4808F6118', + guestModule: '0x02390F3E6E5FD1C6786CB78FD3027C117a9955A7', + multiCallUtils: '0xd130B43062D875a4B7aF3f8fc036Bc6e9D3E1B3E' +} + +export async function deployV1Context(signer: ethers.Signer): Promise<{ + version: 1, + factory: string, + mainModule: string, + mainModuleUpgradable: string, + guestModule: string, + multiCallUtils: string, + walletCreationCode: string +}>{ + // See if signer's provider has the contracts already deployed + const provider = signer.provider + if (!provider) { + throw new Error('Signer has no provider') + } + + if (await Promise.all(Object.values(predefinedAddresses).map(address => isContract(provider, address))).then((r) => r.every((x) => x))) { + console.log('Using predefined addresses for V1 contracts') + + return { + version: 1, + + factory: predefinedAddresses.factory, + mainModule: predefinedAddresses.mainModule, + mainModuleUpgradable: predefinedAddresses.mainModuleUpgradable, + guestModule: predefinedAddresses.guestModule, + multiCallUtils: predefinedAddresses.multiCallUtils, + + walletCreationCode: '0x603a600e3d39601a805130553df3363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3' + } + } + + console.log('Predefined addresses for V1 contracts not found, deploying new ones') + + // Try deploying the v1 contracts using the v1 singleton factory + await signer.sendTransaction({ + to: '0x9c5a87452d4FAC0cbd53BDCA580b20A45526B3AB', + value: ethers.utils.parseEther('0.02170000000014'), + gasLimit: 8000000 + }) + + await signer.provider?.sendTransaction('0xf9010880852416b84e01830222e08080b8b66080604052348015600f57600080fd5b50609980601d6000396000f3fe60a06020601f369081018290049091028201604052608081815260009260609284918190838280828437600092018290525084519495509392505060208401905034f5604080516001600160a01b0383168152905191935081900360200190a0505000fea26469706673582212205a310755225e3c740b2f013fb6343f4c205e7141fcdf15947f5f0e0e818727fb64736f6c634300060a00331ca01820182018201820182018201820182018201820182018201820182018201820a01820182018201820182018201820182018201820182018201820182018201820') + + // Deploy universal deployer + await signer.sendTransaction({ + to: '0x1b926fbb24a9f78dcdd3272f2d86f5d0660e59c0', + data: '0x608060405234801561001057600080fd5b5061013d806100206000396000f3fe60806040526004361061001e5760003560e01c80639c4ae2d014610023575b600080fd5b6100cb6004803603604081101561003957600080fd5b81019060208101813564010000000081111561005457600080fd5b82018360208201111561006657600080fd5b8035906020019184600183028401116401000000008311171561008857600080fd5b91908080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525092955050913592506100cd915050565b005b60008183516020850134f56040805173ffffffffffffffffffffffffffffffffffffffff83168152905191925081900360200190a050505056fea264697066735822122033609f614f03931b92d88c309d698449bb77efcd517328d341fa4f923c5d8c7964736f6c63430007060033', + gasLimit: 8000000 + }) + + // Deploy factory + await signer.sendTransaction({ + to: '0x8a5bc19e22d6ad55a2c763b93a75d09f321fe764', + data: '0x9c4ae2d00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001e8608060405234801561001057600080fd5b506101c8806100206000396000f3fe60806040526004361061001e5760003560e01c806332c02a1414610023575b600080fd5b61005c6004803603604081101561003957600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135169060200135610085565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b60008060405180606001604052806028815260200161016b602891398473ffffffffffffffffffffffffffffffffffffffff166040516020018083805190602001908083835b6020831061010857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016100cb565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0180199092169116179052920193845250604080518085038152938201905282519294508693508401905034f594935050505056fe603a600e3d39601a805130553df3363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3a26469706673582212209b0bce93afab3297b9ebf4e58fa642ef123d74bcbd3bdb4e48b662eb12b430ca64736f6c63430007060033000000000000000000000000000000000000000000000000', + gasLimit: 8000000 + }) + + // Deploy mainModule + await signer.sendTransaction({ + to: '0x8a5bc19e22d6ad55a2c763b93a75d09f321fe764', + data: '0x9c4ae2d0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d8360c06040523480156200001157600080fd5b5060405162002d6338038062002d638339810160408190526200003491620000e2565b80600060405180606001604052806028815260200162002d3b60289139306001600160a01b03166040516020018083805190602001908083835b602083106200008f5780518252601f1990920191602091820191016200006e565b51815160209384036101000a60001901801990921691161790529201938452506040805180850381529382019052825192019190912060805250505060601b6001600160601b03191660a0525062000112565b600060208284031215620000f4578081fd5b81516001600160a01b03811681146200010b578182fd5b9392505050565b60805160a05160601c612bf862000143600039806106d55280611baa5250806106b15280611bdb5250612bf86000f3fe6080604052600436106101125760003560e01c80634fcf3eca116100a557806390042baf11610074578063b93ea7ad11610059578063b93ea7ad146103c5578063bc197c81146103e5578063f23a6e611461040557610119565b806390042baf1461039d578063affed0e0146103b057610119565b80634fcf3eca1461031d57806361c2926c1461033d5780637a9a16281461035d5780638c3f55631461037d57610119565b80631a9b2337116100e15780631a9b23371461029957806320c13b0b146102c6578063257671f5146102e65780632dd310001461030857610119565b806301ffc9a7146101f4578063025b22bc1461022a578063150b7a021461024c5780631626ba7e1461027957610119565b3661011957005b60006101486000357fffffffff0000000000000000000000000000000000000000000000000000000016610425565b905073ffffffffffffffffffffffffffffffffffffffff8116156101f1576000808273ffffffffffffffffffffffffffffffffffffffff166000366040518083838082843760405192019450600093509091505080830381855af49150503d80600081146101d2576040519150601f19603f3d011682016040523d82523d6000602084013e6101d7565b606091505b5091509150816101e957805160208201fd5b805160208201f35b50005b34801561020057600080fd5b5061021461020f366004612401565b61047b565b6040516102219190612633565b60405180910390f35b34801561023657600080fd5b5061024a610245366004612166565b610486565b005b34801561025857600080fd5b5061026c610267366004612237565b6105a7565b6040516102219190612660565b34801561028557600080fd5b5061026c6102943660046123b7565b6105d1565b3480156102a557600080fd5b506102b96102b4366004612401565b61064a565b6040516102219190612612565b3480156102d257600080fd5b5061026c6102e136600461244d565b610655565b3480156102f257600080fd5b506102fb6106af565b604051610221919061263e565b34801561031457600080fd5b506102b96106d3565b34801561032957600080fd5b5061024a610338366004612401565b6106f7565b34801561034957600080fd5b5061024a61035836600461231a565b6107d5565b34801561036957600080fd5b5061024a61037836600461234d565b61086e565b34801561038957600080fd5b506102fb6103983660046124e9565b6108ea565b6102b96103ab3660046124b6565b610916565b3480156103bc57600080fd5b506102fb6109ca565b3480156103d157600080fd5b5061024a6103e036600461241b565b6109db565b3480156103f157600080fd5b5061026c610400366004612180565b610ab4565b34801561041157600080fd5b5061026c6104203660046122a4565b610ae1565b60006104737fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1207fffffffff000000000000000000000000000000000000000000000000000000008416610b0c565b90505b919050565b600061047382610b39565b3330146104de576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612b9c6027913960400191505060405180910390fd5b6104fd8173ffffffffffffffffffffffffffffffffffffffff16610b96565b610552576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526039815260200180612a826039913960400191505060405180910390fd5b61055b81610b9c565b6040805173ffffffffffffffffffffffffffffffffffffffff8316815290517f310ba5f1d2ed074b51e2eccd052a47ae9ab7c6b800d1fca3db3999d6a592ca039181900360200190a150565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b600061061b6105df85610ba0565b84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610c0092505050565b1561064357507f1626ba7e000000000000000000000000000000000000000000000000000000005b9392505050565b600061047382610425565b600061067f6105df86866040518083838082843760405192018290039091209350610ba092505050565b156106a757507f20c13b0b000000000000000000000000000000000000000000000000000000005b949350505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b33301461074f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612b9c6027913960400191505060405180910390fd5b600061075a82610425565b73ffffffffffffffffffffffffffffffffffffffff1614156107c7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b8152602001806128e0602b913960400191505060405180910390fd5b6107d2816000610df8565b50565b33301461082d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612b9c6027913960400191505060405180910390fd5b600061085e82604051602001610843919061277e565b60405160208183030381529060405280519060200120610ba0565b905061086a8183610e5b565b5050565b6108778261102a565b600061088f83856040516020016108439291906127c5565b905061089b8183610c00565b6108da576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108d190612721565b60405180910390fd5b6108e48185610e5b565b50505050565b60006104737f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e83610b0c565b6000333014610970576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612b9c6027913960400191505060405180910390fd5b81516020830134f06040805173ffffffffffffffffffffffffffffffffffffffff8316815290519192507fa506ad4e7f05eceba62a023c3219e5bd98a615f4fa87e2afb08a2da5cf62bf0c919081900360200190a1919050565b60006109d660006108ea565b905090565b333014610a33576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612b9c6027913960400191505060405180910390fd5b6000610a3e83610425565b73ffffffffffffffffffffffffffffffffffffffff1614610aaa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c8152602001806129f4602c913960400191505060405180910390fd5b61086a8282610df8565b7fbc197c810000000000000000000000000000000000000000000000000000000098975050505050505050565b7ff23a6e61000000000000000000000000000000000000000000000000000000009695505050505050565b60408051602080820194909452808201929092528051808303820181526060909201905280519101205490565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f90042baf000000000000000000000000000000000000000000000000000000001415610b8d57506001610476565b610473826110ce565b3b151590565b3055565b604080517f19010000000000000000000000000000000000000000000000000000000000006020808301919091524660228301523060601b6042830152605680830194909452825180830390940184526076909101909152815191012090565b6000806000610c0e8461120f565b909250905061ffff821660005b8551831015610dd55760008080610c32898761127d565b975060ff91821694501691506001831415610c5a57610c5189876112fe565b96509050610d7e565b82610c86576060610c6b8a88611376565b97509050610c798b82611427565b9150828501945050610d7e565b6002831415610d2d57610c9989876112fe565b965090506000610ca98a886117b1565b975061ffff1690506060610cbe8b8984611822565b98509050610ccd8c8483611911565b610d22576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260328152602001806129c26032913960400191505060405180910390fd5b505092810192610d7e565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c8152602001806128b4602c913960400191505060405180910390fd5b848282604051602001808481526020018381526020018273ffffffffffffffffffffffffffffffffffffffff1681526020019350505050604051602081830303815290604052805190602001209450505050610c1b565b8361ffff168110158015610ded5750610ded82611b59565b979650505050505050565b61086a7fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1207fffffffff00000000000000000000000000000000000000000000000000000000841673ffffffffffffffffffffffffffffffffffffffff8416611c37565b60005b8151811015611025576000828281518110610e7557fe5b602002602001015190506000606082604001515a1015610ec1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108d1906126c4565b825115610f5957826060015173ffffffffffffffffffffffffffffffffffffffff168360400151600014610ef9578360400151610efb565b5a5b8460a00151604051610f0d91906125f6565b6000604051808303818686f4925050503d8060008114610f49576040519150601f19603f3d011682016040523d82523d6000602084013e610f4e565b606091505b509092509050610fee565b826060015173ffffffffffffffffffffffffffffffffffffffff1683608001518460400151600014610f8f578460400151610f91565b5a5b908560a00151604051610fa491906125f6565b600060405180830381858888f193505050503d8060008114610fe2576040519150601f19603f3d011682016040523d82523d6000602084013e610fe7565b606091505b5090925090505b811561100f5785604051611002919061263e565b60405180910390a061101a565b61101a838783611c65565b505050600101610e5e565b505050565b60008061103683611cb5565b915091506000611045836108ea565b9050808214611080576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108d19061268d565b6001820161108e8482611cce565b7f1f180c27086c7a39ea2a7b25239d1ab92348f07ca7bb59d1438fcf527568f88184826040516110bf9291906127de565b60405180910390a15050505050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fec6aba5000000000000000000000000000000000000000000000000000000000148061116157507fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e000000000000000000000000000000000000000000000000000000000145b806111ad57507fffffffff0000000000000000000000000000000000000000000000000000000082167f150b7a0200000000000000000000000000000000000000000000000000000000145b806111f957507fffffffff0000000000000000000000000000000000000000000000000000000082167fc0ee0b8a00000000000000000000000000000000000000000000000000000000145b1561120657506001610476565b61047382611cf9565b6020810151815160f09190911c90600290811115611278576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061292e6027913960400191505060405180910390fd5b915091565b8082016020015160f881901c9060f01c60ff166002830183811161129d57fe5b84518111156112f7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180612af76026913960400191505060405180910390fd5b9250925092565b8082016020015160601c6014820182811161131557fe5b835181111561136f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602381526020018061290b6023913960400191505060405180910390fd5b9250929050565b6040805160428082526080820190925260609160009190602082018180368337019050509150828401602001805160208401526020810151604084015260228101516042840152506042830190508281116113cd57fe5b835181111561136f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180612a5f6023913960400191505060405180910390fd5b60008151604214611483576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a81526020018061287a603a913960400191505060405180910390fd5b60008260018451038151811061149557fe5b602001015160f81c60f81b60f81c60ff1690506000836040815181106114b757fe5b016020015160f81c905060006114cd8582611d56565b905060006114dc866020611d56565b90507f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0811115611557576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d81526020018061283d603d913960400191505060405180910390fd5b8260ff16601b1415801561156f57508260ff16601c14155b156115c5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180612955603d913960400191505060405180910390fd5b60018414156116395760018784848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611628573d6000803e3d6000fd5b50505060206040510351945061173b565b60028414156116ea5760018760405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c018281526020019150506040516020818303038152906040528051906020012084848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611628573d6000803e3d6000fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180612abb603c913960400191505060405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff85166117a7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260308152602001806129926030913960400191505060405180910390fd5b5050505092915050565b8082016020015160f01c600282018281116117c857fe5b835181111561136f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180612b3e6022913960400191505060405180910390fd5b606060008267ffffffffffffffff8111801561183d57600080fd5b506040519080825280601f01601f191660200182016040528015611868576020820181803683370190505b509150838501602001600060205b8581101561188f57908201518482015260208101611876565b84860160200180519390920151908501525250828201838110156118af57fe5b8451811115611909576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180612b1d6021913960400191505060405180910390fd5b935093915050565b6000808260018451038151811061192457fe5b016020015160f81c9050600181148061193d5750600281145b15611981578373ffffffffffffffffffffffffffffffffffffffff166119638685611427565b73ffffffffffffffffffffffffffffffffffffffff16149150611b51565b6003811415611b005782517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018452604080517f1626ba7e000000000000000000000000000000000000000000000000000000008152600481018881526024820192835286516044830152865173ffffffffffffffffffffffffffffffffffffffff891693631626ba7e938b938a9390929160640190602085019080838360005b83811015611a3b578181015183820152602001611a23565b50505050905090810190601f168015611a685780820380516001836020036101000a031916815260200191505b50935050505060206040518083038186803b158015611a8657600080fd5b505afa158015611a9a573d6000803e3d6000fd5b505050506040513d6020811015611ab057600080fd5b50519084527fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e00000000000000000000000000000000000000000000000000000000149150611b51565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f815260200180612a20603f913960400191505060405180910390fd5b509392505050565b604080517fff000000000000000000000000000000000000000000000000000000000000006020808301919091527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000060601b166021830152603582018490527f0000000000000000000000000000000000000000000000000000000000000000605580840191909152835180840390910181526075909201909252805191012073ffffffffffffffffffffffffffffffffffffffff163014919050565b6040805160208082019590955280820193909352805180840382018152606090930190528151919092012055565b826020015115611c7757805160208201fd5b7f3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd78282604051611ca8929190612647565b60405180910390a1505050565b606081901c916bffffffffffffffffffffffff90911690565b61086a7f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e8383611c37565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f025b22bc000000000000000000000000000000000000000000000000000000001415611d4d57506001610476565b61047382611dbe565b60008160200183511015611db5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180612b60603c913960400191505060405180910390fd5b50016020015190565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f389901c7000000000000000000000000000000000000000000000000000000001415611e1257506001610476565b6104738260007fffffffff0000000000000000000000000000000000000000000000000000000082161580611e8857507fffffffff0000000000000000000000000000000000000000000000000000000082167f36e7817500000000000000000000000000000000000000000000000000000000145b15611e9557506001610476565b7f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000831614610473565b803573ffffffffffffffffffffffffffffffffffffffff8116811461047657600080fd5b600082601f830112611f13578081fd5b8135602067ffffffffffffffff80831115611f2a57fe5b611f3782838502016127ec565b83815282810190868401865b86811015612013578135890160c0807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0838e03011215611f8157898afd5b604080518281018181108a82111715611f9657fe5b8252611fa3848b01612063565b8152611fb0828501612063565b8a8201526060808501358383015260809250611fcd838601611edf565b9082015260a08481013583830152928401359289841115611fec578c8dfd5b611ffa8f8c868801016120e3565b9082015287525050509285019290850190600101611f43565b509098975050505050505050565b60008083601f840112612032578182fd5b50813567ffffffffffffffff811115612049578182fd5b602083019150836020808302850101111561136f57600080fd5b8035801515811461047657600080fd5b80357fffffffff000000000000000000000000000000000000000000000000000000008116811461047657600080fd5b60008083601f8401126120b4578182fd5b50813567ffffffffffffffff8111156120cb578182fd5b60208301915083602082850101111561136f57600080fd5b600082601f8301126120f3578081fd5b813567ffffffffffffffff81111561210757fe5b61213860207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016127ec565b81815284602083860101111561214c578283fd5b816020850160208301379081016020019190915292915050565b600060208284031215612177578081fd5b61064382611edf565b60008060008060008060008060a0898b03121561219b578384fd5b6121a489611edf565b97506121b260208a01611edf565b9650604089013567ffffffffffffffff808211156121ce578586fd5b6121da8c838d01612021565b909850965060608b01359150808211156121f2578586fd5b6121fe8c838d01612021565b909650945060808b0135915080821115612216578384fd5b506122238b828c016120a3565b999c989b5096995094979396929594505050565b60008060008060006080868803121561224e578081fd5b61225786611edf565b945061226560208701611edf565b935060408601359250606086013567ffffffffffffffff811115612287578182fd5b612293888289016120a3565b969995985093965092949392505050565b60008060008060008060a087890312156122bc578182fd5b6122c587611edf565b95506122d360208801611edf565b94506040870135935060608701359250608087013567ffffffffffffffff8111156122fc578283fd5b61230889828a016120a3565b979a9699509497509295939492505050565b60006020828403121561232b578081fd5b813567ffffffffffffffff811115612341578182fd5b6106a784828501611f03565b600080600060608486031215612361578283fd5b833567ffffffffffffffff80821115612378578485fd5b61238487838801611f03565b94506020860135935060408601359150808211156123a0578283fd5b506123ad868287016120e3565b9150509250925092565b6000806000604084860312156123cb578283fd5b83359250602084013567ffffffffffffffff8111156123e8578283fd5b6123f4868287016120a3565b9497909650939450505050565b600060208284031215612412578081fd5b61064382612073565b6000806040838503121561242d578182fd5b61243683612073565b915061244460208401611edf565b90509250929050565b60008060008060408587031215612462578182fd5b843567ffffffffffffffff80821115612479578384fd5b612485888389016120a3565b9096509450602087013591508082111561249d578384fd5b506124aa878288016120a3565b95989497509550505050565b6000602082840312156124c7578081fd5b813567ffffffffffffffff8111156124dd578182fd5b6106a7848285016120e3565b6000602082840312156124fa578081fd5b5035919050565b6000815180845260208085018081965082840281019150828601855b8581101561259f5782840389528151805115158552858101511515868601526040808201519086015260608082015173ffffffffffffffffffffffffffffffffffffffff16908601526080808201519086015260a09081015160c09186018290529061258b818701836125ac565b9a87019a955050509084019060010161251d565b5091979650505050505050565b600081518084526125c4816020860160208601612810565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60008251612608818460208701612810565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b901515815260200190565b90815260200190565b6000838252604060208301526106a760408301846125ac565b7fffffffff0000000000000000000000000000000000000000000000000000000091909116815260200190565b6020808252601f908201527f4d61696e4d6f64756c65235f617574683a20494e56414c49445f4e4f4e434500604082015260600190565b60208082526024908201527f4d6f64756c6543616c6c73235f657865637574653a204e4f545f454e4f55474860408201527f5f47415300000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526026908201527f4d6f64756c6543616c6c7323657865637574653a20494e56414c49445f53494760408201527f4e41545552450000000000000000000000000000000000000000000000000000606082015260800190565b600060408252600560408301527f73656c663a0000000000000000000000000000000000000000000000000000006060830152608060208301526106436080830184612501565b6000838252604060208301526106a76040830184612501565b918252602082015260400190565b60405181810167ffffffffffffffff8111828210171561280857fe5b604052919050565b60005b8381101561282b578181015183820152602001612813565b838111156108e4575050600091015256fe5369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202773272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265206c656e6774684d6f64756c6541757468235f7369676e617475726556616c69646174696f6e20494e56414c49445f464c41474d6f64756c65486f6f6b732372656d6f7665486f6f6b3a20484f4f4b5f4e4f545f524547495354455245444c696242797465732372656164416464726573733a204f55545f4f465f424f554e44534c696242797465732372656164466972737455696e7431363a204f55545f4f465f424f554e44535369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202776272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20494e56414c49445f5349474e45524d6f64756c6541757468235f7369676e617475726556616c69646174696f6e3a20494e56414c49445f5349474e41545552454d6f64756c65486f6f6b7323616464486f6f6b3a20484f4f4b5f414c52454144595f524547495354455245445369676e617475726556616c696461746f7223697356616c69645369676e61747572653a20554e535550504f525445445f5349474e41545552455f545950454c696242797465732372656164427974657336363a204f55545f4f465f424f554e44534d6f64756c6555706461746523757064617465496d706c656d656e746174696f6e3a20494e56414c49445f494d504c454d454e544154494f4e5369676e617475726556616c696461746f72237265636f7665725369676e65723a20554e535550504f525445445f5349474e41545552455f545950454c69624279746573237265616455696e743855696e74383a204f55545f4f465f424f554e44534c69624279746573237265616442797465733a204f55545f4f465f424f554e44534c69624279746573237265616455696e7431363a204f55545f4f465f424f554e44534c696242797465732372656164427974657333323a20475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f52455155495245444d6f64756c6553656c6641757468236f6e6c7953656c663a204e4f545f415554484f52495a4544a2646970667358221220b34deca9dd75815e4ef8a9279e45750ec5554b22c673e160bdba849d80f5888564736f6c63430007060033603a600e3d39601a805130553df3363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3000000000000000000000000f9d09d634fb818b05149329c1dccfaea53639d960000000000000000000000000000000000000000000000000000000000', + gasLimit: 8000000 + }) + + // Deploy mainModuleUpgradable + await signer.sendTransaction({ + to: '0x8A5Bc19e22D6aD55a2c763B93A75d09F321fe764', + data: '0x9c4ae2d0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d07608060405234801561001057600080fd5b50612ce7806100206000396000f3fe6080604052600436106101125760003560e01c806351605d80116100a557806390042baf11610074578063b93ea7ad11610059578063b93ea7ad146103d0578063bc197c81146103f0578063f23a6e611461041057610119565b806390042baf146103a8578063affed0e0146103bb57610119565b806351605d801461032657806361c2926c146103485780637a9a1628146103685780638c3f55631461038857610119565b80631a9b2337116100e15780631a9b23371461029957806320c13b0b146102c657806329561426146102e65780634fcf3eca1461030657610119565b806301ffc9a7146101f4578063025b22bc1461022a578063150b7a021461024c5780631626ba7e1461027957610119565b3661011957005b60006101486000357fffffffff0000000000000000000000000000000000000000000000000000000016610430565b905073ffffffffffffffffffffffffffffffffffffffff8116156101f1576000808273ffffffffffffffffffffffffffffffffffffffff166000366040518083838082843760405192019450600093509091505080830381855af49150503d80600081146101d2576040519150601f19603f3d011682016040523d82523d6000602084013e6101d7565b606091505b5091509150816101e957805160208201fd5b805160208201f35b50005b34801561020057600080fd5b5061021461020f3660046124d4565b610486565b60405161022191906126eb565b60405180910390f35b34801561023657600080fd5b5061024a610245366004612221565b610491565b005b34801561025857600080fd5b5061026c6102673660046122f2565b6105b2565b6040516102219190612718565b34801561028557600080fd5b5061026c61029436600461248a565b6105dc565b3480156102a557600080fd5b506102b96102b43660046124d4565b610655565b60405161022191906126ca565b3480156102d257600080fd5b5061026c6102e1366004612520565b610660565b3480156102f257600080fd5b5061024a610301366004612472565b6106ba565b34801561031257600080fd5b5061024a6103213660046124d4565b6107c8565b34801561033257600080fd5b5061033b6108a6565b60405161022191906126f6565b34801561035457600080fd5b5061024a6103633660046123d5565b6108d6565b34801561037457600080fd5b5061024a610383366004612408565b61096f565b34801561039457600080fd5b5061033b6103a3366004612472565b6109eb565b6102b96103b6366004612589565b610a17565b3480156103c757600080fd5b5061033b610acb565b3480156103dc57600080fd5b5061024a6103eb3660046124ee565b610ad7565b3480156103fc57600080fd5b5061026c61040b36600461223b565b610bb0565b34801561041c57600080fd5b5061026c61042b36600461235f565b610bdd565b600061047e7fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1207fffffffff000000000000000000000000000000000000000000000000000000008416610c08565b90505b919050565b600061047e82610c35565b3330146104e9576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612c8b6027913960400191505060405180910390fd5b6105088173ffffffffffffffffffffffffffffffffffffffff16610c92565b61055d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526039815260200180612b716039913960400191505060405180910390fd5b61056681610c98565b6040805173ffffffffffffffffffffffffffffffffffffffff8316815290517f310ba5f1d2ed074b51e2eccd052a47ae9ab7c6b800d1fca3db3999d6a592ca039181900360200190a150565b7f150b7a020000000000000000000000000000000000000000000000000000000095945050505050565b60006106266105ea85610c9c565b84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610cfc92505050565b1561064e57507f1626ba7e000000000000000000000000000000000000000000000000000000005b9392505050565b600061047e82610430565b600061068a6105ea86866040518083838082843760405192018290039091209350610c9c92505050565b156106b257507f20c13b0b000000000000000000000000000000000000000000000000000000005b949350505050565b333014610712576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612c8b6027913960400191505060405180910390fd5b80610768576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260378152602001806129986037913960400191505060405180910390fd5b6107927fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf882610ef4565b6040805182815290517f307ed6bd941ee9fc80f369c94af5fa11e25bab5102a6140191756c5474a30bfa9181900360200190a150565b333014610820576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612c8b6027913960400191505060405180910390fd5b600061082b82610430565b73ffffffffffffffffffffffffffffffffffffffff161415610898576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b8152602001806129cf602b913960400191505060405180910390fd5b6108a3816000610ef8565b50565b60006108d17fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8610f5b565b905090565b33301461092e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612c8b6027913960400191505060405180910390fd5b600061095f826040516020016109449190612836565b60405160208183030381529060405280519060200120610c9c565b905061096b8183610f5f565b5050565b6109788261112e565b6000610990838560405160200161094492919061287d565b905061099c8183610cfc565b6109db576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109d2906127d9565b60405180910390fd5b6109e58185610f5f565b50505050565b600061047e7f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e83610c08565b6000333014610a71576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612c8b6027913960400191505060405180910390fd5b81516020830134f06040805173ffffffffffffffffffffffffffffffffffffffff8316815290519192507fa506ad4e7f05eceba62a023c3219e5bd98a615f4fa87e2afb08a2da5cf62bf0c919081900360200190a1919050565b60006108d160006109eb565b333014610b2f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612c8b6027913960400191505060405180910390fd5b6000610b3a83610430565b73ffffffffffffffffffffffffffffffffffffffff1614610ba6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180612ae3602c913960400191505060405180910390fd5b61096b8282610ef8565b7fbc197c810000000000000000000000000000000000000000000000000000000098975050505050505050565b7ff23a6e61000000000000000000000000000000000000000000000000000000009695505050505050565b60408051602080820194909452808201929092528051808303820181526060909201905280519101205490565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f90042baf000000000000000000000000000000000000000000000000000000001415610c8957506001610481565b61047e826111d2565b3b151590565b3055565b604080517f19010000000000000000000000000000000000000000000000000000000000006020808301919091524660228301523060601b6042830152605680830194909452825180830390940184526076909101909152815191012090565b6000806000610d0a84611313565b909250905061ffff821660005b8551831015610ed15760008080610d2e8987611381565b975060ff91821694501691506001831415610d5657610d4d8987611402565b96509050610e7a565b82610d82576060610d678a8861147a565b97509050610d758b8261152b565b9150828501945050610e7a565b6002831415610e2957610d958987611402565b965090506000610da58a886118b5565b975061ffff1690506060610dba8b8984611926565b98509050610dc98c8483611a15565b610e1e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526032815260200180612ab16032913960400191505060405180910390fd5b505092810192610e7a565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c81526020018061296c602c913960400191505060405180910390fd5b848282604051602001808481526020018381526020018273ffffffffffffffffffffffffffffffffffffffff1681526020019350505050604051602081830303815290604052805190602001209450505050610d17565b8361ffff168110158015610ee95750610ee982611c5d565b979650505050505050565b9055565b61096b7fbe27a319efc8734e89e26ba4bc95f5c788584163b959f03fa04e2d7ab4b9a1207fffffffff00000000000000000000000000000000000000000000000000000000841673ffffffffffffffffffffffffffffffffffffffff8416611c9a565b5490565b60005b8151811015611129576000828281518110610f7957fe5b602002602001015190506000606082604001515a1015610fc5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109d29061277c565b82511561105d57826060015173ffffffffffffffffffffffffffffffffffffffff168360400151600014610ffd578360400151610fff565b5a5b8460a0015160405161101191906126ae565b6000604051808303818686f4925050503d806000811461104d576040519150601f19603f3d011682016040523d82523d6000602084013e611052565b606091505b5090925090506110f2565b826060015173ffffffffffffffffffffffffffffffffffffffff1683608001518460400151600014611093578460400151611095565b5a5b908560a001516040516110a891906126ae565b600060405180830381858888f193505050503d80600081146110e6576040519150601f19603f3d011682016040523d82523d6000602084013e6110eb565b606091505b5090925090505b8115611113578560405161110691906126f6565b60405180910390a061111e565b61111e838783611cc8565b505050600101610f62565b505050565b60008061113a83611d18565b915091506000611149836109eb565b9050808214611184576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016109d290612745565b600182016111928482611d31565b7f1f180c27086c7a39ea2a7b25239d1ab92348f07ca7bb59d1438fcf527568f88184826040516111c3929190612896565b60405180910390a15050505050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fec6aba5000000000000000000000000000000000000000000000000000000000148061126557507fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e000000000000000000000000000000000000000000000000000000000145b806112b157507fffffffff0000000000000000000000000000000000000000000000000000000082167f150b7a0200000000000000000000000000000000000000000000000000000000145b806112fd57507fffffffff0000000000000000000000000000000000000000000000000000000082167fc0ee0b8a00000000000000000000000000000000000000000000000000000000145b1561130a57506001610481565b61047e82611d5c565b6020810151815160f09190911c9060029081111561137c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180612a1d6027913960400191505060405180910390fd5b915091565b8082016020015160f881901c9060f01c60ff16600283018381116113a157fe5b84518111156113fb576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180612be66026913960400191505060405180910390fd5b9250925092565b8082016020015160601c6014820182811161141957fe5b8351811115611473576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806129fa6023913960400191505060405180910390fd5b9250929050565b6040805160428082526080820190925260609160009190602082018180368337019050509150828401602001805160208401526020810151604084015260228101516042840152506042830190508281116114d157fe5b8351811115611473576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180612b4e6023913960400191505060405180910390fd5b60008151604214611587576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a815260200180612932603a913960400191505060405180910390fd5b60008260018451038151811061159957fe5b602001015160f81c60f81b60f81c60ff1690506000836040815181106115bb57fe5b016020015160f81c905060006115d18582611db9565b905060006115e0866020611db9565b90507f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a081111561165b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d8152602001806128f5603d913960400191505060405180910390fd5b8260ff16601b1415801561167357508260ff16601c14155b156116c9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180612a44603d913960400191505060405180910390fd5b600184141561173d5760018784848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa15801561172c573d6000803e3d6000fd5b50505060206040510351945061183f565b60028414156117ee5760018760405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c018281526020019150506040516020818303038152906040528051906020012084848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa15801561172c573d6000803e3d6000fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180612baa603c913960400191505060405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff85166118ab576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526030815260200180612a816030913960400191505060405180910390fd5b5050505092915050565b8082016020015160f01c600282018281116118cc57fe5b8351811115611473576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180612c2d6022913960400191505060405180910390fd5b606060008267ffffffffffffffff8111801561194157600080fd5b506040519080825280601f01601f19166020018201604052801561196c576020820181803683370190505b509150838501602001600060205b858110156119935790820151848201526020810161197a565b84860160200180519390920151908501525250828201838110156119b357fe5b8451811115611a0d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180612c0c6021913960400191505060405180910390fd5b935093915050565b60008082600184510381518110611a2857fe5b016020015160f81c90506001811480611a415750600281145b15611a85578373ffffffffffffffffffffffffffffffffffffffff16611a67868561152b565b73ffffffffffffffffffffffffffffffffffffffff16149150611c55565b6003811415611c045782517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018452604080517f1626ba7e000000000000000000000000000000000000000000000000000000008152600481018881526024820192835286516044830152865173ffffffffffffffffffffffffffffffffffffffff891693631626ba7e938b938a9390929160640190602085019080838360005b83811015611b3f578181015183820152602001611b27565b50505050905090810190601f168015611b6c5780820380516001836020036101000a031916815260200191505b50935050505060206040518083038186803b158015611b8a57600080fd5b505afa158015611b9e573d6000803e3d6000fd5b505050506040513d6020811015611bb457600080fd5b50519084527fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e00000000000000000000000000000000000000000000000000000000149150611c55565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f815260200180612b0f603f913960400191505060405180910390fd5b509392505050565b6000811580159061047e5750611c927fea7157fa25e3aa17d0ae2d5280fa4e24d421c61842aa85e45194e1145aa72bf8610f5b565b909114919050565b6040805160208082019590955280820193909352805180840382018152606090930190528151919092012055565b826020015115611cda57805160208201fd5b7f3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd78282604051611d0b9291906126ff565b60405180910390a1505050565b606081901c916bffffffffffffffffffffffff90911690565b61096b7f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e8383611c9a565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f025b22bc000000000000000000000000000000000000000000000000000000001415611db057506001610481565b61047e82611e21565b60008160200183511015611e18576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180612c4f603c913960400191505060405180910390fd5b50016020015190565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f389901c7000000000000000000000000000000000000000000000000000000001415611e7557506001610481565b61047e8260007fffffffff0000000000000000000000000000000000000000000000000000000082167f783649a6000000000000000000000000000000000000000000000000000000001415611ecd57506001610481565b61047e8260007fffffffff0000000000000000000000000000000000000000000000000000000082161580611f4357507fffffffff0000000000000000000000000000000000000000000000000000000082167f36e7817500000000000000000000000000000000000000000000000000000000145b15611f5057506001610481565b7f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000083161461047e565b803573ffffffffffffffffffffffffffffffffffffffff8116811461048157600080fd5b600082601f830112611fce578081fd5b8135602067ffffffffffffffff80831115611fe557fe5b611ff282838502016128a4565b83815282810190868401865b868110156120ce578135890160c0807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0838e0301121561203c57898afd5b604080518281018181108a8211171561205157fe5b825261205e848b0161211e565b815261206b82850161211e565b8a8201526060808501358383015260809250612088838601611f9a565b9082015260a084810135838301529284013592898411156120a7578c8dfd5b6120b58f8c8688010161219e565b9082015287525050509285019290850190600101611ffe565b509098975050505050505050565b60008083601f8401126120ed578182fd5b50813567ffffffffffffffff811115612104578182fd5b602083019150836020808302850101111561147357600080fd5b8035801515811461048157600080fd5b80357fffffffff000000000000000000000000000000000000000000000000000000008116811461048157600080fd5b60008083601f84011261216f578182fd5b50813567ffffffffffffffff811115612186578182fd5b60208301915083602082850101111561147357600080fd5b600082601f8301126121ae578081fd5b813567ffffffffffffffff8111156121c257fe5b6121f360207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116016128a4565b818152846020838601011115612207578283fd5b816020850160208301379081016020019190915292915050565b600060208284031215612232578081fd5b61064e82611f9a565b60008060008060008060008060a0898b031215612256578384fd5b61225f89611f9a565b975061226d60208a01611f9a565b9650604089013567ffffffffffffffff80821115612289578586fd5b6122958c838d016120dc565b909850965060608b01359150808211156122ad578586fd5b6122b98c838d016120dc565b909650945060808b01359150808211156122d1578384fd5b506122de8b828c0161215e565b999c989b5096995094979396929594505050565b600080600080600060808688031215612309578081fd5b61231286611f9a565b945061232060208701611f9a565b935060408601359250606086013567ffffffffffffffff811115612342578182fd5b61234e8882890161215e565b969995985093965092949392505050565b60008060008060008060a08789031215612377578182fd5b61238087611f9a565b955061238e60208801611f9a565b94506040870135935060608701359250608087013567ffffffffffffffff8111156123b7578283fd5b6123c389828a0161215e565b979a9699509497509295939492505050565b6000602082840312156123e6578081fd5b813567ffffffffffffffff8111156123fc578182fd5b6106b284828501611fbe565b60008060006060848603121561241c578283fd5b833567ffffffffffffffff80821115612433578485fd5b61243f87838801611fbe565b945060208601359350604086013591508082111561245b578283fd5b506124688682870161219e565b9150509250925092565b600060208284031215612483578081fd5b5035919050565b60008060006040848603121561249e578283fd5b83359250602084013567ffffffffffffffff8111156124bb578283fd5b6124c78682870161215e565b9497909650939450505050565b6000602082840312156124e5578081fd5b61064e8261212e565b60008060408385031215612500578182fd5b6125098361212e565b915061251760208401611f9a565b90509250929050565b60008060008060408587031215612535578182fd5b843567ffffffffffffffff8082111561254c578384fd5b6125588883890161215e565b90965094506020870135915080821115612570578384fd5b5061257d8782880161215e565b95989497509550505050565b60006020828403121561259a578081fd5b813567ffffffffffffffff8111156125b0578182fd5b6106b28482850161219e565b6000815180845260208085019450848183028601828601855b858110156126575783830389528151805115158452858101511515868501526040808201519085015260608082015173ffffffffffffffffffffffffffffffffffffffff16908501526080808201519085015260a09081015160c09185018290529061264381860183612664565b9a87019a94505050908401906001016125d5565b5090979650505050505050565b6000815180845261267c8160208601602086016128c8565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600082516126c08184602087016128c8565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b901515815260200190565b90815260200190565b6000838252604060208301526106b26040830184612664565b7fffffffff0000000000000000000000000000000000000000000000000000000091909116815260200190565b6020808252601f908201527f4d61696e4d6f64756c65235f617574683a20494e56414c49445f4e4f4e434500604082015260600190565b60208082526024908201527f4d6f64756c6543616c6c73235f657865637574653a204e4f545f454e4f55474860408201527f5f47415300000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526026908201527f4d6f64756c6543616c6c7323657865637574653a20494e56414c49445f53494760408201527f4e41545552450000000000000000000000000000000000000000000000000000606082015260800190565b600060408252600560408301527f73656c663a00000000000000000000000000000000000000000000000000000060608301526080602083015261064e60808301846125bc565b6000838252604060208301526106b260408301846125bc565b918252602082015260400190565b60405181810167ffffffffffffffff811182821017156128c057fe5b604052919050565b60005b838110156128e35781810151838201526020016128cb565b838111156109e5575050600091015256fe5369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202773272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265206c656e6774684d6f64756c6541757468235f7369676e617475726556616c69646174696f6e20494e56414c49445f464c41474d6f64756c654175746855706772616461626c6523757064617465496d6167654861736820494e56414c49445f494d4147455f484153484d6f64756c65486f6f6b732372656d6f7665486f6f6b3a20484f4f4b5f4e4f545f524547495354455245444c696242797465732372656164416464726573733a204f55545f4f465f424f554e44534c696242797465732372656164466972737455696e7431363a204f55545f4f465f424f554e44535369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202776272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20494e56414c49445f5349474e45524d6f64756c6541757468235f7369676e617475726556616c69646174696f6e3a20494e56414c49445f5349474e41545552454d6f64756c65486f6f6b7323616464486f6f6b3a20484f4f4b5f414c52454144595f524547495354455245445369676e617475726556616c696461746f7223697356616c69645369676e61747572653a20554e535550504f525445445f5349474e41545552455f545950454c696242797465732372656164427974657336363a204f55545f4f465f424f554e44534d6f64756c6555706461746523757064617465496d706c656d656e746174696f6e3a20494e56414c49445f494d504c454d454e544154494f4e5369676e617475726556616c696461746f72237265636f7665725369676e65723a20554e535550504f525445445f5349474e41545552455f545950454c69624279746573237265616455696e743855696e74383a204f55545f4f465f424f554e44534c69624279746573237265616442797465733a204f55545f4f465f424f554e44534c69624279746573237265616455696e7431363a204f55545f4f465f424f554e44534c696242797465732372656164427974657333323a20475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f52455155495245444d6f64756c6553656c6641757468236f6e6c7953656c663a204e4f545f415554484f52495a4544a2646970667358221220aebb8d931ef86555b6441c416b208bb9fe8fe0974c5733ebbccce548296c37ce64736f6c6343000706003300000000000000000000000000000000000000000000000000', + gasLimit: 8000000 + }) + + // Deploy guestModule + await signer.sendTransaction({ + to: '0x8A5Bc19e22D6aD55a2c763B93A75d09F321fe764', + data: '0x9c4ae2d0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001dfc608060405234801561001057600080fd5b50611ddc806100206000396000f3fe60806040526004361061007b5760003560e01c80637a9a16281161004e5780637a9a1628146101255780638c3f55631461014557806390042baf14610172578063affed0e0146101925761007b565b806301ffc9a7146100805780631626ba7e146100b657806320c13b0b146100e357806361c2926c14610103575b600080fd5b34801561008c57600080fd5b506100a061009b366004611677565b6101a7565b6040516100ad91906118be565b60405180910390f35b3480156100c257600080fd5b506100d66100d136600461162d565b6101ba565b6040516100ad91906118eb565b3480156100ef57600080fd5b506100d66100fe3660046116b7565b610233565b34801561010f57600080fd5b5061012361011e366004611590565b61028d565b005b34801561013157600080fd5b506101236101403660046115c3565b6102ce565b34801561015157600080fd5b50610165610160366004611753565b6102f6565b6040516100ad91906118c9565b610185610180366004611720565b610322565b6040516100ad919061189d565b34801561019e57600080fd5b506101656103d6565b60006101b2826103e7565b90505b919050565b60006102046101c885610444565b84848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506104a492505050565b1561022c57507f1626ba7e000000000000000000000000000000000000000000000000000000005b9392505050565b600061025d6101c88686604051808383808284376040519201829003909120935061044492505050565b1561028557507f20c13b0b000000000000000000000000000000000000000000000000000000005b949350505050565b60006102be826040516020016102a39190611a19565b60405160208183030381529060405280519060200120610444565b90506102ca818361069c565b5050565b60006102e4846040516020016102a39190611975565b90506102f0818561069c565b50505050565b60006101b27f8d0bf1fd623d628c741362c1289948e57b3e2905218c676d3e69abee36d6ae2e83610817565b600033301461037c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180611d806027913960400191505060405180910390fd5b81516020830134f06040805173ffffffffffffffffffffffffffffffffffffffff8316815290519192507fa506ad4e7f05eceba62a023c3219e5bd98a615f4fa87e2afb08a2da5cf62bf0c919081900360200190a1919050565b60006103e260006102f6565b905090565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f90042baf00000000000000000000000000000000000000000000000000000000141561043b575060016101b5565b6101b282610844565b604080517f19010000000000000000000000000000000000000000000000000000000000006020808301919091524660228301523060601b6042830152605680830194909452825180830390940184526076909101909152815191012090565b60008060006104b2846108a1565b909250905061ffff821660005b855183101561067957600080806104d6898761090f565b975060ff918216945016915060018314156104fe576104f58987610990565b96509050610622565b8261052a57606061050f8a88610a08565b9750905061051d8b82610ab9565b9150828501945050610622565b60028314156105d15761053d8987610990565b96509050600061054d8a88610e43565b975061ffff16905060606105628b8984610eb4565b985090506105718c8483610fa3565b6105c6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526032815260200180611c0b6032913960400191505060405180910390fd5b505092810192610622565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180611b28602c913960400191505060405180910390fd5b848282604051602001808481526020018381526020018273ffffffffffffffffffffffffffffffffffffffff16815260200193505050506040516020818303038152906040528051906020012094505050506104bf565b8361ffff1681101580156106915750610691826111eb565b979650505050505050565b60005b81518110156108125760008282815181106106b657fe5b6020026020010151905060006060826000015115610709576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610700906119bc565b60405180910390fd5b82604001515a1015610747576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161070090611918565b826060015173ffffffffffffffffffffffffffffffffffffffff168360800151846040015160001461077d57846040015161077f565b5a5b908560a001516040516107929190611881565b600060405180830381858888f193505050503d80600081146107d0576040519150601f19603f3d011682016040523d82523d6000602084013e6107d5565b606091505b50909250905081156107fc57856040516107ef91906118c9565b60405180910390a0610807565b6108078387836111f1565b50505060010161069f565b505050565b60408051602080820194909452808201929092528051808303820181526060909201905280519101205490565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f389901c7000000000000000000000000000000000000000000000000000000001415610898575060016101b5565b6101b282611241565b6020810151815160f09190911c9060029081111561090a576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180611b776027913960400191505060405180910390fd5b915091565b8082016020015160f881901c9060f01c60ff166002830183811161092f57fe5b8451811115610989576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180611cdb6026913960400191505060405180910390fd5b9250925092565b8082016020015160601c601482018281116109a757fe5b8351811115610a01576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180611b546023913960400191505060405180910390fd5b9250929050565b604080516042808252608082019092526060916000919060208201818036833701905050915082840160200180516020840152602081015160408401526022810151604284015250604283019050828111610a5f57fe5b8351811115610a01576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180611c7c6023913960400191505060405180910390fd5b60008151604214610b15576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a815260200180611aee603a913960400191505060405180910390fd5b600082600184510381518110610b2757fe5b602001015160f81c60f81b60f81c60ff169050600083604081518110610b4957fe5b016020015160f81c90506000610b5f85826112c9565b90506000610b6e8660206112c9565b90507f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0811115610be9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180611ab1603d913960400191505060405180910390fd5b8260ff16601b14158015610c0157508260ff16601c14155b15610c57576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180611b9e603d913960400191505060405180910390fd5b6001841415610ccb5760018784848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015610cba573d6000803e3d6000fd5b505050602060405103519450610dcd565b6002841415610d7c5760018760405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c018281526020019150506040516020818303038152906040528051906020012084848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015610cba573d6000803e3d6000fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180611c9f603c913960400191505060405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8516610e39576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526030815260200180611bdb6030913960400191505060405180910390fd5b5050505092915050565b8082016020015160f01c60028201828111610e5a57fe5b8351811115610a01576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526022815260200180611d226022913960400191505060405180910390fd5b606060008267ffffffffffffffff81118015610ecf57600080fd5b506040519080825280601f01601f191660200182016040528015610efa576020820181803683370190505b509150838501602001600060205b85811015610f2157908201518482015260208101610f08565b8486016020018051939092015190850152525082820183811015610f4157fe5b8451811115610f9b576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526021815260200180611d016021913960400191505060405180910390fd5b935093915050565b60008082600184510381518110610fb657fe5b016020015160f81c90506001811480610fcf5750600281145b15611013578373ffffffffffffffffffffffffffffffffffffffff16610ff58685610ab9565b73ffffffffffffffffffffffffffffffffffffffff161491506111e3565b60038114156111925782517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018452604080517f1626ba7e000000000000000000000000000000000000000000000000000000008152600481018881526024820192835286516044830152865173ffffffffffffffffffffffffffffffffffffffff891693631626ba7e938b938a9390929160640190602085019080838360005b838110156110cd5781810151838201526020016110b5565b50505050905090810190601f1680156110fa5780820380516001836020036101000a031916815260200191505b50935050505060206040518083038186803b15801561111857600080fd5b505afa15801561112c573d6000803e3d6000fd5b505050506040513d602081101561114257600080fd5b50519084527fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e000000000000000000000000000000000000000000000000000000001491506111e3565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f815260200180611c3d603f913960400191505060405180910390fd5b509392505050565b50600190565b82602001511561120357805160208201fd5b7f3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd782826040516112349291906118d2565b60405180910390a1505050565b60007fffffffff00000000000000000000000000000000000000000000000000000000821615806112b357507fffffffff0000000000000000000000000000000000000000000000000000000082167f36e7817500000000000000000000000000000000000000000000000000000000145b156112c0575060016101b5565b6101b282611331565b60008160200183511015611328576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180611d44603c913960400191505060405180910390fd5b50016020015190565b7fffffffff0000000000000000000000000000000000000000000000000000000081167f01ffc9a70000000000000000000000000000000000000000000000000000000014919050565b803573ffffffffffffffffffffffffffffffffffffffff811681146101b557600080fd5b600082601f8301126113af578081fd5b8135602067ffffffffffffffff808311156113c657fe5b6113d38283850201611a60565b83815282810190868401865b868110156114af578135890160c0807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0838e0301121561141d57898afd5b604080518281018181108a8211171561143257fe5b825261143f848b016114bd565b815261144c8285016114bd565b8a820152606080850135838301526080925061146983860161137b565b9082015260a08481013583830152928401359289841115611488578c8dfd5b6114968f8c8688010161150d565b90820152875250505092850192908501906001016113df565b509098975050505050505050565b803580151581146101b557600080fd5b60008083601f8401126114de578182fd5b50813567ffffffffffffffff8111156114f5578182fd5b602083019150836020828501011115610a0157600080fd5b600082601f83011261151d578081fd5b813567ffffffffffffffff81111561153157fe5b61156260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601611a60565b818152846020838601011115611576578283fd5b816020850160208301379081016020019190915292915050565b6000602082840312156115a1578081fd5b813567ffffffffffffffff8111156115b7578182fd5b6102858482850161139f565b6000806000606084860312156115d7578182fd5b833567ffffffffffffffff808211156115ee578384fd5b6115fa8783880161139f565b9450602086013593506040860135915080821115611616578283fd5b506116238682870161150d565b9150509250925092565b600080600060408486031215611641578283fd5b83359250602084013567ffffffffffffffff81111561165e578283fd5b61166a868287016114cd565b9497909650939450505050565b600060208284031215611688578081fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461022c578182fd5b600080600080604085870312156116cc578081fd5b843567ffffffffffffffff808211156116e3578283fd5b6116ef888389016114cd565b90965094506020870135915080821115611707578283fd5b50611714878288016114cd565b95989497509550505050565b600060208284031215611731578081fd5b813567ffffffffffffffff811115611747578182fd5b6102858482850161150d565b600060208284031215611764578081fd5b5035919050565b60008282518085526020808601955080818302840101818601855b8481101561182a578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00189528151805115158452848101511515858501526040808201519085015260608082015173ffffffffffffffffffffffffffffffffffffffff16908501526080808201519085015260a09081015160c09185018290529061181681860183611837565b9a86019a9450505090830190600101611786565b5090979650505050505050565b6000815180845261184f816020860160208601611a84565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60008251611893818460208701611a84565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b901515815260200190565b90815260200190565b6000838252604060208301526102856040830184611837565b7fffffffff0000000000000000000000000000000000000000000000000000000091909116815260200190565b60208082526029908201527f47756573744d6f64756c65235f6578656375746547756573743a204e4f545f4560408201527f4e4f5547485f4741530000000000000000000000000000000000000000000000606082015260800190565b600060408252600660408301527f67756573743a000000000000000000000000000000000000000000000000000060608301526080602083015261022c608083018461176b565b60208082526033908201527f47756573744d6f64756c65235f6578656375746547756573743a2064656c656760408201527f61746543616c6c206e6f7420616c6c6f77656400000000000000000000000000606082015260800190565b600060408252600560408301527f73656c663a00000000000000000000000000000000000000000000000000000060608301526080602083015261022c608083018461176b565b60405181810167ffffffffffffffff81118282101715611a7c57fe5b604052919050565b60005b83811015611a9f578181015183820152602001611a87565b838111156102f0575050600091015256fe5369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202773272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265206c656e6774684d6f64756c6541757468235f7369676e617475726556616c69646174696f6e20494e56414c49445f464c41474c696242797465732372656164416464726573733a204f55545f4f465f424f554e44534c696242797465732372656164466972737455696e7431363a204f55545f4f465f424f554e44535369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202776272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20494e56414c49445f5349474e45524d6f64756c6541757468235f7369676e617475726556616c69646174696f6e3a20494e56414c49445f5349474e41545552455369676e617475726556616c696461746f7223697356616c69645369676e61747572653a20554e535550504f525445445f5349474e41545552455f545950454c696242797465732372656164427974657336363a204f55545f4f465f424f554e44535369676e617475726556616c696461746f72237265636f7665725369676e65723a20554e535550504f525445445f5349474e41545552455f545950454c69624279746573237265616455696e743855696e74383a204f55545f4f465f424f554e44534c69624279746573237265616442797465733a204f55545f4f465f424f554e44534c69624279746573237265616455696e7431363a204f55545f4f465f424f554e44534c696242797465732372656164427974657333323a20475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f52455155495245444d6f64756c6553656c6641757468236f6e6c7953656c663a204e4f545f415554484f52495a4544a2646970667358221220f5a1de0b650baa2ee828e8766bc6dbd0c74da0cc4735a143852d24f868e4b62464736f6c6343000706003300000000', + gasLimit: 8000000 + }) + + // Deploy multiCallUtils + await signer.sendTransaction({ + to: '0x8A5Bc19e22D6aD55a2c763B93A75d09F321fe764', + data: '0x9c4ae2d0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b1660c06040523480156200001157600080fd5b5060405162002ad638038062002ad68339810160408190526200003491620000cd565b8181816001600160a01b031660a0816001600160a01b031660601b8152505060405180606001604052806028815260200162002aae60289139816001600160a01b03166040516020016200008a92919062000104565b60408051601f198184030181529190528051602090910120608052506200014692505050565b80516001600160a01b0381168114620000c857600080fd5b919050565b60008060408385031215620000e0578182fd5b620000eb83620000b0565b9150620000fb60208401620000b0565b90509250929050565b60008351815b818110156200012657602081870181015185830152016200010a565b81811115620001355782828501525b509190910191825250602001919050565b60805160a05160601c61293762000177600039806106515280610b1b5250806106755280610b3f52506129376000f3fe6080604052600436106101805760003560e01c806398f9fbc4116100d6578063d1db39071161007f578063e90f13e711610059578063e90f13e714610395578063f209883a146103ea578063ffd7d741146103ff57610180565b8063d1db390714610395578063d5b5337f146103aa578063e717aba9146103ca57610180565b8063c272d5c3116100b0578063c272d5c314610333578063c39f2d5c14610348578063c66764e11461036857610180565b806398f9fbc4146102e9578063aeea5fb5146102fe578063b472f0a21461031357610180565b806348acd29f116101385780637ae99638116101125780637ae99638146102875780637f29d538146102a7578063984395bc146102c757610180565b806348acd29f14610227578063543196eb146102475780637082503b1461026757610180565b80631cd05dc4116101695780631cd05dc4146101d057806343d9c935146101f057806344d466c21461020557610180565b80630fdecfac146101855780631551f0ab146101b0575b600080fd5b34801561019157600080fd5b5061019a610420565b6040516101a79190612190565b60405180910390f35b3480156101bc57600080fd5b5061019a6101cb366004611e76565b610424565b3480156101dc57600080fd5b5061019a6101eb366004611bea565b610436565b3480156101fc57600080fd5b5061019a610448565b34801561021157600080fd5b50610225610220366004611ca4565b610450565b005b34801561023357600080fd5b5061019a610242366004611bea565b61080a565b34801561025357600080fd5b5061019a610262366004611bea565b610828565b34801561027357600080fd5b50610225610282366004611c0b565b61082c565b34801561029357600080fd5b5061019a6102a2366004611bea565b610cb0565b3480156102b357600080fd5b506102256102c2366004611e76565b610cc2565b3480156102d357600080fd5b506102dc610cfe565b6040516101a79190612000565b3480156102f557600080fd5b506102dc610d02565b34801561030a57600080fd5b5061019a610d06565b34801561031f57600080fd5b5061022561032e366004611c7b565b610d0a565b34801561033f57600080fd5b5061019a610de8565b34801561035457600080fd5b5061019a610363366004611bea565b610dec565b34801561037457600080fd5b50610388610383366004611bea565b610df0565b6040516101a791906121c5565b3480156103a157600080fd5b5061019a610e35565b3480156103b657600080fd5b5061019a6103c5366004611e76565b610e39565b3480156103d657600080fd5b5061019a6103e5366004611bea565b610e3d565b3480156103f657600080fd5b5061019a610e4f565b61041261040d366004611d34565b610e53565b6040516101a7929190612021565b4690565b60036020526000908152604090205481565b60006020819052908152604090205481565b60005a905090565b8360005b838110156104e9578185858381811061046957fe5b9050604002016000013586868481811061047f57fe5b90506040020160200160208101906104979190611bea565b6040516020016104a993929190612199565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291905280516020909101209150600101610454565b506000808773ffffffffffffffffffffffffffffffffffffffff166351605d8060e01b60405160200161051c9190611f54565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529082905261055491611f81565b6000604051808303816000865af19150503d8060008114610591576040519150601f19603f3d011682016040523d82523d6000602084013e610596565b606091505b50915091508180156105a9575080516020145b1561060e576000818060200190518101906105c49190611e8e565b9050838114610608576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff90612543565b60405180910390fd5b50610732565b60405173ffffffffffffffffffffffffffffffffffffffff89169061069d907fff00000000000000000000000000000000000000000000000000000000000000907f00000000000000000000000000000000000000000000000000000000000000009087907f000000000000000000000000000000000000000000000000000000000000000090602001611ef0565b6040516020818303038152906040528051906020012060001c73ffffffffffffffffffffffffffffffffffffffff1614610703576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff906125a0565b83156107325773ffffffffffffffffffffffffffffffffffffffff881660009081526002602052604090208390555b828873ffffffffffffffffffffffffffffffffffffffff167fb502b7446ca079086188acf3abef47c2f464f2ee9a72fcdf05ffcb74dcc17cee89898960405160200161077f9291906120c7565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290526107b89291612623565b60405180910390a383156108005773ffffffffffffffffffffffffffffffffffffffff8816600090815260016020908152604080832043908190558684526003909252909120555b5050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8116315b919050565b3f90565b600080610838846110c3565b9150915060008046905080898960405160200161085793929190611f9d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012091505061ffff831660008767ffffffffffffffff811180156108ae57600080fd5b506040519080825280602002602001820160405280156108e857816020015b6108d5611b1c565b8152602001906001900390816108cd5790505b50905060005b8751851015610a9f57600080806109058b89611131565b995060ff9182169450169150600183141561092d576109248b896111b2565b98509050610a20565b8261095f57606061093e8c8a61122a565b9950905061094c88826112db565b91506109598f838d611665565b50610a20565b60028314156109ee576109728b896111b2565b9850905060006109828c8a6116f3565b995061ffff16905060606109978d8b84611764565b9a5090506109a6898483611853565b6109dc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff9061242c565b50506109e98e828c611665565b610a20565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff906121d8565b60405180604001604052808381526020018273ffffffffffffffffffffffffffffffffffffffff16815250858581518110610a5757fe5b60200260200101819052508380600101945050858282604051602001610a7f93929190612199565b6040516020818303038152906040528051906020012095505050506108ee565b888114610ad8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff906124e6565b60405173ffffffffffffffffffffffffffffffffffffffff8c1690610b67907fff00000000000000000000000000000000000000000000000000000000000000907f00000000000000000000000000000000000000000000000000000000000000009087907f000000000000000000000000000000000000000000000000000000000000000090602001611ef0565b6040516020818303038152906040528051906020012060001c73ffffffffffffffffffffffffffffffffffffffff1614610bcd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff906123a9565b828b73ffffffffffffffffffffffffffffffffffffffff167fb502b7446ca079086188acf3abef47c2f464f2ee9a72fcdf05ffcb74dcc17cee8885604051602001610c18919061212b565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815290829052610c5192916125fe565b60405180910390a38615610ca35773ffffffffffffffffffffffffffffffffffffffff8b1660008181526001602090815260408083204390819055878452600383528184205592825260029052208390555b5050505050505050505050565b60026020526000908152604090205481565b804210610cfb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff9061234c565b50565b3290565b4190565b4490565b600080610d1683611a9b565b9150915060008473ffffffffffffffffffffffffffffffffffffffff16638c3f5563846040518263ffffffff1660e01b8152600401610d559190612190565b60206040518083038186803b158015610d6d57600080fd5b505afa158015610d81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610da59190611e8e565b905081811015610de1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff906122ef565b5050505050565b3a90565b3b90565b60408051603f833b9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092528181529080600060208401853c50919050565b4590565b4090565b60016020526000908152604090205481565b4290565b606080825167ffffffffffffffff81118015610e6e57600080fd5b50604051908082528060200260200182016040528015610e98578160200160208202803683370190505b509150825167ffffffffffffffff81118015610eb357600080fd5b50604051908082528060200260200182016040528015610ee757816020015b6060815260200190600190039081610ed25790505b50905060005b83518110156110bd576000848281518110610f0457fe5b60200260200101519050806000015115610f4a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff90612489565b80604001515a1015610f88576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff90612292565b806060015173ffffffffffffffffffffffffffffffffffffffff1681608001518260400151600014610fbe578260400151610fc0565b5a5b908360a00151604051610fd39190611f81565b600060405180830381858888f193505050503d8060008114611011576040519150601f19603f3d011682016040523d82523d6000602084013e611016565b606091505b5085848151811061102357fe5b6020026020010185858151811061103657fe5b602002602001018290528215151515815250505083828151811061105657fe5b60200260200101518061107e575084828151811061107057fe5b602002602001015160200151155b6110b4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105ff90612235565b50600101610eed565b50915091565b6020810151815160f09190911c9060029081111561112c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061272b6027913960400191505060405180910390fd5b915091565b8082016020015160f881901c9060f01c60ff166002830183811161115157fe5b84518111156111ab576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602681526020018061285d6026913960400191505060405180910390fd5b9250925092565b8082016020015160601c601482018281116111c957fe5b8351811115611223576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806127086023913960400191505060405180910390fd5b9250929050565b60408051604280825260808201909252606091600091906020820181803683370190505091508284016020018051602084015260208101516040840152602281015160428401525060428301905082811161128157fe5b8351811115611223576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806127fe6023913960400191505060405180910390fd5b60008151604214611337576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a8152602001806126ce603a913960400191505060405180910390fd5b60008260018451038151811061134957fe5b602001015160f81c60f81b60f81c60ff16905060008360408151811061136b57fe5b016020015160f81c905060006113818582611ab4565b90506000611390866020611ab4565b90507f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a081111561140b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180612691603d913960400191505060405180910390fd5b8260ff16601b1415801561142357508260ff16601c14155b15611479576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603d815260200180612752603d913960400191505060405180910390fd5b60018414156114ed5760018784848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156114dc573d6000803e3d6000fd5b5050506020604051035194506115ef565b600284141561159e5760018760405160200180807f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250601c018281526020019150506040516020818303038152906040528051906020012084848460405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156114dc573d6000803e3d6000fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180612821603c913960400191505060405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff851661165b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603081526020018061278f6030913960400191505060405180910390fd5b5050505092915050565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f600ba597427f042bcd559a0d06fa1732cc104d6dd43cbe8845b5a0e804b2b39f60405160405180910390a380156116ee5773ffffffffffffffffffffffffffffffffffffffff821660009081526020819052604090204390555b505050565b8082016020015160f01c6002820182811161170a57fe5b8351811115611223576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806128a46022913960400191505060405180910390fd5b606060008267ffffffffffffffff8111801561177f57600080fd5b506040519080825280601f01601f1916602001820160405280156117aa576020820181803683370190505b509150838501602001600060205b858110156117d1579082015184820152602081016117b8565b84860160200180519390920151908501525250828201838110156117f157fe5b845181111561184b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806128836021913960400191505060405180910390fd5b935093915050565b6000808260018451038151811061186657fe5b016020015160f81c9050600181148061187f5750600281145b156118c3578373ffffffffffffffffffffffffffffffffffffffff166118a586856112db565b73ffffffffffffffffffffffffffffffffffffffff16149150611a93565b6003811415611a425782517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018452604080517f1626ba7e000000000000000000000000000000000000000000000000000000008152600481018881526024820192835286516044830152865173ffffffffffffffffffffffffffffffffffffffff891693631626ba7e938b938a9390929160640190602085019080838360005b8381101561197d578181015183820152602001611965565b50505050905090810190601f1680156119aa5780820380516001836020036101000a031916815260200191505b50935050505060206040518083038186803b1580156119c857600080fd5b505afa1580156119dc573d6000803e3d6000fd5b505050506040513d60208110156119f257600080fd5b50519084527fffffffff00000000000000000000000000000000000000000000000000000000167f1626ba7e00000000000000000000000000000000000000000000000000000000149150611a93565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f8152602001806127bf603f913960400191505060405180910390fd5b509392505050565b606081901c916bffffffffffffffffffffffff90911690565b60008160200183511015611b13576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c8152602001806128c6603c913960400191505060405180910390fd5b50016020015190565b604080518082019091526000808252602082015290565b803573ffffffffffffffffffffffffffffffffffffffff8116811461082357600080fd5b8035801515811461082357600080fd5b600082601f830112611b77578081fd5b813567ffffffffffffffff811115611b8b57fe5b611bbc60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161263c565b818152846020838601011115611bd0578283fd5b816020850160208301379081016020019190915292915050565b600060208284031215611bfb578081fd5b611c0482611b33565b9392505050565b600080600080600060a08688031215611c22578081fd5b611c2b86611b33565b94506020860135935060408601359250606086013567ffffffffffffffff811115611c54578182fd5b611c6088828901611b67565b925050611c6f60808701611b57565b90509295509295909350565b60008060408385031215611c8d578182fd5b611c9683611b33565b946020939093013593505050565b600080600080600060808688031215611cbb578081fd5b611cc486611b33565b945060208601359350604086013567ffffffffffffffff80821115611ce7578283fd5b818801915088601f830112611cfa578283fd5b813581811115611d08578384fd5b896020604083028501011115611d1c578384fd5b602083019550809450505050611c6f60608701611b57565b60006020808385031215611d46578182fd5b823567ffffffffffffffff80821115611d5d578384fd5b818501915085601f830112611d70578384fd5b813581811115611d7c57fe5b611d89848583020161263c565b81815284810190848601875b84811015611e67578135870160c0807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0838f03011215611dd3578a8bfd5b604080518281018181108b82111715611de857fe5b8252611df5848d01611b57565b8152611e02828501611b57565b8c82015260608085013583830152611e1c60808601611b33565b9082015260a08481013560808301529284013592915089831115611e3e578c8dfd5b611e4c8f8d85870101611b67565b91810191909152865250509287019290870190600101611d95565b50909998505050505050505050565b600060208284031215611e87578081fd5b5035919050565b600060208284031215611e9f578081fd5b5051919050565b60008151808452611ebe816020860160208601612660565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b7fff0000000000000000000000000000000000000000000000000000000000000094909416845260609290921b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660018401526015830152603582015260550190565b7fffffffff0000000000000000000000000000000000000000000000000000000091909116815260040190565b60008251611f93818460208701612660565b9190910192915050565b7f19010000000000000000000000000000000000000000000000000000000000008152600281019390935260609190911b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166022830152603682015260560190565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b604080825283519082018190526000906020906060840190828701845b8281101561205c57815115158452928401929084019060010161203e565b5050508381038285015284518082528282019080840283018401878501865b83811015611e67577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08684030185526120b5838351611ea6565b9487019492509086019060010161207b565b6020808252818101839052600090604080840186845b8781101561211e578135835273ffffffffffffffffffffffffffffffffffffffff612109868401611b33565b168386015291830191908301906001016120dd565b5090979650505050505050565b602080825282518282018190526000919060409081850190868401855b828110156121835781518051855286015173ffffffffffffffffffffffffffffffffffffffff16868501529284019290850190600101612148565b5091979650505050505050565b90815260200190565b928352602083019190915273ffffffffffffffffffffffffffffffffffffffff16604082015260600190565b600060208252611c046020830184611ea6565b6020808252603a908201527f526571756972655574696c73237075626c697368496e697469616c5369676e6560408201527f72733a20494e56414c49445f5349474e41545552455f464c4147000000000000606082015260800190565b60208082526027908201527f4d756c746943616c6c5574696c73236d756c746943616c6c3a2043414c4c5f5260408201527f4556455254454400000000000000000000000000000000000000000000000000606082015260800190565b60208082526028908201527f4d756c746943616c6c5574696c73236d756c746943616c6c3a204e4f545f454e60408201527f4f5547485f474153000000000000000000000000000000000000000000000000606082015260800190565b60208082526032908201527f526571756972655574696c7323726571756972654d696e4e6f6e63653a204e4f60408201527f4e43455f42454c4f575f52455155495245440000000000000000000000000000606082015260800190565b60208082526027908201527f526571756972655574696c7323726571756972654e6f6e457870697265643a2060408201527f4558504952454400000000000000000000000000000000000000000000000000606082015260800190565b60208082526048908201527f526571756972655574696c73237075626c697368496e697469616c5369676e6560408201527f72733a20554e45585045435445445f434f554e5445524641435455414c5f494d60608201527f4147455f48415348000000000000000000000000000000000000000000000000608082015260a00190565b60208082526032908201527f4d6f64756c6541757468235f7369676e617475726556616c69646174696f6e3a60408201527f20494e56414c49445f5349474e41545552450000000000000000000000000000606082015260800190565b60208082526032908201527f4d756c746943616c6c5574696c73236d756c746943616c6c3a2064656c65676160408201527f746543616c6c206e6f7420616c6c6f7765640000000000000000000000000000606082015260800190565b60208082526039908201527f526571756972655574696c73237075626c697368496e697469616c5369676e6560408201527f72733a20494e56414c49445f4d454d424552535f434f554e5400000000000000606082015260800190565b60208082526031908201527f526571756972655574696c73237075626c697368436f6e6669673a20554e455860408201527f5045435445445f494d4147455f48415348000000000000000000000000000000606082015260800190565b602080825260409082018190527f526571756972655574696c73237075626c697368436f6e6669673a20554e4558908201527f5045435445445f434f554e5445524641435455414c5f494d4147455f48415348606082015260800190565b600061ffff841682526040602083015261261b6040830184611ea6565b949350505050565b60008382526040602083015261261b6040830184611ea6565b60405181810167ffffffffffffffff8111828210171561265857fe5b604052919050565b60005b8381101561267b578181015183820152602001612663565b8381111561268a576000848401525b5050505056fe5369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202773272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265206c656e6774684c696242797465732372656164416464726573733a204f55545f4f465f424f554e44534c696242797465732372656164466972737455696e7431363a204f55545f4f465f424f554e44535369676e617475726556616c696461746f72237265636f7665725369676e65723a20696e76616c6964207369676e6174757265202776272076616c75655369676e617475726556616c696461746f72237265636f7665725369676e65723a20494e56414c49445f5349474e45525369676e617475726556616c696461746f7223697356616c69645369676e61747572653a20554e535550504f525445445f5349474e41545552455f545950454c696242797465732372656164427974657336363a204f55545f4f465f424f554e44535369676e617475726556616c696461746f72237265636f7665725369676e65723a20554e535550504f525445445f5349474e41545552455f545950454c69624279746573237265616455696e743855696e74383a204f55545f4f465f424f554e44534c69624279746573237265616442797465733a204f55545f4f465f424f554e44534c69624279746573237265616455696e7431363a204f55545f4f465f424f554e44534c696242797465732372656164427974657333323a20475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f5245515549524544a26469706673582212200abb842b6eea58df953f048e3a9aa7589fd3ce15ca086e43b61cdb0c0c42723564736f6c63430007060033603a600e3d39601a805130553df3363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3000000000000000000000000f9d09d634fb818b05149329c1dccfaea53639d96000000000000000000000000d01f11855bccb95f88d7a48492f66410d463731300000000000000000000', + gasLimit: 8000000 + }) + + return deployV1Context(signer) +} diff --git a/packages/tests/src/context/v2.ts b/packages/tests/src/context/v2.ts new file mode 100644 index 000000000..242d94ab4 --- /dev/null +++ b/packages/tests/src/context/v2.ts @@ -0,0 +1,24 @@ +import { ethers } from "ethers" +import { v2 } from '../builds' +import { deployContract } from "../singletonFactory" + +export async function deployV2Context(signer: ethers.Signer) { + // See if signer's provider has the contracts already deployed + const factory = await deployContract(signer, v2.factory) + const mainModuleUpgradable = await deployContract(signer, v2.mainModuleUpgradable) + const mainModule = await deployContract(signer, v2.mainModule, factory.address, mainModuleUpgradable.address) + const guestModule = await deployContract(signer, v2.guestModule) + const universalSigValidator = await deployContract(signer, v2.universalSigValidator) + + return { + version: 2, + + factory: factory.address, + mainModule: mainModule.address, + mainModuleUpgradable: mainModuleUpgradable.address, + guestModule: guestModule.address, + universalSigValidator: universalSigValidator.address, + + walletCreationCode: '0x603a600e3d39601a805130553df3363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3' + } +} diff --git a/packages/tests/src/index.ts b/packages/tests/src/index.ts new file mode 100644 index 000000000..a8edc68c5 --- /dev/null +++ b/packages/tests/src/index.ts @@ -0,0 +1,8 @@ + +export * as context from './context' +export * as builds from './builds' + +export * as utils from './utils' + +export * as configs from './configs' +export * as singleton from './singletonFactory' diff --git a/packages/tests/src/singletonFactory.ts b/packages/tests/src/singletonFactory.ts new file mode 100644 index 000000000..5d5ab1e24 --- /dev/null +++ b/packages/tests/src/singletonFactory.ts @@ -0,0 +1,85 @@ +import { ethers } from "ethers" +import { Artifact } from "./builds" +import { isContract } from "./utils" + +export const deployment = { + tx: '0xf9016c8085174876e8008303c4d88080b90154608060405234801561001057600080fd5b50610134806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80634af63f0214602d575b600080fd5b60cf60048036036040811015604157600080fd5b810190602081018135640100000000811115605b57600080fd5b820183602082011115606c57600080fd5b80359060200191846001830284011164010000000083111715608d57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550509135925060eb915050565b604080516001600160a01b039092168252519081900360200190f35b6000818351602085016000f5939250505056fea26469706673582212206b44f8a82cb6b156bfcc3dc6aadd6df4eefd204bc928a4397fd15dacf6d5320564736f6c634300060200331b83247000822470', + deployer: '0xBb6e024b9cFFACB947A71991E386681B1Cd1477D', + funding: '24700000000000000' +} + +export const address = '0xce0042B868300000d44A59004Da54A005ffdcf9f' + +export const abi = [{ + "constant": false, + "inputs": [{ + "internalType": "bytes", + "type": "bytes" + }, { + "internalType": "bytes32", + "type": "bytes32" + }], + "name": "deploy", + "outputs": [{ + "internalType": "address payable", + "type": "address" + }], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" +}] + +export async function mustExistEIP2470(signer: ethers.Signer): Promise { + const provider = signer.provider + if (!provider) throw new Error('signer has no provider') + + if (!await isContract(provider, address)) { + const balanceDeployer = await provider.getBalance(deployment.deployer) + if (balanceDeployer.lt(deployment.funding)) { + await signer.sendTransaction({ + to: deployment.deployer, + value: ethers.BigNumber.from(deployment.funding).sub(balanceDeployer) + }) + } + + await provider.sendTransaction(deployment.tx) + if (!await isContract(provider, address)) { + throw new Error('EIP2470 deployment failed') + } + } + + return new ethers.Contract(address, abi, signer) +} + +export async function deployContract(signer: ethers.Signer, artifact: Artifact, ...args: any[]): Promise { + const provider = signer.provider + if (!provider) throw new Error('signer has no provider') + + const singletonFactory = await mustExistEIP2470(signer) + + const factory = new ethers.ContractFactory(artifact.abi, artifact.bytecode) + const data = factory.getDeployTransaction(...args).data + if (!data) throw new Error('no deploy data') + + const address = ethers.utils.getAddress(ethers.utils.hexDataSlice( + ethers.utils.keccak256( + ethers.utils.solidityPack( + ['bytes1', 'address', 'bytes32', 'bytes32'], + ['0xff', singletonFactory.address, ethers.constants.HashZero, ethers.utils.keccak256(data)] + ) + ) + , 12)) + + if (await isContract(provider, address)) { + return new ethers.Contract(address, artifact.abi, signer) + } + + const maxGasLimit = await provider.getBlock('latest').then((b) => b.gasLimit.div(2)) + await singletonFactory.deploy(data, ethers.constants.HashZero, { gasLimit: maxGasLimit }).then((tx: any) => tx.wait()) + + if (!await isContract(provider, address)) { + throw new Error('contract deployment failed') + } + + return new ethers.Contract(address, artifact.abi, signer) +} diff --git a/packages/tests/src/utils.ts b/packages/tests/src/utils.ts new file mode 100644 index 000000000..dd1a44d92 --- /dev/null +++ b/packages/tests/src/utils.ts @@ -0,0 +1,37 @@ +import { ethers } from "ethers" +import { Artifact } from "./builds" + +export function deployContract(signer: ethers.Signer, artifact: Artifact, ...args: any[]): Promise { + const factory = new ethers.ContractFactory(artifact.abi, artifact.bytecode, signer) + return factory.deploy(...args) +} + +export function randomBigNumber( + min: ethers.BigNumberish = 0, + max: ethers.BigNumberish = ethers.constants.MaxUint256 +): ethers.BigNumber { + const randomHex = ethers.utils.hexlify(ethers.utils.randomBytes(32)) + const randomBn = ethers.BigNumber.from(randomHex) + const minBn = ethers.BigNumber.from(min) + const maxBn = ethers.BigNumber.from(max) + const range = maxBn.sub(minBn) + + if (range.isNegative() || range.isZero()) { + throw new Error('max must be greater than min') + } + + return randomBn.mod(range).add(minBn) +} + +export function maxForBits(bits: number): ethers.BigNumber { + return ethers.BigNumber.from(2).pow(bits).sub(1) +} + +export function randomBool(): boolean { + return Math.random() >= 0.5 +} + +export async function isContract(provider: ethers.providers.Provider, address: string): Promise { + const c = await provider.getCode(address) + return ethers.utils.arrayify(c).length > 0 +} diff --git a/packages/transactions/CHANGELOG.md b/packages/transactions/CHANGELOG.md deleted file mode 100644 index e09a9a4d7..000000000 --- a/packages/transactions/CHANGELOG.md +++ /dev/null @@ -1,1754 +0,0 @@ -# @0xsequence/transactions - -## 0.43.34 - -### Patch Changes - -- auth: no jwt for indexer -- Updated dependencies - - @0xsequence/abi@0.43.34 - - @0xsequence/config@0.43.34 - - @0xsequence/network@0.43.34 - - @0xsequence/utils@0.43.34 - -## 0.43.33 - -### Patch Changes - -- Adding onConnectOptionsChange handler to WalletRequestHandler -- Updated dependencies - - @0xsequence/abi@0.43.33 - - @0xsequence/config@0.43.33 - - @0xsequence/network@0.43.33 - - @0xsequence/utils@0.43.33 - -## 0.43.32 - -### Patch Changes - -- add Base Goerli network -- Updated dependencies - - @0xsequence/abi@0.43.32 - - @0xsequence/config@0.43.32 - - @0xsequence/network@0.43.32 - - @0xsequence/utils@0.43.32 - -## 0.43.31 - -### Patch Changes - -- remove AuxDataProvider, add promptSignInConnect -- Updated dependencies - - @0xsequence/abi@0.43.31 - - @0xsequence/config@0.43.31 - - @0xsequence/network@0.43.31 - - @0xsequence/utils@0.43.31 - -## 0.43.30 - -### Patch Changes - -- add arbitrum goerli testnet -- Updated dependencies - - @0xsequence/abi@0.43.30 - - @0xsequence/config@0.43.30 - - @0xsequence/network@0.43.30 - - @0xsequence/utils@0.43.30 - -## 0.43.29 - -### Patch Changes - -- provider: check availability of window object -- Updated dependencies - - @0xsequence/abi@0.43.29 - - @0xsequence/config@0.43.29 - - @0xsequence/network@0.43.29 - - @0xsequence/utils@0.43.29 - -## 0.43.28 - -### Patch Changes - -- update api bindings -- Updated dependencies - - @0xsequence/abi@0.43.28 - - @0xsequence/config@0.43.28 - - @0xsequence/network@0.43.28 - - @0xsequence/utils@0.43.28 - -## 0.43.27 - -### Patch Changes - -- Add rpc is sequence method -- Updated dependencies - - @0xsequence/abi@0.43.27 - - @0xsequence/config@0.43.27 - - @0xsequence/network@0.43.27 - - @0xsequence/utils@0.43.27 - -## 0.43.26 - -### Patch Changes - -- add zkevm url to enum -- Updated dependencies - - @0xsequence/abi@0.43.26 - - @0xsequence/config@0.43.26 - - @0xsequence/network@0.43.26 - - @0xsequence/utils@0.43.26 - -## 0.43.25 - -### Patch Changes - -- added polygon zkevm to mainnet networks -- Updated dependencies - - @0xsequence/abi@0.43.25 - - @0xsequence/config@0.43.25 - - @0xsequence/network@0.43.25 - - @0xsequence/utils@0.43.25 - -## 0.43.24 - -### Patch Changes - -- name change from zkevm to polygon-zkevm -- Updated dependencies - - @0xsequence/abi@0.43.24 - - @0xsequence/config@0.43.24 - - @0xsequence/network@0.43.24 - - @0xsequence/utils@0.43.24 - -## 0.43.23 - -### Patch Changes - -- update zkEVM name to Polygon zkEVM -- Updated dependencies - - @0xsequence/abi@0.43.23 - - @0xsequence/config@0.43.23 - - @0xsequence/network@0.43.23 - - @0xsequence/utils@0.43.23 - -## 0.43.22 - -### Patch Changes - -- add zkevm chain -- Updated dependencies - - @0xsequence/abi@0.43.22 - - @0xsequence/config@0.43.22 - - @0xsequence/network@0.43.22 - - @0xsequence/utils@0.43.22 - -## 0.43.21 - -### Patch Changes - -- api: update client bindings -- Updated dependencies - - @0xsequence/abi@0.43.21 - - @0xsequence/config@0.43.21 - - @0xsequence/network@0.43.21 - - @0xsequence/utils@0.43.21 - -## 0.43.20 - -### Patch Changes - -- indexer: update bindings -- Updated dependencies - - @0xsequence/abi@0.43.20 - - @0xsequence/config@0.43.20 - - @0xsequence/network@0.43.20 - - @0xsequence/utils@0.43.20 - -## 0.43.19 - -### Patch Changes - -- session proof update -- Updated dependencies - - @0xsequence/abi@0.43.19 - - @0xsequence/config@0.43.19 - - @0xsequence/network@0.43.19 - - @0xsequence/utils@0.43.19 - -## 0.43.18 - -### Patch Changes - -- rpc client global check, hardening -- Updated dependencies - - @0xsequence/abi@0.43.18 - - @0xsequence/config@0.43.18 - - @0xsequence/network@0.43.18 - - @0xsequence/utils@0.43.18 - -## 0.43.17 - -### Patch Changes - -- rpc clients, check of 'global' is defined -- Updated dependencies - - @0xsequence/abi@0.43.17 - - @0xsequence/config@0.43.17 - - @0xsequence/network@0.43.17 - - @0xsequence/utils@0.43.17 - -## 0.43.16 - -### Patch Changes - -- ethers peerDep to v5, update rpc client global use -- Updated dependencies - - @0xsequence/abi@0.43.16 - - @0xsequence/config@0.43.16 - - @0xsequence/network@0.43.16 - - @0xsequence/utils@0.43.16 - -## 0.43.15 - -### Patch Changes - -- - provider: expand receiver type on some util methods -- Updated dependencies - - @0xsequence/abi@0.43.15 - - @0xsequence/config@0.43.15 - - @0xsequence/network@0.43.15 - - @0xsequence/utils@0.43.15 - -## 0.43.14 - -### Patch Changes - -- bump -- Updated dependencies - - @0xsequence/abi@0.43.14 - - @0xsequence/config@0.43.14 - - @0xsequence/network@0.43.14 - - @0xsequence/utils@0.43.14 - -## 0.43.13 - -### Patch Changes - -- update rpc bindings -- Updated dependencies - - @0xsequence/abi@0.43.13 - - @0xsequence/config@0.43.13 - - @0xsequence/network@0.43.13 - - @0xsequence/utils@0.43.13 - -## 0.43.12 - -### Patch Changes - -- provider: single wallet init, and add new unregisterWallet() method -- Updated dependencies - - @0xsequence/abi@0.43.12 - - @0xsequence/config@0.43.12 - - @0xsequence/network@0.43.12 - - @0xsequence/utils@0.43.12 - -## 0.43.11 - -### Patch Changes - -- fix lockfiles -- re-add mocha type deleter -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.43.11 - - @0xsequence/config@0.43.11 - - @0xsequence/network@0.43.11 - - @0xsequence/utils@0.43.11 - -## 0.43.10 - -### Patch Changes - -- various improvements -- Updated dependencies - - @0xsequence/abi@0.43.10 - - @0xsequence/config@0.43.10 - - @0xsequence/network@0.43.10 - - @0xsequence/utils@0.43.10 - -## 0.43.9 - -### Patch Changes - -- update deps -- Updated dependencies - - @0xsequence/abi@0.43.9 - - @0xsequence/config@0.43.9 - - @0xsequence/network@0.43.9 - - @0xsequence/utils@0.43.9 - -## 0.43.8 - -### Patch Changes - -- network: JsonRpcProvider with caching -- Updated dependencies - - @0xsequence/abi@0.43.8 - - @0xsequence/config@0.43.8 - - @0xsequence/network@0.43.8 - - @0xsequence/utils@0.43.8 - -## 0.43.7 - -### Patch Changes - -- provider: fix wallet network init -- Updated dependencies - - @0xsequence/abi@0.43.7 - - @0xsequence/network@0.43.7 - - @0xsequence/utils@0.43.7 - -## 0.43.6 - -### Patch Changes - -- metadatata: update rpc bindings -- Updated dependencies - - @0xsequence/abi@0.43.6 - - @0xsequence/network@0.43.6 - - @0xsequence/utils@0.43.6 - -## 0.43.5 - -### Patch Changes - -- provider: do not set default network for connect messages -- provider: forward missing error message -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.43.5 - - @0xsequence/network@0.43.5 - - @0xsequence/utils@0.43.5 - -## 0.43.4 - -### Patch Changes - -- no-change version bump to fix incorrectly tagged snapshot build -- Updated dependencies - - @0xsequence/abi@0.43.4 - - @0xsequence/network@0.43.4 - - @0xsequence/utils@0.43.4 - -## 0.43.3 - -### Patch Changes - -- metadata: update bindings -- Updated dependencies - - @0xsequence/abi@0.43.3 - - @0xsequence/network@0.43.3 - - @0xsequence/utils@0.43.3 - -## 0.43.2 - -### Patch Changes - -- provider: implement connectUnchecked -- Updated dependencies - - @0xsequence/abi@0.43.2 - - @0xsequence/network@0.43.2 - - @0xsequence/utils@0.43.2 - -## 0.43.1 - -### Patch Changes - -- update to latest ethauth dep -- Updated dependencies - - @0xsequence/abi@0.43.1 - - @0xsequence/network@0.43.1 - - @0xsequence/utils@0.43.1 - -## 0.43.0 - -### Minor Changes - -- move ethers to a peer dependency - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.43.0 - - @0xsequence/network@0.43.0 - - @0xsequence/utils@0.43.0 - -## 0.42.10 - -### Patch Changes - -- add auxDataProvider -- Updated dependencies - - @0xsequence/abi@0.42.10 - - @0xsequence/network@0.42.10 - - @0xsequence/utils@0.42.10 - -## 0.42.9 - -### Patch Changes - -- provider: add eip-191 exceptions -- Updated dependencies - - @0xsequence/abi@0.42.9 - - @0xsequence/network@0.42.9 - - @0xsequence/utils@0.42.9 - -## 0.42.8 - -### Patch Changes - -- provider: skip setting intent origin if we're unity plugin -- Updated dependencies - - @0xsequence/abi@0.42.8 - - @0xsequence/network@0.42.8 - - @0xsequence/utils@0.42.8 - -## 0.42.7 - -### Patch Changes - -- Add sign in options to connection settings -- Updated dependencies - - @0xsequence/abi@0.42.7 - - @0xsequence/network@0.42.7 - - @0xsequence/utils@0.42.7 - -## 0.42.6 - -### Patch Changes - -- api bindings update -- Updated dependencies - - @0xsequence/abi@0.42.6 - - @0xsequence/network@0.42.6 - - @0xsequence/utils@0.42.6 - -## 0.42.5 - -### Patch Changes - -- relayer: don't treat missing receipt as hard failure -- Updated dependencies - - @0xsequence/abi@0.42.5 - - @0xsequence/network@0.42.5 - - @0xsequence/utils@0.42.5 - -## 0.42.4 - -### Patch Changes - -- provider: add custom app protocol to connect options -- Updated dependencies - - @0xsequence/abi@0.42.4 - - @0xsequence/network@0.42.4 - - @0xsequence/utils@0.42.4 - -## 0.42.3 - -### Patch Changes - -- update api bindings -- Updated dependencies - - @0xsequence/abi@0.42.3 - - @0xsequence/network@0.42.3 - - @0xsequence/utils@0.42.3 - -## 0.42.2 - -### Patch Changes - -- disable rinkeby network -- Updated dependencies - - @0xsequence/abi@0.42.2 - - @0xsequence/network@0.42.2 - - @0xsequence/utils@0.42.2 - -## 0.42.1 - -### Patch Changes - -- wallet: optional waitForReceipt parameter -- Updated dependencies - - @0xsequence/abi@0.42.1 - - @0xsequence/network@0.42.1 - - @0xsequence/utils@0.42.1 - -## 0.42.0 - -### Minor Changes - -- relayer: estimateGasLimits -> simulate -- add simulator package - -### Patch Changes - -- transactions: fix flattenAuxTransactions -- provider: only filter nullish values -- provider: re-map transaction 'gas' back to 'gasLimit' -- Updated dependencies -- Updated dependencies -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.42.0 - - @0xsequence/network@0.42.0 - - @0xsequence/utils@0.42.0 - -## 0.41.3 - -### Patch Changes - -- api bindings update -- Updated dependencies - - @0xsequence/abi@0.41.3 - - @0xsequence/network@0.41.3 - - @0xsequence/utils@0.41.3 - -## 0.41.2 - -### Patch Changes - -- api bindings update -- Updated dependencies - - @0xsequence/abi@0.41.2 - - @0xsequence/network@0.41.2 - - @0xsequence/utils@0.41.2 - -## 0.41.1 - -### Patch Changes - -- update default networks -- Updated dependencies - - @0xsequence/abi@0.41.1 - - @0xsequence/network@0.41.1 - - @0xsequence/utils@0.41.1 - -## 0.41.0 - -### Minor Changes - -- relayer: fix Relayer.wait() interface - - The interface for calling Relayer.wait() has changed. Instead of a single optional ill-defined timeout/delay parameter, there are three optional parameters, in order: - - - timeout: the maximum time to wait for the transaction receipt - - delay: the polling interval, i.e. the time to wait between requests - - maxFails: the maximum number of hard failures to tolerate before giving up - - Please update your codebase accordingly. - -- relayer: add optional waitForReceipt parameter to Relayer.relay - - The behaviour of Relayer.relay() was not well-defined with respect to whether or not it waited for a receipt. - This change allows the caller to specify whether to wait or not, with the default behaviour being to wait. - -### Patch Changes - -- relayer: wait receipt retry logic -- fix wrapped object error -- provider: forward delegateCall and revertOnError transaction fields -- Updated dependencies -- Updated dependencies -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.41.0 - - @0xsequence/network@0.41.0 - - @0xsequence/utils@0.41.0 - -## 0.40.6 - -### Patch Changes - -- add arbitrum-nova chain -- Updated dependencies - - @0xsequence/abi@0.40.6 - - @0xsequence/network@0.40.6 - - @0xsequence/utils@0.40.6 - -## 0.40.5 - -### Patch Changes - -- api: update bindings -- Updated dependencies - - @0xsequence/abi@0.40.5 - - @0xsequence/network@0.40.5 - - @0xsequence/utils@0.40.5 - -## 0.40.4 - -### Patch Changes - -- add unreal transport -- Updated dependencies - - @0xsequence/abi@0.40.4 - - @0xsequence/network@0.40.4 - - @0xsequence/utils@0.40.4 - -## 0.40.3 - -### Patch Changes - -- provider: fix MessageToSign message type -- Updated dependencies - - @0xsequence/abi@0.40.3 - - @0xsequence/network@0.40.3 - - @0xsequence/utils@0.40.3 - -## 0.40.2 - -### Patch Changes - -- Wallet provider, loadSession method -- Updated dependencies - - @0xsequence/abi@0.40.2 - - @0xsequence/network@0.40.2 - - @0xsequence/utils@0.40.2 - -## 0.40.1 - -### Patch Changes - -- export sequence.initWallet and sequence.getWallet -- Updated dependencies - - @0xsequence/abi@0.40.1 - - @0xsequence/network@0.40.1 - - @0xsequence/utils@0.40.1 - -## 0.40.0 - -### Minor Changes - -- add sequence.initWallet(network, config) and sequence.getWallet() helper methods - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.40.0 - - @0xsequence/network@0.40.0 - - @0xsequence/utils@0.40.0 - -## 0.39.6 - -### Patch Changes - -- indexer: update client bindings -- Updated dependencies - - @0xsequence/abi@0.39.6 - - @0xsequence/network@0.39.6 - - @0xsequence/utils@0.39.6 - -## 0.39.5 - -### Patch Changes - -- provider: fix networkRpcUrl config option -- Updated dependencies - - @0xsequence/abi@0.39.5 - - @0xsequence/network@0.39.5 - - @0xsequence/utils@0.39.5 - -## 0.39.4 - -### Patch Changes - -- api: update client bindings -- Updated dependencies - - @0xsequence/abi@0.39.4 - - @0xsequence/network@0.39.4 - - @0xsequence/utils@0.39.4 - -## 0.39.3 - -### Patch Changes - -- add request method on Web3Provider -- Updated dependencies - - @0xsequence/abi@0.39.3 - - @0xsequence/network@0.39.3 - - @0xsequence/utils@0.39.3 - -## 0.39.2 - -### Patch Changes - -- update umd name -- Updated dependencies - - @0xsequence/abi@0.39.2 - - @0xsequence/network@0.39.2 - - @0xsequence/utils@0.39.2 - -## 0.39.1 - -### Patch Changes - -- add Aurora network -- add origin info for accountsChanged event to handle it per dapp -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.39.1 - - @0xsequence/network@0.39.1 - - @0xsequence/utils@0.39.1 - -## 0.39.0 - -### Minor Changes - -- abstract window.localStorage to interface type - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.39.0 - - @0xsequence/network@0.39.0 - - @0xsequence/utils@0.39.0 - -## 0.38.2 - -### Patch Changes - -- provider: add Settings.defaultPurchaseAmount -- Updated dependencies - - @0xsequence/abi@0.38.2 - - @0xsequence/network@0.38.2 - - @0xsequence/utils@0.38.2 - -## 0.38.1 - -### Patch Changes - -- update api and metadata rpc bindings -- Updated dependencies - - @0xsequence/abi@0.38.1 - - @0xsequence/network@0.38.1 - - @0xsequence/utils@0.38.1 - -## 0.38.0 - -### Minor Changes - -- api: update bindings, change TokenPrice interface -- bridge: remove @0xsequence/bridge package -- api: update bindings, rename ContractCallArg to TupleComponent - -### Patch Changes - -- Updated dependencies -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.38.0 - - @0xsequence/network@0.38.0 - - @0xsequence/utils@0.38.0 - -## 0.37.1 - -### Patch Changes - -- Add back sortNetworks - Removing sorting was a breaking change for dapps on older versions which directly integrate sequence. -- Updated dependencies - - @0xsequence/abi@0.37.1 - - @0xsequence/network@0.37.1 - - @0xsequence/utils@0.37.1 - -## 0.37.0 - -### Minor Changes - -- network related fixes and improvements -- api: bindings: exchange rate lookups - -### Patch Changes - -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.37.0 - - @0xsequence/network@0.37.0 - - @0xsequence/utils@0.37.0 - -## 0.36.13 - -### Patch Changes - -- api: update bindings with new price endpoints -- Updated dependencies - - @0xsequence/abi@0.36.13 - - @0xsequence/network@0.36.13 - - @0xsequence/utils@0.36.13 - -## 0.36.12 - -### Patch Changes - -- wallet: skip remote signers if not needed -- auth: check that signature meets threshold before requesting auth token -- Updated dependencies -- Updated dependencies - - @0xsequence/abi@0.36.12 - - @0xsequence/network@0.36.12 - - @0xsequence/utils@0.36.12 - -## 0.36.11 - -### Patch Changes - -- Prefix EIP191 message on wallet-request-handler -- Updated dependencies - - @0xsequence/abi@0.36.11 - - @0xsequence/network@0.36.11 - - @0xsequence/utils@0.36.11 - -## 0.36.10 - -### Patch Changes - -- support bannerUrl on connect -- Updated dependencies - - @0xsequence/abi@0.36.10 - - @0xsequence/network@0.36.10 - - @0xsequence/utils@0.36.10 - -## 0.36.9 - -### Patch Changes - -- minor dev xp improvements -- Updated dependencies - - @0xsequence/abi@0.36.9 - - @0xsequence/network@0.36.9 - - @0xsequence/utils@0.36.9 - -## 0.36.8 - -### Patch Changes - -- more connect options (theme, payment providers, funding currencies) -- Updated dependencies - - @0xsequence/abi@0.36.8 - - @0xsequence/network@0.36.8 - - @0xsequence/utils@0.36.8 - -## 0.36.7 - -### Patch Changes - -- fix missing break -- Updated dependencies - - @0xsequence/abi@0.36.7 - - @0xsequence/network@0.36.7 - - @0xsequence/utils@0.36.7 - -## 0.36.6 - -### Patch Changes - -- wallet_switchEthereumChain support -- Updated dependencies - - @0xsequence/abi@0.36.6 - - @0xsequence/network@0.36.6 - - @0xsequence/utils@0.36.6 - -## 0.36.5 - -### Patch Changes - -- auth: bump ethauth to 0.7.0 - network, wallet: don't assume position of auth network in list - api/indexer/metadata: trim trailing slash on hostname, and add endpoint urls - relayer: Allow to specify local relayer transaction parameters like gas price or gas limit -- Updated dependencies - - @0xsequence/abi@0.36.5 - - @0xsequence/network@0.36.5 - - @0xsequence/utils@0.36.5 - -## 0.36.4 - -### Patch Changes - -- Updating list of chain ids to include other ethereum compatible chains -- Updated dependencies - - @0xsequence/abi@0.36.4 - - @0xsequence/network@0.36.4 - - @0xsequence/utils@0.36.4 - -## 0.36.3 - -### Patch Changes - -- provider: pass connect options to prompter methods -- Updated dependencies - - @0xsequence/abi@0.36.3 - - @0xsequence/network@0.36.3 - - @0xsequence/utils@0.36.3 - -## 0.36.2 - -### Patch Changes - -- transactions: Setting target to 0x0 when empty to during SequenceTxAbiEncode -- Updated dependencies - - @0xsequence/abi@0.36.2 - - @0xsequence/network@0.36.2 - - @0xsequence/utils@0.36.2 - -## 0.36.1 - -### Patch Changes - -- metadata: update client with more fields -- Updated dependencies - - @0xsequence/abi@0.36.1 - - @0xsequence/network@0.36.1 - - @0xsequence/utils@0.36.1 - -## 0.36.0 - -### Minor Changes - -- relayer, wallet: fee quote support - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.36.0 - - @0xsequence/network@0.36.0 - - @0xsequence/utils@0.36.0 - -## 0.35.12 - -### Patch Changes - -- provider: rename wallet.commands to wallet.utils -- Updated dependencies - - @0xsequence/abi@0.35.12 - - @0xsequence/network@0.35.12 - - @0xsequence/utils@0.35.12 - -## 0.35.11 - -### Patch Changes - -- provider/utils: smoother message validation -- Updated dependencies - - @0xsequence/abi@0.35.11 - - @0xsequence/network@0.35.11 - - @0xsequence/utils@0.35.11 - -## 0.35.10 - -### Patch Changes - -- upgrade deps -- Updated dependencies - - @0xsequence/abi@0.35.10 - - @0xsequence/network@0.35.10 - - @0xsequence/utils@0.35.10 - -## 0.35.9 - -### Patch Changes - -- provider: window-transport override event handlers with new wallet instance -- Updated dependencies - - @0xsequence/abi@0.35.9 - - @0xsequence/network@0.35.9 - - @0xsequence/utils@0.35.9 - -## 0.35.8 - -### Patch Changes - -- provider: async wallet sign in improvements -- Updated dependencies - - @0xsequence/abi@0.35.8 - - @0xsequence/network@0.35.8 - - @0xsequence/utils@0.35.8 - -## 0.35.7 - -### Patch Changes - -- config: cache wallet configs -- Updated dependencies - - @0xsequence/abi@0.35.7 - - @0xsequence/network@0.35.7 - - @0xsequence/utils@0.35.7 - -## 0.35.6 - -### Patch Changes - -- provider: support async signin of wallet request handler -- Updated dependencies - - @0xsequence/abi@0.35.6 - - @0xsequence/network@0.35.6 - - @0xsequence/utils@0.35.6 - -## 0.35.5 - -### Patch Changes - -- wallet: skip threshold check during fee estimation -- Updated dependencies - - @0xsequence/abi@0.35.5 - - @0xsequence/network@0.35.5 - - @0xsequence/utils@0.35.5 - -## 0.35.4 - -### Patch Changes - -- - browser extension mode, center window -- Updated dependencies - - @0xsequence/abi@0.35.4 - - @0xsequence/network@0.35.4 - - @0xsequence/utils@0.35.4 - -## 0.35.3 - -### Patch Changes - -- - update window position when in browser extension mode -- Updated dependencies - - @0xsequence/abi@0.35.3 - - @0xsequence/network@0.35.3 - - @0xsequence/utils@0.35.3 - -## 0.35.2 - -### Patch Changes - -- - provider: WindowMessageHandler accept optional windowHref -- Updated dependencies - - @0xsequence/abi@0.35.2 - - @0xsequence/network@0.35.2 - - @0xsequence/utils@0.35.2 - -## 0.35.1 - -### Patch Changes - -- wallet: update config on undeployed too -- Updated dependencies - - @0xsequence/abi@0.35.1 - - @0xsequence/network@0.35.1 - - @0xsequence/utils@0.35.1 - -## 0.35.0 - -### Minor Changes - -- - config: add buildStubSignature - - provider: add checks to signing cases for wallet deployment and config statuses - - provider: add prompt for wallet deployment - - relayer: add BaseRelayer.prependWalletDeploy - - relayer: add Relayer.feeOptions - - relayer: account for wallet deployment in fee estimation - - transactions: add fromTransactionish - - wallet: add Account.prependConfigUpdate - - wallet: add Account.getFeeOptions - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.35.0 - - @0xsequence/network@0.35.0 - - @0xsequence/utils@0.35.0 - -## 0.34.0 - -### Minor Changes - -- - upgrade deps - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.34.0 - - @0xsequence/network@0.34.0 - - @0xsequence/utils@0.34.0 - -## 0.33.2 - -### Patch Changes - -- transactions: fix incorrect nonce comparison - -## 0.31.0 - -### Minor Changes - -- - upgrading to ethers v5.5 - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.31.0 - - @0xsequence/network@0.31.0 - - @0xsequence/utils@0.31.0 - -## 0.30.0 - -### Minor Changes - -- - upgrade most deps - -### Patch Changes - -- Updated dependencies - - @0xsequence/abi@0.30.0 - - @0xsequence/network@0.30.0 - - @0xsequence/utils@0.30.0 - -## 0.29.8 - -### Patch Changes - -- update api -- Updated dependencies [undefined] - - @0xsequence/abi@0.29.8 - - @0xsequence/network@0.29.8 - - @0xsequence/utils@0.29.8 - -## 0.29.6 - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/network@0.29.6 - -## 0.29.0 - -### Minor Changes - -- major architectural changes in Sequence design - - - only one API instance, API is no longer a per-chain service - - separate per-chain indexer service, API no longer handles indexing - - single contract metadata service, API no longer serves metadata - - chaind package has been removed, indexer and metadata packages have been added - - stronger typing with new explicit ChainId type - - multicall fixes and improvements - - forbid "wait" transactions in sendTransactionBatch calls - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/network@0.29.0 - - @0xsequence/abi@0.29.0 - - @0xsequence/utils@0.29.0 - -## 0.28.0 - -### Minor Changes - -- extension provider - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.28.0 - - @0xsequence/chaind@0.28.0 - - @0xsequence/network@0.28.0 - - @0xsequence/utils@0.28.0 - -## 0.27.0 - -### Minor Changes - -- Add requireFreshSigner lib to sessions - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.27.0 - - @0xsequence/chaind@0.27.0 - - @0xsequence/network@0.27.0 - - @0xsequence/utils@0.27.0 - -## 0.25.1 - -### Patch Changes - -- Fix build typescrypt issue -- Updated dependencies [undefined] - - @0xsequence/abi@0.25.1 - - @0xsequence/chaind@0.25.1 - - @0xsequence/network@0.25.1 - - @0xsequence/utils@0.25.1 - -## 0.25.0 - -### Minor Changes - -- 10c8af8: Add estimator package - Fix multicall few calls bug - -### Patch Changes - -- Updated dependencies [10c8af8] - - @0xsequence/abi@0.25.0 - - @0xsequence/chaind@0.25.0 - - @0xsequence/network@0.25.0 - - @0xsequence/utils@0.25.0 - -## 0.23.0 - -### Minor Changes - -- - relayer: offer variety of gas fee options from the relayer service" - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.23.0 - - @0xsequence/chaind@0.23.0 - - @0xsequence/network@0.23.0 - - @0xsequence/utils@0.23.0 - -## 0.22.2 - -### Patch Changes - -- e1c109e: Fix authProof on expired sessions -- Updated dependencies [e1c109e] - - @0xsequence/abi@0.22.2 - - @0xsequence/chaind@0.22.2 - - @0xsequence/network@0.22.2 - - @0xsequence/utils@0.22.2 - -## 0.22.1 - -### Patch Changes - -- transport session cache -- Updated dependencies [undefined] - - @0xsequence/abi@0.22.1 - - @0xsequence/chaind@0.22.1 - - @0xsequence/network@0.22.1 - - @0xsequence/utils@0.22.1 - -## 0.22.0 - -### Minor Changes - -- e667b65: Expose all relayer options on networks - -### Patch Changes - -- Updated dependencies [e667b65] - - @0xsequence/abi@0.22.0 - - @0xsequence/network@0.22.0 - - @0xsequence/utils@0.22.0 - - @0xsequence/chaind@0.22.0 - -## 0.21.5 - -### Patch Changes - -- Give priority to metaTxnId returned by relayer -- Updated dependencies [undefined] - - @0xsequence/abi@0.21.5 - - @0xsequence/chaind@0.21.5 - - @0xsequence/network@0.21.5 - - @0xsequence/utils@0.21.5 - -## 0.21.4 - -### Patch Changes - -- Add has enough signers method -- Updated dependencies [undefined] - - @0xsequence/abi@0.21.4 - - @0xsequence/chaind@0.21.4 - - @0xsequence/network@0.21.4 - - @0xsequence/utils@0.21.4 - -## 0.21.3 - -### Patch Changes - -- add window session cache -- Updated dependencies [undefined] - - @0xsequence/abi@0.21.3 - - @0xsequence/chaind@0.21.3 - - @0xsequence/network@0.21.3 - - @0xsequence/utils@0.21.3 - -## 0.21.2 - -### Patch Changes - -- exception handlind in relayer -- Updated dependencies [undefined] - - @0xsequence/abi@0.21.2 - - @0xsequence/chaind@0.21.2 - - @0xsequence/network@0.21.2 - - @0xsequence/utils@0.21.2 - -## 0.21.0 - -### Minor Changes - -- - fix gas estimation on wallets with large number of signers - - update to session handling and wallet config construction upon auth - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.21.0 - - @0xsequence/chaind@0.21.0 - - @0xsequence/network@0.21.0 - - @0xsequence/utils@0.21.0 - -## 0.19.3 - -### Patch Changes - -- jwtAuth visibility, package version sync -- Updated dependencies [undefined] - - @0xsequence/abi@0.19.3 - - @0xsequence/chaind@0.19.3 - - @0xsequence/network@0.19.3 - - @0xsequence/utils@0.19.3 - -## 0.19.2 - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.19.2 - -## 0.19.0 - -### Minor Changes - -- - provider, improve dapp / wallet transport io - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.19.0 - - @0xsequence/chaind@0.19.0 - - @0xsequence/network@0.19.0 - - @0xsequence/utils@0.19.0 - -## 0.18.0 - -### Minor Changes - -- relayer improvements and pending transaction handling - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.18.0 - - @0xsequence/chaind@0.18.0 - - @0xsequence/network@0.18.0 - - @0xsequence/utils@0.18.0 - -## 0.16.0 - -### Minor Changes - -- relayer as its own service separate from chaind - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.16.0 - - @0xsequence/chaind@0.16.0 - - @0xsequence/network@0.16.0 - -## 0.15.1 - -### Patch Changes - -- update api clients -- Updated dependencies [undefined] - - @0xsequence/abi@0.15.1 - - @0xsequence/chaind@0.15.1 - - @0xsequence/network@0.15.1 - -## 0.15.0 - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/chaind@0.15.0 - -## 0.14.3 - -### Patch Changes - -- Fix 0xSequence relayer dependencies -- Updated dependencies [undefined] - - @0xsequence/abi@0.14.3 - - @0xsequence/chaind@0.14.3 - - @0xsequence/network@0.14.3 - -## 0.14.2 - -### Patch Changes - -- Add debug logs to rpc-relayer -- Updated dependencies [undefined] - - @0xsequence/abi@0.14.2 - - @0xsequence/chaind@0.14.2 - - @0xsequence/network@0.14.2 - -## 0.14.0 - -### Minor Changes - -- update sequence utils finder which includes optimization - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.14.0 - - @0xsequence/chaind@0.14.0 - - @0xsequence/network@0.14.0 - -## 0.13.0 - -### Minor Changes - -- Update SequenceUtils deployed contract - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.13.0 - - @0xsequence/chaind@0.13.0 - - @0xsequence/network@0.13.0 - -## 0.12.1 - -### Patch Changes - -- npm bump -- Updated dependencies [undefined] - - @0xsequence/abi@0.12.1 - - @0xsequence/chaind@0.12.1 - - @0xsequence/network@0.12.1 - -## 0.12.0 - -### Minor Changes - -- provider: improvements to window transport - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.12.0 - - @0xsequence/chaind@0.12.0 - - @0xsequence/network@0.12.0 - -## 0.11.4 - -### Patch Changes - -- update api client -- Updated dependencies [undefined] - - @0xsequence/abi@0.11.4 - - @0xsequence/chaind@0.11.4 - - @0xsequence/network@0.11.4 - -## 0.11.3 - -### Patch Changes - -- improve openWindow state options handling -- Updated dependencies [undefined] - - @0xsequence/abi@0.11.3 - - @0xsequence/chaind@0.11.3 - - @0xsequence/network@0.11.3 - -## 0.11.2 - -### Patch Changes - -- Fix multicall proxy scopes -- Updated dependencies [undefined] - - @0xsequence/abi@0.11.2 - - @0xsequence/chaind@0.11.2 - - @0xsequence/network@0.11.2 - -## 0.11.1 - -### Patch Changes - -- Add support for dynamic and nested signatures -- Updated dependencies [undefined] - - @0xsequence/abi@0.11.1 - - @0xsequence/chaind@0.11.1 - - @0xsequence/network@0.11.1 - -## 0.11.0 - -### Minor Changes - -- Update wallet context to 1.7 contracts - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.11.0 - - @0xsequence/chaind@0.11.0 - - @0xsequence/network@0.11.0 - -## 0.10.9 - -### Patch Changes - -- add support for public addresses as signers in session.open -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.9 - - @0xsequence/chaind@0.10.9 - - @0xsequence/network@0.10.9 - -## 0.10.8 - -### Patch Changes - -- Multicall production configuration -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.8 - - @0xsequence/chaind@0.10.8 - - @0xsequence/network@0.10.8 - -## 0.10.7 - -### Patch Changes - -- allow provider transport to force disconnect -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.7 - - @0xsequence/chaind@0.10.7 - - @0xsequence/network@0.10.7 - -## 0.10.6 - -### Patch Changes - -- - fix getWalletState method -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.6 - - @0xsequence/chaind@0.10.6 - - @0xsequence/network@0.10.6 - -## 0.10.5 - -### Patch Changes - -- update relayer gas refund options -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.5 - - @0xsequence/chaind@0.10.5 - - @0xsequence/network@0.10.5 - -## 0.10.4 - -### Patch Changes - -- Update api proto -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.4 - - @0xsequence/chaind@0.10.4 - - @0xsequence/network@0.10.4 - -## 0.10.3 - -### Patch Changes - -- Fix loading config cross-chain -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.3 - - @0xsequence/chaind@0.10.3 - - @0xsequence/network@0.10.3 - -## 0.10.2 - -### Patch Changes - -- - message digest fix -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.2 - - @0xsequence/chaind@0.10.2 - - @0xsequence/network@0.10.2 - -## 0.10.1 - -### Patch Changes - -- upgrade deps -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.1 - - @0xsequence/chaind@0.10.1 - - @0xsequence/network@0.10.1 - -## 0.10.0 - -### Minor Changes - -- Deployed new contracts with ERC1271 signer support - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.10.0 - - @0xsequence/chaind@0.10.0 - - @0xsequence/network@0.10.0 - -## 0.9.6 - -### Patch Changes - -- Update ABIs for latest sequence contracts -- Updated dependencies [undefined] - - @0xsequence/network@0.9.6 - - @0xsequence/abi@0.9.6 - - @0xsequence/chaind@0.9.6 - -## 0.9.5 - -### Patch Changes - -- Implemented session class -- Updated dependencies [undefined] - - @0xsequence/network@0.9.5 - -## 0.9.3 - -### Patch Changes - -- - minor improvements -- Updated dependencies [undefined] - - @0xsequence/abi@0.9.3 - - @0xsequence/chaind@0.9.3 - - @0xsequence/network@0.9.3 - -## 0.9.1 - -### Patch Changes - -- - patch bump -- Updated dependencies [undefined] - - @0xsequence/abi@0.9.1 - - @0xsequence/chaind@0.9.1 - - @0xsequence/network@0.9.1 - -## 0.9.0 - -### Minor Changes - -- - provider transport hardening - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.9.0 - - @0xsequence/chaind@0.9.0 - - @0xsequence/network@0.9.0 - -## 0.8.5 - -### Patch Changes - -- - use latest wallet-contracts -- Updated dependencies [undefined] - - @0xsequence/abi@0.8.5 - - @0xsequence/chaind@0.8.5 - - @0xsequence/network@0.8.5 - -## 0.8.4 - -### Patch Changes - -- - minor improvements, name updates and comments -- Updated dependencies [undefined] - - @0xsequence/abi@0.8.4 - - @0xsequence/chaind@0.8.4 - - @0xsequence/network@0.8.4 - -## 0.8.3 - -### Patch Changes - -- - refinements - - - normalize signer address in config - - - provider: getWalletState() method to WalletProvider - -- Updated dependencies [undefined] - - @0xsequence/abi@0.8.3 - - @0xsequence/chaind@0.8.3 - - @0xsequence/network@0.8.3 - -## 0.8.2 - -### Patch Changes - -- - field rename and ethauth dependency bump -- Updated dependencies [undefined] - - @0xsequence/abi@0.8.2 - - @0xsequence/chaind@0.8.2 - - @0xsequence/network@0.8.2 - -## 0.8.1 - -### Patch Changes - -- - variety of optimizations -- Updated dependencies [undefined] - - @0xsequence/abi@0.8.1 - - @0xsequence/chaind@0.8.1 - - @0xsequence/network@0.8.1 - -## 0.8.0 - -### Minor Changes - -- - changeset fix - -### Patch Changes - -- Updated dependencies [undefined] - - @0xsequence/abi@0.8.0 - - @0xsequence/chaind@0.8.0 - - @0xsequence/network@0.8.0 - -## 0.7.2 - -### Patch Changes - -- package.json fix - -## 0.7.0 - -### Patch Changes - -- 6f11ed7: sequence.js, init release -- Updated dependencies [6f11ed7] - - @0xsequence/abi@0.7.0 - - @0xsequence/chaind@0.7.0 - - @0xsequence/network@0.7.0 diff --git a/packages/transactions/README.md b/packages/transactions/README.md deleted file mode 100644 index 59711ba2d..000000000 --- a/packages/transactions/README.md +++ /dev/null @@ -1,4 +0,0 @@ -@0xsequence/transactions -======================== - -See [0xsequence project page](https://github.com/0xsequence/sequence.js). diff --git a/packages/transactions/package.json b/packages/transactions/package.json deleted file mode 100644 index 3464ac305..000000000 --- a/packages/transactions/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "@0xsequence/transactions", - "version": "0.43.34", - "description": "transactions sub-package for Sequence", - "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/transactions", - "source": "src/index.ts", - "main": "dist/0xsequence-transactions.cjs.js", - "module": "dist/0xsequence-transactions.esm.js", - "author": "Horizon Blockchain Games", - "license": "Apache-2.0", - "scripts": { - "test": "pnpm test:file tests/**/*.spec.ts", - "test:file": "NODE_OPTIONS='--loader tsx' mocha --timeout 30000", - "typecheck": "tsc --noEmit" - }, - "dependencies": { - "@0xsequence/abi": "^0.43.34", - "@0xsequence/config": "^0.43.34", - "@0xsequence/network": "^0.43.34", - "@0xsequence/utils": "^0.43.34" - }, - "peerDependencies": { - "ethers": ">=5.5 < 6" - }, - "devDependencies": { - "ethers": "^5.7.2" - }, - "files": [ - "src", - "dist" - ] -} diff --git a/packages/transactions/src/index.ts b/packages/transactions/src/index.ts deleted file mode 100644 index ce4acb574..000000000 --- a/packages/transactions/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './types' -export * from './utils' diff --git a/packages/transactions/src/types.ts b/packages/transactions/src/types.ts deleted file mode 100644 index fcbb6a5ab..000000000 --- a/packages/transactions/src/types.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { BigNumberish, BytesLike, providers } from 'ethers' -import { DecodedSignature, WalletConfig } from '@0xsequence/config' -import { WalletContext } from '@0xsequence/network' - -type EthersTransactionRequest = providers.TransactionRequest -type EthersTransactionResponse = providers.TransactionResponse - -// Transaction is a Sequence transaction payload. Note, we do not include gasPrice as an option in this form, -// as we expect the gasPrice to be optimally estimated by the transaction relayer. -export interface Transaction { - to: string - value?: BigNumberish - data?: BytesLike - nonce?: BigNumberish - gasLimit?: BigNumberish - delegateCall?: boolean - revertOnError?: boolean -} - -export interface TransactionEncoded { - delegateCall: boolean - revertOnError: boolean - gasLimit: BigNumberish - target: string - value: BigNumberish - data: BytesLike -} - -export interface TransactionRequest extends EthersTransactionRequest { - auxiliary?: Transactionish[] - expiration?: BigNumberish - afterNonce?: NonceDependency | BigNumberish -} - -export interface NonceDependency { - address: string - nonce: BigNumberish - space?: BigNumberish -} - -export type Transactionish = TransactionRequest | TransactionRequest[] | Transaction | Transaction[] - -export type SignedTransactions = { - digest: string, - chainId: BigNumberish, - config: WalletConfig, - context: WalletContext, - transactions: Transaction[], - nonce: BigNumberish, - signature: string | DecodedSignature | Promise | Promise -} - -export interface TransactionResponse extends EthersTransactionResponse { - receipt?: R -} diff --git a/packages/transactions/src/utils.ts b/packages/transactions/src/utils.ts deleted file mode 100644 index 46d0fde49..000000000 --- a/packages/transactions/src/utils.ts +++ /dev/null @@ -1,282 +0,0 @@ -import { ethers, Signer, BigNumberish, utils } from 'ethers' -import { walletContracts } from '@0xsequence/abi' -import { WalletContext } from '@0xsequence/network' -import { Transaction, TransactionRequest, Transactionish, TransactionEncoded, NonceDependency, SignedTransactions } from './types' -import { subDigestOf } from '@0xsequence/utils' - -export const MetaTransactionsType = `tuple( - bool delegateCall, - bool revertOnError, - uint256 gasLimit, - address target, - uint256 value, - bytes data -)[]` - -export function packMetaTransactionsData(...txs: Transaction[]): string { - const nonce = readSequenceNonce(...txs) - if (nonce === undefined) throw new Error('Encoding transactions without defined nonce') - return packMetaTransactionsNonceData(nonce, ...txs) -} - -export function packMetaTransactionsNonceData(nonce: BigNumberish, ...txs: Transaction[]): string { - return ethers.utils.defaultAbiCoder.encode(['uint256', MetaTransactionsType], [nonce, sequenceTxAbiEncode(txs)]) -} - -export function digestOfTransactions(...txs: Transaction[]): string { - const nonce = readSequenceNonce(...txs) - if (nonce === undefined) throw new Error('Computing hash for transactions without defined nonce') - return digestOfTransactionsNonce(nonce, ...txs) -} - -export function digestOfTransactionsNonce(nonce: BigNumberish, ...txs: Transaction[]) { - return ethers.utils.keccak256(packMetaTransactionsNonceData(nonce, ...txs)) -} - -export function computeMetaTxnHash(address: string, chainId: BigNumberish, ...txs: Transaction[]): string { - return subDigestOf(address, chainId, digestOfTransactions(...txs)).replace(/^0x/, '') -} - -export async function toSequenceTransactions( - wallet: Signer | string, - txs: (Transaction | TransactionRequest)[], - revertOnError: boolean = false, - gasLimit?: BigNumberish -): Promise { - // Bundles all transactions, including the auxiliary ones - const allTxs = flattenAuxTransactions(txs) - - // Uses the lowest nonce found on TransactionRequest - // if there are no nonces, it leaves an undefined nonce - const nonces = (await Promise.all(txs.map(t => t.nonce))).filter(n => n !== undefined).map(n => ethers.BigNumber.from(n)) - const nonce = nonces.length !== 0 ? nonces.reduce((p, c) => (p.lt(c) ? p : c)) : undefined - - // Maps all transactions into SequenceTransactions - return Promise.all(allTxs.map(tx => toSequenceTransaction(wallet, tx, revertOnError, gasLimit, nonce))) -} - -export function flattenAuxTransactions(txs: Transactionish | Transactionish[]): (TransactionRequest | Transaction)[] { - if (!Array.isArray(txs)) { - if ('auxiliary' in txs) { - const aux = txs.auxiliary - - const tx = { ...txs } - delete tx.auxiliary - - if (aux) { - return [tx, ...flattenAuxTransactions(aux)] - } else { - return [tx] - } - } else { - return [txs] - } - } - - return txs.flatMap(flattenAuxTransactions) -} - -export async function toSequenceTransaction( - wallet: Signer | string, - tx: TransactionRequest | Transaction, - revertOnError: boolean = false, - gasLimit?: BigNumberish, - nonce?: BigNumberish -): Promise { - if (isSequenceTransaction(tx)) { - return tx as Transaction - } - - const txGas = tx.gasLimit === undefined ? (tx).gas : tx.gasLimit - - if (tx.to) { - return { - delegateCall: false, - revertOnError: revertOnError, - gasLimit: txGas ? await txGas : gasLimit, - to: await tx.to, - value: tx.value ? await tx.value : 0, - data: (await tx.data)!, - nonce: nonce ? nonce : await tx.nonce - } - } else { - const walletInterface = new utils.Interface(walletContracts.mainModule.abi) - const data = walletInterface.encodeFunctionData(walletInterface.getFunction('createContract'), [tx.data]) - const address = typeof wallet === 'string' ? wallet : wallet.getAddress() - - return { - delegateCall: false, - revertOnError: revertOnError, - gasLimit: txGas ? await txGas : gasLimit, - to: await address, - value: tx.value ? await tx.value : 0, - data: data, - nonce: nonce ? nonce : await tx.nonce - } - } -} - -export function isAsyncSendable(target: any) { - return target.send || target.sendAsync -} - -export function isSequenceTransaction(tx: any): tx is Transaction { - return tx.delegateCall !== undefined || tx.revertOnError !== undefined -} - -export function hasSequenceTransactions(txs: any[]) { - return txs.find(t => isSequenceTransaction(t)) !== undefined -} - -export function readSequenceNonce(...txs: Transaction[]): BigNumberish | undefined { - const sample = txs.find(t => t.nonce !== undefined) - if (!sample) { - return undefined - } - const sampleNonce = ethers.BigNumber.from(sample.nonce) - - if (txs.find(t => t.nonce !== undefined && !ethers.BigNumber.from(t.nonce).eq(sampleNonce))) { - throw new Error('Mixed nonces on Sequence transactions') - } - - return sample ? sample.nonce : undefined -} - -export function sequenceTxAbiEncode(txs: Transaction[]): TransactionEncoded[] { - return txs.map(t => ({ - delegateCall: t.delegateCall === true, - revertOnError: t.revertOnError === true, - gasLimit: t.gasLimit !== undefined ? t.gasLimit : ethers.constants.Zero, - target: t.to ?? ethers.constants.AddressZero, - value: t.value !== undefined ? t.value : ethers.constants.Zero, - data: t.data !== undefined ? t.data : [] - })) -} - -export function appendNonce(txs: Transaction[], nonce: BigNumberish): Transaction[] { - return txs.map((t: Transaction) => ({ ...t, nonce })) -} - -export function makeExpirable(context: WalletContext, txs: Transaction[], expiration: BigNumberish): Transaction[] { - const sequenceUtils = new utils.Interface(walletContracts.sequenceUtils.abi) - - if (!context || !context.sequenceUtils) { - throw new Error('Undefined sequenceUtils') - } - - return [ - { - delegateCall: false, - revertOnError: true, - gasLimit: 0, - to: context.sequenceUtils, - value: 0, - data: sequenceUtils.encodeFunctionData(sequenceUtils.getFunction('requireNonExpired'), [expiration]) - }, - ...txs - ] -} - -export function makeAfterNonce(context: WalletContext, txs: Transaction[], dep: NonceDependency): Transaction[] { - const sequenceUtils = new utils.Interface(walletContracts.sequenceUtils.abi) - - if (!context || !context.sequenceUtils) { - throw new Error('Undefined sequenceUtils') - } - - return [ - { - delegateCall: false, - revertOnError: true, - gasLimit: 0, - to: context.sequenceUtils, - value: 0, - data: sequenceUtils.encodeFunctionData(sequenceUtils.getFunction('requireMinNonce'), [ - dep.address, - dep.space ? encodeNonce(dep.space, dep.nonce) : dep.nonce - ]) - }, - ...txs - ] -} - -export function encodeNonce(space: BigNumberish, nonce: BigNumberish): BigNumberish { - const bspace = ethers.BigNumber.from(space) - const bnonce = ethers.BigNumber.from(nonce) - - const shl = ethers.constants.Two.pow(ethers.BigNumber.from(96)) - - if (!bnonce.div(shl).eq(ethers.constants.Zero)) { - throw new Error('Space already encoded') - } - - return bnonce.add(bspace.mul(shl)) -} - -export function decodeNonce(nonce: BigNumberish): [BigNumberish, BigNumberish] { - const bnonce = ethers.BigNumber.from(nonce) - const shr = ethers.constants.Two.pow(ethers.BigNumber.from(96)) - - return [bnonce.div(shr), bnonce.mod(shr)] -} - -export function isSignedTransactions(cand: any): cand is SignedTransactions { - return ( - cand !== undefined && - cand.chainId !== undefined && - cand.config !== undefined && - cand.context !== undefined && - cand.signature !== undefined && - cand.transactions !== undefined && - Array.isArray(cand.transactions) && - (cand).transactions.reduce((p, c) => p && isSequenceTransaction(c), true) - ) -} - -export async function fromTransactionish( - context: WalletContext, - wallet: string, - transaction: Transactionish -): Promise { - let stx: Transaction[] = [] - - if (Array.isArray(transaction)) { - if (hasSequenceTransactions(transaction)) { - stx = flattenAuxTransactions(transaction) as Transaction[] - } else { - stx = await toSequenceTransactions(wallet, transaction) - } - } else if (isSequenceTransaction(transaction)) { - stx = flattenAuxTransactions([transaction]) as Transaction[] - } else { - stx = await toSequenceTransactions(wallet, [transaction]) - } - - // If transaction is marked as expirable - // append expirable require - if ((transaction).expiration) { - stx = makeExpirable(context, stx, (transaction).expiration!) - } - - // If transaction depends on another nonce - // append after nonce requirement - if ((transaction).afterNonce) { - const after = (transaction).afterNonce - stx = makeAfterNonce( - context, - stx, - (after).address - ? { - address: (after).address, - nonce: (after).nonce, - space: (after).space - } - : { - address: wallet, - nonce: after - } - ) - } - - return stx -} diff --git a/packages/transactions/tests/mock.spec.ts b/packages/transactions/tests/mock.spec.ts deleted file mode 100644 index 15e5119aa..000000000 --- a/packages/transactions/tests/mock.spec.ts +++ /dev/null @@ -1,5 +0,0 @@ -// see provider/tests and wallet/tests -describe('transactions', function () { - it('', () => { - }) -}) diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 325360a9a..233452aec 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -1,14 +1,16 @@ export * from './base64' +export * from './big-number' export * from './digest' export * from './is-node-or-browser' export * from './jwt-decode' export * from './logger' +export * from './network' +export * from './promise-cache' export * from './promisify' -export * from './rand' export * from './query-string' +export * from './rand' export * from './sanitize' export * from './sleep' export * from './typed-data' export * from './types' export * from './web' -export * from './big-number' diff --git a/packages/utils/src/network.ts b/packages/utils/src/network.ts new file mode 100644 index 000000000..1a1cadbad --- /dev/null +++ b/packages/utils/src/network.ts @@ -0,0 +1,15 @@ +import { ethers } from 'ethers' + +export const getDefaultConnectionInfo = (url: string): ethers.utils.ConnectionInfo => { + return { + url, + skipFetchSetup: true, + fetchOptions: { + mode: 'cors', + cache: 'force-cache', + credentials: 'same-origin', + redirect: 'follow', + referrer: 'client' + } + } +} diff --git a/packages/utils/src/promise-cache.ts b/packages/utils/src/promise-cache.ts new file mode 100644 index 000000000..0504adda8 --- /dev/null +++ b/packages/utils/src/promise-cache.ts @@ -0,0 +1,58 @@ +import { ethers } from 'ethers' + +export class PromiseCache { + private readonly cache: Map + + constructor() { + this.cache = new Map() + } + + do, T>( + key: string, + validMilliseconds: number | undefined, + task: (...args: S) => Promise, + ...args: S + ): Promise { + key = `${key}:${ethers.utils.keccak256(ethers.utils.toUtf8Bytes(JSON.stringify(args, deterministically)))}` + + let entry = this.cache.get(key) + + if (entry) { + if (entry.expiration) { + if (new Date() >= entry.expiration) { + entry = undefined + this.cache.delete(key) + } + } + } + + if (!entry) { + const entry_: Entry = { promise: task(...args) } + + if (validMilliseconds !== undefined) { + entry_.promise = entry_.promise.then(result => { + entry_.expiration = new Date(Date.now() + validMilliseconds) + return result + }) + } + + entry = entry_ + this.cache.set(key, entry) + } + + return entry.promise as Promise + } +} + +type Entry = { + promise: Promise + expiration?: Date +} + +function deterministically(_key: string, value: any): any { + if (typeof value === 'object' && value !== null && !Array.isArray(value)) { + return Object.fromEntries(Object.entries(value).sort()) + } + + return value +} diff --git a/packages/wallet/hardhat.config.js b/packages/wallet/hardhat.config.js index fa224f51e..65a997e19 100644 --- a/packages/wallet/hardhat.config.js +++ b/packages/wallet/hardhat.config.js @@ -1,20 +1,11 @@ -/** - * @type import('hardhat/config').HardhatUserConfig - */ -module.exports = { - solidity: '0.7.6', +module.exports = { networks: { hardhat: { - // gas: 10000000000000, - // blockGasLimit: 10000000000000, - // gasPrice: 2, chainId: 31337, accounts: { mnemonic: 'ripple axis someone ridge uniform wrist prosper there frog rate olympic knee' - }, - // loggingEnabled: true - // verbose: true + } }, } } diff --git a/packages/wallet/hardhat2.config.js b/packages/wallet/hardhat2.config.js index aa8774f01..e984fc2e7 100644 --- a/packages/wallet/hardhat2.config.js +++ b/packages/wallet/hardhat2.config.js @@ -1,9 +1,5 @@ -/** - * @type import('hardhat/config').HardhatUserConfig - */ -module.exports = { - solidity: '0.7.6', +module.exports = { networks: { hardhat: { chainId: 31338, diff --git a/packages/wallet/package.json b/packages/wallet/package.json index 43aa81523..737da46bb 100644 --- a/packages/wallet/package.json +++ b/packages/wallet/package.json @@ -17,20 +17,21 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@0xsequence/abi": "^0.43.34", - "@0xsequence/config": "^0.43.34", - "@0xsequence/guard": "^0.43.34", - "@0xsequence/network": "^0.43.34", - "@0xsequence/relayer": "^0.43.34", - "@0xsequence/transactions": "^0.43.34", - "@0xsequence/utils": "^0.43.34" + "@0xsequence/abi": "workspace:*", + "@0xsequence/core": "workspace:*", + "@0xsequence/guard": "workspace:*", + "@0xsequence/network": "workspace:*", + "@0xsequence/signhub": "workspace:*", + "@0xsequence/relayer": "workspace:*", + "@0xsequence/utils": "workspace:*" }, "peerDependencies": { "ethers": ">=5.5 < 6" }, "devDependencies": { - "@0xsequence/ethauth": "^0.8.0", - "@0xsequence/wallet-contracts": "1.10.0", + "@0xsequence/ethauth": "^0.8.1", + "@0xsequence/tests": "workspace:*", + "@0xsequence/wallet-contracts": "^1.10.0", "@istanbuljs/nyc-config-typescript": "^1.0.1", "ethers": "^5.7.2", "web3": "^1.8.1" diff --git a/packages/wallet/src/account.ts b/packages/wallet/src/account.ts deleted file mode 100644 index bbe3c5463..000000000 --- a/packages/wallet/src/account.ts +++ /dev/null @@ -1,623 +0,0 @@ -import { Signer as AbstractSigner, BytesLike, providers, utils, TypedDataDomain, TypedDataField } from 'ethers' -import { Signer, NotEnoughSigners, SignedTransactionsCallback } from './signer' -import { - SignedTransactions, - Transactionish, - Transaction, - computeMetaTxnHash, - fromTransactionish, - TransactionResponse -} from '@0xsequence/transactions' -import { - WalletConfig, - WalletState, - addressOf, - isConfigEqual, - sortConfig, - ConfigFinder, - SequenceUtilsFinder -} from '@0xsequence/config' -import { - ChainIdLike, - NetworkConfig, - WalletContext, - sequenceContext, - mainnetNetworks, - ensureValidNetworks, - getChainId, - sortNetworks -} from '@0xsequence/network' -import { Wallet } from './wallet' -import { resolveArrayProperties } from './utils' -import { isRelayer, FeeOption, FeeQuote, Relayer, RpcRelayer, isRpcRelayerOptions } from '@0xsequence/relayer' -import { encodeTypedDataDigest } from '@0xsequence/utils' - -export interface AccountOptions { - initialConfig: WalletConfig - networks?: NetworkConfig[] - context?: WalletContext - configFinder?: ConfigFinder -} - -// Account is an interface to a multi-network smart contract wallet. -export class Account extends Signer { - private readonly options: AccountOptions - - private _wallets: { - wallet: Wallet - network: NetworkConfig - }[] - - private _signers: (BytesLike | AbstractSigner)[] - - // provider points at the main chain for compatability with the Signer. - // Use getProvider(chainId) to get the provider for the respective network. - provider: providers.JsonRpcProvider - - // memoized value - private _chainId?: number - - constructor(options: AccountOptions, ...signers: (BytesLike | AbstractSigner)[]) { - super() - - this.options = options - this._signers = signers - - // Use deployed wallet context by default if not provided - if (!options.context) this.options.context = { ...sequenceContext } - - // Network config, defaults will be used if none are provided - if (this.options.networks) { - this.setNetworks(this.options.networks) - } else { - this.setNetworks([...mainnetNetworks]) - } - } - - useSigners(...signers: (BytesLike | AbstractSigner)[]): Account { - this._signers = signers - this._wallets.forEach(w => { - w.wallet = w.wallet.useSigners(...signers) - }) - return this - } - - async getWalletContext(): Promise { - return this.options.context! - } - - getConfigFinder(): ConfigFinder { - if (this.options.configFinder) return this.options.configFinder - return new SequenceUtilsFinder(this.authWallet().wallet.provider) - } - - // getWalletConfig builds a list of WalletConfigs across all networks. - // This is useful to shows all keys/devices connected to a wallet across networks. - async getWalletConfig(chainId?: ChainIdLike): Promise { - let wallets: { wallet: Wallet; network: NetworkConfig }[] = [] - if (chainId) { - const v = this.getWalletByNetwork(chainId) - if (v) { - wallets.push(v) - } - } else { - wallets = this._wallets - } - return (await Promise.all(wallets.map(w => w.wallet.getWalletConfig()))).flat() - } - - async getWalletState(chainId?: ChainIdLike): Promise { - let wallets: { wallet: Wallet; network: NetworkConfig }[] = [] - if (chainId) { - const v = this.getWalletByNetwork(chainId) - if (v) { - wallets.push(v) - } - } else { - wallets = this._wallets - } - - const configsPromise = Promise.all( - wallets.map(w => - this.getConfigFinder().findCurrentConfig({ - address: w.wallet.address, - provider: w.wallet.provider, - context: w.wallet.context, - knownConfigs: [w.wallet.config] - }) - ) - ) - - const states = (await Promise.all(wallets.map(w => w.wallet.getWalletState()))).flat() - - // fetch the current config for the AuthChain, as it will be available - const idx = states.findIndex(s => s.chainId === this.getAuthChainId()) - if (idx >= 0) { - states[idx].config = await this.currentConfig(wallets[idx].wallet) - } - - const configs = await configsPromise - - return states.map((s, i) => ({ - ...s, - config: configs[i]?.config - })) - } - - // address getter - get address(): string { - return this._wallets[0].wallet.address - } - - // getAddress returns the address of the wallet -- note the account address is the same - // across all wallets on all different networks - getAddress(): Promise { - return this._wallets[0].wallet.getAddress() - } - - // getSigners returns the multi-sig signers with permission to control the wallet - async getSigners(): Promise { - return this._wallets[0].wallet.getSigners() - } - - async getProvider(chainId?: number): Promise { - if (!chainId) return this.mainWallet()?.wallet.getProvider() - return this._wallets.find(w => w.network.chainId === chainId)?.wallet.getProvider() - } - - async getRelayer(chainId?: number): Promise { - if (!chainId) return this.mainWallet()?.wallet.getRelayer() - return this._wallets.find(w => w.network.chainId === chainId)?.wallet.getRelayer() - } - - async getNetworks(): Promise { - return this.options.networks! - } - - // NOTE: this is copied over on top of ethers, and is memoized - async getChainId(): Promise { - if (this._chainId) return this._chainId - const network = await this.provider.getNetwork() - this._chainId = network.chainId - return this._chainId - } - - getAuthChainId(): number { - try { - return this.options.networks!.find(network => network.isAuthChain)!.chainId - } catch { - throw new Error('no auth network') - } - } - - async signMessage( - message: BytesLike, - target?: Wallet | ChainIdLike, - allSigners: boolean = true, - isDigest: boolean = false - ): Promise { - let { wallet } = await (async () => { - // eslint-disable-line - if (!target) { - return this.mainWallet() - } - if ((target).address) { - const chainId = await (target).getChainId() - return this.getWalletByNetwork(chainId) - } - return this.getWalletByNetwork(target as ChainIdLike) - })() - - // Fetch the latest config of the wallet. - // - // We skip this step if wallet is authWallet. The assumption is that authWallet - // will already have the latest config, but lets confirm that. - // TODO: instead, memoize the currentConfig, as below will break - // if we skip - // if (!network.isAuthChain) { - let thisConfig = await this.currentConfig(wallet) - thisConfig = thisConfig ? thisConfig : this._wallets[0].wallet.config - wallet = wallet.useConfig(thisConfig) - // } - - // See if wallet and available signers set has enough signer power, - // but if allSigners is false, we allow partial signing - const weight = await wallet.signWeight() - if (weight.lt(wallet.config.threshold) && allSigners !== false) { - throw new NotEnoughSigners( - `Sign message - wallet combined weight ${weight.toString()} below required ${wallet.config.threshold.toString()}` - ) - } - - return wallet.signMessage(message, undefined, allSigners, isDigest) - } - - // TODO: should allSigners default to false here..? - async signAuthMessage(message: BytesLike, allSigners: boolean = true, isDigest: boolean = false): Promise { - return this.signMessage(message, this.authWallet()?.wallet, allSigners, isDigest) - } - - async signTypedData( - domain: TypedDataDomain, - types: Record>, - message: Record, - chainId?: ChainIdLike, - allSigners: boolean = true - ): Promise { - const wallet = chainId ? this.getWalletByNetwork(chainId).wallet : this.mainWallet().wallet - const digest = encodeTypedDataDigest({ domain, types, message }) - return this.signMessage(digest, wallet, allSigners, true) - } - - async _signTypedData( - domain: TypedDataDomain, - types: Record>, - message: Record, - chainId?: ChainIdLike, - allSigners: boolean = true - ): Promise { - return this.signTypedData(domain, types, message, chainId, allSigners) - } - - async hasEnoughSigners(chainId?: ChainIdLike): Promise { - const wallet = chainId ? this.getWalletByNetwork(chainId).wallet : this.mainWallet().wallet - const thisConfig = await this.currentConfig(wallet) - return wallet.useConfig(thisConfig!).hasEnoughSigners() - } - - async getFeeOptions( - transaction: utils.Deferrable, - chainId?: ChainIdLike, - allSigners: boolean = true - ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { - const wallet = chainId ? this.getWalletByNetwork(chainId).wallet : this.mainWallet().wallet - - const context = this.options.context - if (!context) { - throw new Error(`missing wallet context`) - } - - // TODO: can we avoid calling `this.currentConfig(wallet)` everytime here.. this is an expensive - // operations and we shouldn't be doing it so liberally. What is the minimum information we require here..? - // and what is the config used for, and how can we optimize..? - - // TODO: prependConfigUpdate also looks like its calling currentConfig() again, so we're doubling this. - - // A few thoughts.. first off, we must add some kind of memoization for this, but with great care, because - // the config might change. This make me think we need some king of "ConfigSource" class, or "ConfigXXX" (name?), - // which we can ask to give us a wallet config. This config would also be used when we update/change a config, - // such that it can memoize, but also since its the sole interface, it will also properly expire or update the config - // in cache as necessary. Further to this, I think we need to only get config details for what is required, and try - // to optimize by using imageHashes of the config everywhere, as this is a much more inexpensive value to fetch. - - const [config, updatedTransaction] = await Promise.all([ - this.currentConfig(wallet), - this.prependConfigUpdate(transaction, chainId, allSigners, true) - ]) - if (!config) { - throw new Error(`missing current config for chain ${chainId}`) - } - - const finalTransactions = await fromTransactionish(context, this.address, updatedTransaction) - return wallet.relayer.getFeeOptions(config, context, ...finalTransactions) - } - - async sendTransaction( - dtransactionish: utils.Deferrable, - chainId?: ChainIdLike, - allSigners: boolean = true, - quote?: FeeQuote, - callback?: SignedTransactionsCallback - ): Promise { - const signedTxs = await this.signTransactions(dtransactionish, chainId, allSigners) - - if (callback) { - const address = addressOf(signedTxs.config, signedTxs.context) - const metaTxnHash = computeMetaTxnHash(address, signedTxs.chainId, ...signedTxs.transactions) - callback(signedTxs, metaTxnHash) - } - - const wallet = chainId ? this.getWalletByNetwork(chainId).wallet : this.mainWallet().wallet - return wallet.sendSignedTransactions(signedTxs, chainId, quote) - } - - async sendTransactionBatch( - transactions: utils.Deferrable, - chainId?: ChainIdLike, - allSigners: boolean = true, - quote?: FeeQuote, - callback?: SignedTransactionsCallback - ): Promise { - return this.sendTransaction(transactions, chainId, allSigners, quote, callback) - } - - async signTransactions( - dtransactionish: utils.Deferrable, - chainId?: ChainIdLike, - allSigners?: boolean - ): Promise { - const wallet = chainId ? this.getWalletByNetwork(chainId).wallet : this.mainWallet().wallet - let currentConfig = await this.currentConfig(wallet) - - if (!currentConfig) { - currentConfig = await this.currentConfig() - if (!currentConfig) { - throw new Error('missing auth chain config') - } - } - - const transactions = await this.prependConfigUpdate(dtransactionish, chainId, allSigners) - return wallet.useConfig(currentConfig).signTransactions(transactions) - } - - async prependConfigUpdate( - dtransactionish: utils.Deferrable, - chainId?: ChainIdLike, - allSigners?: boolean, - skipThresholdCheck?: boolean - ): Promise { - const transaction = await resolveArrayProperties(dtransactionish) - const wallet = chainId ? this.getWalletByNetwork(chainId).wallet : this.mainWallet().wallet - - // TODO: Skip this step if wallet is authWallet - const [thisConfig, lastConfig] = await Promise.all([this.currentConfig(wallet), this.currentConfig()]) - - // We have to skip the threshold check during fee estimation because we - // might not have the necessary signers until the wallet actually signs the - // transactions. - // - // By design, the Torus login key only exists in memory in Sequence wallet - // and cannot generally be assumed to be available. However the Torus login - // key might be required in order to transact on other non-auth chains, - // because the wallet config might not recognize the current session's - // signing key. In these cases, the Torus key is retrieved when the user - // confirms the transaction, which happens after fee estimation. So the - // wallet might not meet the threshold during fee estimation despite - // meeting it at confirmation time. - if (!skipThresholdCheck) { - // See if wallet has enough signer power - const weight = await wallet.useConfig(thisConfig!).signWeight() - if (weight.lt(thisConfig!.threshold) && allSigners) { - throw new NotEnoughSigners( - `wallet combined weight ${weight.toString()} below required threshold ${thisConfig!.threshold.toString()}` - ) - } - } - - // If the wallet is updated, just sign as-is - if ((await wallet.isDeployed()) && isConfigEqual(lastConfig!, thisConfig!)) { - return transaction - } - - // Bundle with configuration update - const transactionParts = (() => { - if (Array.isArray(transaction)) { - return transaction - } else { - return [transaction] - } - })() - - return [...(await wallet.buildUpdateConfigTransaction(lastConfig!, false)), ...transactionParts] - } - - async sendSignedTransactions( - signedTxs: SignedTransactions, - chainId?: ChainIdLike, - quote?: FeeQuote - ): Promise { - const wallet = chainId ? this.getWalletByNetwork(chainId).wallet : this.mainWallet().wallet - return wallet.sendSignedTransactions(signedTxs, undefined, quote) - } - - // updateConfig will build an updated config transaction, update the imageHash on-chain and also publish - // the wallet config to the authChain. Other chains are lazy-updated on-demand as batched transactions. - async updateConfig( - newConfig?: WalletConfig, - index?: boolean, - quote?: FeeQuote, - callback?: SignedTransactionsCallback - ): Promise<[WalletConfig, TransactionResponse | undefined]> { - const authWallet = this.authWallet().wallet - - if (!newConfig) { - newConfig = authWallet.config - } else { - // ensure its normalized - newConfig = sortConfig(newConfig) - } - - // The config is the default config, see if the wallet has been deployed - if (isConfigEqual(authWallet.config, newConfig)) { - if (!(await this.isDeployed())) { - // Deploy the wallet and publish initial configuration - return await authWallet.updateConfig(newConfig, undefined, true, index, quote, callback) - } - } - - // Get latest config, update only if neccesary - const lastConfig = await this.currentConfig() - if (isConfigEqual(lastConfig!, newConfig)) { - return [ - { - ...lastConfig!, - address: this.address - }, - undefined - ] - } - - // Update to new configuration on the authWallet. Other networks will be lazily updated - // once used. The wallet config is also auto-published to the authChain. - const [_, tx] = await authWallet.useConfig(lastConfig!).updateConfig(newConfig, undefined, true, index, quote, callback) - - return [ - { - ...newConfig, - address: this.address - }, - tx - ] - } - - // publishConfig will publish the wallet config to the network via the relayer. Publishing - // the config will also store the entire object of signers. - publishConfig( - indexed?: boolean, - requireFreshSigners: string[] = [], - quote?: FeeQuote, - callback?: SignedTransactionsCallback - ): Promise { - return this.authWallet().wallet.publishConfig(indexed, undefined, requireFreshSigners, quote, callback) - } - - async isDeployed(target?: Wallet | ChainIdLike): Promise { - const wallet = (() => { - if (!target) return this.authWallet().wallet - if ((target).address) { - return target as Wallet - } - return this.getWalletByNetwork(target as NetworkConfig).wallet - })() - return wallet.isDeployed() - } - - // TODO: Split this to it's own class "configProvider" or something - // this process can be done in different ways (caching, api, utils, etc) - async currentConfig(target?: Wallet | NetworkConfig): Promise { - const wallet = (() => { - if (!target) return this.authWallet().wallet - if ((target).address) { - return target as Wallet - } - return this.getWalletByNetwork(target as NetworkConfig).wallet - })() - - return ( - await this.getConfigFinder().findCurrentConfig({ - address: this.address, - provider: wallet.provider, - context: wallet.context, - knownConfigs: [wallet.config] - }) - ).config - } - - getWallets(): { wallet: Wallet; network: NetworkConfig }[] { - return this._wallets - } - - getWalletByNetwork(chainId: ChainIdLike) { - const networkId = getChainId(chainId) - const network = this._wallets.find(w => w.network.chainId === networkId) - if (!network) { - throw new Error(`network ${chainId} not found in wallets list`) - } - return network - } - - // mainWallet is the DefaultChain wallet - mainWallet(): { wallet: Wallet; network: NetworkConfig } { - const found = this._wallets.find(w => w.network.isDefaultChain) - if (!found) { - throw new Error('mainWallet not found') - } - return found - } - - // authWallet is the AuthChain wallet - authWallet(): { wallet: Wallet; network: NetworkConfig } { - const found = this._wallets.find(w => w.network.isAuthChain) - if (!found) { - throw new Error('authChain wallet not found') - } - return found - } - - setNetworks(mainnetNetworks: NetworkConfig[], testnetNetworks: NetworkConfig[] = [], defaultChainId?: string | number): number { - let networks: NetworkConfig[] = [] - this._chainId = undefined // clear memoized value - - // find chain between mainnet and testnet network groups, and set that network group. - // otherwise use mainnetNetworks without changes - if (defaultChainId) { - // force-convert to a number in case someone sends a number in a string like "1" - const defaultChainIdNum = parseInt(defaultChainId as any) - - const foundMainnetNetwork = mainnetNetworks.find(n => n.name === defaultChainId || n.chainId === defaultChainIdNum) - const foundTestnetNetwork = testnetNetworks.find(n => n.name === defaultChainId || n.chainId === defaultChainIdNum) - - if (foundMainnetNetwork || foundTestnetNetwork) { - if (foundMainnetNetwork) { - mainnetNetworks.forEach(n => (n.isDefaultChain = false)) - foundMainnetNetwork.isDefaultChain = true - networks = mainnetNetworks - } else if (foundTestnetNetwork) { - testnetNetworks.forEach(n => (n.isDefaultChain = false)) - foundTestnetNetwork.isDefaultChain = true - networks = testnetNetworks - } - } else { - throw new Error(`unable to set default network as chain '${defaultChainId}' does not exist`) - } - } else { - networks = mainnetNetworks - } - - // assign while validating network list - // TODO - we should remove sortNetworks in the future but this is a breaking change - this.options.networks = ensureValidNetworks(sortNetworks(networks)) - - // Account/wallet instances using the initial configuration and network list - // - // TODO: we can make an optimization where if mainnetNetworks and testnetNetworks lists - // haven't changed between calls, and only the defaultChainId, as well, the group between - // mainnet vs testnet has not changed either -- aka just defaultChainId within a group, - // then we can avoid rebuilding all of these objects and instead just sort them - this._wallets = this.options.networks.map(network => { - const wallet = new Wallet( - { - config: this.options.initialConfig, - context: this.options.context - }, - ...this._signers - ) - - if (network.provider) { - wallet.setProvider(network.provider, network.chainId) - } else if (network.rpcUrl && network.rpcUrl !== '') { - wallet.setProvider(network.rpcUrl, network.chainId) - } else { - throw new Error(`network config is missing provider settings for chainId ${network.chainId}`) - } - - if (isRelayer(network.relayer)) { - wallet.setRelayer(network.relayer) - } else if (isRpcRelayerOptions(network.relayer)) { - wallet.setRelayer(new RpcRelayer({ provider: wallet.provider, ...network.relayer })) - } else { - throw new Error(`network config is missing relayer settings for chainId ${network.chainId}`) - } - - if (network.isDefaultChain) { - this._chainId = network.chainId - this.provider = wallet.provider - } - return { - network: network, - wallet: wallet - } - }) - - // return the default chain id as number - return this.options.networks.find(network => network.isDefaultChain)!.chainId - } - - connect(_: providers.Provider): AbstractSigner { - throw new Error('connect method is not supported in MultiWallet') - } - - signTransaction(_: utils.Deferrable): Promise { - throw new Error('signTransaction method is not supported in MultiWallet, please use signTransactions(...)') - } -} diff --git a/packages/wallet/src/config.ts b/packages/wallet/src/config.ts deleted file mode 100644 index 21ef96213..000000000 --- a/packages/wallet/src/config.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { WalletConfig, DecodedSignature, isDecodedEOASigner, isDecodedFullSigner, isDecodedAddress, decodeSignature, recoverEOASigner } from '@0xsequence/config' -import { BytesLike, ethers, Contract } from 'ethers' -import { Signer } from './signer' -import { walletContracts } from '@0xsequence/abi' -import { isValidSignature } from './validate' -import { WalletContext } from '@0xsequence/network' - -export interface DecodedOwner { - weight: number - address: string -} - -export interface DecodedSigner { - r: string - s: string - v: number - t: number - weight: number -} - -export const fetchImageHash = async (signer: Signer): Promise => { - const address = await signer.getAddress() - const walletContract = new Contract(address, walletContracts.mainModuleUpgradable.abi, await signer.getProvider()) - const currentImageHash = await (walletContract.functions.imageHash.call([]).catch(() => [])) as string[] - return currentImageHash && currentImageHash.length > 0 ? currentImageHash[0] : '' -} - -// recoverConfig decodes a WalletConfig from the subDigest and signature combo. Note: the subDigest argument -// is an encoding format of the original message, encoded by: -// -// subDigest = packMessageData(wallet.address, chainId, ethers.utils.keccak256(message)) -export const recoverConfig = async ( - subDigest: - BytesLike, - signature: string | DecodedSignature, - provider?: ethers.providers.Provider, - context?: WalletContext, - chainId?: number, - walletSignersValidation?: boolean -): Promise => { - const digest = ethers.utils.arrayify(ethers.utils.keccak256(subDigest)) - return recoverConfigFromDigest(digest, signature, provider, context, chainId, walletSignersValidation) -} - -// recoverConfigFromDigest decodes a WalletConfig from a digest and signature combo. Note: the digest -// is the keccak256 of the subDigest, see `recoverConfig` method. -export const recoverConfigFromDigest = async ( - digest: BytesLike, - signature: string | DecodedSignature, - provider?: ethers.providers.Provider, - context?: WalletContext, - chainId?: number, - walletSignersValidation?: boolean -): Promise => { - const decoded = (signature).threshold !== undefined ? signature : decodeSignature(signature as string) - - const signers = await Promise.all(decoded.signers.map(async (s) => { - if (isDecodedEOASigner(s)) { - return { - weight: s.weight, - address: recoverEOASigner(digest, s) - } - } else if (isDecodedAddress(s)) { - return { - weight: s.weight, - address: ethers.utils.getAddress((s).address) - } - } else if (isDecodedFullSigner(s)) { - if (walletSignersValidation) { - if (!(await isValidSignature( - s.address, - ethers.utils.arrayify(digest), - ethers.utils.hexlify(s.signature), - provider, - context, - chainId - ))) throw Error('Invalid signature') - } - - return { - weight: s.weight, - address: s.address - } - } else { - throw Error('Uknown signature type') - } - })) - - return { - threshold: decoded.threshold, - signers: signers - } -} diff --git a/packages/wallet/src/index.ts b/packages/wallet/src/index.ts index fc9c58bd1..c13fd4d63 100644 --- a/packages/wallet/src/index.ts +++ b/packages/wallet/src/index.ts @@ -1,7 +1,6 @@ -export * from './account' -export * from './config' -export * from './remote-signers' + export * from './signer' export * from './utils' -export * from './validate' export * from './wallet' + +export * from './orchestrator/wrapper' diff --git a/packages/wallet/src/orchestrator/wrapper.ts b/packages/wallet/src/orchestrator/wrapper.ts new file mode 100644 index 000000000..5983f9980 --- /dev/null +++ b/packages/wallet/src/orchestrator/wrapper.ts @@ -0,0 +1,42 @@ + +import { commons } from "@0xsequence/core" +import { signers, Status } from "@0xsequence/signhub" +import { ethers } from "ethers" +import { Wallet } from "../wallet" + +// Implements a wrapper for using Sequence wallets as nested signers +// in the signhub orchestrator. It only works for nested signatures. +export class SequenceOrchestratorWrapper implements signers.SapientSigner { + constructor(public wallet: Wallet) {} + + async getAddress(): Promise { + return this.wallet.address + } + + async requestSignature( + _id: string, + message: ethers.utils.BytesLike, + metadata: Object, + callbacks: { + onSignature: (signature: ethers.utils.BytesLike) => void; + onRejection: (error: string) => void; + onStatus: (situation: string) => void + } + ): Promise { + if (!commons.isWalletSignRequestMetadata(metadata)) { + throw new Error('SequenceOrchestratorWrapper only supports nested Sequence signatures') + } + + // For Sequence nested signatures we must use `signDigest` and not `signMessage` + // otherwise the wallet will hash the digest and the signature will be invalid. + callbacks.onSignature(await this.wallet.signDigest(message, { nested: metadata })) + + return true + } + + notifyStatusChange(_i: string, _s: Status, _m: Object): void {} + + suffix(): ethers.utils.BytesLike { + return [ 3 ] + } +} diff --git a/packages/wallet/src/remote-signers/guard-remote-signer.ts b/packages/wallet/src/remote-signers/guard-remote-signer.ts deleted file mode 100644 index dfa1b477e..000000000 --- a/packages/wallet/src/remote-signers/guard-remote-signer.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { BigNumber, ethers, BytesLike } from 'ethers' -import { RemoteSigner } from './remote-signer' -import { Guard } from '@0xsequence/guard' -import { ChainId, ChainIdLike } from '@0xsequence/network' - -const fetch = typeof global === 'object' ? global.fetch : window.fetch - -export class GuardRemoteSigner extends RemoteSigner { - private readonly _guard: Guard - private readonly _address: string - - constructor( - address: string, - hostname: string, - public isSequence: boolean = false, - public defaultChainId: number = ChainId.MAINNET - ) { - super() - this._guard = new Guard(hostname, fetch) - this._address = address - } - - async signMessageWithData(message: BytesLike, auxData?: BytesLike, chainId?: ChainIdLike): Promise { - const request = { - msg: ethers.utils.hexlify(message), - auxData: ethers.utils.hexlify(auxData ? auxData : []), - chainId: chainId ? BigNumber.from(chainId).toNumber() : this.defaultChainId - } - const res = await this._guard.sign({ request: request }) - - // TODO: The guard service doesn't include the EIP2126 signature type on it's reponse - // maybe it should be more explicit and include it? the EIP2126 is only required for non-sequence signatures - return this.isSequence ? res.sig : res.sig + '02' - } - - async getAddress(): Promise { - return this._address - } -} diff --git a/packages/wallet/src/remote-signers/index.ts b/packages/wallet/src/remote-signers/index.ts deleted file mode 100644 index 530a5376c..000000000 --- a/packages/wallet/src/remote-signers/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { RemoteSigner } from './remote-signer' -export { GuardRemoteSigner } from './guard-remote-signer' -export { LocalRemoteSigner } from './local-remote-signer' diff --git a/packages/wallet/src/remote-signers/local-remote-signer.ts b/packages/wallet/src/remote-signers/local-remote-signer.ts deleted file mode 100644 index 303035d06..000000000 --- a/packages/wallet/src/remote-signers/local-remote-signer.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { BytesLike, Signer as AbstractSigner, providers, utils } from 'ethers' -import { RemoteSigner } from './remote-signer' - -export class LocalRemoteSigner extends RemoteSigner { - private readonly _signer: AbstractSigner - - constructor(signer: AbstractSigner) { - super() - this._signer = signer - } - - signMessageWithData(message: BytesLike, _?: BytesLike): Promise { - return this._signer.signMessage(message) - } - - getAddress(): Promise { - return this._signer.getAddress() - } -} diff --git a/packages/wallet/src/remote-signers/remote-signer.ts b/packages/wallet/src/remote-signers/remote-signer.ts deleted file mode 100644 index ff6244eee..000000000 --- a/packages/wallet/src/remote-signers/remote-signer.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { BytesLike, Signer as AbstractSigner, providers, utils } from 'ethers' -import { ChainIdLike } from '@0xsequence/network' - -type Provider = providers.Provider -type TransactionRequest = providers.TransactionRequest -type TransactionResponse = providers.TransactionResponse -type Deferrable = utils.Deferrable - -export abstract class RemoteSigner extends AbstractSigner { - abstract signMessageWithData(message: BytesLike, data?: BytesLike, chainId?: ChainIdLike): Promise - - signMessage(message: BytesLike, chainId?: number): Promise { - return this.signMessageWithData(message) - } - - sendTransaction(_: TransactionRequest): Promise { - throw new Error('sendTransaction method is not supported in RemoteSigner') - } - - signTransaction(_: Deferrable): Promise { - throw new Error('signTransaction method is not supported in RemoteSigner') - } - - connect(_: Provider): AbstractSigner { - throw new Error('connect method is not supported in RemoteSigner') - } - - static signMessageWithData(signer: AbstractSigner, message: BytesLike, data?: BytesLike, chainId?: number): Promise { - if (this.isRemoteSigner(signer)) { - return (signer as RemoteSigner).signMessageWithData(message, data, chainId) - } - return signer.signMessage(message) - } - - static isRemoteSigner(signer: AbstractSigner): signer is RemoteSigner { - return (signer).signMessageWithData !== undefined - } -} diff --git a/packages/wallet/src/signer.ts b/packages/wallet/src/signer.ts index 691484431..50071a3d9 100644 --- a/packages/wallet/src/signer.ts +++ b/packages/wallet/src/signer.ts @@ -1,16 +1,10 @@ -import { BytesLike, Signer as AbstractSigner, providers, TypedDataDomain, TypedDataField } from 'ethers' -import { NetworkConfig, ChainIdLike, WalletContext } from '@0xsequence/network' +import { BytesLike, Signer as AbstractSigner, providers, TypedDataDomain, TypedDataField, ethers } from 'ethers' +import { NetworkConfig, ChainIdLike } from '@0xsequence/network' import { FeeQuote, Relayer } from '@0xsequence/relayer' -import { - SignedTransactions, - Transactionish, - TransactionRequest, - Transaction, - TransactionResponse -} from '@0xsequence/transactions' import { Deferrable } from '@0xsequence/utils' -import { WalletConfig, WalletState } from '@0xsequence/config' +import { commons } from '@0xsequence/core' +// TODO: Move to account ? export abstract class Signer extends AbstractSigner { static isSequenceSigner(cand: any): cand is Signer { return isSequenceSigner(cand) @@ -19,9 +13,9 @@ export abstract class Signer extends AbstractSigner { abstract getProvider(chainId?: number): Promise abstract getRelayer(chainId?: number): Promise - abstract getWalletContext(): Promise - abstract getWalletConfig(chainId?: ChainIdLike): Promise - abstract getWalletState(chainId?: ChainIdLike): Promise + // abstract getWalletContext(): Promise + abstract getWalletConfig(chainId?: ChainIdLike): Promise + // abstract getWalletState(chainId?: ChainIdLike): Promise abstract getNetworks(): Promise @@ -37,53 +31,53 @@ export abstract class Signer extends AbstractSigner { domain: TypedDataDomain, types: Record>, message: Record, - chainId?: ChainIdLike, + chainId: ChainIdLike, allSigners?: boolean ): Promise // sendTransaction takes an unsigned transaction, or list of unsigned transactions, and then has it signed by // the signer, and finally sends it to the relayer for submission to an Ethereum network. abstract sendTransaction( - transaction: Deferrable, + transaction: Deferrable, chainId?: ChainIdLike, allSigners?: boolean, quote?: FeeQuote - ): Promise + ): Promise // sendTransactionBatch provides the ability to send an array/batch of transactions as a single native on-chain transaction. // This method works identically to sendTransaction but offers a different syntax for convience, readability and type clarity. abstract sendTransactionBatch( - transactions: Deferrable, + transactions: Deferrable, chainId?: ChainIdLike, allSigners?: boolean, quote?: FeeQuote - ): Promise + ): Promise // Low-level methods to sign and send/relayer signed transactions separately. The combination of these methods // is like calling just sendTransaction(..) above. Also note that sendSignedTransactions is identical // to calling getRelayer().relay(signedTxs), but included in this interface for convenience. abstract signTransactions( - txs: Deferrable, + txs: Deferrable, chainId?: ChainIdLike, allSigners?: boolean - ): Promise - abstract sendSignedTransactions(signedTxs: SignedTransactions, chainId?: ChainIdLike, quote?: FeeQuote): Promise + ): Promise + abstract sendSignedTransactions(signedTxs: commons.transaction.SignedTransactionBundle, chainId?: ChainIdLike, quote?: FeeQuote): Promise // updateConfig will update the wallet image hash on-chain, aka deploying a smart wallet config to chain. If // newConfig argument is undefined, then it will use the existing config. Config contents will also be // automatically published to the authChain when updating the config image hash. - abstract updateConfig(newConfig?: WalletConfig): Promise<[WalletConfig, TransactionResponse | undefined]> + abstract updateConfig(newConfig?: commons.config.Config): Promise<[commons.config.Config, commons.transaction.TransactionResponse | undefined]> // publishConfig will store the raw WalletConfig object on-chain, note: this may be expensive, // and is only necessary for config data-availability, in case of Account the contents are published // to the authChain. - abstract publishConfig(): Promise + abstract publishConfig(): Promise // isDeployed .. abstract isDeployed(chainId?: ChainIdLike): Promise } -export type SignedTransactionsCallback = (signedTxs: SignedTransactions, metaTxnHash: string) => void +export type SignedTransactionsCallback = (signedTxs: commons.transaction.SignedTransactionBundle, metaTxnHash: string) => void export function isSequenceSigner(signer: AbstractSigner): signer is Signer { const cand = signer as Signer @@ -91,7 +85,6 @@ export function isSequenceSigner(signer: AbstractSigner): signer is Signer { cand && cand.updateConfig !== undefined && cand.publishConfig !== undefined && - cand.getWalletContext !== undefined && cand.getWalletConfig !== undefined ) } diff --git a/packages/wallet/src/validate.ts b/packages/wallet/src/validate.ts deleted file mode 100644 index 8d4febb2a..000000000 --- a/packages/wallet/src/validate.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { ethers, providers } from 'ethers' -import { WalletContext } from '@0xsequence/network' -import { walletContracts } from '@0xsequence/abi' -import { packMessageData } from '@0xsequence/utils' -import { isDecodedEOASigner, isDecodedFullSigner, decodeSignature, compareAddr, addressOf } from '@0xsequence/config' -import { recoverConfigFromDigest } from './config' - -export async function isValidSignature( - address: string, - digest: Uint8Array, - sig: string, - provider?: providers.Provider, - walletContext?: WalletContext, - chainId?: number -): Promise { - // Check if valid EOA signature - // - // TODO: the EOA check here assume its being passed a digest, but its not a correct assumption - // as often the message signing is of a string of text and not a digest. - if ( - isValidEIP712Signature(address, digest, sig) || - isValidEthSignSignature(address, digest, sig) - ) return true - - // Check if valid deployed smart wallet (via erc1271 check) - const erc1271Check = await isValidContractWalletSignature(address, digest, sig, provider) - - if (erc1271Check === undefined) { - // If validity of wallet signature can't be determined - // it could be a signature of a non-deployed sequence wallet - return !!(await isValidSequenceUndeployedWalletSignature(address, digest, sig, walletContext, provider, chainId)) - } - - return erc1271Check -} - -export function isValidEIP712Signature( - address: string, - digest: Uint8Array, - sig: string -): boolean { - try { - return compareAddr( - ethers.utils.recoverAddress( - digest, - ethers.utils.splitSignature(sig) - ), - address - ) === 0 - } catch { - return false - } -} - -export function isValidEthSignSignature( - address: string, - digest: Uint8Array, - sig: string -): boolean { - try { - const subDigest = ethers.utils.keccak256( - ethers.utils.solidityPack( - ['string', 'bytes32'], - ['\x19Ethereum Signed Message:\n32', digest] - ) - ) - return compareAddr( - ethers.utils.recoverAddress( - subDigest, - ethers.utils.splitSignature(sig) - ), - address - ) === 0 - } catch { - return false - } -} - -// Check if valid Smart Contract Wallet signature, via ERC1271 -export async function isValidContractWalletSignature( - address: string, - digest: Uint8Array, - sig: string, - provider?: providers.Provider -) { - if (!provider) return undefined - try { - if ((await provider.getCode(address)) === '0x') { - // Signature validity can't be determined - return undefined - } - - const wallet = new ethers.Contract(address, walletContracts.erc1271.abi, provider) - const response = await wallet.isValidSignature(digest, sig) - return walletContracts.erc1271.returns.isValidSignatureBytes32 === response - } catch { - return false - } -} - -export async function isValidSequenceUndeployedWalletSignature( - address: string, - digest: Uint8Array, - sig: string, - walletContext?: WalletContext, - provider?: providers.Provider, - chainId?: number -) { - if (!provider && !chainId) return undefined // Signature validity can't be determined - if (!walletContext) return undefined // Signature validity can't be determined - - try { - const cid = chainId ? chainId : (await provider!.getNetwork()).chainId - const signature = decodeSignature(sig) - const subDigest = ethers.utils.arrayify(ethers.utils.keccak256(packMessageData(address, cid, digest))) - const config = await recoverConfigFromDigest(subDigest, signature, provider, walletContext, chainId, true) - const weight = signature.signers.reduce((v, s) => isDecodedEOASigner(s) || isDecodedFullSigner(s) ? v + s.weight : v, 0) - return compareAddr(addressOf(config, walletContext), address) === 0 && weight >= signature.threshold - } catch { - return false - } -} diff --git a/packages/wallet/src/wallet.ts b/packages/wallet/src/wallet.ts index 7369e396a..3345569eb 100644 --- a/packages/wallet/src/wallet.ts +++ b/packages/wallet/src/wallet.ts @@ -1,809 +1,362 @@ -import { - BytesLike, - BigNumber, BigNumberish, - ethers, - Signer as AbstractSigner, - providers, - utils, - TypedDataDomain, TypedDataField, -} from 'ethers' - +import { ethers } from "ethers" +import { commons, v1, v2 } from "@0xsequence/core" +import { isSignerStatusSigned, Orchestrator, Status } from "@0xsequence/signhub" +import { Deferrable, subDigestOf } from "@0xsequence/utils" +import { FeeQuote, Relayer, SimulateResult } from "@0xsequence/relayer" import { walletContracts } from '@0xsequence/abi' -import { - Transaction, - Transactionish, - TransactionRequest, - readSequenceNonce, - appendNonce, - sequenceTxAbiEncode, - SignedTransactions, - computeMetaTxnHash, - digestOfTransactionsNonce, - decodeNonce, - fromTransactionish, - TransactionResponse -} from '@0xsequence/transactions' - -import { FeeQuote, Relayer } from '@0xsequence/relayer' - -import { - ChainIdLike, - WalletContext, - JsonRpcSender, - NetworkConfig, - isJsonRpcProvider, - sequenceContext, - getChainId, - JsonRpcProvider -} from '@0xsequence/network' - -import { - WalletConfig, - WalletState, - addressOf, - sortConfig, - compareAddr, - imageHash, - isUsableConfig, - DecodedSignature, - encodeSignature, - joinSignatures, - recoverEOASigner, - decodeSignature, - isDecodedSigner, - isDecodedFullSigner -} from '@0xsequence/config' - -import { encodeTypedDataDigest, subDigestOf } from '@0xsequence/utils' - -import { RemoteSigner } from './remote-signers' - -import { resolveArrayProperties } from './utils' - -import { isSequenceSigner, Signer, SignedTransactionsCallback } from './signer' -import { fetchImageHash } from '.' - -type BlockTag = providers.BlockTag -type ConnectionInfo = utils.ConnectionInfo -type Deferrable = utils.Deferrable - -// Wallet is a signer interface to a Smart Contract based Ethereum account. -// -// Wallet allows managing the account/wallet sub-keys, wallet address, signing -// messages, signing transactions and updating/deploying the wallet config on a specific chain. -// -// Wallet instances represent a wallet at a particular config-state, in someways, the Wallet -// instance is immutable, and if you update the config, then you'll need to call useConfig() -// to instantiate a new Wallet instance with the updated config. - -export interface WalletOptions { - // config is the wallet multi-sig configuration. Note: the first config of any wallet - // before it is deployed is used to derive it's the account address of the wallet. - config: WalletConfig - - // context is the WalletContext of deployed wallet-contract modules for the Smart Wallet - context?: WalletContext - - // strict mode will ensure the WalletConfig is usable otherwise throw (on by default) - strict?: boolean -} - -export class Wallet extends Signer { - readonly context: WalletContext - readonly config: WalletConfig - - private readonly _signers: AbstractSigner[] +import { resolveArrayProperties } from "./utils" - // provider is an Ethereum Json RPC provider that is connected to a particular network (aka chain) - // and access to the signer for signing transactions. - provider: providers.JsonRpcProvider +export type WalletOptions< + T extends commons.signature.Signature, + Y extends commons.config.Config, + Z extends commons.signature.UnrecoveredSignature +> = { + // Sequence version configurator + coders: { + config: commons.config.ConfigCoder, + signature: commons.signature.SignatureCoder + } - // sender is a minimal Json RPC sender interface. It's here for convenience for other web3 - // interfaces to use. - sender: JsonRpcSender + context: commons.context.WalletContext, + config: Y, - // relayer dispatches transactions to an Ethereum node directly - // or through a remote transaction Web Service. - relayer: Relayer + chainId: ethers.BigNumberish, + address: string - // chainId is the node network id, used for memoization - chainId?: number + orchestrator: Orchestrator + reader?: commons.reader.Reader - constructor(options: WalletOptions, ...signers: (BytesLike | AbstractSigner)[]) { - super() + provider?: ethers.providers.Provider + relayer?: Relayer +} - const { config, context, strict } = options +const statusToSignatureParts = (status: Status) => { + const parts = new Map() - if (context) { - this.context = { ...context } - } else { - // default context is to use @0xsequence/network deployed context - this.context = { ...sequenceContext } - } + for (const signer of Object.keys(status.signers)) { + const value = status.signers[signer] + if (isSignerStatusSigned(value)) { + const suffix = ethers.utils.arrayify(value.suffix) + const suffixed = ethers.utils.solidityPack( + ['bytes', 'bytes'], + [value.signature, suffix] + ) - if (strict === true) { - this.context.nonStrict = undefined - } else if (strict === false) { - this.context.nonStrict = true - } - if (!this.context.nonStrict && !isUsableConfig(config)) { - throw new Error('wallet config is not usable (strict mode)') + parts.set(signer, { signature: suffixed, isDynamic: suffix.length !== 1 || suffix[0] !== 2 }) } - - this.config = sortConfig(config) - this._signers = signers.map(s => (AbstractSigner.isSigner(s) ? s : new ethers.Wallet(s))) - - // cache wallet config for future imageHash lookups - this.imageHash - } - - // useConfig creates a new Wallet instance with the provided config, and uses the current provider - // and relayer. It's common to initialize a counter-factual / undeployed wallet by initializing - // it with the Wallet's init config, then calling useConfig() with the most-up-to-date config, - // ie. new Wallet({ config: initConfig }).useConfig(latestConfig).useSigners(signers) - useConfig(config: WalletConfig, strict?: boolean): Wallet { - return new Wallet({ config, context: this.context, strict }, ...this._signers) - .setProvider(this.provider, this.chainId) - .setRelayer(this.relayer) - } - - useSigners(...signers: (BytesLike | AbstractSigner)[]): Wallet { - return new Wallet({ config: this.config, context: this.context }, ...signers) - .setProvider(this.provider, this.chainId) - .setRelayer(this.relayer) } - // connect is a short-hand to create an Account instance and set the provider and relayer. - // - // The connect method is defined on the AbstractSigner as connect(Provider): AbstractSigner - connect(provider: providers.Provider, relayer?: Relayer): Wallet { - if (isJsonRpcProvider(provider)) { - return new Wallet({ config: this.config, context: this.context }, ...this._signers) - .setProvider(provider, this.chainId) - .setRelayer(relayer!) - } else { - throw new Error('Wallet provider argument is expected to be a JsonRpcProvider') - } - } + return parts +} - // setProvider assigns a json-rpc provider to this wallet instance - setProvider(provider: providers.JsonRpcProvider | ConnectionInfo | string, chainId?: number): Wallet { - if (provider === undefined) return this - if (providers.Provider.isProvider(provider)) { - this.provider = provider - this.sender = new JsonRpcSender(provider) - } else { - const jsonProvider = new JsonRpcProvider(provider, { chainId, blockCache: true }) - this.provider = jsonProvider - this.sender = new JsonRpcSender(jsonProvider) +export type WalletV2 = Wallet +export type WalletV1 = Wallet + +/** + * The wallet is the minimum interface to interact with a single Sequence wallet/contract. + * it doesn't have any knowledge of any on-chain state, instead it relies solely on the information + * provided by the user. This building block is used to create higher level abstractions. + * + * Wallet can also be used to create Sequence wallets, but it's not recommended to use it directly + * + * @notice: TODO: This class is meant to replace the one in ../wallet.ts !!! + * + */ +export class Wallet< + Y extends commons.config.Config = commons.config.Config, + T extends commons.signature.Signature = commons.signature.Signature, + Z extends commons.signature.UnrecoveredSignature = commons.signature.UnrecoveredSignature +> extends ethers.Signer { + public context: commons.context.WalletContext + public config: Y + public address: string + public chainId: ethers.BigNumberish + + public provider?: ethers.providers.Provider + public relayer?: Relayer + + public coders: { + signature: commons.signature.SignatureCoder + config: commons.config.ConfigCoder + } + + private orchestrator: Orchestrator + private _reader?: commons.reader.Reader + + constructor(options: WalletOptions) { + if (ethers.constants.Zero.eq(options.chainId) && !options.coders.signature.supportsNoChainId) { + throw new Error(`Sequence version ${options.config.version} doesn't support chainId 0`) } - this.chainId = chainId // reset chainId value - return this - } - // setRelayer assigns a Sequence transaction relayer to this wallet instance - setRelayer(relayer: Relayer): Wallet { - if (relayer === undefined) return this - this.relayer = relayer - return this - } + super() - async getProvider(chainId?: number): Promise { - if (chainId) await this.getChainIdNumber(chainId) - return this.provider - } + this.context = options.context + this.config = options.config + this.orchestrator = options.orchestrator + this.coders = options.coders + this.address = options.address + this.chainId = options.chainId + this.provider = options.provider + this.relayer = options.relayer - async getRelayer(chainId?: number): Promise { - if (chainId) await this.getChainIdNumber(chainId) - return this.relayer + this._reader = options.reader } - async getWalletContext(): Promise { - return this.context + static newWallet< + Y extends commons.config.Config = commons.config.Config, + T extends commons.signature.Signature = commons.signature.Signature, + Z extends commons.signature.UnrecoveredSignature = commons.signature.UnrecoveredSignature + >(options: Omit, 'address'>): Wallet { + const address = commons.context.addressOf(options.context, options.coders.config.imageHashOf(options.config)) + return new Wallet({ ...options, address }) } - async getWalletConfig(chainId?: ChainIdLike): Promise { - chainId = await this.getChainIdNumber(chainId) - const config = { - ...this.config, - chainId - } - return [config] + reader(): commons.reader.Reader { + if (this._reader) return this._reader + if (!this.provider) throw new Error("Wallet status provider requires a provider") + return new commons.reader.OnChainReader(this.provider) } - async getWalletState(_?: ChainIdLike): Promise { - const [address, chainId, isDeployed] = await Promise.all([this.getAddress(), this.getChainId(), this.isDeployed()]) - - const state: WalletState = { - context: this.context, - config: this.config, - address: address, - chainId: chainId, - deployed: isDeployed, - imageHash: this.imageHash, - lastImageHash: isDeployed ? await fetchImageHash(this) : undefined - } - - // TODO: set published boolean by checking if we have the latest logs - // that compute to the same hash as in lastImageHash - - return [state] + setConfig(config: Y) { + this.config = config } - // connected reports if json-rpc provider has been connected - get connected(): boolean { - return this.sender !== undefined + setOrchestrator(orchestrator: Orchestrator) { + this.orchestrator = orchestrator } - // address returns the address of the wallet account address - get address(): string { - return addressOf(this.config, this.context) + setAddress(address: string) { + this.address = address } - // imageHash is the unique hash of the WalletConfig - get imageHash(): string { - return imageHash(this.config) + getSigners(): Promise { + return this.orchestrator.getSigners() } - // getAddress returns the address of the wallet account address - // - // The getAddress method is defined on the AbstractSigner async getAddress(): Promise { return this.address } - // getSigners returns the list of public account addresses to the currently connected - // signer objects for this wallet. Note: for a complete list of configured signers - // on the wallet, query getWalletConfig() - async getSigners(): Promise { - if (!this._signers || this._signers.length === 0) { - return [] - } - return Promise.all(this._signers.map(s => s.getAddress().then(s => ethers.utils.getAddress(s)))) - } + async decorateTransactions( + bundle: commons.transaction.IntendedTransactionBundle + ): Promise { + if (await this.reader().isDeployed(this.address)) return bundle - // chainId returns the network connected to this wallet instance - async getChainId(): Promise { - if (this.chainId) return this.chainId - if (!this.provider) { - throw new Error('provider is not set, first connect a provider') - } + const deployTx = this.buildDeployTransaction() - this.chainId = (await this.provider.getNetwork()).chainId - return this.chainId - } + // TODO: If entrypoint is guestModule we can flatten the bundle + // and avoid calling guestModule twice - async getNetworks(): Promise { - const chainId = await this.getChainId() - return [ - { - chainId: chainId, - name: '', - rpcUrl: '' - } - ] - } - - // getNonce returns the transaction nonce for this wallet, via the relayer - async getNonce(blockTag?: BlockTag, space?: BigNumberish): Promise { - return this.relayer.getNonce(this.config, this.context, space, blockTag) - } - - // getTransactionCount returns the number of transactions (aka nonce) - // - // getTransactionCount method is defined on the AbstractSigner - async getTransactionCount(blockTag?: BlockTag): Promise { - const encodedNonce = await this.getNonce(blockTag, 0) - const [_, decodedNonce] = decodeNonce(encodedNonce) - return ethers.BigNumber.from(decodedNonce).toNumber() - } - - // sendTransaction will dispatch the transaction to the relayer for submission to the network. - async sendTransaction( - transaction: Deferrable, - chainId?: ChainIdLike, - allSigners?: boolean, - quote?: FeeQuote, - callback?: SignedTransactionsCallback, - waitForReceipt?: boolean - ): Promise { - const signedTxs = await this.signTransactions(transaction, chainId, allSigners) - if (callback) { - const address = addressOf(signedTxs.config, signedTxs.context) - const metaTxnHash = computeMetaTxnHash(address, signedTxs.chainId, ...signedTxs.transactions) - callback(signedTxs, metaTxnHash) + return { + entrypoint: this.context.guestModule, + chainId: this.chainId, + intent: bundle.intent, + transactions: [ + ...deployTx.transactions, + { + to: bundle.entrypoint, + data: commons.transaction.encodeBundleExecData(bundle), + gasLimit: 0, + delegateCall: false, + revertOnError: true, + value: 0 + } + ] } - return this.relayer.relay(signedTxs, quote, waitForReceipt) - } - - // sendTransactionBatch is a sugar for better readability, but is the same as sendTransaction - async sendTransactionBatch( - transactions: Deferrable, - chainId?: ChainIdLike, - allSigners: boolean = true, - quote?: FeeQuote, - callback?: SignedTransactionsCallback, - waitForReceipt?: boolean - ): Promise { - return this.sendTransaction(transactions, chainId, allSigners, quote, callback, waitForReceipt) } - // signTransactions will sign a Sequence transaction with the wallet signers - // - // NOTE: the txs argument of type Transactionish can accept one or many transactions. - async signTransactions( - txs: Deferrable, - chainId?: ChainIdLike, - allSigners?: boolean - ): Promise { - const signChainId = await this.getChainIdNumber(chainId) - - const transaction = await resolveArrayProperties(txs) + buildDeployTransaction(): commons.transaction.TransactionBundle { + const imageHash = this.coders.config.imageHashOf(this.config) - if (!this.provider) { - throw new Error('missing provider') - } - if (!this.relayer) { - throw new Error('missing relayer') + if (commons.context.addressOf(this.context, imageHash) !== this.address) { + throw new Error(`First address of config ${imageHash} doesn't match wallet address ${this.address}`) } - // Convert Transactionish into Sequence transactions - let stx = await fromTransactionish(this.context, this.address, transaction) + return Wallet.buildDeployTransaction(this.context, imageHash) + } - // Fill missing gas limits via simulation if needed - if (stx.some(transaction => transaction.gasLimit === undefined)) { - const results = await this.relayer.simulate(this.address, ...stx) - for (const i in stx) { - if (stx[i].gasLimit === undefined) { - stx[i].gasLimit = results[i].gasLimit - } + deploy(): Promise { + const deployTx = this.buildDeployTransaction() + if (!this.relayer) throw new Error("Wallet deploy requires a relayer") + return this.relayer.relay({ ...deployTx, chainId: this.chainId, intent: { + id: ethers.utils.hexlify(ethers.utils.randomBytes(32)), + wallet: this.address } - } - - // If provided nonce append it to all other transactions - // otherwise get next nonce for this wallet - const providedNonce = readSequenceNonce(...stx) - const nonce = providedNonce ? providedNonce : await this.getNonce() - stx = appendNonce(stx, nonce) + }) + } - // Get transactions digest - const digest = digestOfTransactionsNonce(nonce, ...stx) + static buildDeployTransaction( + context: commons.context.WalletContext, + imageHash: string, + ): commons.transaction.TransactionBundle { + const factoryInterface = new ethers.utils.Interface(walletContracts.factory.abi) - // Bundle with signature return { - digest: digest, - chainId: signChainId, - context: this.context, - config: this.config, - transactions: stx, - nonce, - signature: await this.sign(digest, true, chainId, allSigners) + entrypoint: context.guestModule, + transactions: [{ + to: context.factory, + data: factoryInterface.encodeFunctionData(factoryInterface.getFunction('deploy'), + [context.mainModule, imageHash] + ), + gasLimit: 100000, + delegateCall: false, + revertOnError: true, + value: 0 + }] } } - async sendSignedTransactions(signedTxs: SignedTransactions, chainId?: ChainIdLike, quote?: FeeQuote): Promise { - if (!this.relayer) { - throw new Error('relayer is not set, first connect a relayer') + async buildUpdateConfigurationTransaction(config: Y): Promise { + if (this.coders.config.update.isKindUsed) { + const implementation = await this.reader().implementation(this.address) + const isLaterUpdate = implementation && implementation === this.context.mainModuleUpgradable + return this.coders.config.update.buildTransaction(this.address, config, this.context, isLaterUpdate ? 'later' : 'first') } - await this.getChainIdNumber(chainId) - return this.relayer.relay(signedTxs, quote) - } - // signMessage will sign a message for a particular chainId with the wallet signers - // - // NOTE: signMessage(message: Bytes | string): Promise is defined on AbstractSigner - async signMessage(message: BytesLike, chainId?: ChainIdLike, allSigners?: boolean, isDigest: boolean = false): Promise { - const data = typeof message === 'string' && !message.startsWith('0x') ? ethers.utils.toUtf8Bytes(message) : message - return this.sign(data, isDigest, chainId, allSigners) + return this.coders.config.update.buildTransaction(this.address, config, this.context) } - async signTypedData( - domain: TypedDataDomain, - types: Record>, - message: Record, - chainId?: ChainIdLike, - allSigners?: boolean - ): Promise { - const signChainId = await this.getChainIdNumber(chainId) - - const domainChainId = domain.chainId ? BigNumber.from(domain.chainId).toNumber() : undefined - if (domainChainId && domainChainId !== signChainId) { - throw new Error(`signTypedData: domain.chainId (${domain.chainId}) is expected to be ${signChainId}`) + async signDigest( + digest: ethers.utils.BytesLike, + request?: { + message?: ethers.utils.BytesLike, + transactions?: commons.transaction.Transaction[] + nested?: commons.WalletSignRequestMetadata } - - const hash = encodeTypedDataDigest({ domain, types, message }) - return this.sign(hash, true, signChainId, allSigners) - } - - async _signTypedData( - domain: TypedDataDomain, - types: Record>, - message: Record, - chainId?: ChainIdLike, - allSigners?: boolean ): Promise { - return this.signTypedData(domain, types, message, chainId, allSigners) - } - - async subDigest(digest: BytesLike, chainId?: ChainIdLike): Promise { - const solvedChainId = await this.getChainIdNumber(chainId) - return ethers.utils.arrayify(subDigestOf(this.address, solvedChainId, digest)) - } - - // sign is a helper method to sign a payload with the wallet signers - async sign(msg: BytesLike, isDigest: boolean = true, chainId?: ChainIdLike, allSigners?: boolean): Promise { - const signChainId = await this.getChainIdNumber(chainId) - - const digest = isDigest ? msg : ethers.utils.keccak256(msg) - - // Generate sub-digest - const subDigest = await this.subDigest(digest, chainId) - - // Sign sub-digest using a set of signers and some optional data - const signWith = async (signers: AbstractSigner[], auxData?: string): Promise => { - const signersAddr = await Promise.all(signers.map(s => s.getAddress())) - const parts = await Promise.all( - this.config.signers.map(async s => { - try { - const signer = signers[signersAddr.indexOf(s.address)] - - // Is not a signer, return config entry as-is - if (!signer) { - return s - } - - // Is another Sequence wallet as signer, sign and append '03' (ERC1271 type) - if (isSequenceSigner(signer)) { - if (signer === this) throw Error("Can't sign transactions for self") - const signature = (await signer.signMessage(subDigest, signChainId, allSigners, true)) + '03' - - return { - ...s, - signature: signature - } - } - - // Is remote signer, call and deduce signature type - if (RemoteSigner.isRemoteSigner(signer)) { - const signature = await signer.signMessageWithData(subDigest, auxData, signChainId) - - try { - // Check if signature can be recovered as EOA signature - const isEOASignature = recoverEOASigner(subDigest, { weight: s.weight, signature: signature }) === s.address - - if (isEOASignature) { - // Exclude address on EOA signatures - return { - weight: s.weight, - signature: signature - } - } - } catch {} - - // Prepare signature for full encoding - return { - ...s, - signature: signature - } - } - - // Is EOA signer - return { - weight: s.weight, - signature: (await signer.signMessage(subDigest)) + '02' - } - } catch (err) { - if (allSigners) { - throw err - } else { - console.warn(`Skipped signer ${s.address}`) - return s - } - } - }) - ) - - return { - threshold: this.config.threshold, - signers: parts - } + // The subdigest may be statically defined on the configuration + // in that case we just encode the proof, no need to sign anything + const subdigest = subDigestOf(this.address, this.chainId, digest) + if (this.coders.config.hasSubdigest(this.config, subdigest)) { + return this.coders.signature.encodeSigners(this.config, new Map(), [subdigest], this.chainId).encoded } - // Sign message first using localSigners - const localSigners = this._signers.filter(s => !RemoteSigner.isRemoteSigner(s)) - const localSignature = await signWith(localSigners, this.packMsgAndSig(digest, [], signChainId)) - - // Skip remote signers if we already meet threshold - const totalWeight = localSignature.signers.filter(isDecodedSigner).reduce((totalWeight, signer) => totalWeight + signer.weight, 0) - if (totalWeight >= this.config.threshold) { - return encodeSignature(localSignature) + // We build the metadata object, this contains additional information + // that may be needed to sign the digest (by the other signers, or by the guard) + const metadata: commons.WalletSignRequestMetadata = { + digest, + chainId: this.chainId, + address: this.address, + config: this.config, + ...request } - // include local signatures for remote signers - const remoteSigners = this._signers.filter(s => RemoteSigner.isRemoteSigner(s)) - const remoteSignature = await signWith( - remoteSigners, - this.packMsgAndSig(digest, encodeSignature(localSignature), signChainId) - ) - - // Aggregate both local and remote signatures - return encodeSignature(joinSignatures(localSignature, remoteSignature)) - } - - // signWeight will return the total weight of all signers available based on the config - async signWeight(): Promise { - const signers = await this.getSigners() - return signers.reduce((p, s) => { - const sconfig = this.config.signers.find(c => c.address === s) - if (!sconfig) return p - return p.add(sconfig.weight) - }, ethers.constants.Zero) - } - - async isDeployed(chainId?: ChainIdLike): Promise { - await this.getChainIdNumber(chainId) - const walletCode = await this.provider.getCode(this.address) - return !!walletCode && walletCode !== '0x' - } + // We ask the orchestrator to sign the digest, as soon as we have enough signature parts + // to reach the threshold we returns true, that means the orchestrator will stop asking + // and we can encode the final signature + const subdigestBytes = ethers.utils.arrayify(subdigest) + const signature = await this.orchestrator.signMessage({ + candidates: this.coders.config.signersOf(this.config).map((s) => s.address), + message: subdigestBytes, + metadata, + callback: (status: Status, onNewMetadata: (metadata: Object) => void): boolean => { + const parts = statusToSignatureParts(status) + + const newMetadata = { ...metadata, parts } + onNewMetadata(newMetadata) + + return this.coders.signature.hasEnoughSigningPower(this.config, parts) + } + }) - // updateConfig will build an updated config transaction and send it to the Ethereum - // network via the relayer. Note, the updated wallet config is stored as an image hash, - // unlike `publishConfig` which will store the entire WalletConfig object in logs. - async updateConfig( - config?: WalletConfig, - nonce?: number, - publish = false, - indexed?: boolean, - quote?: FeeQuote, - callback?: SignedTransactionsCallback, - waitForReceipt?: boolean - ): Promise<[WalletConfig, TransactionResponse]> { - if (!config) config = this.config - - const [txs, n] = await Promise.all([this.buildUpdateConfigTransaction(config, publish, indexed), nonce ?? this.getNonce()]) - - return [ - { address: this.address, ...config }, - await this.sendTransaction(appendNonce(txs, n), undefined, undefined, quote, callback, waitForReceipt) - ] + const parts = statusToSignatureParts(signature) + return this.coders.signature.encodeSigners(this.config, parts, [], this.chainId).encoded } - // publishConfig will publish the current wallet config to the network via the relayer. - // Publishing the config will also store the entire object of signers. - async publishConfig( - indexed?: boolean, - nonce?: number, - requireFreshSigners: string[] = [], - quote?: FeeQuote, - callback?: SignedTransactionsCallback, - waitForReceipt?: boolean - ): Promise { - return this.sendTransaction( - this.config.address - ? this.buildPublishConfigTransaction(this.config, indexed, nonce) - : await this.buildPublishSignersTransaction(indexed, nonce, requireFreshSigners), - undefined, - undefined, - quote, - callback, - waitForReceipt - ) + signMessage(message: ethers.BytesLike): Promise { + return this.signDigest(ethers.utils.keccak256(message), { message }) } - // buildUpdateConfigTransaction creates a transaction to update the imageHash of the wallet's config - // on chain. Note, the transaction is not sent to the network by this method. - // - // The `publish` argument when true will also store the contents of the WalletConfig to a chain's logs. - async buildUpdateConfigTransaction(config: WalletConfig, publish = false, indexed?: boolean): Promise { - if (!this.context.nonStrict && !isUsableConfig(config)) throw new Error('wallet config is not usable (strict mode)') - - const isUpgradable = await (async () => { - try { - const implementation = await this.provider.getStorageAt( - this.address, - ethers.utils.defaultAbiCoder.encode(['address'], [this.address]) - ) - return compareAddr(implementation, this.context.mainModuleUpgradable) === 0 - } catch { - return false - } - })() - - const walletInterface = new utils.Interface(walletContracts.mainModule.abi) - - // empirically, this seems to work for the tests: - // const gasLimit = 100000 + 1800 * config.signers.length - // - // but we're going to play it safe with this instead: - const gasLimit = 2 * (100000 + 1800 * config.signers.length) - - const preTransaction = isUpgradable - ? [] - : [ - { - delegateCall: false, - revertOnError: true, - gasLimit: ethers.constants.Zero, - to: this.address, - value: ethers.constants.Zero, - data: walletInterface.encodeFunctionData(walletInterface.getFunction('updateImplementation'), [ - this.context.mainModuleUpgradable - ]) - } - ] - - const mainModuleInterface = new utils.Interface(walletContracts.mainModuleUpgradable.abi) - - const transaction = { - delegateCall: false, - revertOnError: true, - gasLimit: ethers.constants.Zero, - to: this.address, - value: ethers.constants.Zero, - data: mainModuleInterface.encodeFunctionData(mainModuleInterface.getFunction('updateImageHash'), [imageHash(config)]) + signTransactionBundle(bundle: commons.transaction.TransactionBundle): Promise { + if (bundle.entrypoint !== this.address) { + throw new Error(`Invalid entrypoint: ${bundle.entrypoint} !== ${this.address}`) } - const postTransaction = publish ? await this.buildPublishConfigTransaction(config, indexed) : [] + return this.signTransactions(bundle.transactions, bundle.nonce) + } - const transactions = [...preTransaction, transaction, ...postTransaction] + async signTransactions(txs: Deferrable, nonce?: ethers.BigNumberish): Promise { + const transaction = await resolveArrayProperties(txs) + const transactions = commons.transaction.fromTransactionish(this.address, transaction) - // If update config reguires a single transaction - // skip nested selfExecute bundle - if (transactions.length === 1) { - return transactions - } - - return [ - { - delegateCall: false, - revertOnError: false, - gasLimit: gasLimit, + // NOTICE: If the `transactions` list is empty, then we add a dummy transaction + // otherwise the `TxExecuted` event will not be emitted, and we won't be able to + // find the transaction hash + if (transactions.length === 0) { + transactions.push({ to: this.address, - value: ethers.constants.Zero, - data: walletInterface.encodeFunctionData(walletInterface.getFunction('selfExecute'), [sequenceTxAbiEncode(transactions)]) - } - ] - } - - buildPublishConfigTransaction(config: WalletConfig, indexed: boolean = true, nonce?: number): Transaction[] { - const sequenceUtilsInterface = new utils.Interface(walletContracts.sequenceUtils.abi) - return [ - { + data: '0x', + value: 0, + gasLimit: 0, delegateCall: false, - revertOnError: true, - gasLimit: ethers.constants.Zero, - to: this.context.sequenceUtils!, - value: ethers.constants.Zero, - nonce: nonce, - data: sequenceUtilsInterface.encodeFunctionData(sequenceUtilsInterface.getFunction('publishConfig'), [ - this.address, - config.threshold, - sortConfig(config).signers.map(s => ({ - weight: s.weight, - signer: s.address - })), - indexed - ]) - } - ] - } - - async buildPublishSignersTransaction( - indexed: boolean = true, - nonce?: number, - requireFreshSigners: string[] = [] - ): Promise { - const sequenceUtilsInterface = new utils.Interface(walletContracts.sequenceUtils.abi) - const requireFreshSignersInterface = new utils.Interface(walletContracts.requireFreshSigner.abi) - - const message = ethers.utils.randomBytes(32) - - const signature = await this.signMessage(message, this.chainId, false) - - // TODO: This is only required because RequireUtils doesn't support dynamic signatures - // remove this filtering of dynamic once a new version of RequireUtils is deployed - const decodedSignature = decodeSignature(signature) - const filteredSignature = encodeSignature({ - threshold: decodedSignature.threshold, - signers: decodedSignature.signers.map((s, i) => { - if (isDecodedFullSigner(s)) { - const a = this.config.signers[i] - return { - weight: a.weight, - address: a.address - } - } - - return s + revertOnError: true }) - }) - - const contextRequireFreshSigner = this.context.libs?.requireFreshSigner - if (requireFreshSigners.length > 0 && contextRequireFreshSigner === undefined) { - throw Error('requireFreshSigners missing library') } - return [ - ...requireFreshSigners.map(signer => ({ - delegateCall: false, - revertOnError: true, - gasLimit: ethers.constants.Zero, - to: contextRequireFreshSigner!, - value: ethers.constants.Zero, - nonce: nonce, - data: requireFreshSignersInterface.encodeFunctionData(requireFreshSignersInterface.getFunction('requireFreshSigner'), [ - signer - ]) - })), - { - delegateCall: false, - revertOnError: true, - gasLimit: ethers.constants.Zero, - to: this.context.sequenceUtils!, - value: ethers.constants.Zero, - nonce: nonce, - data: sequenceUtilsInterface.encodeFunctionData(sequenceUtilsInterface.getFunction('publishInitialSigners'), [ - this.address, - ethers.utils.keccak256(message), - this.config.signers.length, - filteredSignature, - indexed - ]) - } - ] - } - - // getChainIdFromArgument will return the chainId of the argument, as well as ensure - // we're not providing an invalid chainId that isn't connected to this wallet. - private async getChainIdNumber(chainId?: ChainIdLike): Promise { - if (!chainId) { - // it's valid for chainId argument to be undefined, in which case - // we will use the connected value - return await this.getChainId() + let defaultedNonce = nonce + if (defaultedNonce === undefined) { + defaultedNonce = await this.reader().nonce(this.address, 0) + if (defaultedNonce === undefined) throw new Error("Unable to determine nonce") } - const id = getChainId(chainId) - - if (this.context.nonStrict) { - // in non-strict mode, just return the chainId from argument - return id - } + const digest = commons.transaction.digestOfTransactions(defaultedNonce, transactions) + const signature = await this.signDigest(digest, { transactions }) - const connectedChainId = await this.getChainId() - if (connectedChainId !== id) { - throw new Error(`the specified chainId ${id} does not match the wallet's connected chainId ${connectedChainId}`) + return { + intent: { + // Maybe is better if signDigest returns the subdigest directly + id: subDigestOf(this.address, this.chainId, digest), + wallet: this.address + }, + chainId: this.chainId, + transactions, + entrypoint: this.address, + nonce: defaultedNonce, + signature } - - return connectedChainId } - // packMsgAndSig is used by RemoteSigners to include details as a string blob of data. - private packMsgAndSig(msg: BytesLike, sig: BytesLike, chainId: BigNumberish): string { - return ethers.utils.defaultAbiCoder.encode(['address', 'uint256', 'bytes', 'bytes'], [this.address, chainId, msg, sig]) + async sendSignedTransaction( + signedBundle: commons.transaction.IntendedTransactionBundle, + quote?: FeeQuote + ): Promise { + if (!this.relayer) throw new Error("Wallet sendTransaction requires a relayer") + return this.relayer.relay(signedBundle, quote) } - signTransaction(_: Deferrable): Promise { - throw new Error('signTransaction method is not supported in Wallet, please use signTransactions(...)') + async sendTransaction( + txs: Deferrable, + nonce?: ethers.BigNumberish, + quote?: FeeQuote + ): Promise { + const signed = await this.signTransactions(txs, nonce) + const decorated = await this.decorateTransactions(signed) + return this.sendSignedTransaction(decorated, quote) + } + + async fillGasLimits( + txs: Deferrable + ): Promise { + const transaction = await resolveArrayProperties(txs) + const transactions = commons.transaction.fromTransactionish(this.address, transaction) + const relayer = this.relayer + if (!relayer) throw new Error("Wallet fillGasLimits requires a relayer") + + const simulations = await relayer.simulate(this.address, ...transactions) + return transactions.map((tx, i) => { + const gasLimit = tx.gasLimit ? ethers.BigNumber.from(tx.gasLimit).toNumber() : simulations[i].gasLimit + return { ...tx, ...simulations[i], gasLimit } + }) } - // singleOwner will create a Wallet instance with a single signer (ie. from a single EOA account) - static async singleOwner(owner: BytesLike | AbstractSigner, context?: WalletContext): Promise { - const signer = AbstractSigner.isSigner(owner) ? owner : new ethers.Wallet(owner) - const config = { - threshold: 1, - signers: [ - { - weight: 1, - address: ethers.utils.getAddress(await signer.getAddress()) - } - ] - } - return new Wallet({ config, context }, signer) + connect(provider: ethers.providers.Provider, relayer?: Relayer): Wallet { + this.provider = provider + this.relayer = relayer + return this } - async hasEnoughSigners(chainId?: ChainIdLike): Promise { - if (chainId) await this.getChainIdNumber(chainId) - return (await this.signWeight()).gte(this.config.threshold) + signTransaction(transaction: ethers.utils.Deferrable): Promise { + throw new Error("Method not implemented."); } } diff --git a/packages/wallet/tests/account.spec.ts b/packages/wallet/tests/account.spec.ts deleted file mode 100644 index 5ef75c39a..000000000 --- a/packages/wallet/tests/account.spec.ts +++ /dev/null @@ -1,599 +0,0 @@ -import chaiAsPromised from 'chai-as-promised' -import * as chai from 'chai' - -import { ethers, providers } from 'ethers' -import hardhat from 'hardhat' -import { WalletContext, NetworkConfig as _NetworkConfig } from '@0xsequence/network' -import { LocalRelayer, RpcRelayer } from '@0xsequence/relayer' -import { deployWalletContext } from './utils/deploy-wallet-context' -import { isValidConfigSigners, imageHash, SequenceUtilsFinder } from '@0xsequence/config' -import { configureLogger } from '@0xsequence/utils' - -import * as lib from '../src' -import { isWalletUpToDate } from '../../provider/src/utils' -import { isConfigEqual } from '../../config/src/config' -import { describe } from 'mocha' - -const { expect } = chai.use(chaiAsPromised) - -configureLogger({ logLevel: 'DEBUG', silence: false }) - -interface NetworkConfig extends _NetworkConfig { - provider: providers.JsonRpcProvider -} - -describe('Account integration', () => { - let context: WalletContext - let account: lib.Account - let owner: ethers.Wallet - - const provider = new ethers.providers.Web3Provider(hardhat.network.provider.send) - - const nodeB = 'http://127.0.0.1:7047/' - const providerB = new ethers.providers.JsonRpcProvider(nodeB) - const signerB = providerB.getSigner() - - const networks: NetworkConfig[] = [ - { - chainId: 31337, - name: 'hardhat', - rpcUrl: '', - provider: provider, - relayer: new LocalRelayer({ signer: provider.getSigner() }), - isDefaultChain: true, - isAuthChain: true - }, - { - chainId: 31338, - name: 'hardhat2', - rpcUrl: nodeB, - provider: providerB, - relayer: new LocalRelayer({ signer: signerB }), - isDefaultChain: false, - isAuthChain: false - } - ] - - before(async () => { - // Deploy Sequence context - const [factory, mainModule, mainModuleUpgradable, guestModule, sequenceUtils, requireFreshSigner] = await deployWalletContext( - provider.getSigner() - ) - - // Deploy Sequence context b - await deployWalletContext(signerB) - - // Create fixed context obj - context = { - factory: factory.address, - mainModule: mainModule.address, - mainModuleUpgradable: mainModuleUpgradable.address, - guestModule: guestModule.address, - sequenceUtils: sequenceUtils.address, - libs: { - requireFreshSigner: requireFreshSigner.address - } - } - }) - - beforeEach(async () => { - // Create account - owner = new ethers.Wallet(ethers.utils.randomBytes(32)) - const wallet = await lib.Wallet.singleOwner(owner, context) - - account = new lib.Account( - { - initialConfig: wallet.config, - networks, - context - }, - owner - ) - }) - - describe('find wallet by signer', () => { - it('should find wallet of an indexed signer', async () => { - const owner = new ethers.Wallet(ethers.utils.randomBytes(32)) - const wallet = (await lib.Wallet.singleOwner(owner, context)).connect( - networks[0].provider, - networks[0].relayer as RpcRelayer - ) - - await wallet.publishConfig(true) - - const found = await new SequenceUtilsFinder(networks[0].provider).findLastWalletOfInitialSigner({ - signer: owner.address, - context: context, - provider: networks[0].provider! - }) - - expect(found.wallet).to.equal(wallet.address) - }) - it('should find wallet of not indexed signer', async () => { - const owner = new ethers.Wallet(ethers.utils.randomBytes(32)) - const wallet = (await lib.Wallet.singleOwner(owner, context)).connect( - networks[0].provider, - networks[0].relayer as RpcRelayer - ) - - await wallet.publishConfig(false) - - const found = await new SequenceUtilsFinder(networks[0].provider).findLastWalletOfInitialSigner({ - signer: owner.address, - context: context, - provider: networks[0].provider - }) - - expect(found.wallet).to.equal(wallet.address) - }) - it('should find wallet of indexed signer, ignoring index', async () => { - const owner = new ethers.Wallet(ethers.utils.randomBytes(32)) - const wallet = (await lib.Wallet.singleOwner(owner, context)).connect( - networks[0].provider, - networks[0].relayer as RpcRelayer - ) - - await wallet.publishConfig(true) - - const found = await new SequenceUtilsFinder(networks[0].provider).findLastWalletOfInitialSigner({ - signer: owner.address, - context: context, - provider: networks[0].provider, - ignoreIndex: true - }) - - expect(found.wallet).to.equal(wallet.address) - }) - it('should not find wallet of not published signer', async () => { - const owner = new ethers.Wallet(ethers.utils.randomBytes(32)) - - const found = await new SequenceUtilsFinder(networks[0].provider).findLastWalletOfInitialSigner({ - signer: owner.address, - context: context, - provider: networks[0].provider - }) - - expect(found.wallet).to.be.undefined - }) - }) - - describe('config', () => { - it('should create new instance', async () => { - const owner = new ethers.Wallet(ethers.utils.randomBytes(32)) - const wallet = (await lib.Wallet.singleOwner(owner)).connect(networks[0].provider) - - expect(await wallet.getChainId()).to.equal(31337) - expect((await wallet.getWalletConfig())[0].signers[0].address).to.equal(await owner.getAddress()) - - const account = new lib.Account({ - initialConfig: (await wallet.getWalletConfig())[0], - networks - }).useSigners(owner) - - expect(await account.getChainId()).to.equal(31337) - expect((await account.getWalletConfig())[0].signers[0].address).to.equal(await owner.getAddress()) - - expect(await wallet.getAddress()).to.equal(await account.getAddress()) - expect(await wallet.getSigners()).to.deep.equal(await account.getSigners()) - }) - - it('should update config and get current config from chain', async () => { - const { wallet } = account.getWallets()[0] - expect(await wallet.getAddress()).to.equal(await account.getAddress()) - - const signers = await account.getSigners() - expect(signers[0]).to.equal(await owner.getAddress()) - expect(isValidConfigSigners((await account.getWalletConfig())[0], await account.getSigners())).to.be.true - - expect(await account.isDeployed()).to.be.false - - // deploy the wallet - const newSigner = ethers.Wallet.createRandom() - await account.updateConfig((await lib.Wallet.singleOwner(newSigner)).config) - expect(await account.isDeployed()).to.be.true - - // instanciate account without known new config - const account2 = new lib.Account( - { - initialConfig: wallet.config, - networks, - context - }, - owner - ) - - // currentConfig which fetches wallet details from the authChain - const currentConfig = await account2.currentConfig() - expect(currentConfig!.address).to.equal(await account2.getAddress()) - expect(currentConfig!.signers.length).to.equal(1) - expect(currentConfig!.signers[0].weight).to.equal(1) - expect(currentConfig!.signers[0].address).to.equal(await newSigner.getAddress()) - expect(currentConfig!.chainId).to.equal(await account2.getChainId()) - - // wallet state - const state = (await account2.getWalletState())[0] - expect(state.config!.address).to.equal(await account2.getAddress()) - expect(state.deployed).to.equal(true) - expect(state.imageHash).to.not.equal(state.lastImageHash) - expect(state.lastImageHash).to.equal(imageHash(currentConfig!)) - }) - - it('should update config and get current config from chain, not indexed', async () => { - const { wallet } = account.getWallets()[0] - expect(await wallet.getAddress()).to.equal(await account.getAddress()) - - const signers = await account.getSigners() - expect(signers[0]).to.equal(await owner.getAddress()) - expect(isValidConfigSigners((await account.getWalletConfig())[0], await account.getSigners())).to.be.true - - expect(await account.isDeployed()).to.be.false - - // deploy the wallet - const newSigner = ethers.Wallet.createRandom() - await account.updateConfig((await lib.Wallet.singleOwner(newSigner)).config, false) - expect(await account.isDeployed()).to.be.true - - // instanciate account without known new config - const account2 = new lib.Account( - { - initialConfig: wallet.config, - networks, - context - }, - owner - ) - - // currentConfig which fetches wallet details from the authChain - const currentConfig = await account2.currentConfig() - expect(currentConfig!.address).to.equal(await account2.getAddress()) - expect(currentConfig!.signers.length).to.equal(1) - expect(currentConfig!.signers[0].weight).to.equal(1) - expect(currentConfig!.signers[0].address).to.equal(await newSigner.getAddress()) - expect(currentConfig!.chainId).to.equal(await account2.getChainId()) - - // wallet state - const state = (await account2.getWalletState())[0] - expect(state.config!.address).to.equal(await account2.getAddress()) - expect(state.deployed).to.equal(true) - expect(state.imageHash).to.not.equal(state.lastImageHash) - expect(state.lastImageHash).to.equal(imageHash(currentConfig!)) - }) - - it('should find current config from published config on counter-factual wallet', async () => { - const { wallet } = account.getWallets()[0] - expect(await wallet.getAddress()).to.equal(await account.getAddress()) - - const signers = await account.getSigners() - expect(signers[0]).to.equal(await owner.getAddress()) - expect(isValidConfigSigners((await account.getWalletConfig())[0], await account.getSigners())).to.be.true - - expect(await account.isDeployed()).to.be.false - await account.publishConfig() - - // instanciate account without config - const account2 = new lib.Account( - { - initialConfig: { address: account.address, threshold: 0, signers: [] }, - networks, - context - }, - owner - ) - - expect(account2.address).to.equal(account.address) - - // currentConfig which fetches wallet details from the authChain - const currentConfig = await account2.currentConfig() - expect(currentConfig!.address).to.equal(await account2.getAddress()) - expect(currentConfig!.signers.length).to.equal(1) - expect(currentConfig!.signers[0].weight).to.equal(1) - expect(currentConfig!.signers[0].address).to.equal(await owner.getAddress()) - expect(currentConfig!.chainId).to.equal(await account2.getChainId()) - - // wallet state - const state = (await account2.getWalletState())[0] - expect(state.config!.address).to.equal(await account2.getAddress()) - expect(state.deployed).to.equal(true) - expect(state.imageHash).to.not.equal(state.lastImageHash) - expect(state.lastImageHash).to.equal('') - }) - - it('should find current config from published config on counter-factual wallet, not indexed', async () => { - const { wallet } = account.getWallets()[0] - expect(await wallet.getAddress()).to.equal(await account.getAddress()) - - const signers = await account.getSigners() - expect(signers[0]).to.equal(await owner.getAddress()) - expect(isValidConfigSigners((await account.getWalletConfig())[0], await account.getSigners())).to.be.true - - expect(await account.isDeployed()).to.be.false - await account.publishConfig(false) - - // instanciate account without config - const account2 = new lib.Account( - { - initialConfig: { address: account.address, threshold: 0, signers: [] }, - networks, - context - }, - owner - ) - - expect(account2.address).to.equal(account.address) - - // currentConfig which fetches wallet details from the authChain - const currentConfig = await account2.currentConfig() - expect(currentConfig!.address).to.equal(await account2.getAddress()) - expect(currentConfig!.signers.length).to.equal(1) - expect(currentConfig!.signers[0].weight).to.equal(1) - expect(currentConfig!.signers[0].address).to.equal(await owner.getAddress()) - expect(currentConfig!.chainId).to.equal(await account2.getChainId()) - - // wallet state - const state = (await account2.getWalletState())[0] - expect(state.config!.address).to.equal(await account2.getAddress()) - expect(state.deployed).to.equal(true) - expect(state.imageHash).to.not.equal(state.lastImageHash) - expect(state.lastImageHash).to.equal('') - }) - - it('should update config and get current config from chain, matching defined imageHash', async () => { - const { wallet } = account.getWallets()[0] - expect(await wallet.getAddress()).to.equal(await account.getAddress()) - - const signers = await account.getSigners() - expect(signers[0]).to.equal(await owner.getAddress()) - expect(isValidConfigSigners((await account.getWalletConfig())[0], await account.getSigners())).to.be.true - - expect(await account.isDeployed()).to.be.false - - // deploy the wallet - await account.updateConfig() - expect(await account.isDeployed()).to.be.true - - // currentConfig which fetches wallet details from the authChain - const currentConfig = await account.currentConfig() - expect(currentConfig!.address).to.equal(await account.getAddress()) - expect(currentConfig!.signers.length).to.equal(1) - expect(currentConfig!.signers[0].weight).to.equal(1) - expect(currentConfig!.signers[0].address).to.equal(await owner.getAddress()) - expect(currentConfig!.chainId).to.equal(await account.getChainId()) - - // wallet state - const state = (await account.getWalletState())[0] - expect(state.config!.address).to.equal(await account.getAddress()) - expect(state.deployed).to.equal(true) - expect(state.imageHash).to.equal(state.lastImageHash) - expect(state.imageHash).to.equal(imageHash(currentConfig!)) - }) - - it('should return different configs for different chains (indexed)', async () => { - const newSigner = ethers.Wallet.createRandom() - await account.updateConfig({ threshold: 3, signers: [{ address: newSigner.address, weight: 10 }] }, true) - - const state = await account.getWalletState() - const authState = state[0].config - const altState = state[1].config - - expect(authState!.threshold).to.equal(3) - expect(authState!.signers.length).to.equal(1) - expect(authState!.signers[0].weight).to.equal(10) - expect(authState!.signers[0].address).to.equal(newSigner.address) - - expect(altState!.threshold).to.equal(1) - expect(altState!.signers.length).to.equal(1) - expect(altState!.signers[0].weight).to.equal(1) - expect(altState!.signers[0].address).to.equal(owner.address) - }) - - it('should return different configs for different chains (not-indexed)', async () => { - const newSigner = ethers.Wallet.createRandom() - await account.updateConfig({ threshold: 3, signers: [{ address: newSigner.address, weight: 10 }] }, false) - - const state = await account.getWalletState() - const authState = state[0].config - const altState = state[1].config - - expect(authState!.threshold).to.equal(3) - expect(authState!.signers.length).to.equal(1) - expect(authState!.signers[0].weight).to.equal(10) - expect(authState!.signers[0].address).to.equal(newSigner.address) - - expect(altState!.threshold).to.equal(1) - expect(altState!.signers.length).to.equal(1) - expect(altState!.signers[0].weight).to.equal(1) - expect(altState!.signers[0].address).to.equal(owner.address) - }) - - it('should return different configs for different chains after reload auth config (indexed)', async () => { - const newSigner = ethers.Wallet.createRandom() - await account.updateConfig({ threshold: 3, signers: [{ address: newSigner.address, weight: 10 }] }, true) - - const importedAccount = new lib.Account( - { - initialConfig: account.authWallet().wallet.config, - networks, - context - }, - owner - ) - - const state = await importedAccount.getWalletState() - const authState = state[0].config - const altState = state[1].config - - expect(authState!.threshold).to.equal(3) - expect(authState!.signers.length).to.equal(1) - expect(authState!.signers[0].weight).to.equal(10) - expect(authState!.signers[0].address).to.equal(newSigner.address) - - expect(altState!.threshold).to.equal(1) - expect(altState!.signers.length).to.equal(1) - expect(altState!.signers[0].weight).to.equal(1) - expect(altState!.signers[0].address).to.equal(owner.address) - }) - - it('should return different configs for different chains after reload auth config (not-indexed)', async () => { - const newSigner = ethers.Wallet.createRandom() - await account.updateConfig({ threshold: 3, signers: [{ address: newSigner.address, weight: 10 }] }, false) - - const importedAccount = new lib.Account( - { - initialConfig: account.authWallet().wallet.config, - networks, - context - }, - owner - ) - - const state = await importedAccount.getWalletState() - const authState = state[0].config - const altState = state[1].config - - expect(authState!.threshold).to.equal(3) - expect(authState!.signers.length).to.equal(1) - expect(authState!.signers[0].weight).to.equal(10) - expect(authState!.signers[0].address).to.equal(newSigner.address) - - expect(altState!.threshold).to.equal(1) - expect(altState!.signers.length).to.equal(1) - expect(altState!.signers[0].weight).to.equal(1) - expect(altState!.signers[0].address).to.equal(owner.address) - }) - - it('should return different configs for different chains after reload alt config (indexed)', async () => { - const newSigner = ethers.Wallet.createRandom() - const newConfig = { threshold: 3, signers: [{ address: newSigner.address, weight: 10 }] } - - await account.publishConfig(true) - await account.updateConfig(newConfig, true) - - const importedAccount = new lib.Account( - { - initialConfig: { address: account.address, ...newConfig }, - networks, - context - }, - owner - ) - - const state = await importedAccount.getWalletState() - const authState = state[0].config - const altState = state[1].config - - expect(authState!.threshold).to.equal(3) - expect(authState!.signers.length).to.equal(1) - expect(authState!.signers[0].weight).to.equal(10) - expect(authState!.signers[0].address).to.equal(newSigner.address) - - expect(altState!.threshold).to.equal(1) - expect(altState!.signers.length).to.equal(1) - expect(altState!.signers[0].weight).to.equal(1) - expect(altState!.signers[0].address).to.equal(owner.address) - }) - - it('should return different configs for different chains after reload alt config (not-indexed)', async () => { - const newSigner = ethers.Wallet.createRandom() - const newConfig = { threshold: 3, signers: [{ address: newSigner.address, weight: 10 }] } - - await account.publishConfig(false) - await account.updateConfig(newConfig, false) - - const importedAccount = new lib.Account( - { - initialConfig: { address: account.address, ...newConfig }, - networks, - context - }, - owner - ) - - const state = await importedAccount.getWalletState() - const authState = state[0].config - const altState = state[1].config - - expect(authState!.threshold).to.equal(3) - expect(authState!.signers.length).to.equal(1) - expect(authState!.signers[0].weight).to.equal(10) - expect(authState!.signers[0].address).to.equal(newSigner.address) - - expect(altState!.threshold).to.equal(1) - expect(altState!.signers.length).to.equal(1) - expect(altState!.signers[0].weight).to.equal(1) - expect(altState!.signers[0].address).to.equal(owner.address) - }) - }) - - describe('networks', () => { - it('should set valid default network', async () => { - // expect(() => { - // account.setNetworks(networks, [], 31337) - // }).to.not.throw - - account.setNetworks(networks, [], 31338) - - expect(await account.getChainId()).to.equal(31338) - }) - - it('should fail to set invalid default network', async () => { - expect(() => { - account.setNetworks(networks, [], 123) - }).to.throw(`unable to set default network as chain '123' does not exist`) - }) - }) - - describe('isWalletUpToDate util', () => { - it('Should return false if wallet is not deployed', async () => { - const { wallet } = account.getWallets()[0] - expect(await wallet.getAddress()).to.equal(await account.getAddress()) - - const signers = await account.getSigners() - expect(signers[0]).to.equal(await owner.getAddress()) - expect(isValidConfigSigners((await account.getWalletConfig())[0], await account.getSigners())).to.be.true - - expect(await account.isDeployed()).to.be.false - expect(await isWalletUpToDate(account, 31337)).to.be.false - }) - - it('Should return true if wallet is deployed and config is up to date', async () => { - const { wallet } = account.getWallets()[0] - expect(await wallet.getAddress()).to.equal(await account.getAddress()) - - const signers = await account.getSigners() - expect(signers[0]).to.equal(await owner.getAddress()) - expect(isValidConfigSigners((await account.getWalletConfig())[0], await account.getSigners())).to.be.true - - expect(await account.isDeployed()).to.be.false - - // deploy the wallet - const newSigner = ethers.Wallet.createRandom() - await account.updateConfig((await lib.Wallet.singleOwner(newSigner)).config) - expect(await account.isDeployed()).to.be.true - expect(await isWalletUpToDate(account, 31337)).to.be.true - }) - - it('Should return true if non auth chain wallet is deployed and configs are equal', async () => { - const { wallet } = account.getWallets()[0] - expect(await wallet.getAddress()).to.equal(await account.getAddress()) - const initialConfig = (await account.getWalletConfig())[0] - - const signers = await account.getSigners() - expect(signers[0]).to.equal(await owner.getAddress()) - expect(isValidConfigSigners(initialConfig, await account.getSigners())).to.be.true - - // update config / deploy for auth chain - const newSigner = ethers.Wallet.createRandom() - await account.updateConfig((await lib.Wallet.singleOwner(newSigner)).config) - expect(await account.isDeployed()).to.be.true - expect(await isWalletUpToDate(account, 31337)).to.be.true - expect(await isWalletUpToDate(account, 31338)).to.be.false - - // update config / deploy for chainId 31338 - await account.getWalletByNetwork(31338).wallet.updateConfig((await lib.Wallet.singleOwner(newSigner)).config) - expect(await account.getWalletByNetwork(31338).wallet.isDeployed()).to.be.true - expect(isConfigEqual(account.getWalletByNetwork(31337).wallet.config, account.getWalletByNetwork(31338).wallet.config)).to - .be.true - expect(await isWalletUpToDate(account, 31338)).to.be.true - }) - }) -}) diff --git a/packages/wallet/tests/utils.unit.spec.ts b/packages/wallet/tests/utils.unit.spec.ts deleted file mode 100644 index 3eec06708..000000000 --- a/packages/wallet/tests/utils.unit.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { expect } from 'chai' -import { ethers } from 'ethers' - -import { imageHash, addressOf } from '@0xsequence/config' - -describe('Wallet utils', function () { - it('should generate image hash', () => { - const config = { - threshold: 1, - signers: [ - { - weight: 1, - address: ethers.constants.AddressZero - } - ] - } - - const expected = '0xd5eb26a4673c3bf5bb325d407fe1544f0325b97d4b68afa6a28851b6dbbbd29f' - expect(imageHash(config)).to.be.equal(expected) - }) - - it('should generate wallet address', () => { - const config = { - threshold: 1, - signers: [ - { - weight: 1, - address: '0xd63A09C47FDc03e2Cff620446b37f205A7D0679D' - } - ] - } - - const context = { - factory: '0x7c2C195CD6D34B8F845992d380aADB2730bB9C6F', - mainModule: '0x8858eeB3DfffA017D4BCE9801D340D36Cf895CCf', - mainModuleUpgradable: '0xC7cE8a07f69F226E52AEfF57085d8C915ff265f7' - } - - const expected = '0xF0BA65550F2d1DCCf4B131B774844DC3d801D886' - expect(addressOf(config, context)).to.be.equal(expected) - }) -}) diff --git a/packages/wallet/tests/wallet.spec.ts b/packages/wallet/tests/wallet.spec.ts index 8b23c9d5b..a0d61e764 100644 --- a/packages/wallet/tests/wallet.spec.ts +++ b/packages/wallet/tests/wallet.spec.ts @@ -1,1837 +1,300 @@ + import hardhat from 'hardhat' -import chaiAsPromised from 'chai-as-promised' import * as chai from 'chai' -import { deployWalletContext } from './utils/deploy-wallet-context' -import { encodeData } from './utils' -import { Proof } from '@0xsequence/ethauth' - -import { - CallReceiverMock, - HookCallerMock, - CallReceiverMock__factory, - HookCallerMock__factory, -} from '@0xsequence/wallet-contracts' - -import { - toSequenceTransaction, - toSequenceTransactions, - encodeNonce, - Transactionish, - isSignedTransactions -} from '@0xsequence/transactions' - +import { commons, v1, v2 } from "@0xsequence/core" +import { context } from "@0xsequence/tests" +import { ethers } from 'ethers' +import { SequenceOrchestratorWrapper, Wallet } from '../src/index' +import { Orchestrator, signers as hubsigners } from '@0xsequence/signhub' import { LocalRelayer } from '@0xsequence/relayer' -import { WalletContext, NetworkConfig } from '@0xsequence/network' -import { Contract, ethers, Signer as AbstractSigner, providers } from 'ethers' - -import { addressOf, joinSignatures, encodeSignature, imageHash, WalletConfig } from '@0xsequence/config' - -import { configureLogger, encodeTypedDataDigest } from '@0xsequence/utils' - -import * as lib from '../src' - -import { - isValidSignature, - isValidEthSignSignature, - isValidSequenceUndeployedWalletSignature, - fetchImageHash, - isValidContractWalletSignature, - RemoteSigner -} from '../src' +const { expect } = chai -import { LocalWeb3Provider, prefixEIP191Message } from '../../provider/src' - -import { BytesLike, utils } from 'ethers' -import { walletContracts } from '@0xsequence/abi' - -const MainModuleArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/modules/MainModule.sol/MainModule.json') -const CallReceiverMockArtifact = require('@0xsequence/wallet-contracts/artifacts/contracts/mocks/CallReceiverMock.sol/CallReceiverMock.json') - -const Web3 = require('web3') -const { expect } = chai.use(chaiAsPromised) - -configureLogger({ logLevel: 'DEBUG', silence: false }) - -type EthereumInstance = { - chainId: number - provider: providers.JsonRpcProvider - signer: AbstractSigner +type Coders = { + signature: commons.signature.SignatureCoder, + config: commons.config.ConfigCoder, } -describe('Wallet integration', function () { - const ethnode: EthereumInstance = {} as any +describe('Wallet (primitive)', () => { + let provider: ethers.providers.JsonRpcProvider + let signers: ethers.Signer[] + let contexts: Awaited> let relayer: LocalRelayer - let callReceiver: CallReceiverMock - let hookCaller: HookCallerMock - - let context: WalletContext - let networks: NetworkConfig[] - let wallet: lib.Wallet before(async () => { - // Provider from hardhat without a server instance - ethnode.provider = new providers.Web3Provider(hardhat.network.provider.send) - - // NOTE: if you'd like to test with ganache or hardhat in server mode, just uncomment the line below - // and make sure your ganache or hardhat instance is running separately - // NOTE2: ganache will fail at getStorageAt(), as hardhat and ganache treat it a bit differently, - // which is strange. Hardhat is at fault here IMHO. - // ethnode.provider = new ethers.providers.JsonRpcProvider(`http://127.0.0.1:8545/`) - - ethnode.signer = ethnode.provider.getSigner() - ethnode.chainId = 31337 - - networks = [ - { - name: 'local', - chainId: ethnode.chainId, - provider: ethnode.provider, - isDefaultChain: true, - isAuthChain: true - } - ] - - // Deploy Sequence env - const [factory, mainModule, mainModuleUpgradable, guestModule, sequenceUtils, requireFreshSigner] = await deployWalletContext( - ethnode.signer - ) - - // Create fixed context obj - context = { - factory: factory.address, - mainModule: mainModule.address, - mainModuleUpgradable: mainModuleUpgradable.address, - guestModule: guestModule.address, - sequenceUtils: sequenceUtils.address, - libs: { - requireFreshSigner: requireFreshSigner.address - } - } - - // Deploy call receiver mock - callReceiver = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - - // Deploy hook caller mock - hookCaller = await (new HookCallerMock__factory()).connect(ethnode.signer).deploy() - - // Deploy local relayer - relayer = new LocalRelayer({ signer: ethnode.signer }) - }) - - beforeEach(async () => { - // Create wallet - const pk = ethers.utils.randomBytes(32) - wallet = await lib.Wallet.singleOwner(pk, context) - wallet = wallet.connect(ethnode.provider, relayer) - }) - - after(async () => { - // if (ethnode.server) { - // ethnode.server.close() - // } - }) - - describe('with ethers.js', () => { - let w3provider: providers.ExternalProvider - let provider: providers.Web3Provider - - const options = [ - { - name: 'sequence-wallet', - signer: () => wallet, - prefixMessage: (m: BytesLike) => m - }, - { - name: 'ethers-signer', - signer: () => provider.getSigner(), - prefixMessage: (m: BytesLike) => prefixEIP191Message(m) - } - ] - - beforeEach(async () => { - provider = new LocalWeb3Provider(wallet) - }) - - it('Should return accounts', async () => { - const accounts = await provider.listAccounts() - expect(accounts.length).to.be.equal(1) - expect(accounts[0]).to.be.equal(wallet.address) - }) - - describe('using sequence signer', () => { - it('should compute valid signedTypeData digest', async () => { - const typedData = { - types: { - Person: [ - { name: 'name', type: 'string' }, - { name: 'wallet', type: 'address' }, - { name: 'count', type: 'uint8' } - ] - }, - primaryType: 'Person' as const, - domain: { - name: 'Ether Mail', - version: '1', - chainId: 1, //ethnode.chainId, - verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC' - }, - message: { - name: 'Bob', - wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB', - count: 4 - } - } - - const digest = ethers.utils._TypedDataEncoder.hash(typedData.domain, typedData.types, typedData.message) - expect(digest).to.equal('0x2218fda59750be7bb9e5dfb2b49e4ec000dc2542862c5826f1fe980d6d727e95') - - const digestChk2 = ethers.utils.hexlify(encodeTypedDataDigest(typedData)) - expect(digestChk2).to.equal(digest) - }) - - it('Should sign a typed message', async () => { - const typedData = { - types: { - Person: [ - { name: 'name', type: 'string' }, - { name: 'wallet', type: 'address' }, - { name: 'count', type: 'uint8' } - ] - }, - primaryType: 'Person' as const, - domain: { - name: 'Ether Mail', - version: '1', - chainId: ethnode.chainId, - verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC' - }, - message: { - name: 'Bob', - wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB', - count: 4 - } - } - - const digest = ethers.utils._TypedDataEncoder.hash(typedData.domain, typedData.types, typedData.message) - expect(digest).to.equal('0x69d3381dfd41c0a9cea56d325bcd482eace26dd2e7b95df398cb6d8edc00290c') - - const sig = await wallet.signTypedData(typedData.domain, typedData.types, typedData.message) - - expect(sig).to.not.be.undefined - expect(sig).to.not.equal('') - - await relayer.deployWallet(wallet.config, context) - // const call = hookCaller.callERC1271isValidSignatureHash(wallet.address, ethers.utils.arrayify(digest), sig) - const call = hookCaller.callERC1271isValidSignatureHash(wallet.address, ethers.utils.arrayify(digest), sig) - await expect(call).to.be.fulfilled - }) - }) - describe('Nested wallets', async () => { - it('Should use wallet as wallet signer', async () => { - const walletA = (await lib.Wallet.singleOwner(ethers.Wallet.createRandom(), context)).connect(ethnode.provider, relayer) - const walletB = (await lib.Wallet.singleOwner(walletA, context)).connect(ethnode.provider, relayer) - - // TODO: Bundle deployment with child wallets - await relayer.deployWallet(walletA.config, walletA.context) - - const contractWithSigner = callReceiver.connect(walletB)// as CallReceiverMock - - // await contractWithSigner.testCall(412313, '0x12222334') - await contractWithSigner.testCall(ethers.BigNumber.from(412313), '0x12222334') - expect(await contractWithSigner.lastValB()).to.equal('0x12222334') - }) - }) - options.forEach(s => { - describe(`using ${s.name} provider`, () => { - let signer: AbstractSigner - - beforeEach(async () => { - signer = s.signer() - }) - - it('Should call contract method', async () => { - const contractWithSigner = callReceiver.connect(signer)// as CallReceiverMock - - // await contractWithSigner.testCall(412313, '0x11222334') - await contractWithSigner.testCall(ethers.BigNumber.from(412313), '0x11222334') - expect(await contractWithSigner.lastValB()).to.equal('0x11222334') - }) - - it('Should deploy contract', async () => { - await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() + provider = new ethers.providers.Web3Provider(hardhat.network.provider.send) + signers = new Array(8).fill(0).map((_, i) => provider.getSigner(i)) + contexts = await context.deploySequenceContexts(signers[0]) + relayer = new LocalRelayer(signers[0]) + }); + + ([{ + version: 1, + coders: { signature: v1.signature.SignatureCoder, config: v1.config.ConfigCoder }, + }, { + version: 2, + coders: { signature: v2.signature.SignatureCoder, config: v2.config.ConfigCoder }, + }] as { version: number, coders: Coders }[]).map(({ version, coders }) => { + describe(`Using v${version} version`, () => { + it('Should deploy a new wallet', async () => { + const signer = ethers.Wallet.createRandom() + + const config = coders.config.fromSimple({ + threshold: 1, + checkpoint: 0, + signers: [{ address: signer.address, weight: 1 }] }) - it('Should perform multiple transactions', async () => { - const contractWithSigner = callReceiver.connect(signer) - - await contractWithSigner.testCall(ethers.BigNumber.from(412313), '0x11222334') - await contractWithSigner.testCall(ethers.BigNumber.from(11111), '0x') + const wallet = Wallet.newWallet({ + coders: coders, + context: contexts[version], + config, + orchestrator: new Orchestrator([new hubsigners.SignerWrapper(signer)]), + chainId: provider.network.chainId, + provider, + relayer }) - it('Should return transaction count', async () => { - const contractWithSigner = callReceiver.connect(signer) - - expect(await provider.getTransactionCount(wallet.address)).to.equal(0) - - await contractWithSigner.testCall(ethers.BigNumber.from(1), '0x') - expect(await provider.getTransactionCount(wallet.address)).to.equal(1) - - await contractWithSigner.testCall(ethers.BigNumber.from(2), '0x') - expect(await provider.getTransactionCount(wallet.address)).to.equal(2) - - await contractWithSigner.testCall(ethers.BigNumber.from(3), '0x') - expect(await provider.getTransactionCount(wallet.address)).to.equal(3) - }) + await wallet.deploy() - describe('Signing', async () => { - it('Should sign a message', async () => { - const message = ethers.utils.toUtf8Bytes('Hi! this is a test message') + expect(await wallet.reader().isDeployed(wallet.address)).to.be.true + }); - const signature = await signer.signMessage(message) + // + // Run tests using different combinations of signers + // + ([{ + name: '1/1 signer', + signers: () => { + const signer = ethers.Wallet.createRandom() - // Contract wallet must be deployed before calling ERC1271 - const txn = await relayer.deployWallet(wallet.config, context) - - // const receipt = await provider.getTransactionReceipt(txn.hash) - // console.log('status?', receipt.status) - - const call = hookCaller.callERC1271isValidSignatureData(wallet.address, s.prefixMessage(message), signature) - await expect(call).to.be.fulfilled - }) - }) - describe('Gas limits', async () => { - it('Should send custom gas-limit', async () => { - const callReceiver1 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - - const receiver = new ethers.Contract(callReceiver1.address, CallReceiverMockArtifact.abi, signer) - - const tx = await receiver.functions.testCall(2, '0x030233', { - gasLimit: ethers.BigNumber.from(1048575) - }) - - expect(tx.data).to.contain('00fffff') - }) - - it('Should estimate gas for transaction with 0 gasLimit and revertOnError false', async () => { - await new ethers.ContractFactory(MainModuleArtifact.abi, MainModuleArtifact.bytecode, signer).deploy(wallet.address) - }) - - it('Should be able to update the config for a wallet with many signers', async () => { - // first, we try just two signers - - const signers = wallet.config.signers - while (signers.length < 2) { - signers.push({ - address: ethers.Wallet.createRandom().address, - weight: 1 - }) - } - - const newConfig = { + const config = coders.config.fromSimple({ threshold: 1, - signers - } - let expectedImageHash = imageHash(newConfig) - - let tx = (await wallet.updateConfig(newConfig, undefined, true))[1] - let receipt = await tx.wait() - // console.log(`gas usage: ${receipt.gasUsed.toString()} of ${tx.gasLimit.toString()}`) - expect(receipt.status).to.equal(1) - - let actualImageHash = await fetchImageHash(wallet) - expect(actualImageHash).to.equal(expectedImageHash) - - const gasLimit1 = tx.gasLimit - - // next, we try 100 signers - - while (signers.length < 100) { - signers.push({ - address: ethers.Wallet.createRandom().address, - weight: 1 - }) - } - - newConfig.signers = signers - expectedImageHash = imageHash(newConfig) - - tx = (await wallet.updateConfig(newConfig, undefined, true))[1] - receipt = await tx.wait() - // console.log(`gas usage: ${receipt.gasUsed.toString()} of ${tx.gasLimit.toString()}`) - expect(receipt.status).to.equal(1) - - actualImageHash = await fetchImageHash(wallet) - expect(actualImageHash).to.equal(expectedImageHash) - - const gasLimit2 = tx.gasLimit - - // the second operation should have more gas allocated than the first one - expect(gasLimit2.gt(gasLimit1)).to.be.true - }) - }) - }) - - describe('batch transactions', async () => { - it('Should send two transactions at once', async () => { - const callReceiver1 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - const callReceiver2 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - - const transaction = { - gas: '121000', - to: callReceiver1.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 1, '0x112233'), - auxiliary: [ - { - gas: '121000', - to: callReceiver2.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 2, '0x445566') - } - ] - } - - await wallet.sendTransaction(transaction) - - expect(await callReceiver1.lastValB()).to.equal('0x112233') - expect(await callReceiver2.lastValB()).to.equal('0x445566') - }) - - it('Should send two transactions at once, alternate syntax', async () => { - const callReceiver1 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - const callReceiver2 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - - const transactions = [ - { - gas: '121000', - to: callReceiver1.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 1, '0x112233') - }, - { - gas: '121000', - to: callReceiver2.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 2, '0x445566') - } - ] - - await wallet.sendTransactionBatch(transactions) - - expect(await callReceiver1.lastValB()).to.equal('0x112233') - expect(await callReceiver2.lastValB()).to.equal('0x445566') - }) - - it('Should send a single transaction with sendTransaction', async () => { - const callReceiver1 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - - const transaction = { - gas: '121000', - to: callReceiver1.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 1, '0x015361') - } - - await wallet.sendTransaction(transaction) - expect(await callReceiver1.lastValB()).to.equal('0x015361') - }) - - it('Should send three transactions at once', async () => { - const callReceiver1 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - const callReceiver2 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - const callReceiver3 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - - const transaction = { - gas: '121000', - to: callReceiver1.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 1, '0x112233'), - auxiliary: [ - { - gas: '100000', - to: callReceiver2.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 2, '0x445566') - }, - { - gas: '70000', - to: callReceiver3.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 2, '0x778899') - } - ] - } - - await wallet.sendTransaction(transaction) - - expect(await callReceiver1.lastValB()).to.equal('0x112233') - expect(await callReceiver2.lastValB()).to.equal('0x445566') - expect(await callReceiver3.lastValB()).to.equal('0x778899') - }) - - it('Should send nested transactions', async () => { - const callReceiver1 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - const callReceiver2 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - const callReceiver3 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - - const transaction = { - from: wallet.address, - gas: '121000', - to: callReceiver1.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 1, '0x112233'), - auxiliary: [ - { - gas: '100000', - to: callReceiver2.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 2, '0x445566'), - auxiliary: [ - { - gas: '70000', - to: callReceiver3.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 2, '0x778899') - } - ] - } - ] - } - - await wallet.sendTransaction(transaction) - - expect(await callReceiver1.lastValB()).to.equal('0x112233') - expect(await callReceiver2.lastValB()).to.equal('0x445566') - expect(await callReceiver3.lastValB()).to.equal('0x778899') - }) - }) - - describe('expirable transactions', async () => { - it('Should generate and send a non-expired transaction', async () => { - const callReceiver1 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - - const transaction = { - gas: '121000', - to: callReceiver1.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 1, '0x015561'), - expiration: Math.floor(Date.now() / 1000) + 86400 * 90 - } - - await wallet.sendTransaction(transaction) - expect(await callReceiver1.lastValB()).to.equal('0x015561') - }) - it('Should generate and fail to send a expired transaction', async () => { - const callReceiver1 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - - const transaction = { - gas: '121000', - to: callReceiver1.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 1, '0x015561'), - expiration: Math.floor(Date.now() / 1000) - 86400 * 90 - } - - const tx = wallet.sendTransaction(transaction) - await expect(tx).to.be.rejected - - expect(await callReceiver1.lastValB()).to.equal('0x') - }) - it('Should fail to generate a expired transaction without sequenceUtils', async () => { - // Create wallet - const pk = ethers.utils.randomBytes(32) - - const context1 = { ...context } - context1.sequenceUtils = undefined - - let wallet1 = await lib.Wallet.singleOwner(pk, context1) - wallet1 = wallet1.connect(ethnode.provider, relayer) - - const callReceiver1 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - - const transaction = { - gas: '121000', - to: callReceiver1.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 1, '0x015561'), - expiration: Math.floor(Date.now() / 1000) + 86400 * 90 - } - - const tx = wallet1.sendTransaction(transaction) - await expect(tx).to.be.rejected - expect(await callReceiver1.lastValB()).to.equal('0x') - }) - }) - - describe('linked transactions', async () => { - it('Should send transaction linked to same-wallet space', async () => { - await wallet.sendTransaction({ - revertOnError: true, - to: wallet.address, - value: 0, - data: '0x', - nonce: encodeNonce(5, 0) - }) - - const callReceiver1 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - - const transaction = { - gas: '121000', - to: callReceiver1.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 1, '0x015561'), - expiration: Math.floor(Date.now() / 1000) + 86400 * 90, - afterNonce: encodeNonce(5, 1), - nonce: encodeNonce(6, 0) - } - - await wallet.sendTransaction(transaction) - expect(await callReceiver1.lastValB()).to.equal('0x015561') - }) - it('Should falil to send transaction linked to same-wallet space', async () => { - const callReceiver1 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - - const transaction = { - gas: '121000', - to: callReceiver1.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 1, '0x015561'), - expiration: Math.floor(Date.now() / 1000) + 86400 * 90, - afterNonce: encodeNonce(5, 1), - nonce: encodeNonce(6, 0) - } - - const tx = wallet.sendTransaction(transaction) - await expect(tx).to.be.rejected - expect(await callReceiver1.lastValB()).to.equal('0x') - }) - it('Should send transaction linked to other wallet nonce space', async () => { - // Create wallet - const pk = ethers.utils.randomBytes(32) - let wallet2 = await lib.Wallet.singleOwner(pk, context) - wallet2 = wallet2.connect(ethnode.provider, relayer) - - await wallet2.sendTransaction({ - revertOnError: true, - to: wallet.address, - value: 0, - data: '0x', - nonce: encodeNonce(5, 0) - }) - - const callReceiver1 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - - const transaction = { - gas: '121000', - to: callReceiver1.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 1, '0x015561'), - expiration: Math.floor(Date.now() / 1000) + 86400 * 90, - afterNonce: { - address: wallet2.address, - nonce: 1, - space: 5 - } - } - - await wallet.sendTransaction(transaction) - expect(await callReceiver1.lastValB()).to.equal('0x015561') - }) - it('Should fail to send transaction linked to other wallet nonce space', async () => { - // Create wallet - const pk = ethers.utils.randomBytes(32) - let wallet2 = await lib.Wallet.singleOwner(pk, context) - wallet2 = wallet2.connect(ethnode.provider, relayer) - - const callReceiver1 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - - const transaction = { - gas: '121000', - to: callReceiver1.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 1, '0x015561'), - expiration: Math.floor(Date.now() / 1000) + 86400 * 90, - afterNonce: { - address: wallet2.address, - nonce: 1, - space: 5 - } - } - - const tx = wallet.sendTransaction(transaction) - await expect(tx).to.be.rejected - expect(await callReceiver1.lastValB()).to.equal('0x') - }) - }) - }) - - describe('wallet batch transactions', async () => { - it('Should send two transactions at once', async () => { - const callReceiver1 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - const callReceiver2 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - - const transaction = [ - { - gasPrice: '20000000000', - gas: '121000', - to: callReceiver1.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 1, '0x112233') - }, - { - gasPrice: '20000000000', - gas: '121000', - to: callReceiver2.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 2, '0x445566') - } - ] - - await wallet.sendTransaction(transaction) - - expect(await callReceiver1.lastValB()).to.equal('0x112233') - expect(await callReceiver2.lastValB()).to.equal('0x445566') - }) + checkpoint: 0, + signers: [{ address: signer.address, weight: 1 }] + }) - it('Should send three transactions at once', async () => { - const callReceiver1 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - const callReceiver2 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - const callReceiver3 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() + const orchestrator = new Orchestrator([new hubsigners.SignerWrapper(signer)]) - const transaction = [ - { - gas: '121000', - to: callReceiver1.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 1, '0x112233') - }, - { - gas: '100000', - to: callReceiver2.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 2, '0x445566') - }, - { - gas: '70000', - to: callReceiver3.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 2, '0x778899') + return { config, orchestrator } } - ] - - await wallet.sendTransaction(transaction) - - expect(await callReceiver1.lastValB()).to.equal('0x112233') - expect(await callReceiver2.lastValB()).to.equal('0x445566') - expect(await callReceiver3.lastValB()).to.equal('0x778899') - }) - }) - }) - - describe('with web3', () => { - let provider: providers.ExternalProvider - let w3: any - - beforeEach(async () => { - provider = new LocalWeb3Provider(wallet).provider - w3 = new Web3(provider) - }) - - it('Should return accounts', async () => { - const accounts = await w3.eth.getAccounts() - expect(accounts.length).to.be.equal(1) - expect(accounts[0]).to.be.equal(wallet.address) - }) - - it('Should call contract method', async () => { - const contractWithSigner = new w3.eth.Contract(CallReceiverMockArtifact.abi, callReceiver.address) - - await contractWithSigner.methods.testCall(412313, '0x11222334').send({ from: wallet.address }) - expect(await contractWithSigner.methods.lastValB().call()).to.equal('0x11222334') - }) - - it('Should deploy contract', async () => { - const contractWithSigner = new w3.eth.Contract(CallReceiverMockArtifact.abi) - await contractWithSigner.deploy({ - data: CallReceiverMockArtifact.bytecode - }) - }) - - it('Should perform multiple transactions', async () => { - const contractWithSigner = new w3.eth.Contract(CallReceiverMockArtifact.abi, callReceiver.address) - - await contractWithSigner.methods.testCall(412313, '0x11222334').send({ from: wallet.address }) - await contractWithSigner.methods.testCall(11111, '0x').send({ from: wallet.address }) - }) - - it('Should return transaction count', async () => { - const contractWithSigner = new w3.eth.Contract(CallReceiverMockArtifact.abi, callReceiver.address) - - expect(await w3.eth.getTransactionCount(wallet.address)).to.equal(0) - - await contractWithSigner.methods.testCall(1, '0x').send({ from: wallet.address }) - expect(await w3.eth.getTransactionCount(wallet.address)).to.equal(1) - - await contractWithSigner.methods.testCall(2, '0x').send({ from: wallet.address }) - expect(await w3.eth.getTransactionCount(wallet.address)).to.equal(2) - - await contractWithSigner.methods.testCall(3, '0x').send({ from: wallet.address }) - expect(await w3.eth.getTransactionCount(wallet.address)).to.equal(3) - }) - - describe('signing', async () => { - it('Should sign transaction', async () => { - const signed = await w3.eth.signTransaction({ - from: wallet.address, - gasPrice: '20000000000', - gasLimit: '121000', - to: '0x3535353535353535353535353535353535353535', - value: '1000000000000000000', - data: '0x9988776655' - }) - - expect(isSignedTransactions(signed)).to.be.true - expect(signed.config).to.deep.equal(wallet.config) - expect(signed.context).to.deep.equal(wallet.context) - expect(signed.signature).to.be.a('string') - expect(signed.transactions.length).to.equal(1) - expect(signed.transactions[0].gasLimit).to.equal('121000') - expect(signed.transactions[0].to).to.equal('0x3535353535353535353535353535353535353535') - expect(signed.transactions[0].value).to.equal('0xde0b6b3a7640000') - expect(signed.transactions[0].data).to.equal('0x9988776655') - expect(signed.transactions[0].delegateCall).to.equal(false) - expect(signed.transactions[0].revertOnError).to.equal(false) - }) - - it('Should sign a message', async () => { - const message = 'Hi! this is a test message' - - const signature = await w3.eth.sign(message, wallet.address) - - // Contract wallet must be deployed before calling ERC1271 - await relayer.deployWallet(wallet.config, context) - - const call = hookCaller.callERC1271isValidSignatureData(wallet.address, prefixEIP191Message(message), signature) - await expect(call).to.be.fulfilled - }) - - it('Should sign and send transaction', async () => { - const signed = await w3.eth.signTransaction({ - from: wallet.address, - gasPrice: '20000000000', - gas: '121000', - to: callReceiver.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 123, '0x445566') - }) - - const tx = await w3.eth.sendSignedTransaction(signed) - expect(tx.transactionHash).to.be.a('string') - - expect(await callReceiver.lastValB()).to.equal('0x445566') - }) - - it('Should sign, joinSignatures and send a transaction with decoded signature', async () => { - const s1 = new ethers.Wallet(ethers.utils.randomBytes(32)) - const s2 = new ethers.Wallet(ethers.utils.randomBytes(32)) - const s3 = new ethers.Wallet(ethers.utils.randomBytes(32)) - - const config = { - threshold: 3, - signers: [ - { - address: s1.address, + }, { + name: '1/2 signers', + signers: () => { + const signer = ethers.Wallet.createRandom() + const signers = [{ + address: signer.address, weight: 1 - }, - { - address: s2.address, - weight: 1 - }, - { - address: s3.address, + }, { + address: ethers.Wallet.createRandom().address, weight: 1 - } - ] - } - - const wallet_1 = new lib.Wallet({ config, context }, s1).connect(ethnode.provider, relayer) - const wallet_2 = new lib.Wallet({ config, context }, s2).connect(ethnode.provider, relayer) - const wallet_3 = new lib.Wallet({ config, context }, s3).connect(ethnode.provider, relayer) - - expect(wallet_1.address).to.equal(wallet_2.address) - expect(wallet_2.address).to.equal(wallet_3.address) - - const w3_1 = new Web3(new LocalWeb3Provider(wallet_1)) - const w3_2 = new Web3(new LocalWeb3Provider(wallet_2)) - const w3_3 = new Web3(new LocalWeb3Provider(wallet_3)) + }].sort(() => Math.random() > 0.5 ? 1 : -1) - const transaction = { - from: wallet_1.address, - gasPrice: '20000000000', - gas: '121000', - to: callReceiver.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 123, '0x445566') - } - - const signed_1 = await w3_1.eth.signTransaction(transaction) - const signed_2 = await w3_2.eth.signTransaction(transaction) - const signed_3 = await w3_3.eth.signTransaction(transaction) - - const full_signed = { - ...signed_1, - signature: joinSignatures(signed_1.signature, signed_2.signature, signed_3.signature) - } - - const tx = await w3_1.eth.sendSignedTransaction(full_signed) - expect(tx.transactionHash).to.be.a('string') - - expect(await callReceiver.lastValB()).to.equal('0x445566') - }) - - it('Should sign, joinSignatures and send a transaction with encoded signature', async () => { - const s1 = new ethers.Wallet(ethers.utils.randomBytes(32)) - const s2 = new ethers.Wallet(ethers.utils.randomBytes(32)) - const s3 = new ethers.Wallet(ethers.utils.randomBytes(32)) - - const config = { - threshold: 3, - signers: [ - { - address: s1.address, - weight: 1 - }, - { - address: s2.address, - weight: 1 - }, - { - address: s3.address, - weight: 1 - } - ] - } - - const wallet_1 = new lib.Wallet({ config, context }, s1).connect(ethnode.provider, relayer) - const wallet_2 = new lib.Wallet({ config, context }, s2).connect(ethnode.provider, relayer) - const wallet_3 = new lib.Wallet({ config, context }, s3).connect(ethnode.provider, relayer) - - expect(wallet_1.address).to.equal(wallet_2.address) - expect(wallet_2.address).to.equal(wallet_3.address) - - const w3_1 = new Web3(new LocalWeb3Provider(wallet_1)) - const w3_2 = new Web3(new LocalWeb3Provider(wallet_2)) - const w3_3 = new Web3(new LocalWeb3Provider(wallet_3)) - - const transaction = { - from: wallet_1.address, - gasPrice: '20000000000', - gas: '121000', - to: callReceiver.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 123, '0x445566') - } - - const signed_1 = await w3_1.eth.signTransaction(transaction) - const signed_2 = await w3_2.eth.signTransaction(transaction) - const signed_3 = await w3_3.eth.signTransaction(transaction) - - const full_signed = { - ...signed_1, - signature: encodeSignature(joinSignatures(signed_1.signature, signed_2.signature, signed_3.signature)) - } - - const tx = await w3_1.eth.sendSignedTransaction(full_signed) - expect(tx.transactionHash).to.be.a('string') - - expect(await callReceiver.lastValB()).to.equal('0x445566') - }) - }) - - describe('estimate gas', async () => { - it('Should estimate gas for a single meta-tx', async () => { - await callReceiver.testCall(ethers.BigNumber.from(0), '0x') - - const transaction = { - from: wallet.address, - gasPrice: '20000000000', - gasLimit: 0, - to: callReceiver.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 123, '0x445566') - } - - const stx = await toSequenceTransaction(wallet, transaction) - const results = await relayer.simulate(wallet.address, stx) - const gasLimits = results.map(result => result.gasLimit) - expect(gasLimits[0]).to.be.above(60000) - expect(gasLimits[0]).to.be.below(100000) - }) - it('Should estimate gas for a single big meta-tx', async () => { - await callReceiver.testCall(ethers.BigNumber.from(0), '0x') - - const data = ethers.utils.randomBytes(512) - const transaction = { - from: wallet.address, - gasPrice: '20000000000', - gasLimit: 0, - to: callReceiver.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 23, data) - } - - const stx = await toSequenceTransaction(wallet, transaction) - const results = await relayer.simulate(wallet.address, stx) - const gasLimits = results.map(result => result.gasLimit) - expect(gasLimits[0]).to.be.above(390000) - expect(gasLimits[0]).to.be.below(450000) - }) - it('Should estimate gas for a batch of meta-txs', async () => { - await callReceiver.testCall(ethers.BigNumber.from(0), '0x') - - const data = ethers.utils.randomBytes(512) - const transactions = [ - { - from: wallet.address, - gasPrice: '20000000000', - gasLimit: 0, - to: callReceiver.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 123, data) - }, - { - from: wallet.address, - gasPrice: '20000000000', - gasLimit: 0, - to: callReceiver.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 123, '0x445566') - } - ] - - const stxs = await toSequenceTransactions(wallet, transactions) - const results = await relayer.simulate(wallet.address, ...stxs) - const gasLimits = results.map(result => result.gasLimit) - expect(gasLimits[0]).to.be.above(390000) - expect(gasLimits[0]).to.be.below(450000) - expect(gasLimits[1]).to.be.above(60000) - expect(gasLimits[1]).to.be.below(100000) - }) - }) - - describe('batch transactions', async () => { - it('Should send two transactions at once', async () => { - const callReceiver1 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - const callReceiver2 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - - const transaction = { - from: wallet.address, - gas: '121000', - to: callReceiver1.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 1, '0x112233'), - auxiliary: [ - { - gas: '121000', - to: callReceiver2.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 2, '0x445566') - } - ] - } - - const signed = await w3.eth.signTransaction(transaction) - await w3.eth.sendSignedTransaction(signed) - - expect(await callReceiver1.lastValB()).to.equal('0x112233') - expect(await callReceiver2.lastValB()).to.equal('0x445566') - }) - - it('Should send three transactions at once', async () => { - const callReceiver1 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - const callReceiver2 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - const callReceiver3 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - - const transaction = { - from: wallet.address, - gas: '121000', - to: callReceiver1.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 1, '0x112233'), - auxiliary: [ - { - gas: '100000', - to: callReceiver2.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 2, '0x445566') - }, - { - gas: '70000', - to: callReceiver3.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 2, '0x778899') - } - ] - } - - const signed = await w3.eth.signTransaction(transaction) - await w3.eth.sendSignedTransaction(signed) - - expect(await callReceiver1.lastValB()).to.equal('0x112233') - expect(await callReceiver2.lastValB()).to.equal('0x445566') - expect(await callReceiver3.lastValB()).to.equal('0x778899') - }) - - it('Should send nested transactions', async () => { - const callReceiver1 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - const callReceiver2 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - const callReceiver3 = await (new CallReceiverMock__factory()).connect(ethnode.signer).deploy() - - const transaction = { - from: wallet.address, - gas: '121000', - to: callReceiver1.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 1, '0x112233'), - auxiliary: [ - { - gas: '100000', - to: callReceiver2.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 2, '0x445566'), - auxiliary: [ - { - gas: '70000', - to: callReceiver3.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 2, '0x778899') - } - ] - } - ] - } - - const signed = await w3.eth.signTransaction(transaction) - await w3.eth.sendSignedTransaction(signed) - - expect(await callReceiver1.lastValB()).to.equal('0x112233') - expect(await callReceiver2.lastValB()).to.equal('0x445566') - expect(await callReceiver3.lastValB()).to.equal('0x778899') - }) - }) - }) - - describe('Validate signatures', () => { - const message = ethers.utils.toUtf8Bytes('Hi! this is a test message') - const digest = ethers.utils.arrayify(ethers.utils.keccak256(message)) - - describe('ethSign', () => { - it('Should validate ethSign signature', async () => { - const signer = new ethers.Wallet(ethers.utils.randomBytes(32)) - const signature = await signer.signMessage(digest) - expect(await isValidSignature(signer.address, digest, signature)).to.be.true - }) - it('Should validate ethSign signature using direct method', async () => { - const signer = new ethers.Wallet(ethers.utils.randomBytes(32)) - const signature = await signer.signMessage(digest) - expect(isValidEthSignSignature(signer.address, digest, signature)).to.be.true - }) - it('Should reject invalid ethSign signature using direct method', async () => { - const signer1 = new ethers.Wallet(ethers.utils.randomBytes(32)) - const signer2 = new ethers.Wallet(ethers.utils.randomBytes(32)) - const signature = await signer1.signMessage(digest) - expect(await isValidSignature(signer2.address, digest, signature)).to.be.false - }) - }) - describe('deployed sequence wallet sign', async () => { - it('Should validate sequence wallet signature', async () => { - const signature = await wallet.sign(message, false, ethnode.chainId) - await relayer.deployWallet(wallet.config, context) - expect(await isValidSignature(wallet.address, digest, signature, ethnode.provider)).to.be.true - }) - it('Should validate sequence wallet signature using direct method', async () => { - const signature = await wallet.signMessage(message, ethnode.chainId) - await relayer.deployWallet(wallet.config, context) - expect(await isValidContractWalletSignature(wallet.address, digest, signature, ethnode.provider)).to.be.true - }) - it('Should reject sequence wallet invalid signature', async () => { - const wallet2 = (await lib.Wallet.singleOwner(new ethers.Wallet(ethers.utils.randomBytes(32)), context)).setProvider( - ethnode.provider - ) - const signature = await wallet2.signMessage(message, ethnode.chainId) - await relayer.deployWallet(wallet.config, context) - expect(await isValidSignature(wallet.address, digest, signature, ethnode.provider, context)).to.be.false - }) - it('Should validate sequence wallet signature via signTypedData', async () => { - // ensure its deployed, as in our test we're assuming we're testing to a deployed wallet - await relayer.deployWallet(wallet.config, context) + const config = coders.config.fromSimple({ + threshold: 1, + checkpoint: 0, + signers + }) - const typedData = { - types: { - Person: [ - { name: 'name', type: 'string' }, - { name: 'wallet', type: 'address' }, - { name: 'count', type: 'uint8' } - ] - }, - primaryType: 'Person' as const, - domain: { - name: 'Ether Mail', - version: '1', - chainId: ethnode.chainId, - verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC' - }, - message: { - name: 'Bob', - wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB', - count: 4 + const orchestrator = new Orchestrator([new hubsigners.SignerWrapper(signer)]) + return { config, orchestrator } } - } - - const digest = encodeTypedDataDigest(typedData) - expect(ethers.utils.hexlify(digest)).to.equal('0x69d3381dfd41c0a9cea56d325bcd482eace26dd2e7b95df398cb6d8edc00290c') - - // an eip712 signed message is just a 712 object's encoded digest, signed as a message. - // therefore, first we will do so directly - { - const signature = await wallet.sign(digest, true, ethnode.chainId) - expect(await isValidContractWalletSignature(wallet.address, digest, signature, ethnode.provider)).to.be.true - } - - // second, we use the signTypedData method directly for convenience - { - const signature = await wallet.signTypedData(typedData.domain, typedData.types, typedData.message, ethnode.chainId) - expect(await isValidContractWalletSignature(wallet.address, digest, signature, ethnode.provider)).to.be.true - } - }) - describe('After updating the owners', () => { - let wallet2: lib.Wallet - - beforeEach(async () => { - const s1 = new ethers.Wallet(ethers.utils.randomBytes(32)) - const s2 = new ethers.Wallet(ethers.utils.randomBytes(32)) + }, { + name: '2/4 signers', + signers: () => { + const members = new Array(4).fill(0).map(() => ethers.Wallet.createRandom()) + + const signers = members.map((m) => ({ + address: m.address, + weight: 2 + })).sort(() => Math.random() > 0.5 ? 1 : -1) + + const config = coders.config.fromSimple({ + threshold: 2, + checkpoint: 0, + signers + }) - const newConfig = { - threshold: 2, - signers: [ - { - address: s1.address, - weight: 1 - }, - { - address: s2.address, - weight: 1 - } - ] + const orchestrator = new Orchestrator(members.slice(0, 2).map((m) => new hubsigners.SignerWrapper(m))) + return { config, orchestrator } } + }, { + name: '11/90 signers', + signers: () => { + const members = new Array(90).fill(0).map(() => ethers.Wallet.createRandom()) - const [config, tx] = await wallet.updateConfig(newConfig) - await tx.wait() - - wallet2 = new lib.Wallet({ config, context }, s1, s2).connect(ethnode.provider, relayer) - }) - it('Should reject previous wallet configuration signature', async () => { - const signature = await wallet.signMessage(message, ethnode.chainId) - expect(await isValidSignature(wallet.address, digest, signature, ethnode.provider, context)).to.be.false - }) - it('Should validate new wallet configuration signature', async () => { - const signature = await wallet2.signMessage(message, ethnode.chainId) - expect(await isValidSignature(wallet.address, digest, signature, ethnode.provider, context)).to.be.true - }) - }) - }) - describe('non-deployed sequence wallet sign', async () => { - it('Should validate sequence wallet signature', async () => { - const signature = await wallet.signMessage(message) - expect(await isValidSignature(wallet.address, digest, signature, ethnode.provider, context)).to.be.true - }) - it('Should valdiate sequence wallet multi-signature', async () => { - const s1 = new ethers.Wallet(ethers.utils.randomBytes(32)) - const s2 = new ethers.Wallet(ethers.utils.randomBytes(32)) - - const newConfig = { - threshold: 2, - signers: [ - { - address: s1.address, + const signers = members.map((m) => ({ + address: m.address, weight: 1 - }, - { - address: s2.address, - weight: 1 - } - ] - } - - const wallet2 = new lib.Wallet({ config: newConfig, context }, s1, s2).connect(ethnode.provider, relayer) - const signature = await wallet2.signMessage(message) - expect(await isValidSignature(wallet2.address, digest, signature, ethnode.provider, context, ethnode.chainId)).to.be.true - }) - it('Should validate sequence wallet signature using direct method', async () => { - const signature = await wallet.signMessage(message) - expect(await isValidSequenceUndeployedWalletSignature(wallet.address, digest, signature, context, ethnode.provider)).to.be - .true - }) - it('Should reject sequence wallet invalid signature', async () => { - const wallet2 = ( - await lib.Wallet.singleOwner(new ethers.Wallet(ethers.utils.randomBytes(32)), { ...context, nonStrict: true }) - ).setProvider(ethnode.provider) - const signature = await wallet2.signMessage(message, 1) - expect(await isValidSignature(wallet.address, digest, signature, ethnode.provider, context)).to.be.false - }) - it('Should reject signature with not enough weight', async () => { - const s1 = new ethers.Wallet(ethers.utils.randomBytes(32)) - const s2 = new ethers.Wallet(ethers.utils.randomBytes(32)) - - const newConfig = { - threshold: 2, - signers: [ - { - address: s1.address, - weight: 1 - }, - { - address: s2.address, - weight: 1 - } - ] - } - - const wallet2 = new lib.Wallet({ config: newConfig, context }, s1).connect(ethnode.provider, relayer) - const signature = await wallet2.signMessage(message) - expect(await isValidSignature(wallet2.address, digest, signature, ethnode.provider, context, 1)).to.be.false - }) - it('Should reject signature with not enough weight but enough signers', async () => { - const s1 = new ethers.Wallet(ethers.utils.randomBytes(32)) - const s2 = new ethers.Wallet(ethers.utils.randomBytes(32)) - const s3 = new ethers.Wallet(ethers.utils.randomBytes(32)) - - const newConfig = { - threshold: 2, - signers: [ - { - address: s1.address, - weight: 0 - }, - { - address: s2.address, - weight: 0 - }, - { - address: s3.address, - weight: 1 - } - ] - } - - const wallet2 = new lib.Wallet({ config: newConfig, context, strict: false }, s1, s2).connect(ethnode.provider, relayer) - const signature = await wallet2.signMessage(message) - expect(await isValidSignature(wallet2.address, digest, signature, ethnode.provider, context, ethnode.chainId)).to.be.false - }) - it('Should be able to just deploy a new wallet and have valid signatures', async () => { - const pk = ethers.utils.randomBytes(32) - const wallet2 = (await lib.Wallet.singleOwner(pk, context)).connect(ethnode.provider, relayer) - const signature = await wallet2.sign(message, false, ethnode.chainId) - expect(await isValidSignature(wallet2.address, digest, signature, ethnode.provider)).to.not.be.true - await wallet2.sendTransaction([]) - expect(await isValidSignature(wallet2.address, digest, signature, ethnode.provider)).to.be.true - }) - }) - describe('deployed wallet sign', () => { - it('Should validate wallet signature', async () => { - const signature = await wallet.signMessage(message) - await relayer.deployWallet(wallet.config, context) - expect(await isValidSignature(wallet.address, digest, signature, ethnode.provider)).to.be.true - }) - it('Should validate wallet signature using direct method', async () => { - const signature = await wallet.signMessage(message) - await relayer.deployWallet(wallet.config, context) - expect(await isValidContractWalletSignature(wallet.address, digest, signature, ethnode.provider)).to.be.true - }) - it('Should reject invalid wallet signature', async () => { - const wallet2 = (await lib.Wallet.singleOwner(new ethers.Wallet(ethers.utils.randomBytes(32)), context)).setProvider( - ethnode.provider - ) - const signature = await wallet2.signMessage(message, ethnode.chainId) - await relayer.deployWallet(wallet.config, context) - expect(await isValidSignature(wallet.address, digest, signature, ethnode.provider, context)).to.be.false - }) - }) - it('Should sign typed data', async () => { - const proof = new Proof({ - address: wallet.address - }) - - proof.setExpiryIn(3e7) // 1 year - proof.claims.app = 'SkyWeaver' + })).sort(() => Math.random() > 0.5 ? 1 : -1) - const messageTypedData = proof.messageTypedData() - - const sigResp = await wallet.signTypedData(messageTypedData.domain, messageTypedData.types, messageTypedData.message) - - await relayer.deployWallet(wallet.config, wallet.context) - - expect( - await new Contract(wallet.address, MainModuleArtifact.abi, wallet.provider)['isValidSignature(bytes32,bytes)']( - proof.messageDigest(), - sigResp - ) - ).to.equal('0x1626ba7e') - }) - describe('Broken signers', () => { - describe('Broken EOA signer', async () => { - let s1: ethers.Wallet - let s2: ethers.Wallet - let config: WalletConfig - - beforeEach(() => { - s1 = new ethers.Wallet(ethers.utils.randomBytes(32)) - s2 = new ethers.Wallet(ethers.utils.randomBytes(32)) - - s2.signMessage = (() => { - throw Error('ups') - }) as any + const config = coders.config.fromSimple({ + threshold: 11, + checkpoint: 0, + signers + }) - config = { - threshold: 1, - signers: [ - { - address: s1.address, - weight: 1 - }, - { - address: s2.address, - weight: 1 - } - ] + const orchestrator = new Orchestrator(members.slice(0, 11).map((m) => new hubsigners.SignerWrapper(m))) + return { config, orchestrator } } - }) - - it('Should skip broken signer', async () => { - const wallet2 = new lib.Wallet({ config: config, context }, s1, s2).connect(ethnode.provider, relayer) - const signature = await wallet2.signMessage(message, await wallet2.getChainId(), false) - expect(await isValidSignature(wallet2.address, digest, signature, ethnode.provider, context, ethnode.chainId)).to.be - .true - }) - it('Should reject broken signer', async () => { - const wallet2 = new lib.Wallet({ config: config, context }, s1, s2).connect(ethnode.provider, relayer) - const signature = wallet2.signMessage(message, await wallet2.getChainId(), true) - await expect(signature).to.be.rejected - }) - }) - describe('Broken nested sequence signer', async () => { - let s1: ethers.Wallet - let w2: lib.Wallet - let config: WalletConfig - - beforeEach(async () => { - s1 = new ethers.Wallet(ethers.utils.randomBytes(32)) + }, { + name: '1/1 signer (nested)', + signers: async () => { + const nestedSigner = ethers.Wallet.createRandom() - const walletA = (await lib.Wallet.singleOwner(ethers.Wallet.createRandom(), context)).connect(ethnode.provider, relayer) - w2 = (await lib.Wallet.singleOwner(walletA, context)).connect(ethnode.provider, relayer) - - // TODO: Bundle deployment with child wallets - await relayer.deployWallet(walletA.config, walletA.context) - - w2.sign = (() => { - throw Error('ups') - }) as any - - config = { + const nestedConfig = coders.config.fromSimple({ threshold: 1, - signers: [ - { - address: s1.address, - weight: 1 - }, - { - address: w2.address, - weight: 1 - } - ] - } - }) - - it('Should skip broken nested signer', async () => { - const wallet2 = new lib.Wallet({ config: config, context }, s1, w2).connect(ethnode.provider, relayer) - const signature = await wallet2.signMessage(message, await wallet2.getChainId(), false) - expect(await isValidSignature(wallet2.address, digest, signature, ethnode.provider, context, ethnode.chainId)).to.be - .true - }) - it('Should reject broken nested signer', async () => { - const wallet2 = new lib.Wallet({ config: config, context }, s1, w2).connect(ethnode.provider, relayer) - const signature = wallet2.signMessage(message, await wallet2.getChainId(), true) - await expect(signature).to.be.rejected - }) - }) - describe('Broken remote signer', async () => { - let s1: ethers.Wallet - let r2: RemoteSigner - let config: WalletConfig - - beforeEach(async () => { - s1 = new ethers.Wallet(ethers.utils.randomBytes(32)) + checkpoint: 0, + signers: [{ address: nestedSigner.address, weight: 1 }] + }) - const r2Addr = ethers.Wallet.createRandom().address + const nestedOrchestrator = new Orchestrator([nestedSigner]) + const nestedWallet = Wallet.newWallet({ + coders: coders, + context: contexts[version], + config: nestedConfig, + orchestrator: nestedOrchestrator, + chainId: provider.network.chainId, + provider, + relayer + }) - r2 = { - _isSigner: true, - getAddress: async () => r2Addr, - signMessageWithData: () => { - throw Error('Ups') - } - } as any + await nestedWallet.deploy() + expect(await nestedWallet.reader().isDeployed(nestedWallet.address)).to.be.true - config = { + const config = coders.config.fromSimple({ threshold: 1, - signers: [ - { - address: s1.address, - weight: 1 - }, - { - address: await r2.getAddress(), - weight: 1 - } - ] - } - }) - - it('Should skip broken remote signer', async () => { - const wallet2 = new lib.Wallet({ config: config, context }, s1, r2).connect(ethnode.provider, relayer) - const signature = await wallet2.signMessage(message, await wallet2.getChainId(), false) - expect(await isValidSignature(wallet2.address, digest, signature, ethnode.provider, context, ethnode.chainId)).to.be - .true - }) - it('Should reject broken remote signer', async () => { - const wallet2 = new lib.Wallet({ config: config, context }, r2).connect(ethnode.provider, relayer) - const signature = wallet2.signMessage(message, await wallet2.getChainId(), true) - await expect(signature).to.be.rejected - }) - }) - }) - }) - describe('Update wallet configuration', () => { - let transaction: Transactionish - beforeEach(async () => { - transaction = { - from: wallet.address, - gasPrice: '20000000000', - to: callReceiver.address, - value: 0, - data: await encodeData(callReceiver, 'testCall', 123, '0x445566') - } - }) - it('Should migrate and update to a new single owner configuration', async () => { - const address = await wallet.getAddress() - - const s1 = new ethers.Wallet(ethers.utils.randomBytes(32)) - - const newConfig = { - threshold: 1, - signers: [ - { - address: s1.address, - weight: 1 - } - ] - } - - expect(await wallet.isDeployed()).to.be.false - - const [updatedConfig, tx] = await wallet.updateConfig(newConfig) - await tx.wait() - - expect(await wallet.isDeployed()).to.be.true - - const updatedWallet = wallet.useConfig(updatedConfig).useSigners(s1) - expect(updatedWallet.imageHash).to.equal(await fetchImageHash(updatedWallet)) - expect(await updatedWallet.getAddress()).to.equal(address) - - expect( - ethers.utils.defaultAbiCoder.decode(['address'], await ethnode.provider.getStorageAt(wallet.address, wallet.address))[0] - ).to.equal(ethers.utils.getAddress(context.mainModuleUpgradable)) - - expect(updatedWallet.address).to.be.equal(wallet.address) - expect(updatedWallet.address).to.not.be.equal(addressOf(newConfig, context)) - - await updatedWallet.sendTransaction(transaction) - }) - it('Should migrate and update to a new multiple owner configuration', async () => { - const s1 = new ethers.Wallet(ethers.utils.randomBytes(32)) - const s2 = new ethers.Wallet(ethers.utils.randomBytes(32)) - - const newConfig = { - threshold: 2, - signers: [ - { - address: s1.address, - weight: 1 - }, - { - address: s2.address, - weight: 1 - } - ] - } - - const [config, tx] = await wallet.updateConfig(newConfig) - await tx.wait() - - const updatedWallet = new lib.Wallet({ config, context }, s1, s2).connect(ethnode.provider, relayer) - - expect( - ethers.utils.defaultAbiCoder.decode(['address'], await ethnode.provider.getStorageAt(wallet.address, wallet.address))[0] - ).to.equal(ethers.utils.getAddress(context.mainModuleUpgradable)) - - expect(updatedWallet.address).to.be.equal(wallet.address) - expect(updatedWallet.address).to.not.be.equal(addressOf(newConfig, context)) - - await updatedWallet.sendTransaction(transaction) - }) - it('Should skip mainModule implementation upgrade if already up to date', async () => { - const s1 = new ethers.Wallet(ethers.utils.randomBytes(32)) - const s2 = new ethers.Wallet(ethers.utils.randomBytes(32)) - - const newConfig = { - threshold: 2, - signers: [ - { - address: s1.address, - weight: 1 - }, - { - address: s2.address, - weight: 1 - } - ] - } - - const oldConfig = wallet.config - const [config, tx] = await wallet.updateConfig(newConfig) - await tx.wait() - - const updatedWallet = new lib.Wallet({ config, context }, s1, s2).connect(ethnode.provider, relayer) - - const updateTx = await updatedWallet.buildUpdateConfigTransaction(oldConfig, true, true) - - const mainModuleInterface = new utils.Interface(walletContracts.mainModule.abi) - const mainModuleUpgradableInterface = new utils.Interface(walletContracts.mainModuleUpgradable.abi) - const sequenceUtilsInterface = new utils.Interface(walletContracts.sequenceUtils.abi) - - expect(updateTx.length).to.equal(1) - - const decoded = mainModuleInterface.decodeFunctionData('selfExecute', updateTx[0].data!)[0] - expect(decoded.length).to.equal(2) - - const decoded0 = mainModuleUpgradableInterface.decodeFunctionData('updateImageHash', decoded[0].data) - expect(decoded0).to.not.be.undefined - - const decoded1 = sequenceUtilsInterface.decodeFunctionData('publishConfig', decoded[1].data) - expect(decoded1).to.not.be.undefined - }) - it('Should skip selfExecute if update requires a single transaction', async () => { - const s1 = new ethers.Wallet(ethers.utils.randomBytes(32)) - const s2 = new ethers.Wallet(ethers.utils.randomBytes(32)) - - const newConfig = { - threshold: 2, - signers: [ - { - address: s1.address, - weight: 1 - }, - { - address: s2.address, - weight: 1 - } - ] - } - - const oldConfig = wallet.config - const [config, tx] = await wallet.updateConfig(newConfig) - await tx.wait() - - const updatedWallet = new lib.Wallet({ config, context }, s1, s2).connect(ethnode.provider, relayer) - - const updateTx = await updatedWallet.buildUpdateConfigTransaction(oldConfig, false) - - const mainModuleInterface = new utils.Interface(walletContracts.mainModule.abi) - const mainModuleUpgradableInterface = new utils.Interface(walletContracts.mainModuleUpgradable.abi) - - expect(updateTx.length).to.equal(1) - - await expect((async () => mainModuleInterface.decodeFunctionData('selfExecute', updateTx[0].data!))()).to.be.rejected - - const decoded = mainModuleUpgradableInterface.decodeFunctionData('updateImageHash', updateTx[0].data!) - expect(decoded).to.not.be.undefined - }) - it('Should migrate and publish config', async () => { - const s1 = new ethers.Wallet(ethers.utils.randomBytes(32)) - const s2 = new ethers.Wallet(ethers.utils.randomBytes(32)) - - const newConfig = { - threshold: 2, - signers: [ - { - address: s1.address, - weight: 1 - }, - { - address: s2.address, - weight: 1 - } - ] - } - - const [, tx] = await wallet.updateConfig(newConfig, undefined, true) - const receipt = await tx.wait() - expect(receipt.logs[6].data).to.contain(s1.address.slice(2).toLowerCase()) - expect(receipt.logs[6].data).to.contain(s2.address.slice(2).toLowerCase()) - }) - it('Should publish config', async () => { - const receipt = await (await wallet.publishConfig()).wait() - expect(receipt.logs[3].data).to.contain(wallet.config.signers[0].address.slice(2).toLowerCase()) - }) - describe('after migrating and updating', () => { - let wallet2: lib.Wallet + checkpoint: 0, + signers: [{ address: nestedWallet.address, weight: 1 }] + }) - beforeEach(async () => { - const s1 = new ethers.Wallet(ethers.utils.randomBytes(32)) + const orchestrator = new Orchestrator([new SequenceOrchestratorWrapper(nestedWallet)]) - const newConfig = { - threshold: 1, - signers: [ - { - address: s1.address, - weight: 1 - } - ] + return { config, orchestrator } } + }]).map(({ name, signers }) => { + describe(`Using ${name}`, () => { + let orchestrator: Orchestrator + let config: commons.config.Config + + beforeEach(async () => { + const { config: _config, orchestrator: _orchestrator } = await signers() + config = _config + orchestrator = _orchestrator + }) - const [config, tx] = await wallet.updateConfig(newConfig) - await tx.wait() - - wallet2 = new lib.Wallet({ config, context }, s1).connect(ethnode.provider, relayer) - }) - it('Should update to a new single owner configuration', async () => { - const s1 = new ethers.Wallet(ethers.utils.randomBytes(32)) - - const newConfig = { - threshold: 1, - signers: [ - { - address: s1.address, - weight: 1 - } - ] - } - const [config, tx] = await wallet2.updateConfig(newConfig) - await tx.wait() + it('Should sign and validate a message', async () => { + const wallet = Wallet.newWallet({ + coders: coders, + context: contexts[version], + config, + orchestrator, + chainId: provider.network.chainId, + provider, + relayer + }) - const updatedWallet = new lib.Wallet({ config, context }, s1).connect(ethnode.provider, relayer) + await wallet.deploy() + expect(await wallet.reader().isDeployed(wallet.address)).to.be.true - expect( - ethers.utils.defaultAbiCoder.decode( - ['address'], - await ethnode.provider.getStorageAt(wallet2.address, wallet2.address) - )[0] - ).to.equal(ethers.utils.getAddress(context.mainModuleUpgradable)) + const message = ethers.utils.toUtf8Bytes( + `This is a random message: ${ethers.utils.hexlify(ethers.utils.randomBytes(96))}` + ) - expect(updatedWallet.address).to.be.equal(wallet2.address) - expect(updatedWallet.address).to.not.be.equal(addressOf(newConfig, context)) + const signature = await wallet.signMessage(message) + const digest = ethers.utils.keccak256(message) - await updatedWallet.sendTransaction(transaction) - }) - it('Should update to a new multiple owner configuration', async () => { - const s1 = new ethers.Wallet(ethers.utils.randomBytes(32)) - const s2 = new ethers.Wallet(ethers.utils.randomBytes(32)) + expect(await wallet.reader().isValidSignature(wallet.address, digest, signature)).to.be.true + }); - const newConfig = { - threshold: 2, - signers: [ - { - address: s1.address, - weight: 1 + // + // Run tests for deployed and undeployed wallets + // transactions should be decorated automatically + // + ([{ + name: 'After deployment', + setup: async (wallet: Wallet) => { + await wallet.deploy() }, - { - address: s2.address, - weight: 1 - } - ] - } - - const [config, tx] = await wallet2.updateConfig(newConfig) - await tx.wait() + deployed: true + }, { + name: 'Before deployment', + setup: async (_: Wallet) => { }, + deployed: false + }]).map(({ name, setup, deployed }) => { + describe(name, () => { + let wallet: Wallet + + beforeEach(async () => { + wallet = Wallet.newWallet({ + coders: coders, + context: contexts[version], + config, + orchestrator, + chainId: provider.network.chainId, + provider, + relayer + }) + + await setup(wallet) + }) + + it('Should send an empty list of transactions', async () => { + await wallet.sendTransaction([]) + }) + + it('Should send a transaction with an empty call', async () => { + await wallet.sendTransaction([{ + to: ethers.Wallet.createRandom().address + }]) + }) - const updatedWallet = new lib.Wallet({ config, context }, s1, s2).connect(ethnode.provider, relayer) + it('Should build and execute a wallet update transaction', async () => { + const newConfig = coders.config.fromSimple({ + threshold: 1, + checkpoint: 0, + signers: [{ + address: ethers.Wallet.createRandom().address, + weight: 1 + }] + }) + + const updateTx = await wallet.buildUpdateConfigurationTransaction(newConfig) + + expect(updateTx.entrypoint).to.equal(wallet.address) + expect(updateTx.transactions[0].to).to.equal(wallet.address) + expect(updateTx.transactions[0].delegateCall).to.equal(false) + expect(updateTx.transactions[0].revertOnError).to.equal(true) + expect(updateTx.transactions[0].gasLimit).to.equal(0) + expect(updateTx.transactions[0].value).to.equal(0) + + if (version === 1) { + expect(updateTx.transactions.length).to.be.equal(2) + expect(updateTx.transactions[1].to).to.equal(wallet.address) + expect(updateTx.transactions[1].delegateCall).to.equal(false) + expect(updateTx.transactions[1].revertOnError).to.equal(true) + expect(updateTx.transactions[1].gasLimit).to.equal(0) + expect(updateTx.transactions[1].value).to.equal(0) + } else if (version === 2) { + expect(updateTx.transactions.length).to.be.equal(1) + } else { + throw new Error('Version not supported in test') + } - expect( - ethers.utils.defaultAbiCoder.decode( - ['address'], - await ethnode.provider.getStorageAt(wallet2.address, wallet2.address) - )[0] - ).to.equal(ethers.utils.getAddress(context.mainModuleUpgradable)) + const prevImplentation = await wallet.reader().implementation(wallet.address) - expect(updatedWallet.address).to.be.equal(wallet2.address) - expect(updatedWallet.address).to.not.be.equal(addressOf(newConfig, context)) + await wallet.sendTransaction(updateTx.transactions) - await updatedWallet.sendTransaction(transaction) - }) - it('Should reject transaction of previous owner', async () => { - const tx = wallet.sendTransaction(transaction) - expect(tx).to.be.rejected + expect(await wallet.reader().imageHash(wallet.address)).to.equal(coders.config.imageHashOf(newConfig)) + expect(await wallet.reader().implementation(wallet.address)).to.not.equal(prevImplentation) + }) + }) + }) + }) }) }) - it('Should reject a non-usable configuration', async () => { - const s1 = new ethers.Wallet(ethers.utils.randomBytes(32)) - const s2 = new ethers.Wallet(ethers.utils.randomBytes(32)) - - const newConfig = { - threshold: 3, - signers: [ - { - address: s1.address, - weight: 1 - }, - { - address: s2.address, - weight: 1 - } - ] - } - - const prom = wallet.buildUpdateConfigTransaction(newConfig) - await expect(prom).to.be.rejected - }) - it('Should accept a non-usable configuration in non-strict mode', async () => { - const wallet = ( - await lib.Wallet.singleOwner(new ethers.Wallet(ethers.utils.randomBytes(32)), { ...context, nonStrict: true }) - ).connect(ethnode.provider, relayer) - - const s1 = new ethers.Wallet(ethers.utils.randomBytes(32)) - const s2 = new ethers.Wallet(ethers.utils.randomBytes(32)) - - const newConfig = { - threshold: 3, - signers: [ - { - address: s1.address, - weight: 1 - }, - { - address: s2.address, - weight: 1 - } - ] - } - - const prom = wallet.buildUpdateConfigTransaction(newConfig) - await expect(prom).to.be.not.rejected - }) }) }) diff --git a/packages/wallet/tests/wallet.unit.spec.ts b/packages/wallet/tests/wallet.unit.spec.ts deleted file mode 100644 index 9551620f6..000000000 --- a/packages/wallet/tests/wallet.unit.spec.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { expect } from 'chai' - -import * as lib from '../src' -import { ethers } from 'ethers' -import { recoverConfig } from '../src' -import { packMessageData } from "@0xsequence/utils" - -describe('Wallet units', function() { - const context = { - factory: '0x7c2C195CD6D34B8F845992d380aADB2730bB9C6F', - mainModule: '0x8858eeB3DfffA017D4BCE9801D340D36Cf895CCf', - mainModuleUpgradable: '0xC7cE8a07f69F226E52AEfF57085d8C915ff265f7' - } - - describe('wallet creation', () => { - - it('Should return wallet address', () => { - const config = { - threshold: 1, - signers: [{ - weight: 1, - address: '0xd63A09C47FDc03e2Cff620446b37f205A7D0679D' - }] - } - - const pk = '0x87306d4b9fe56c2af23c7cc3bc69914eba8f7c8fc1d35b4c9a7dd7ea198a428b' - const wallet = new lib.Wallet({ config, context }, pk) - - const expected = '0xF0BA65550F2d1DCCf4B131B774844DC3d801D886' - expect(wallet.address).to.be.equal(expected) - }) - - it('Should reject non-usable config', () => { - const config = { - threshold: 4, - signers: [ - { - address: '0x173C645E3a784612bC3132cA8ae47AFE4Ef405c4', - weight: 1 - }, - { - address: '0xEc5526D3C399f9810a70D44c90a680Dce93b7bEc', - weight: 1 - } - ] - } - - expect(() => new lib.Wallet({ config })).to.throw(Error) - }) - - it('Should accept non-usable config on non-strict mode', () => { - const config = { - threshold: 4, - signers: [ - { - address: '0x173C645E3a784612bC3132cA8ae47AFE4Ef405c4', - weight: 1 - }, - { - address: '0xEc5526D3C399f9810a70D44c90a680Dce93b7bEc', - weight: 1 - } - ] - } - - expect(() => new lib.Wallet({ config, context, strict: false })).to.not.throw(Error) - }) - }) - - describe('signing', () => { - it('Should sign a nested message', async () => { - const context = { - factory: '0x5FbDB2315678afecb367f032d93F642f64180aa3', - mainModule: '0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512', - mainModuleUpgradable: ethers.constants.AddressZero, - nonStrict: true - } - - const signer = new ethers.Wallet('0x2bf2dfccb8c9fb4bb4d46ac9e2b537c373b44ae4c2ee66de92e02f132f7c2237') - expect(signer.address).to.equal('0x1d76701Ba8B8B87Eb36C4cB30B17aea32c22846c') - - const wallet_a = await lib.Wallet.singleOwner(signer, context) - expect(wallet_a.address).to.equal('0xcBf920895f0A101b85f94903775E61d1B652b6Ca') - - const wallet = await lib.Wallet.singleOwner(wallet_a, context) - expect(wallet.address).to.equal('0x78505fd3f2C93cc112e5653C559c901760A64662') - - const message = "0xa5c192879af3d12649fddbc1923aec506ac2a2697421ca198f2443efca1e17cd46e1090945dd946f32c0f957a013a564c8006e1e756534b4d8821bba94731b98a2c00daf2e5d69275fdbf4753b912878f2abd37fbb4de896d11147c8e7a3f8" - - const signed = await wallet.signMessage(message, 31337, true) - expect(signed).to.equal("0x00010201cbf920895f0a101b85f94903775e61d1b652b6ca004700010001bb1be304f3e34bca7ce59067d47f70cddf41c6888166a924570319b6d0e1606e37d92b1ad79895d7e78e8e621a3ecadc884a6cbca4fff1b3e130c7313c1772591c0203") - }) - it('Should sign a message', async () => { - const message = '0x1901f0ba65550f2d1dccf4b131b774844dc3d801d886bbd4edcf660f395f21fe94792f7c1da94638270a049646e541004312b3ec1ac5' - const digest = ethers.utils.arrayify(ethers.utils.keccak256(message)) - - const config = { - threshold: 1, - signers: [{ - weight: 1, - address: '0xd63A09C47FDc03e2Cff620446b37f205A7D0679D' - }] - } - - const pk = '0x87306d4b9fe56c2af23c7cc3bc69914eba8f7c8fc1d35b4c9a7dd7ea198a428b' - const wallet = (new lib.Wallet({ config, context, strict: false }, pk)) - - const expected = '0x00010001a0fb306480bc3027c04d33a16370f4618b29f2d5b89464f526045c94802bc9d1525389c364b75daf58e859ed0d6105aac6b3718e4659814c7793c626653edb871b02' - expect(await wallet.sign(digest, true, 1)).to.equal(expected) - }) - - it('Should sign and recover the configuration of a single signer', async () => { - const pk = ethers.utils.randomBytes(32) - const wallet = await lib.Wallet.singleOwner(pk, { ...context, nonStrict: true }) - - const message = ethers.utils.toUtf8Bytes('Hi! this is a test message') - const chainId = 3 - - const sig = await wallet.signMessage(message, chainId) - const digest = packMessageData(wallet.address, chainId, ethers.utils.keccak256(message)) - const recovered = await recoverConfig(digest, sig) - - expect(recovered.threshold).to.equal(1) - expect(recovered.signers.length).to.equal(1) - expect(recovered.signers[0].weight).to.equal(1) - expect(recovered.signers[0].address).to.equal(wallet.config.signers[0].address) - }) - - it('Should sign and recover the configuration of multiple signers', async () => { - const signer1 = new ethers.Wallet(ethers.utils.randomBytes(32)) - const signer2 = new ethers.Wallet(ethers.utils.randomBytes(32)) - const wallet = new lib.Wallet({ - config: { - threshold: 3, - signers: [{ - weight: 2, - address: signer1.address - }, { - weight: 5, - address: signer2.address - }] - }, - context, - strict: false - }, signer1) - - const message = ethers.utils.toUtf8Bytes('Hi! this is a test message') - const chainId = 3 - - const sig = await wallet.signMessage(message, chainId) - const digest = packMessageData(wallet.address, chainId, ethers.utils.keccak256(message)) - const recovered = await recoverConfig(digest, sig) - - expect(recovered.threshold).to.equal(3) - expect(recovered.signers.length).to.equal(2) - expect(recovered.signers.find((s) => s.address === signer1.address)!.weight).to.equal(2) - expect(recovered.signers.find((s) => s.address === signer2.address)!.weight).to.equal(5) - }) - }) - -}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 38291be59..0616723d3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,9 +23,6 @@ importers: '@0xsequence/auth': specifier: workspace:* version: link:packages/auth - '@0xsequence/config': - specifier: workspace:* - version: link:packages/config '@0xsequence/deployer': specifier: workspace:* version: link:packages/deployer @@ -56,9 +53,6 @@ importers: '@0xsequence/simulator': specifier: workspace:* version: link:packages/simulator - '@0xsequence/transactions': - specifier: workspace:* - version: link:packages/transactions '@0xsequence/utils': specifier: workspace:* version: link:packages/utils @@ -66,26 +60,26 @@ importers: specifier: workspace:* version: link:packages/wallet '@babel/core': - specifier: ^7.20.2 - version: 7.20.5 + specifier: ^7.21.4 + version: 7.21.4 '@babel/plugin-proposal-class-properties': specifier: ^7.18.6 - version: 7.18.6(@babel/core@7.20.5) + version: 7.18.6(@babel/core@7.21.4) '@babel/preset-env': - specifier: ^7.20.2 - version: 7.20.2(@babel/core@7.20.5) + specifier: ^7.21.4 + version: 7.21.4(@babel/core@7.21.4) '@babel/preset-typescript': - specifier: ^7.18.6 - version: 7.18.6(@babel/core@7.20.5) + specifier: ^7.21.4 + version: 7.21.4(@babel/core@7.21.4) '@babel/runtime': - specifier: ^7.20.1 - version: 7.20.6 + specifier: ^7.21.0 + version: 7.21.0 '@changesets/changelog-github': specifier: ^0.4.7 version: 0.4.8 '@changesets/cli': - specifier: ^2.25.2 - version: 2.26.0 + specifier: ^2.26.1 + version: 2.26.1 '@preconstruct/cli': specifier: ^2.2.2 version: 2.2.2 @@ -99,14 +93,14 @@ importers: specifier: ^10.0.0 version: 10.0.1 '@types/node': - specifier: ^18.11.17 - version: 18.11.17 + specifier: ^18.16.1 + version: 18.16.1 '@typescript-eslint/eslint-plugin': - specifier: ^5.43.0 - version: 5.47.0(@typescript-eslint/parser@5.47.0)(eslint@8.30.0)(typescript@4.9.4) + specifier: ^5.59.1 + version: 5.59.1(@typescript-eslint/parser@5.59.1)(eslint@8.39.0)(typescript@4.9.4) '@typescript-eslint/parser': - specifier: ^5.43.0 - version: 5.47.0(eslint@8.30.0)(typescript@4.9.4) + specifier: ^5.59.1 + version: 5.59.1(eslint@8.39.0)(typescript@4.9.4) ava: specifier: ^3.15.0 version: 3.15.0 @@ -120,17 +114,17 @@ importers: specifier: ^7.5.0 version: 7.6.0 eslint: - specifier: ^8.27.0 - version: 8.30.0 + specifier: ^8.39.0 + version: 8.39.0 eslint-config-prettier: - specifier: ^8.5.0 - version: 8.5.0(eslint@8.30.0) + specifier: ^8.8.0 + version: 8.8.0(eslint@8.39.0) eslint-plugin-import: - specifier: ^2.26.0 - version: 2.26.0(@typescript-eslint/parser@5.47.0)(eslint@8.30.0) + specifier: ^2.27.5 + version: 2.27.5(@typescript-eslint/parser@5.59.1)(eslint@8.39.0) eslint-plugin-prettier: specifier: ^4.2.1 - version: 4.2.1(eslint-config-prettier@8.5.0)(eslint@8.30.0)(prettier@2.8.1) + version: 4.2.1(eslint-config-prettier@8.8.0)(eslint@8.39.0)(prettier@2.8.8) ethers: specifier: ^5.7.2 version: 5.7.2 @@ -141,8 +135,8 @@ importers: specifier: ^2.12.2 version: 2.12.4(ts-node@10.9.1)(typescript@4.9.4) husky: - specifier: 4.3.8 - version: 4.3.8 + specifier: ^8.0.0 + version: 8.0.3 mocha: specifier: ^10.1.0 version: 10.2.0 @@ -150,17 +144,17 @@ importers: specifier: ^15.1.0 version: 15.1.0 prettier: - specifier: ^2.7.1 - version: 2.8.1 + specifier: ^2.8.8 + version: 2.8.8 puppeteer: specifier: ^19.7.2 version: 19.7.2(typescript@4.9.4) rimraf: - specifier: ^3.0.2 - version: 3.0.2 + specifier: ^5.0.0 + version: 5.0.0 ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@18.11.17)(typescript@4.9.4) + version: 10.9.1(@types/node@18.16.1)(typescript@4.9.4) tsx: specifier: ^3.12.1 version: 3.12.1 @@ -174,57 +168,69 @@ importers: packages/0xsequence: dependencies: '@0xsequence/abi': - specifier: ^0.43.34 + specifier: workspace:* version: link:../abi + '@0xsequence/account': + specifier: workspace:* + version: link:../account '@0xsequence/api': - specifier: ^0.43.34 + specifier: workspace:* version: link:../api '@0xsequence/auth': - specifier: ^0.43.34 + specifier: workspace:* version: link:../auth - '@0xsequence/config': - specifier: ^0.43.34 - version: link:../config + '@0xsequence/core': + specifier: workspace:* + version: link:../core '@0xsequence/guard': - specifier: ^0.43.34 + specifier: workspace:* version: link:../guard '@0xsequence/indexer': - specifier: ^0.43.34 + specifier: workspace:* version: link:../indexer '@0xsequence/metadata': - specifier: ^0.43.34 + specifier: workspace:* version: link:../metadata + '@0xsequence/migration': + specifier: workspace:* + version: link:../migration '@0xsequence/multicall': - specifier: ^0.43.34 + specifier: workspace:* version: link:../multicall '@0xsequence/network': - specifier: ^0.43.34 + specifier: workspace:* version: link:../network '@0xsequence/provider': - specifier: ^0.43.34 + specifier: workspace:* version: link:../provider '@0xsequence/relayer': - specifier: ^0.43.34 + specifier: workspace:* version: link:../relayer - '@0xsequence/transactions': - specifier: ^0.43.34 - version: link:../transactions + '@0xsequence/sessions': + specifier: workspace:* + version: link:../sessions + '@0xsequence/signhub': + specifier: workspace:* + version: link:../signhub '@0xsequence/utils': - specifier: ^0.43.34 + specifier: workspace:* version: link:../utils '@0xsequence/wallet': - specifier: ^0.43.34 + specifier: workspace:* version: link:../wallet devDependencies: + '@0xsequence/tests': + specifier: workspace:* + version: link:../tests '@0xsequence/wallet-contracts': - specifier: 1.10.0 + specifier: ^1.10.0 version: 1.10.0 '@babel/plugin-transform-runtime': specifier: ^7.19.6 - version: 7.19.6(@babel/core@7.20.5) + version: 7.19.6(@babel/core@7.21.4) babel-loader: specifier: ^9.1.0 - version: 9.1.0(@babel/core@7.20.5)(webpack@5.75.0) + version: 9.1.0(@babel/core@7.21.4)(webpack@5.75.0) ethers: specifier: ^5.7.2 version: 5.7.2 @@ -249,43 +255,95 @@ importers: packages/abi: {} + packages/account: + dependencies: + '@0xsequence/core': + specifier: workspace:* + version: link:../core + '@0xsequence/migration': + specifier: workspace:* + version: link:../migration + '@0xsequence/network': + specifier: workspace:* + version: link:../network + '@0xsequence/relayer': + specifier: workspace:* + version: link:../relayer + '@0xsequence/sessions': + specifier: workspace:* + version: link:../sessions + '@0xsequence/utils': + specifier: workspace:* + version: link:../utils + '@0xsequence/wallet': + specifier: workspace:* + version: link:../wallet + ethers: + specifier: ^5.5.2 + version: 5.7.2 + devDependencies: + '@0xsequence/signhub': + specifier: workspace:* + version: link:../signhub + '@0xsequence/tests': + specifier: workspace:* + version: link:../tests + '@istanbuljs/nyc-config-typescript': + specifier: ^1.0.2 + version: 1.0.2(nyc@15.1.0) + nyc: + specifier: ^15.1.0 + version: 15.1.0 + packages/api: {} packages/auth: dependencies: '@0xsequence/abi': - specifier: ^0.43.34 + specifier: workspace:* version: link:../abi + '@0xsequence/account': + specifier: workspace:* + version: link:../account '@0xsequence/api': - specifier: ^0.43.34 + specifier: workspace:* version: link:../api - '@0xsequence/config': - specifier: ^0.43.34 - version: link:../config + '@0xsequence/core': + specifier: workspace:* + version: link:../core '@0xsequence/ethauth': - specifier: ^0.8.0 + specifier: ^0.8.1 version: 0.8.1(ethers@5.7.2) '@0xsequence/indexer': - specifier: ^0.43.34 + specifier: workspace:* version: link:../indexer '@0xsequence/metadata': - specifier: ^0.43.34 + specifier: workspace:* version: link:../metadata + '@0xsequence/migration': + specifier: workspace:* + version: link:../migration '@0xsequence/network': - specifier: ^0.43.34 + specifier: workspace:* version: link:../network - '@0xsequence/provider': - specifier: ^0.43.34 - version: link:../provider + '@0xsequence/sessions': + specifier: workspace:* + version: link:../sessions + '@0xsequence/signhub': + specifier: workspace:* + version: link:../signhub '@0xsequence/utils': - specifier: ^0.43.34 + specifier: workspace:* version: link:../utils '@0xsequence/wallet': - specifier: ^0.43.34 + specifier: workspace:* version: link:../wallet devDependencies: + '@0xsequence/tests': + specifier: workspace:* + version: link:../tests '@0xsequence/wallet-contracts': - specifier: 1.10.0 + specifier: ^1.10.0 version: 1.10.0 concurrently: specifier: ^7.5.0 @@ -300,32 +358,26 @@ importers: specifier: ^3.6.0 version: 3.6.2 - packages/config: + packages/core: dependencies: '@0xsequence/abi': - specifier: ^0.43.34 + specifier: workspace:* version: link:../abi - '@0xsequence/multicall': - specifier: ^0.43.34 - version: link:../multicall - '@0xsequence/network': - specifier: ^0.43.34 - version: link:../network - '@0xsequence/utils': - specifier: ^0.43.34 - version: link:../utils - devDependencies: - '@0xsequence/wallet-contracts': - specifier: 1.10.0 - version: 1.10.0 ethers: - specifier: ^5.7.2 + specifier: '>=5.5' version: 5.7.2 + devDependencies: + '@istanbuljs/nyc-config-typescript': + specifier: ^1.0.2 + version: 1.0.2(nyc@15.1.0) + nyc: + specifier: ^15.1.0 + version: 15.1.0 packages/deployer: dependencies: '@0xsequence/utils': - specifier: ^0.43.34 + specifier: workspace:* version: link:../utils devDependencies: '@ethersproject/abi': @@ -336,10 +388,10 @@ importers: version: 5.7.2 '@nomiclabs/hardhat-ethers': specifier: ^2.2.1 - version: 2.2.1(ethers@5.7.2)(hardhat@2.12.4) + version: 2.2.1(ethers@5.7.2)(hardhat@2.14.0) '@nomiclabs/hardhat-web3': specifier: ^2.0.0 - version: 2.0.0(hardhat@2.12.4)(web3@1.8.1) + version: 2.0.0(hardhat@2.14.0)(web3@1.9.0) '@typechain/ethers-v5': specifier: ^10.1.1 version: 10.2.0(@ethersproject/abi@5.7.0)(@ethersproject/bytes@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typechain@8.1.1)(typescript@4.9.4) @@ -356,24 +408,24 @@ importers: packages/estimator: dependencies: '@0xsequence/abi': - specifier: ^0.43.34 + specifier: workspace:* version: link:../abi - '@0xsequence/config': - specifier: ^0.43.34 - version: link:../config - '@0xsequence/network': - specifier: ^0.43.34 - version: link:../network - '@0xsequence/transactions': - specifier: ^0.43.34 - version: link:../transactions + '@0xsequence/core': + specifier: workspace:* + version: link:../core '@0xsequence/utils': - specifier: ^0.43.34 + specifier: workspace:* version: link:../utils '@0xsequence/wallet-contracts': - specifier: 1.10.0 + specifier: ^1.10.0 version: 1.10.0 devDependencies: + '@0xsequence/signhub': + specifier: workspace:* + version: link:../signhub + '@0xsequence/tests': + specifier: workspace:* + version: link:../tests '@ethersproject/abstract-signer': specifier: ^5.7.0 version: 5.7.0 @@ -384,26 +436,58 @@ importers: specifier: ^5.7.2 version: 5.7.2 - packages/guard: {} + packages/guard: + dependencies: + '@0xsequence/core': + specifier: workspace:* + version: link:../core + '@0xsequence/signhub': + specifier: workspace:* + version: link:../signhub + ethers: + specifier: ^5.7.2 + version: 5.7.2 packages/indexer: {} packages/metadata: {} + packages/migration: + dependencies: + '@0xsequence/abi': + specifier: workspace:* + version: link:../abi + '@0xsequence/core': + specifier: workspace:* + version: link:../core + '@0xsequence/wallet': + specifier: workspace:* + version: link:../wallet + ethers: + specifier: ^5.5.2 + version: 5.7.2 + devDependencies: + '@istanbuljs/nyc-config-typescript': + specifier: ^1.0.2 + version: 1.0.2(nyc@15.1.0) + nyc: + specifier: ^15.1.0 + version: 15.1.0 + packages/multicall: dependencies: '@0xsequence/abi': - specifier: ^0.43.34 + specifier: workspace:* version: link:../abi '@0xsequence/network': - specifier: ^0.43.34 + specifier: workspace:* version: link:../network '@0xsequence/utils': - specifier: ^0.43.34 + specifier: workspace:* version: link:../utils devDependencies: '@0xsequence/wallet-contracts': - specifier: 1.10.0 + specifier: ^1.10.0 version: 1.10.0 '@ethersproject/providers': specifier: ^5.7.2 @@ -428,21 +512,21 @@ importers: version: 1.8.1 web3-provider-engine: specifier: ^16.0.4 - version: 16.0.4(@babel/core@7.20.5) + version: 16.0.4(@babel/core@7.21.4) packages/network: dependencies: + '@0xsequence/core': + specifier: workspace:* + version: link:../core '@0xsequence/indexer': - specifier: ^0.43.34 + specifier: workspace:* version: link:../indexer - '@0xsequence/provider': - specifier: ^0.43.34 - version: link:../provider '@0xsequence/relayer': - specifier: ^0.43.34 + specifier: workspace:* version: link:../relayer '@0xsequence/utils': - specifier: ^0.43.34 + specifier: workspace:* version: link:../utils devDependencies: ethers: @@ -452,28 +536,31 @@ importers: packages/provider: dependencies: '@0xsequence/abi': - specifier: ^0.43.34 + specifier: workspace:* version: link:../abi + '@0xsequence/account': + specifier: workspace:* + version: link:../account '@0xsequence/auth': - specifier: ^0.43.34 + specifier: workspace:* version: link:../auth - '@0xsequence/config': - specifier: ^0.43.34 - version: link:../config + '@0xsequence/core': + specifier: workspace:* + version: link:../core + '@0xsequence/migration': + specifier: workspace:* + version: link:../migration '@0xsequence/network': - specifier: ^0.43.34 + specifier: workspace:* version: link:../network '@0xsequence/relayer': - specifier: ^0.43.34 + specifier: workspace:* version: link:../relayer - '@0xsequence/transactions': - specifier: ^0.43.34 - version: link:../transactions '@0xsequence/utils': - specifier: ^0.43.34 + specifier: workspace:* version: link:../utils '@0xsequence/wallet': - specifier: ^0.43.34 + specifier: workspace:* version: link:../wallet eventemitter2: specifier: ^6.4.5 @@ -492,59 +579,121 @@ importers: packages/relayer: dependencies: '@0xsequence/abi': - specifier: ^0.43.34 + specifier: workspace:* version: link:../abi - '@0xsequence/config': - specifier: ^0.43.34 - version: link:../config - '@0xsequence/network': - specifier: ^0.43.34 - version: link:../network - '@0xsequence/transactions': - specifier: ^0.43.34 - version: link:../transactions + '@0xsequence/core': + specifier: workspace:* + version: link:../core '@0xsequence/utils': - specifier: ^0.43.34 + specifier: workspace:* version: link:../utils devDependencies: + '@0xsequence/signhub': + specifier: workspace:* + version: link:../signhub + '@0xsequence/tests': + specifier: workspace:* + version: link:../tests '@0xsequence/wallet-contracts': - specifier: 1.10.0 + specifier: ^1.10.0 version: 1.10.0 ethers: specifier: ^5.7.2 version: 5.7.2 + packages/replacer: + dependencies: + '@0xsequence/abi': + specifier: workspace:* + version: link:../abi + '@0xsequence/core': + specifier: workspace:* + version: link:../core + ethers: + specifier: '>=5.5' + version: 5.7.2 + + packages/sessions: + dependencies: + '@0xsequence/core': + specifier: workspace:* + version: link:../core + '@0xsequence/migration': + specifier: workspace:* + version: link:../migration + '@0xsequence/replacer': + specifier: workspace:* + version: link:../replacer + ethers: + specifier: ^5.5.2 + version: 5.7.2 + idb: + specifier: ^7.1.1 + version: 7.1.1 + devDependencies: + '@0xsequence/signhub': + specifier: workspace:* + version: link:../signhub + '@0xsequence/tests': + specifier: workspace:* + version: link:../tests + '@istanbuljs/nyc-config-typescript': + specifier: ^1.0.2 + version: 1.0.2(nyc@15.1.0) + fake-indexeddb: + specifier: ^4.0.1 + version: 4.0.1 + nyc: + specifier: ^15.1.0 + version: 15.1.0 + + packages/signhub: + dependencies: + ethers: + specifier: ^5.5.2 + version: 5.7.2 + devDependencies: + '@istanbuljs/nyc-config-typescript': + specifier: ^1.0.2 + version: 1.0.2(nyc@15.1.0) + nyc: + specifier: ^15.1.0 + version: 15.1.0 + packages/simulator: dependencies: - '@0xsequence/transactions': - specifier: ^0.43.34 - version: link:../transactions + '@0xsequence/core': + specifier: workspace:* + version: link:../core '@0xsequence/wallet-contracts': - specifier: 1.10.0 + specifier: ^1.10.0 version: 1.10.0 devDependencies: + '@0xsequence/signhub': + specifier: workspace:* + version: link:../signhub + '@0xsequence/tests': + specifier: workspace:* + version: link:../tests ethers: specifier: ^5.7.2 version: 5.7.2 - packages/transactions: + packages/tests: dependencies: - '@0xsequence/abi': - specifier: ^0.43.34 - version: link:../abi - '@0xsequence/config': - specifier: ^0.43.34 - version: link:../config - '@0xsequence/network': - specifier: ^0.43.34 - version: link:../network - '@0xsequence/utils': - specifier: ^0.43.34 - version: link:../utils + '@0xsequence/core': + specifier: workspace:* + version: link:../core devDependencies: + '@istanbuljs/nyc-config-typescript': + specifier: ^1.0.1 + version: 1.0.2(nyc@15.1.0) ethers: specifier: ^5.7.2 version: 5.7.2 + web3: + specifier: ^1.8.1 + version: 1.8.1 packages/utils: dependencies: @@ -559,32 +708,35 @@ importers: packages/wallet: dependencies: '@0xsequence/abi': - specifier: ^0.43.34 + specifier: workspace:* version: link:../abi - '@0xsequence/config': - specifier: ^0.43.34 - version: link:../config + '@0xsequence/core': + specifier: workspace:* + version: link:../core '@0xsequence/guard': - specifier: ^0.43.34 + specifier: workspace:* version: link:../guard '@0xsequence/network': - specifier: ^0.43.34 + specifier: workspace:* version: link:../network '@0xsequence/relayer': - specifier: ^0.43.34 + specifier: workspace:* version: link:../relayer - '@0xsequence/transactions': - specifier: ^0.43.34 - version: link:../transactions + '@0xsequence/signhub': + specifier: workspace:* + version: link:../signhub '@0xsequence/utils': - specifier: ^0.43.34 + specifier: workspace:* version: link:../utils devDependencies: '@0xsequence/ethauth': - specifier: ^0.8.0 + specifier: ^0.8.1 version: 0.8.1(ethers@5.7.2) + '@0xsequence/tests': + specifier: workspace:* + version: link:../tests '@0xsequence/wallet-contracts': - specifier: 1.10.0 + specifier: ^1.10.0 version: 1.10.0 '@istanbuljs/nyc-config-typescript': specifier: ^1.0.1 @@ -616,12 +768,12 @@ packages: - bufferutil - utf-8-validate - /@ampproject/remapping@2.2.0: - resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==} + /@ampproject/remapping@2.2.1: + resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} engines: {node: '>=6.0.0'} dependencies: - '@jridgewell/gen-mapping': 0.1.1 - '@jridgewell/trace-mapping': 0.3.17 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.18 dev: true /@babel/code-frame@7.18.6: @@ -631,40 +783,48 @@ packages: '@babel/highlight': 7.18.6 dev: true - /@babel/compat-data@7.20.5: - resolution: {integrity: sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==} + /@babel/code-frame@7.21.4: + resolution: {integrity: sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.18.6 + dev: true + + /@babel/compat-data@7.21.4: + resolution: {integrity: sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==} engines: {node: '>=6.9.0'} dev: true - /@babel/core@7.20.5: - resolution: {integrity: sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==} + /@babel/core@7.21.4: + resolution: {integrity: sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==} engines: {node: '>=6.9.0'} dependencies: - '@ampproject/remapping': 2.2.0 - '@babel/code-frame': 7.18.6 - '@babel/generator': 7.20.5 - '@babel/helper-compilation-targets': 7.20.0(@babel/core@7.20.5) - '@babel/helper-module-transforms': 7.20.2 - '@babel/helpers': 7.20.6 - '@babel/parser': 7.20.5 - '@babel/template': 7.18.10 - '@babel/traverse': 7.20.5 - '@babel/types': 7.20.5 + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.21.4 + '@babel/generator': 7.21.4 + '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) + '@babel/helper-module-transforms': 7.21.2 + '@babel/helpers': 7.21.0 + '@babel/parser': 7.21.4 + '@babel/template': 7.20.7 + '@babel/traverse': 7.21.4 + '@babel/types': 7.21.4 convert-source-map: 1.9.0 debug: 4.3.4(supports-color@6.1.0) gensync: 1.0.0-beta.2 - json5: 2.2.2 + json5: 2.2.3 semver: 6.3.0 transitivePeerDependencies: - supports-color dev: true - /@babel/generator@7.20.5: - resolution: {integrity: sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==} + /@babel/generator@7.21.4: + resolution: {integrity: sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.5 - '@jridgewell/gen-mapping': 0.3.2 + '@babel/types': 7.21.4 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.18 jsesc: 2.5.2 dev: true @@ -672,7 +832,7 @@ packages: resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.5 + '@babel/types': 7.21.4 dev: true /@babel/helper-builder-binary-assignment-operator-visitor@7.18.9: @@ -680,29 +840,30 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/helper-explode-assignable-expression': 7.18.6 - '@babel/types': 7.20.5 + '@babel/types': 7.21.4 dev: true - /@babel/helper-compilation-targets@7.20.0(@babel/core@7.20.5): - resolution: {integrity: sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==} + /@babel/helper-compilation-targets@7.21.4(@babel/core@7.21.4): + resolution: {integrity: sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/compat-data': 7.20.5 - '@babel/core': 7.20.5 - '@babel/helper-validator-option': 7.18.6 - browserslist: 4.21.4 + '@babel/compat-data': 7.21.4 + '@babel/core': 7.21.4 + '@babel/helper-validator-option': 7.21.0 + browserslist: 4.21.5 + lru-cache: 5.1.1 semver: 6.3.0 dev: true - /@babel/helper-create-class-features-plugin@7.20.5(@babel/core@7.20.5): + /@babel/helper-create-class-features-plugin@7.20.5(@babel/core@7.21.4): resolution: {integrity: sha512-3RCdA/EmEaikrhayahwToF0fpweU/8o2p8vhc1c/1kftHOdTKuC65kik/TLc+qfbS8JKw4qqJbne4ovICDhmww==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-function-name': 7.19.0 @@ -714,28 +875,47 @@ packages: - supports-color dev: true - /@babel/helper-create-regexp-features-plugin@7.20.5(@babel/core@7.20.5): - resolution: {integrity: sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w==} + /@babel/helper-create-class-features-plugin@7.21.4(@babel/core@7.21.4): + resolution: {integrity: sha512-46QrX2CQlaFRF4TkwfTt6nJD7IHq8539cCL7SDpqWSDeJKY1xylKKY5F/33mJhLZ3mFvKv2gGrVS6NkyF6qs+Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.21.4 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.21.0 + '@babel/helper-member-expression-to-functions': 7.21.0 + '@babel/helper-optimise-call-expression': 7.18.6 + '@babel/helper-replace-supers': 7.20.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 + '@babel/helper-split-export-declaration': 7.18.6 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-create-regexp-features-plugin@7.21.4(@babel/core@7.21.4): + resolution: {integrity: sha512-M00OuhU+0GyZ5iBBN9czjugzWrEq2vDpf/zCYHxxf93ul/Q5rv+a5h+/+0WnI1AebHNVtl5bFV0qsJoH23DbfA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-annotate-as-pure': 7.18.6 - regexpu-core: 5.2.2 + regexpu-core: 5.3.2 dev: true - /@babel/helper-define-polyfill-provider@0.3.3(@babel/core@7.20.5): + /@babel/helper-define-polyfill-provider@0.3.3(@babel/core@7.21.4): resolution: {integrity: sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==} peerDependencies: '@babel/core': ^7.4.0-0 dependencies: - '@babel/core': 7.20.5 - '@babel/helper-compilation-targets': 7.20.0(@babel/core@7.20.5) + '@babel/core': 7.21.4 + '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) '@babel/helper-plugin-utils': 7.20.2 debug: 4.3.4(supports-color@6.1.0) lodash.debounce: 4.0.8 - resolve: 1.22.1 + resolve: 1.22.2 semver: 6.3.0 transitivePeerDependencies: - supports-color @@ -750,50 +930,72 @@ packages: resolution: {integrity: sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.5 + '@babel/types': 7.21.4 dev: true /@babel/helper-function-name@7.19.0: resolution: {integrity: sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/template': 7.18.10 - '@babel/types': 7.20.5 + '@babel/template': 7.20.7 + '@babel/types': 7.21.4 + dev: true + + /@babel/helper-function-name@7.21.0: + resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.20.7 + '@babel/types': 7.21.4 dev: true /@babel/helper-hoist-variables@7.18.6: resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.5 + '@babel/types': 7.21.4 dev: true /@babel/helper-member-expression-to-functions@7.18.9: resolution: {integrity: sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.5 + '@babel/types': 7.21.4 + dev: true + + /@babel/helper-member-expression-to-functions@7.21.0: + resolution: {integrity: sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.4 dev: true /@babel/helper-module-imports@7.18.6: resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.5 + '@babel/types': 7.21.4 + dev: true + + /@babel/helper-module-imports@7.21.4: + resolution: {integrity: sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.4 dev: true - /@babel/helper-module-transforms@7.20.2: - resolution: {integrity: sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==} + /@babel/helper-module-transforms@7.21.2: + resolution: {integrity: sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-module-imports': 7.18.6 + '@babel/helper-module-imports': 7.21.4 '@babel/helper-simple-access': 7.20.2 '@babel/helper-split-export-declaration': 7.18.6 '@babel/helper-validator-identifier': 7.19.1 - '@babel/template': 7.18.10 - '@babel/traverse': 7.20.5 - '@babel/types': 7.20.5 + '@babel/template': 7.20.7 + '@babel/traverse': 7.21.4 + '@babel/types': 7.21.4 transitivePeerDependencies: - supports-color dev: true @@ -802,7 +1004,7 @@ packages: resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.5 + '@babel/types': 7.21.4 dev: true /@babel/helper-plugin-utils@7.20.2: @@ -810,17 +1012,17 @@ packages: engines: {node: '>=6.9.0'} dev: true - /@babel/helper-remap-async-to-generator@7.18.9(@babel/core@7.20.5): + /@babel/helper-remap-async-to-generator@7.18.9(@babel/core@7.21.4): resolution: {integrity: sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-wrap-function': 7.20.5 - '@babel/types': 7.20.5 + '@babel/types': 7.21.4 transitivePeerDependencies: - supports-color dev: true @@ -832,8 +1034,22 @@ packages: '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-member-expression-to-functions': 7.18.9 '@babel/helper-optimise-call-expression': 7.18.6 - '@babel/traverse': 7.20.5 - '@babel/types': 7.20.5 + '@babel/traverse': 7.21.4 + '@babel/types': 7.21.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-replace-supers@7.20.7: + resolution: {integrity: sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-member-expression-to-functions': 7.21.0 + '@babel/helper-optimise-call-expression': 7.18.6 + '@babel/template': 7.20.7 + '@babel/traverse': 7.21.4 + '@babel/types': 7.21.4 transitivePeerDependencies: - supports-color dev: true @@ -842,21 +1058,21 @@ packages: resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.5 + '@babel/types': 7.21.4 dev: true /@babel/helper-skip-transparent-expression-wrappers@7.20.0: resolution: {integrity: sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.5 + '@babel/types': 7.21.4 dev: true /@babel/helper-split-export-declaration@7.18.6: resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.20.5 + '@babel/types': 7.21.4 dev: true /@babel/helper-string-parser@7.19.4: @@ -869,8 +1085,8 @@ packages: engines: {node: '>=6.9.0'} dev: true - /@babel/helper-validator-option@7.18.6: - resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==} + /@babel/helper-validator-option@7.21.0: + resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==} engines: {node: '>=6.9.0'} dev: true @@ -878,21 +1094,21 @@ packages: resolution: {integrity: sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-function-name': 7.19.0 - '@babel/template': 7.18.10 - '@babel/traverse': 7.20.5 - '@babel/types': 7.20.5 + '@babel/helper-function-name': 7.21.0 + '@babel/template': 7.20.7 + '@babel/traverse': 7.21.4 + '@babel/types': 7.21.4 transitivePeerDependencies: - supports-color dev: true - /@babel/helpers@7.20.6: - resolution: {integrity: sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==} + /@babel/helpers@7.21.0: + resolution: {integrity: sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/template': 7.18.10 - '@babel/traverse': 7.20.5 - '@babel/types': 7.20.5 + '@babel/template': 7.20.7 + '@babel/traverse': 7.21.4 + '@babel/types': 7.21.4 transitivePeerDependencies: - supports-color dev: true @@ -906,908 +1122,941 @@ packages: js-tokens: 4.0.0 dev: true - /@babel/parser@7.20.5: - resolution: {integrity: sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==} + /@babel/parser@7.21.4: + resolution: {integrity: sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==} engines: {node: '>=6.0.0'} + hasBin: true dependencies: - '@babel/types': 7.20.5 + '@babel/types': 7.21.4 dev: true - /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.20.5): + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.21.4): resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.18.9(@babel/core@7.20.5): - resolution: {integrity: sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==} + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.20.7(@babel/core@7.21.4): + resolution: {integrity: sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.13.0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/plugin-proposal-optional-chaining': 7.18.9(@babel/core@7.20.5) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.21.4) dev: true - /@babel/plugin-proposal-async-generator-functions@7.20.1(@babel/core@7.20.5): - resolution: {integrity: sha512-Gh5rchzSwE4kC+o/6T8waD0WHEQIsDmjltY8WnWRXHUdH8axZhuH86Ov9M72YhJfDrZseQwuuWaaIT/TmePp3g==} + /@babel/plugin-proposal-async-generator-functions@7.20.7(@babel/core@7.21.4): + resolution: {integrity: sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.20.5) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.20.5) + '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.21.4) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.4) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.20.5): + /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.21.4): resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 - '@babel/helper-create-class-features-plugin': 7.20.5(@babel/core@7.20.5) + '@babel/core': 7.21.4 + '@babel/helper-create-class-features-plugin': 7.20.5(@babel/core@7.21.4) '@babel/helper-plugin-utils': 7.20.2 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-class-static-block@7.18.6(@babel/core@7.20.5): - resolution: {integrity: sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==} + /@babel/plugin-proposal-class-static-block@7.21.0(@babel/core@7.21.4): + resolution: {integrity: sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 dependencies: - '@babel/core': 7.20.5 - '@babel/helper-create-class-features-plugin': 7.20.5(@babel/core@7.20.5) + '@babel/core': 7.21.4 + '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.21.4) '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.20.5) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.21.4) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-dynamic-import@7.18.6(@babel/core@7.20.5): + /@babel/plugin-proposal-dynamic-import@7.18.6(@babel/core@7.21.4): resolution: {integrity: sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.20.5) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.21.4) dev: true - /@babel/plugin-proposal-export-namespace-from@7.18.9(@babel/core@7.20.5): + /@babel/plugin-proposal-export-namespace-from@7.18.9(@babel/core@7.21.4): resolution: {integrity: sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.20.5) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.21.4) dev: true - /@babel/plugin-proposal-json-strings@7.18.6(@babel/core@7.20.5): + /@babel/plugin-proposal-json-strings@7.18.6(@babel/core@7.21.4): resolution: {integrity: sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.20.5) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.4) dev: true - /@babel/plugin-proposal-logical-assignment-operators@7.18.9(@babel/core@7.20.5): - resolution: {integrity: sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==} + /@babel/plugin-proposal-logical-assignment-operators@7.20.7(@babel/core@7.21.4): + resolution: {integrity: sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.20.5) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.4) dev: true - /@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.20.5): + /@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.21.4): resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.20.5) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.4) dev: true - /@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.20.5): + /@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.21.4): resolution: {integrity: sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.20.5) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.4) dev: true - /@babel/plugin-proposal-object-rest-spread@7.20.2(@babel/core@7.20.5): - resolution: {integrity: sha512-Ks6uej9WFK+fvIMesSqbAto5dD8Dz4VuuFvGJFKgIGSkJuRGcrwGECPA1fDgQK3/DbExBJpEkTeYeB8geIFCSQ==} + /@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.21.4): + resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.20.5 - '@babel/core': 7.20.5 - '@babel/helper-compilation-targets': 7.20.0(@babel/core@7.20.5) + '@babel/compat-data': 7.21.4 + '@babel/core': 7.21.4 + '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.20.5) - '@babel/plugin-transform-parameters': 7.20.5(@babel/core@7.20.5) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.4) + '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.21.4) dev: true - /@babel/plugin-proposal-optional-catch-binding@7.18.6(@babel/core@7.20.5): + /@babel/plugin-proposal-optional-catch-binding@7.18.6(@babel/core@7.21.4): resolution: {integrity: sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.20.5) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.4) dev: true - /@babel/plugin-proposal-optional-chaining@7.18.9(@babel/core@7.20.5): - resolution: {integrity: sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==} + /@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.21.4): + resolution: {integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.20.5) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.4) dev: true - /@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.20.5): + /@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.21.4): resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 - '@babel/helper-create-class-features-plugin': 7.20.5(@babel/core@7.20.5) + '@babel/core': 7.21.4 + '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.21.4) '@babel/helper-plugin-utils': 7.20.2 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-private-property-in-object@7.20.5(@babel/core@7.20.5): - resolution: {integrity: sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ==} + /@babel/plugin-proposal-private-property-in-object@7.21.0(@babel/core@7.21.4): + resolution: {integrity: sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-create-class-features-plugin': 7.20.5(@babel/core@7.20.5) + '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.21.4) '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.20.5) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.21.4) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-proposal-unicode-property-regex@7.18.6(@babel/core@7.20.5): + /@babel/plugin-proposal-unicode-property-regex@7.18.6(@babel/core@7.21.4): resolution: {integrity: sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==} engines: {node: '>=4'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 - '@babel/helper-create-regexp-features-plugin': 7.20.5(@babel/core@7.20.5) + '@babel/core': 7.21.4 + '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.21.4) '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.20.5): + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.21.4): resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.20.5): + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.21.4): resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.20.5): + /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.21.4): resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.20.5): + /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.21.4): resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.20.5): + /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.21.4): resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-import-assertions@7.20.0(@babel/core@7.20.5): + /@babel/plugin-syntax-import-assertions@7.20.0(@babel/core@7.21.4): resolution: {integrity: sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.20.5): + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.21.4): resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.20.5): + /@babel/plugin-syntax-jsx@7.21.4(@babel/core@7.21.4): + resolution: {integrity: sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.4 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.21.4): resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.20.5): + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.21.4): resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.20.5): + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.21.4): resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.20.5): + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.21.4): resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.20.5): + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.21.4): resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.20.5): + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.21.4): resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.20.5): + /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.21.4): resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.20.5): + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.21.4): resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-syntax-typescript@7.20.0(@babel/core@7.20.5): - resolution: {integrity: sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==} + /@babel/plugin-syntax-typescript@7.21.4(@babel/core@7.21.4): + resolution: {integrity: sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-arrow-functions@7.18.6(@babel/core@7.20.5): - resolution: {integrity: sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==} + /@babel/plugin-transform-arrow-functions@7.20.7(@babel/core@7.21.4): + resolution: {integrity: sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-async-to-generator@7.18.6(@babel/core@7.20.5): - resolution: {integrity: sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==} + /@babel/plugin-transform-async-to-generator@7.20.7(@babel/core@7.21.4): + resolution: {integrity: sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 - '@babel/helper-module-imports': 7.18.6 + '@babel/core': 7.21.4 + '@babel/helper-module-imports': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.20.5) + '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.21.4) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-block-scoped-functions@7.18.6(@babel/core@7.20.5): + /@babel/plugin-transform-block-scoped-functions@7.18.6(@babel/core@7.21.4): resolution: {integrity: sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-block-scoping@7.20.5(@babel/core@7.20.5): - resolution: {integrity: sha512-WvpEIW9Cbj9ApF3yJCjIEEf1EiNJLtXagOrL5LNWEZOo3jv8pmPoYTSNJQvqej8OavVlgOoOPw6/htGZro6IkA==} + /@babel/plugin-transform-block-scoping@7.21.0(@babel/core@7.21.4): + resolution: {integrity: sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-classes@7.20.2(@babel/core@7.20.5): - resolution: {integrity: sha512-9rbPp0lCVVoagvtEyQKSo5L8oo0nQS/iif+lwlAz29MccX2642vWDlSZK+2T2buxbopotId2ld7zZAzRfz9j1g==} + /@babel/plugin-transform-classes@7.21.0(@babel/core@7.21.4): + resolution: {integrity: sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-compilation-targets': 7.20.0(@babel/core@7.20.5) + '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.19.0 + '@babel/helper-function-name': 7.21.0 '@babel/helper-optimise-call-expression': 7.18.6 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-replace-supers': 7.19.1 + '@babel/helper-replace-supers': 7.20.7 '@babel/helper-split-export-declaration': 7.18.6 globals: 11.12.0 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-computed-properties@7.18.9(@babel/core@7.20.5): - resolution: {integrity: sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==} + /@babel/plugin-transform-computed-properties@7.20.7(@babel/core@7.21.4): + resolution: {integrity: sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 + '@babel/template': 7.20.7 dev: true - /@babel/plugin-transform-destructuring@7.20.2(@babel/core@7.20.5): - resolution: {integrity: sha512-mENM+ZHrvEgxLTBXUiQ621rRXZes3KWUv6NdQlrnr1TkWVw+hUjQBZuP2X32qKlrlG2BzgR95gkuCRSkJl8vIw==} + /@babel/plugin-transform-destructuring@7.21.3(@babel/core@7.21.4): + resolution: {integrity: sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-dotall-regex@7.18.6(@babel/core@7.20.5): + /@babel/plugin-transform-dotall-regex@7.18.6(@babel/core@7.21.4): resolution: {integrity: sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 - '@babel/helper-create-regexp-features-plugin': 7.20.5(@babel/core@7.20.5) + '@babel/core': 7.21.4 + '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.21.4) '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-duplicate-keys@7.18.9(@babel/core@7.20.5): + /@babel/plugin-transform-duplicate-keys@7.18.9(@babel/core@7.21.4): resolution: {integrity: sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-exponentiation-operator@7.18.6(@babel/core@7.20.5): + /@babel/plugin-transform-exponentiation-operator@7.18.6(@babel/core@7.21.4): resolution: {integrity: sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-builder-binary-assignment-operator-visitor': 7.18.9 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-for-of@7.18.8(@babel/core@7.20.5): - resolution: {integrity: sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==} + /@babel/plugin-transform-for-of@7.21.0(@babel/core@7.21.4): + resolution: {integrity: sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-function-name@7.18.9(@babel/core@7.20.5): + /@babel/plugin-transform-function-name@7.18.9(@babel/core@7.21.4): resolution: {integrity: sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 - '@babel/helper-compilation-targets': 7.20.0(@babel/core@7.20.5) - '@babel/helper-function-name': 7.19.0 + '@babel/core': 7.21.4 + '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) + '@babel/helper-function-name': 7.21.0 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-literals@7.18.9(@babel/core@7.20.5): + /@babel/plugin-transform-literals@7.18.9(@babel/core@7.21.4): resolution: {integrity: sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-member-expression-literals@7.18.6(@babel/core@7.20.5): + /@babel/plugin-transform-member-expression-literals@7.18.6(@babel/core@7.21.4): resolution: {integrity: sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-modules-amd@7.19.6(@babel/core@7.20.5): - resolution: {integrity: sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg==} + /@babel/plugin-transform-modules-amd@7.20.11(@babel/core@7.21.4): + resolution: {integrity: sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 - '@babel/helper-module-transforms': 7.20.2 + '@babel/core': 7.21.4 + '@babel/helper-module-transforms': 7.21.2 '@babel/helper-plugin-utils': 7.20.2 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-modules-commonjs@7.19.6(@babel/core@7.20.5): + /@babel/plugin-transform-modules-commonjs@7.19.6(@babel/core@7.21.4): resolution: {integrity: sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 - '@babel/helper-module-transforms': 7.20.2 + '@babel/core': 7.21.4 + '@babel/helper-module-transforms': 7.21.2 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-simple-access': 7.20.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-modules-commonjs@7.21.2(@babel/core@7.21.4): + resolution: {integrity: sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.4 + '@babel/helper-module-transforms': 7.21.2 '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-simple-access': 7.20.2 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-modules-systemjs@7.19.6(@babel/core@7.20.5): - resolution: {integrity: sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ==} + /@babel/plugin-transform-modules-systemjs@7.20.11(@babel/core@7.21.4): + resolution: {integrity: sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-hoist-variables': 7.18.6 - '@babel/helper-module-transforms': 7.20.2 + '@babel/helper-module-transforms': 7.21.2 '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-validator-identifier': 7.19.1 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-modules-umd@7.18.6(@babel/core@7.20.5): + /@babel/plugin-transform-modules-umd@7.18.6(@babel/core@7.21.4): resolution: {integrity: sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 - '@babel/helper-module-transforms': 7.20.2 + '@babel/core': 7.21.4 + '@babel/helper-module-transforms': 7.21.2 '@babel/helper-plugin-utils': 7.20.2 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-named-capturing-groups-regex@7.20.5(@babel/core@7.20.5): + /@babel/plugin-transform-named-capturing-groups-regex@7.20.5(@babel/core@7.21.4): resolution: {integrity: sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.20.5 - '@babel/helper-create-regexp-features-plugin': 7.20.5(@babel/core@7.20.5) + '@babel/core': 7.21.4 + '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.21.4) '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-new-target@7.18.6(@babel/core@7.20.5): + /@babel/plugin-transform-new-target@7.18.6(@babel/core@7.21.4): resolution: {integrity: sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-object-super@7.18.6(@babel/core@7.20.5): + /@babel/plugin-transform-object-super@7.18.6(@babel/core@7.21.4): resolution: {integrity: sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-replace-supers': 7.19.1 + '@babel/helper-replace-supers': 7.20.7 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-parameters@7.20.5(@babel/core@7.20.5): - resolution: {integrity: sha512-h7plkOmcndIUWXZFLgpbrh2+fXAi47zcUX7IrOQuZdLD0I0KvjJ6cvo3BEcAOsDOcZhVKGJqv07mkSqK0y2isQ==} + /@babel/plugin-transform-parameters@7.21.3(@babel/core@7.21.4): + resolution: {integrity: sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-property-literals@7.18.6(@babel/core@7.20.5): + /@babel/plugin-transform-property-literals@7.18.6(@babel/core@7.21.4): resolution: {integrity: sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-regenerator@7.20.5(@babel/core@7.20.5): + /@babel/plugin-transform-regenerator@7.20.5(@babel/core@7.21.4): resolution: {integrity: sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 regenerator-transform: 0.15.1 dev: true - /@babel/plugin-transform-reserved-words@7.18.6(@babel/core@7.20.5): + /@babel/plugin-transform-reserved-words@7.18.6(@babel/core@7.21.4): resolution: {integrity: sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-runtime@7.19.6(@babel/core@7.20.5): + /@babel/plugin-transform-runtime@7.19.6(@babel/core@7.21.4): resolution: {integrity: sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-module-imports': 7.18.6 '@babel/helper-plugin-utils': 7.20.2 - babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.20.5) - babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.20.5) - babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.20.5) + babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.21.4) + babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.21.4) + babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.21.4) semver: 6.3.0 transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-shorthand-properties@7.18.6(@babel/core@7.20.5): + /@babel/plugin-transform-shorthand-properties@7.18.6(@babel/core@7.21.4): resolution: {integrity: sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-spread@7.19.0(@babel/core@7.20.5): - resolution: {integrity: sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==} + /@babel/plugin-transform-spread@7.20.7(@babel/core@7.21.4): + resolution: {integrity: sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-skip-transparent-expression-wrappers': 7.20.0 dev: true - /@babel/plugin-transform-sticky-regex@7.18.6(@babel/core@7.20.5): + /@babel/plugin-transform-sticky-regex@7.18.6(@babel/core@7.21.4): resolution: {integrity: sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-template-literals@7.18.9(@babel/core@7.20.5): + /@babel/plugin-transform-template-literals@7.18.9(@babel/core@7.21.4): resolution: {integrity: sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-typeof-symbol@7.18.9(@babel/core@7.20.5): + /@babel/plugin-transform-typeof-symbol@7.18.9(@babel/core@7.21.4): resolution: {integrity: sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-typescript@7.20.2(@babel/core@7.20.5): - resolution: {integrity: sha512-jvS+ngBfrnTUBfOQq8NfGnSbF9BrqlR6hjJ2yVxMkmO5nL/cdifNbI30EfjRlN4g5wYWNnMPyj5Sa6R1pbLeag==} + /@babel/plugin-transform-typescript@7.21.3(@babel/core@7.21.4): + resolution: {integrity: sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 - '@babel/helper-create-class-features-plugin': 7.20.5(@babel/core@7.20.5) + '@babel/core': 7.21.4 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-create-class-features-plugin': 7.21.4(@babel/core@7.21.4) '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-typescript': 7.20.0(@babel/core@7.20.5) + '@babel/plugin-syntax-typescript': 7.21.4(@babel/core@7.21.4) transitivePeerDependencies: - supports-color dev: true - /@babel/plugin-transform-unicode-escapes@7.18.10(@babel/core@7.20.5): + /@babel/plugin-transform-unicode-escapes@7.18.10(@babel/core@7.21.4): resolution: {integrity: sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-unicode-regex@7.18.6(@babel/core@7.20.5): + /@babel/plugin-transform-unicode-regex@7.18.6(@babel/core@7.21.4): resolution: {integrity: sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 - '@babel/helper-create-regexp-features-plugin': 7.20.5(@babel/core@7.20.5) + '@babel/core': 7.21.4 + '@babel/helper-create-regexp-features-plugin': 7.21.4(@babel/core@7.21.4) '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/preset-env@7.20.2(@babel/core@7.20.5): - resolution: {integrity: sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==} + /@babel/preset-env@7.21.4(@babel/core@7.21.4): + resolution: {integrity: sha512-2W57zHs2yDLm6GD5ZpvNn71lZ0B/iypSdIeq25OurDKji6AdzV07qp4s3n1/x5BqtiGaTrPN3nerlSCaC5qNTw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.20.5 - '@babel/core': 7.20.5 - '@babel/helper-compilation-targets': 7.20.0(@babel/core@7.20.5) + '@babel/compat-data': 7.21.4 + '@babel/core': 7.21.4 + '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.18.6 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.18.9(@babel/core@7.20.5) - '@babel/plugin-proposal-async-generator-functions': 7.20.1(@babel/core@7.20.5) - '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-proposal-class-static-block': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-proposal-dynamic-import': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-proposal-export-namespace-from': 7.18.9(@babel/core@7.20.5) - '@babel/plugin-proposal-json-strings': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-proposal-logical-assignment-operators': 7.18.9(@babel/core@7.20.5) - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-proposal-object-rest-spread': 7.20.2(@babel/core@7.20.5) - '@babel/plugin-proposal-optional-catch-binding': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-proposal-optional-chaining': 7.18.9(@babel/core@7.20.5) - '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-proposal-private-property-in-object': 7.20.5(@babel/core@7.20.5) - '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.20.5) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.20.5) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.20.5) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.20.5) - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.20.5) - '@babel/plugin-syntax-import-assertions': 7.20.0(@babel/core@7.20.5) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.20.5) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.20.5) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.20.5) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.20.5) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.20.5) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.20.5) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.20.5) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.20.5) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.20.5) - '@babel/plugin-transform-arrow-functions': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-transform-async-to-generator': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-transform-block-scoped-functions': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-transform-block-scoping': 7.20.5(@babel/core@7.20.5) - '@babel/plugin-transform-classes': 7.20.2(@babel/core@7.20.5) - '@babel/plugin-transform-computed-properties': 7.18.9(@babel/core@7.20.5) - '@babel/plugin-transform-destructuring': 7.20.2(@babel/core@7.20.5) - '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-transform-duplicate-keys': 7.18.9(@babel/core@7.20.5) - '@babel/plugin-transform-exponentiation-operator': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-transform-for-of': 7.18.8(@babel/core@7.20.5) - '@babel/plugin-transform-function-name': 7.18.9(@babel/core@7.20.5) - '@babel/plugin-transform-literals': 7.18.9(@babel/core@7.20.5) - '@babel/plugin-transform-member-expression-literals': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-transform-modules-amd': 7.19.6(@babel/core@7.20.5) - '@babel/plugin-transform-modules-commonjs': 7.19.6(@babel/core@7.20.5) - '@babel/plugin-transform-modules-systemjs': 7.19.6(@babel/core@7.20.5) - '@babel/plugin-transform-modules-umd': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-transform-named-capturing-groups-regex': 7.20.5(@babel/core@7.20.5) - '@babel/plugin-transform-new-target': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-transform-object-super': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-transform-parameters': 7.20.5(@babel/core@7.20.5) - '@babel/plugin-transform-property-literals': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-transform-regenerator': 7.20.5(@babel/core@7.20.5) - '@babel/plugin-transform-reserved-words': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-transform-shorthand-properties': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-transform-spread': 7.19.0(@babel/core@7.20.5) - '@babel/plugin-transform-sticky-regex': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-transform-template-literals': 7.18.9(@babel/core@7.20.5) - '@babel/plugin-transform-typeof-symbol': 7.18.9(@babel/core@7.20.5) - '@babel/plugin-transform-unicode-escapes': 7.18.10(@babel/core@7.20.5) - '@babel/plugin-transform-unicode-regex': 7.18.6(@babel/core@7.20.5) - '@babel/preset-modules': 0.1.5(@babel/core@7.20.5) - '@babel/types': 7.20.5 - babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.20.5) - babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.20.5) - babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.20.5) - core-js-compat: 3.26.1 + '@babel/helper-validator-option': 7.21.0 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6(@babel/core@7.21.4) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.20.7(@babel/core@7.21.4) + '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.21.4) + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.21.4) + '@babel/plugin-proposal-class-static-block': 7.21.0(@babel/core@7.21.4) + '@babel/plugin-proposal-dynamic-import': 7.18.6(@babel/core@7.21.4) + '@babel/plugin-proposal-export-namespace-from': 7.18.9(@babel/core@7.21.4) + '@babel/plugin-proposal-json-strings': 7.18.6(@babel/core@7.21.4) + '@babel/plugin-proposal-logical-assignment-operators': 7.20.7(@babel/core@7.21.4) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.21.4) + '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.21.4) + '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.21.4) + '@babel/plugin-proposal-optional-catch-binding': 7.18.6(@babel/core@7.21.4) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.21.4) + '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.21.4) + '@babel/plugin-proposal-private-property-in-object': 7.21.0(@babel/core@7.21.4) + '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.21.4) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.4) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.21.4) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.21.4) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.21.4) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.21.4) + '@babel/plugin-syntax-import-assertions': 7.20.0(@babel/core@7.21.4) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.4) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.4) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.4) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.4) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.4) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.4) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.4) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.21.4) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.21.4) + '@babel/plugin-transform-arrow-functions': 7.20.7(@babel/core@7.21.4) + '@babel/plugin-transform-async-to-generator': 7.20.7(@babel/core@7.21.4) + '@babel/plugin-transform-block-scoped-functions': 7.18.6(@babel/core@7.21.4) + '@babel/plugin-transform-block-scoping': 7.21.0(@babel/core@7.21.4) + '@babel/plugin-transform-classes': 7.21.0(@babel/core@7.21.4) + '@babel/plugin-transform-computed-properties': 7.20.7(@babel/core@7.21.4) + '@babel/plugin-transform-destructuring': 7.21.3(@babel/core@7.21.4) + '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.21.4) + '@babel/plugin-transform-duplicate-keys': 7.18.9(@babel/core@7.21.4) + '@babel/plugin-transform-exponentiation-operator': 7.18.6(@babel/core@7.21.4) + '@babel/plugin-transform-for-of': 7.21.0(@babel/core@7.21.4) + '@babel/plugin-transform-function-name': 7.18.9(@babel/core@7.21.4) + '@babel/plugin-transform-literals': 7.18.9(@babel/core@7.21.4) + '@babel/plugin-transform-member-expression-literals': 7.18.6(@babel/core@7.21.4) + '@babel/plugin-transform-modules-amd': 7.20.11(@babel/core@7.21.4) + '@babel/plugin-transform-modules-commonjs': 7.21.2(@babel/core@7.21.4) + '@babel/plugin-transform-modules-systemjs': 7.20.11(@babel/core@7.21.4) + '@babel/plugin-transform-modules-umd': 7.18.6(@babel/core@7.21.4) + '@babel/plugin-transform-named-capturing-groups-regex': 7.20.5(@babel/core@7.21.4) + '@babel/plugin-transform-new-target': 7.18.6(@babel/core@7.21.4) + '@babel/plugin-transform-object-super': 7.18.6(@babel/core@7.21.4) + '@babel/plugin-transform-parameters': 7.21.3(@babel/core@7.21.4) + '@babel/plugin-transform-property-literals': 7.18.6(@babel/core@7.21.4) + '@babel/plugin-transform-regenerator': 7.20.5(@babel/core@7.21.4) + '@babel/plugin-transform-reserved-words': 7.18.6(@babel/core@7.21.4) + '@babel/plugin-transform-shorthand-properties': 7.18.6(@babel/core@7.21.4) + '@babel/plugin-transform-spread': 7.20.7(@babel/core@7.21.4) + '@babel/plugin-transform-sticky-regex': 7.18.6(@babel/core@7.21.4) + '@babel/plugin-transform-template-literals': 7.18.9(@babel/core@7.21.4) + '@babel/plugin-transform-typeof-symbol': 7.18.9(@babel/core@7.21.4) + '@babel/plugin-transform-unicode-escapes': 7.18.10(@babel/core@7.21.4) + '@babel/plugin-transform-unicode-regex': 7.18.6(@babel/core@7.21.4) + '@babel/preset-modules': 0.1.5(@babel/core@7.21.4) + '@babel/types': 7.21.4 + babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.21.4) + babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.21.4) + babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.21.4) + core-js-compat: 3.30.1 semver: 6.3.0 transitivePeerDependencies: - supports-color dev: true - /@babel/preset-modules@0.1.5(@babel/core@7.20.5): + /@babel/preset-modules@0.1.5(@babel/core@7.21.4): resolution: {integrity: sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.20.5) - '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.20.5) - '@babel/types': 7.20.5 + '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.21.4) + '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.21.4) + '@babel/types': 7.21.4 esutils: 2.0.3 dev: true - /@babel/preset-typescript@7.18.6(@babel/core@7.20.5): - resolution: {integrity: sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==} + /@babel/preset-typescript@7.21.4(@babel/core@7.21.4): + resolution: {integrity: sha512-sMLNWY37TCdRH/bJ6ZeeOH1nPuanED7Ai9Y/vH31IPqalioJ6ZNFUWONsakhv4r4n+I6gm5lmoE0olkgib/j/A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.18.6 - '@babel/plugin-transform-typescript': 7.20.2(@babel/core@7.20.5) + '@babel/helper-validator-option': 7.21.0 + '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.4) + '@babel/plugin-transform-modules-commonjs': 7.21.2(@babel/core@7.21.4) + '@babel/plugin-transform-typescript': 7.21.3(@babel/core@7.21.4) transitivePeerDependencies: - supports-color dev: true - /@babel/runtime@7.20.6: - resolution: {integrity: sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==} + /@babel/regjsgen@0.8.0: + resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==} + dev: true + + /@babel/runtime@7.21.0: + resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==} engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.13.11 dev: true - /@babel/template@7.18.10: - resolution: {integrity: sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==} + /@babel/template@7.20.7: + resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.18.6 - '@babel/parser': 7.20.5 - '@babel/types': 7.20.5 + '@babel/code-frame': 7.21.4 + '@babel/parser': 7.21.4 + '@babel/types': 7.21.4 dev: true - /@babel/traverse@7.20.5: - resolution: {integrity: sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==} + /@babel/traverse@7.21.4: + resolution: {integrity: sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.18.6 - '@babel/generator': 7.20.5 + '@babel/code-frame': 7.21.4 + '@babel/generator': 7.21.4 '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.19.0 + '@babel/helper-function-name': 7.21.0 '@babel/helper-hoist-variables': 7.18.6 '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.20.5 - '@babel/types': 7.20.5 + '@babel/parser': 7.21.4 + '@babel/types': 7.21.4 debug: 4.3.4(supports-color@6.1.0) globals: 11.12.0 transitivePeerDependencies: - supports-color dev: true - /@babel/types@7.20.5: - resolution: {integrity: sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==} + /@babel/types@7.21.4: + resolution: {integrity: sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-string-parser': 7.19.4 @@ -1815,10 +2064,41 @@ packages: to-fast-properties: 2.0.0 dev: true + /@chainsafe/as-sha256@0.3.1: + resolution: {integrity: sha512-hldFFYuf49ed7DAakWVXSJODuq3pzJEguD8tQ7h+sGkM18vja+OFoJI9krnGmgzyuZC2ETX0NOIcCTy31v2Mtg==} + dev: true + + /@chainsafe/persistent-merkle-tree@0.4.2: + resolution: {integrity: sha512-lLO3ihKPngXLTus/L7WHKaw9PnNJWizlOF1H9NNzHP6Xvh82vzg9F2bzkXhYIFshMZ2gTCEz8tq6STe7r5NDfQ==} + dependencies: + '@chainsafe/as-sha256': 0.3.1 + dev: true + + /@chainsafe/persistent-merkle-tree@0.5.0: + resolution: {integrity: sha512-l0V1b5clxA3iwQLXP40zYjyZYospQLZXzBVIhhr9kDg/1qHZfzzHw0jj4VPBijfYCArZDlPkRi1wZaV2POKeuw==} + dependencies: + '@chainsafe/as-sha256': 0.3.1 + dev: true + + /@chainsafe/ssz@0.10.2: + resolution: {integrity: sha512-/NL3Lh8K+0q7A3LsiFq09YXS9fPE+ead2rr7vM2QK8PLzrNsw3uqrif9bpRX5UxgeRjM+vYi+boCM3+GM4ovXg==} + dependencies: + '@chainsafe/as-sha256': 0.3.1 + '@chainsafe/persistent-merkle-tree': 0.5.0 + dev: true + + /@chainsafe/ssz@0.9.4: + resolution: {integrity: sha512-77Qtg2N1ayqs4Bg/wvnWfg5Bta7iy7IRh8XqXh7oNMeP2HBbBwx8m6yTpA8p0EHItWPEBkgZd5S5/LSlp3GXuQ==} + dependencies: + '@chainsafe/as-sha256': 0.3.1 + '@chainsafe/persistent-merkle-tree': 0.4.2 + case: 1.6.3 + dev: true + /@changesets/apply-release-plan@6.1.3: resolution: {integrity: sha512-ECDNeoc3nfeAe1jqJb5aFQX7CqzQhD2klXRez2JDb/aVpGUbX673HgKrnrgJRuQR/9f2TtLoYIzrGB9qwD77mg==} dependencies: - '@babel/runtime': 7.20.6 + '@babel/runtime': 7.21.0 '@changesets/config': 2.3.0 '@changesets/get-version-range-type': 0.3.2 '@changesets/git': 2.0.0 @@ -1828,7 +2108,7 @@ packages: fs-extra: 7.0.1 lodash.startcase: 4.4.0 outdent: 0.5.0 - prettier: 2.8.1 + prettier: 2.8.8 resolve-from: 5.0.0 semver: 5.7.1 dev: true @@ -1836,7 +2116,7 @@ packages: /@changesets/assemble-release-plan@5.2.3: resolution: {integrity: sha512-g7EVZCmnWz3zMBAdrcKhid4hkHT+Ft1n0mLussFMcB1dE2zCuwcvGoy9ec3yOgPGF4hoMtgHaMIk3T3TBdvU9g==} dependencies: - '@babel/runtime': 7.20.6 + '@babel/runtime': 7.21.0 '@changesets/errors': 0.1.4 '@changesets/get-dependents-graph': 1.3.5 '@changesets/types': 5.2.1 @@ -1860,10 +2140,11 @@ packages: - encoding dev: true - /@changesets/cli@2.26.0: - resolution: {integrity: sha512-0cbTiDms+ICTVtEwAFLNW0jBNex9f5+fFv3I771nBvdnV/mOjd1QJ4+f8KtVSOrwD9SJkk9xbDkWFb0oXd8d1Q==} + /@changesets/cli@2.26.1: + resolution: {integrity: sha512-XnTa+b51vt057fyAudvDKGB0Sh72xutQZNAdXkCqPBKO2zvs2yYZx5hFZj1u9cbtpwM6Sxtcr02/FQJfZOzemQ==} + hasBin: true dependencies: - '@babel/runtime': 7.20.6 + '@babel/runtime': 7.21.0 '@changesets/apply-release-plan': 6.1.3 '@changesets/assemble-release-plan': 5.2.3 '@changesets/changelog-git': 0.1.14 @@ -1895,7 +2176,7 @@ packages: semver: 5.7.1 spawndamnit: 2.0.0 term-size: 2.2.1 - tty-table: 4.1.6 + tty-table: 4.2.1 dev: true /@changesets/config@2.3.0: @@ -1938,7 +2219,7 @@ packages: /@changesets/get-release-plan@3.0.16: resolution: {integrity: sha512-OpP9QILpBp1bY2YNIKFzwigKh7Qe9KizRsZomzLe6pK8IUo8onkAAVUD8+JRKSr8R7d4+JRuQrfSSNlEwKyPYg==} dependencies: - '@babel/runtime': 7.20.6 + '@babel/runtime': 7.21.0 '@changesets/assemble-release-plan': 5.2.3 '@changesets/config': 2.3.0 '@changesets/pre': 1.0.14 @@ -1954,7 +2235,7 @@ packages: /@changesets/git@2.0.0: resolution: {integrity: sha512-enUVEWbiqUTxqSnmesyJGWfzd51PY4H7mH9yUw0hPVpZBJ6tQZFMU3F3mT/t9OJ/GjyiM4770i+sehAn6ymx6A==} dependencies: - '@babel/runtime': 7.20.6 + '@babel/runtime': 7.21.0 '@changesets/errors': 0.1.4 '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 @@ -1979,7 +2260,7 @@ packages: /@changesets/pre@1.0.14: resolution: {integrity: sha512-dTsHmxQWEQekHYHbg+M1mDVYFvegDh9j/kySNuDKdylwfMEevTeDouR7IfHNyVodxZXu17sXoJuf2D0vi55FHQ==} dependencies: - '@babel/runtime': 7.20.6 + '@babel/runtime': 7.21.0 '@changesets/errors': 0.1.4 '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 @@ -1989,7 +2270,7 @@ packages: /@changesets/read@0.5.9: resolution: {integrity: sha512-T8BJ6JS6j1gfO1HFq50kU3qawYxa4NTbI/ASNVVCBTsKquy2HYwM9r7ZnzkiMe8IEObAJtUVGSrePCOxAK2haQ==} dependencies: - '@babel/runtime': 7.20.6 + '@babel/runtime': 7.21.0 '@changesets/git': 2.0.0 '@changesets/logger': 0.0.5 '@changesets/parse': 0.3.16 @@ -2010,11 +2291,11 @@ packages: /@changesets/write@0.2.3: resolution: {integrity: sha512-Dbamr7AIMvslKnNYsLFafaVORx4H0pvCA2MHqgtNCySMe1blImEyAEOzDmcgKAkgz4+uwoLz7demIrX+JBr/Xw==} dependencies: - '@babel/runtime': 7.20.6 + '@babel/runtime': 7.21.0 '@changesets/types': 5.2.1 fs-extra: 7.0.1 human-id: 1.0.2 - prettier: 2.8.1 + prettier: 2.8.8 dev: true /@concordance/react@2.0.0: @@ -2075,14 +2356,29 @@ packages: dev: true optional: true - /@eslint/eslintrc@1.4.0: - resolution: {integrity: sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A==} + /@eslint-community/eslint-utils@4.4.0(eslint@8.39.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.39.0 + eslint-visitor-keys: 3.4.0 + dev: true + + /@eslint-community/regexpp@4.5.0: + resolution: {integrity: sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.0.2: + resolution: {integrity: sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 debug: 4.3.4(supports-color@6.1.0) - espree: 9.4.1 - globals: 13.19.0 + espree: 9.5.1 + globals: 13.20.0 ignore: 5.2.4 import-fresh: 3.3.0 js-yaml: 4.1.0 @@ -2092,6 +2388,11 @@ packages: - supports-color dev: true + /@eslint/js@8.39.0: + resolution: {integrity: sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + /@ethereumjs/common@2.5.0: resolution: {integrity: sha512-DEHjW6e38o+JmB/NO3GZBpW4lpaiBpkFgXF6jLcJ6gETBYpEyaA5nTimsWBUJR3Vmtm/didUEbNjajskugZORg==} dependencies: @@ -2426,7 +2727,7 @@ packages: dependencies: '@graphql-tools/utils': 8.9.0(graphql@15.8.0) graphql: 15.8.0 - tslib: 2.4.1 + tslib: 2.5.0 dev: true /@graphql-tools/schema@8.5.1(graphql@15.8.0): @@ -2437,7 +2738,7 @@ packages: '@graphql-tools/merge': 8.3.1(graphql@15.8.0) '@graphql-tools/utils': 8.9.0(graphql@15.8.0) graphql: 15.8.0 - tslib: 2.4.1 + tslib: 2.5.0 value-or-promise: 1.0.11 dev: true @@ -2447,7 +2748,7 @@ packages: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: graphql: 15.8.0 - tslib: 2.4.1 + tslib: 2.5.0 dev: true /@graphql-tools/utils@8.9.0(graphql@15.8.0): @@ -2456,7 +2757,7 @@ packages: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: graphql: 15.8.0 - tslib: 2.4.1 + tslib: 2.5.0 dev: true /@hapi/hoek@9.3.0: @@ -2473,7 +2774,7 @@ packages: resolution: {integrity: sha512-IkTpczmtH8XM/vAL5SL2/aLJYVD1m9KOMZEfl5AaI+xve7EBrj7NRPztk4YKC364tes3cnzCFg5JMjVpVqzWUw==} engines: {node: '>=12.0.0'} dependencies: - '@types/node': 16.18.10 + '@types/node': 16.18.25 dev: true /@httptoolkit/subscriptions-transport-ws@0.11.2(graphql@15.8.0): @@ -2554,21 +2855,13 @@ packages: engines: {node: '>=8'} dev: true - /@jridgewell/gen-mapping@0.1.1: - resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.14 - dev: true - - /@jridgewell/gen-mapping@0.3.2: - resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} engines: {node: '>=6.0.0'} dependencies: '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.14 - '@jridgewell/trace-mapping': 0.3.17 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.18 dev: true /@jridgewell/resolve-uri@3.1.0: @@ -2584,14 +2877,18 @@ packages: /@jridgewell/source-map@0.3.2: resolution: {integrity: sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==} dependencies: - '@jridgewell/gen-mapping': 0.3.2 - '@jridgewell/trace-mapping': 0.3.17 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.18 dev: true /@jridgewell/sourcemap-codec@1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} dev: true + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + /@jridgewell/trace-mapping@0.3.17: resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} dependencies: @@ -2599,6 +2896,13 @@ packages: '@jridgewell/sourcemap-codec': 1.4.14 dev: true + /@jridgewell/trace-mapping@0.3.18: + resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} + dependencies: + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + /@jridgewell/trace-mapping@0.3.9: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} dependencies: @@ -2609,7 +2913,7 @@ packages: /@manypkg/find-root@1.1.0: resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} dependencies: - '@babel/runtime': 7.20.6 + '@babel/runtime': 7.21.0 '@types/node': 12.20.55 find-up: 4.1.0 fs-extra: 8.1.0 @@ -2618,7 +2922,7 @@ packages: /@manypkg/get-packages@1.1.3: resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} dependencies: - '@babel/runtime': 7.20.6 + '@babel/runtime': 7.21.0 '@changesets/types': 4.1.0 '@manypkg/find-root': 1.1.0 fs-extra: 8.1.0 @@ -2668,10 +2972,18 @@ packages: resolution: {integrity: sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==} dev: true + /@noble/hashes@1.2.0: + resolution: {integrity: sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==} + dev: true + /@noble/secp256k1@1.6.3: resolution: {integrity: sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ==} dev: true + /@noble/secp256k1@1.7.1: + resolution: {integrity: sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==} + dev: true + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -2690,7 +3002,7 @@ packages: engines: {node: '>= 8'} dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.14.0 + fastq: 1.15.0 dev: true /@nomicfoundation/ethereumjs-block@4.0.0: @@ -2705,6 +3017,22 @@ packages: ethereum-cryptography: 0.1.3 dev: true + /@nomicfoundation/ethereumjs-block@5.0.1: + resolution: {integrity: sha512-u1Yioemi6Ckj3xspygu/SfFvm8vZEO8/Yx5a1QLzi6nVU0jz3Pg2OmHKJ5w+D9Ogk1vhwRiqEBAqcb0GVhCyHw==} + engines: {node: '>=14'} + dependencies: + '@nomicfoundation/ethereumjs-common': 4.0.1 + '@nomicfoundation/ethereumjs-rlp': 5.0.1 + '@nomicfoundation/ethereumjs-trie': 6.0.1 + '@nomicfoundation/ethereumjs-tx': 5.0.1 + '@nomicfoundation/ethereumjs-util': 9.0.1 + ethereum-cryptography: 0.1.3 + ethers: 5.7.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + /@nomicfoundation/ethereumjs-blockchain@6.0.0: resolution: {integrity: sha512-pLFEoea6MWd81QQYSReLlLfH7N9v7lH66JC/NMPN848ySPPQA5renWnE7wPByfQFzNrPBuDDRFFULMDmj1C0xw==} engines: {node: '>=14'} @@ -2725,6 +3053,29 @@ packages: - supports-color dev: true + /@nomicfoundation/ethereumjs-blockchain@7.0.1: + resolution: {integrity: sha512-NhzndlGg829XXbqJEYrF1VeZhAwSPgsK/OB7TVrdzft3y918hW5KNd7gIZ85sn6peDZOdjBsAXIpXZ38oBYE5A==} + engines: {node: '>=14'} + dependencies: + '@nomicfoundation/ethereumjs-block': 5.0.1 + '@nomicfoundation/ethereumjs-common': 4.0.1 + '@nomicfoundation/ethereumjs-ethash': 3.0.1 + '@nomicfoundation/ethereumjs-rlp': 5.0.1 + '@nomicfoundation/ethereumjs-trie': 6.0.1 + '@nomicfoundation/ethereumjs-tx': 5.0.1 + '@nomicfoundation/ethereumjs-util': 9.0.1 + abstract-level: 1.0.3 + debug: 4.3.4(supports-color@6.1.0) + ethereum-cryptography: 0.1.3 + level: 8.0.0 + lru-cache: 5.1.1 + memory-level: 1.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + /@nomicfoundation/ethereumjs-common@3.0.0: resolution: {integrity: sha512-WS7qSshQfxoZOpHG/XqlHEGRG1zmyjYrvmATvc4c62+gZXgre1ymYP8ZNgx/3FyZY0TWe9OjFlKOfLqmgOeYwA==} dependencies: @@ -2732,6 +3083,13 @@ packages: crc-32: 1.2.2 dev: true + /@nomicfoundation/ethereumjs-common@4.0.1: + resolution: {integrity: sha512-OBErlkfp54GpeiE06brBW/TTbtbuBJV5YI5Nz/aB2evTDo+KawyEzPjBlSr84z/8MFfj8wS2wxzQX1o32cev5g==} + dependencies: + '@nomicfoundation/ethereumjs-util': 9.0.1 + crc-32: 1.2.2 + dev: true + /@nomicfoundation/ethereumjs-ethash@2.0.0: resolution: {integrity: sha512-WpDvnRncfDUuXdsAXlI4lXbqUDOA+adYRQaEezIkxqDkc+LDyYDbd/xairmY98GnQzo1zIqsIL6GB5MoMSJDew==} engines: {node: '>=14'} @@ -2744,6 +3102,21 @@ packages: ethereum-cryptography: 0.1.3 dev: true + /@nomicfoundation/ethereumjs-ethash@3.0.1: + resolution: {integrity: sha512-KDjGIB5igzWOp8Ik5I6QiRH5DH+XgILlplsHR7TEuWANZA759G6krQ6o8bvj+tRUz08YygMQu/sGd9mJ1DYT8w==} + engines: {node: '>=14'} + dependencies: + '@nomicfoundation/ethereumjs-block': 5.0.1 + '@nomicfoundation/ethereumjs-rlp': 5.0.1 + '@nomicfoundation/ethereumjs-util': 9.0.1 + abstract-level: 1.0.3 + bigint-crypto-utils: 3.2.2 + ethereum-cryptography: 0.1.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + /@nomicfoundation/ethereumjs-evm@1.0.0: resolution: {integrity: sha512-hVS6qRo3V1PLKCO210UfcEQHvlG7GqR8iFzp0yyjTg2TmJQizcChKgWo8KFsdMw6AyoLgLhHGHw4HdlP8a4i+Q==} engines: {node: '>=14'} @@ -2760,11 +3133,35 @@ packages: - supports-color dev: true + /@nomicfoundation/ethereumjs-evm@2.0.1: + resolution: {integrity: sha512-oL8vJcnk0Bx/onl+TgQOQ1t/534GKFaEG17fZmwtPFeH8S5soiBYPCLUrvANOl4sCp9elYxIMzIiTtMtNNN8EQ==} + engines: {node: '>=14'} + dependencies: + '@ethersproject/providers': 5.7.2 + '@nomicfoundation/ethereumjs-common': 4.0.1 + '@nomicfoundation/ethereumjs-tx': 5.0.1 + '@nomicfoundation/ethereumjs-util': 9.0.1 + debug: 4.3.4(supports-color@6.1.0) + ethereum-cryptography: 0.1.3 + mcl-wasm: 0.7.9 + rustbn.js: 0.2.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + /@nomicfoundation/ethereumjs-rlp@4.0.0: resolution: {integrity: sha512-GaSOGk5QbUk4eBP5qFbpXoZoZUj/NrW7MRa0tKY4Ew4c2HAS0GXArEMAamtFrkazp0BO4K5p2ZCG3b2FmbShmw==} engines: {node: '>=14'} dev: true + /@nomicfoundation/ethereumjs-rlp@5.0.1: + resolution: {integrity: sha512-xtxrMGa8kP4zF5ApBQBtjlSbN5E2HI8m8FYgVSYAnO6ssUoY5pVPGy2H8+xdf/bmMa22Ce8nWMH3aEW8CcqMeQ==} + engines: {node: '>=14'} + hasBin: true + dev: true + /@nomicfoundation/ethereumjs-statemanager@1.0.0: resolution: {integrity: sha512-jCtqFjcd2QejtuAMjQzbil/4NHf5aAWxUc+CvS0JclQpl+7M0bxMofR2AJdtz+P3u0ke2euhYREDiE7iSO31vQ==} dependencies: @@ -2779,6 +3176,21 @@ packages: - supports-color dev: true + /@nomicfoundation/ethereumjs-statemanager@2.0.1: + resolution: {integrity: sha512-B5ApMOnlruVOR7gisBaYwFX+L/AP7i/2oAahatssjPIBVDF6wTX1K7Qpa39E/nzsH8iYuL3krkYeUFIdO3EMUQ==} + dependencies: + '@nomicfoundation/ethereumjs-common': 4.0.1 + '@nomicfoundation/ethereumjs-rlp': 5.0.1 + debug: 4.3.4(supports-color@6.1.0) + ethereum-cryptography: 0.1.3 + ethers: 5.7.2 + js-sdsl: 4.4.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + /@nomicfoundation/ethereumjs-trie@5.0.0: resolution: {integrity: sha512-LIj5XdE+s+t6WSuq/ttegJzZ1vliwg6wlb+Y9f4RlBpuK35B9K02bO7xU+E6Rgg9RGptkWd6TVLdedTI4eNc2A==} engines: {node: '>=14'} @@ -2789,6 +3201,17 @@ packages: readable-stream: 3.6.0 dev: true + /@nomicfoundation/ethereumjs-trie@6.0.1: + resolution: {integrity: sha512-A64It/IMpDVODzCgxDgAAla8jNjNtsoQZIzZUfIV5AY6Coi4nvn7+VReBn5itlxMiL2yaTlQr9TRWp3CSI6VoA==} + engines: {node: '>=14'} + dependencies: + '@nomicfoundation/ethereumjs-rlp': 5.0.1 + '@nomicfoundation/ethereumjs-util': 9.0.1 + '@types/readable-stream': 2.3.15 + ethereum-cryptography: 0.1.3 + readable-stream: 3.6.2 + dev: true + /@nomicfoundation/ethereumjs-tx@4.0.0: resolution: {integrity: sha512-Gg3Lir2lNUck43Kp/3x6TfBNwcWC9Z1wYue9Nz3v4xjdcv6oDW9QSMJxqsKw9QEGoBBZ+gqwpW7+F05/rs/g1w==} engines: {node: '>=14'} @@ -2799,6 +3222,21 @@ packages: ethereum-cryptography: 0.1.3 dev: true + /@nomicfoundation/ethereumjs-tx@5.0.1: + resolution: {integrity: sha512-0HwxUF2u2hrsIM1fsasjXvlbDOq1ZHFV2dd1yGq8CA+MEYhaxZr8OTScpVkkxqMwBcc5y83FyPl0J9MZn3kY0w==} + engines: {node: '>=14'} + dependencies: + '@chainsafe/ssz': 0.9.4 + '@ethersproject/providers': 5.7.2 + '@nomicfoundation/ethereumjs-common': 4.0.1 + '@nomicfoundation/ethereumjs-rlp': 5.0.1 + '@nomicfoundation/ethereumjs-util': 9.0.1 + ethereum-cryptography: 0.1.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + /@nomicfoundation/ethereumjs-util@8.0.0: resolution: {integrity: sha512-2emi0NJ/HmTG+CGY58fa+DQuAoroFeSH9gKu9O6JnwTtlzJtgfTixuoOqLEgyyzZVvwfIpRueuePb8TonL1y+A==} engines: {node: '>=14'} @@ -2807,6 +3245,15 @@ packages: ethereum-cryptography: 0.1.3 dev: true + /@nomicfoundation/ethereumjs-util@9.0.1: + resolution: {integrity: sha512-TwbhOWQ8QoSCFhV/DDfSmyfFIHjPjFBj957219+V3jTZYZ2rf9PmDtNOeZWAE3p3vlp8xb02XGpd0v6nTUPbsA==} + engines: {node: '>=14'} + dependencies: + '@chainsafe/ssz': 0.10.2 + '@nomicfoundation/ethereumjs-rlp': 5.0.1 + ethereum-cryptography: 0.1.3 + dev: true + /@nomicfoundation/ethereumjs-vm@6.0.0: resolution: {integrity: sha512-JMPxvPQ3fzD063Sg3Tp+UdwUkVxMoo1uML6KSzFhMH3hoQi/LMuXBoEHAoW83/vyNS9BxEe6jm6LmT5xdeEJ6w==} engines: {node: '>=14'} @@ -2831,6 +3278,29 @@ packages: - supports-color dev: true + /@nomicfoundation/ethereumjs-vm@7.0.1: + resolution: {integrity: sha512-rArhyn0jPsS/D+ApFsz3yVJMQ29+pVzNZ0VJgkzAZ+7FqXSRtThl1C1prhmlVr3YNUlfpZ69Ak+RUT4g7VoOuQ==} + engines: {node: '>=14'} + dependencies: + '@nomicfoundation/ethereumjs-block': 5.0.1 + '@nomicfoundation/ethereumjs-blockchain': 7.0.1 + '@nomicfoundation/ethereumjs-common': 4.0.1 + '@nomicfoundation/ethereumjs-evm': 2.0.1 + '@nomicfoundation/ethereumjs-rlp': 5.0.1 + '@nomicfoundation/ethereumjs-statemanager': 2.0.1 + '@nomicfoundation/ethereumjs-trie': 6.0.1 + '@nomicfoundation/ethereumjs-tx': 5.0.1 + '@nomicfoundation/ethereumjs-util': 9.0.1 + debug: 4.3.4(supports-color@6.1.0) + ethereum-cryptography: 0.1.3 + mcl-wasm: 0.7.9 + rustbn.js: 0.2.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + /@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.0: resolution: {integrity: sha512-vEF3yKuuzfMHsZecHQcnkUrqm8mnTWfJeEVFHpg+cO+le96xQA4lAJYdUan8pXZohQxv1fSReQsn4QGNuBNuCw==} engines: {node: '>= 10'} @@ -2840,6 +3310,15 @@ packages: dev: true optional: true + /@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.1: + resolution: {integrity: sha512-KcTodaQw8ivDZyF+D76FokN/HdpgGpfjc/gFCImdLUyqB6eSWVaZPazMbeAjmfhx3R0zm/NYVzxwAokFKgrc0w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@nomicfoundation/solidity-analyzer-darwin-x64@0.1.0: resolution: {integrity: sha512-dlHeIg0pTL4dB1l9JDwbi/JG6dHQaU1xpDK+ugYO8eJ1kxx9Dh2isEUtA4d02cQAl22cjOHTvifAk96A+ItEHA==} engines: {node: '>= 10'} @@ -2849,6 +3328,15 @@ packages: dev: true optional: true + /@nomicfoundation/solidity-analyzer-darwin-x64@0.1.1: + resolution: {integrity: sha512-XhQG4BaJE6cIbjAVtzGOGbK3sn1BO9W29uhk9J8y8fZF1DYz0Doj8QDMfpMu+A6TjPDs61lbsmeYodIDnfveSA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@nomicfoundation/solidity-analyzer-freebsd-x64@0.1.0: resolution: {integrity: sha512-WFCZYMv86WowDA4GiJKnebMQRt3kCcFqHeIomW6NMyqiKqhK1kIZCxSLDYsxqlx396kKLPN1713Q1S8tu68GKg==} engines: {node: '>= 10'} @@ -2858,6 +3346,15 @@ packages: dev: true optional: true + /@nomicfoundation/solidity-analyzer-freebsd-x64@0.1.1: + resolution: {integrity: sha512-GHF1VKRdHW3G8CndkwdaeLkVBi5A9u2jwtlS7SLhBc8b5U/GcoL39Q+1CSO3hYqePNP+eV5YI7Zgm0ea6kMHoA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@nomicfoundation/solidity-analyzer-linux-arm64-gnu@0.1.0: resolution: {integrity: sha512-DTw6MNQWWlCgc71Pq7CEhEqkb7fZnS7oly13pujs4cMH1sR0JzNk90Mp1zpSCsCs4oKan2ClhMlLKtNat/XRKQ==} engines: {node: '>= 10'} @@ -2867,6 +3364,15 @@ packages: dev: true optional: true + /@nomicfoundation/solidity-analyzer-linux-arm64-gnu@0.1.1: + resolution: {integrity: sha512-g4Cv2fO37ZsUENQ2vwPnZc2zRenHyAxHcyBjKcjaSmmkKrFr64yvzeNO8S3GBFCo90rfochLs99wFVGT/0owpg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@nomicfoundation/solidity-analyzer-linux-arm64-musl@0.1.0: resolution: {integrity: sha512-wUpUnR/3GV5Da88MhrxXh/lhb9kxh9V3Jya2NpBEhKDIRCDmtXMSqPMXHZmOR9DfCwCvG6vLFPr/+YrPCnUN0w==} engines: {node: '>= 10'} @@ -2876,6 +3382,15 @@ packages: dev: true optional: true + /@nomicfoundation/solidity-analyzer-linux-arm64-musl@0.1.1: + resolution: {integrity: sha512-WJ3CE5Oek25OGE3WwzK7oaopY8xMw9Lhb0mlYuJl/maZVo+WtP36XoQTb7bW/i8aAdHW5Z+BqrHMux23pvxG3w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@nomicfoundation/solidity-analyzer-linux-x64-gnu@0.1.0: resolution: {integrity: sha512-lR0AxK1x/MeKQ/3Pt923kPvwigmGX3OxeU5qNtQ9pj9iucgk4PzhbS3ruUeSpYhUxG50jN4RkIGwUMoev5lguw==} engines: {node: '>= 10'} @@ -2885,6 +3400,15 @@ packages: dev: true optional: true + /@nomicfoundation/solidity-analyzer-linux-x64-gnu@0.1.1: + resolution: {integrity: sha512-5WN7leSr5fkUBBjE4f3wKENUy9HQStu7HmWqbtknfXkkil+eNWiBV275IOlpXku7v3uLsXTOKpnnGHJYI2qsdA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@nomicfoundation/solidity-analyzer-linux-x64-musl@0.1.0: resolution: {integrity: sha512-A1he/8gy/JeBD3FKvmI6WUJrGrI5uWJNr5Xb9WdV+DK0F8msuOqpEByLlnTdLkXMwW7nSl3awvLezOs9xBHJEg==} engines: {node: '>= 10'} @@ -2894,6 +3418,15 @@ packages: dev: true optional: true + /@nomicfoundation/solidity-analyzer-linux-x64-musl@0.1.1: + resolution: {integrity: sha512-KdYMkJOq0SYPQMmErv/63CwGwMm5XHenEna9X9aB8mQmhDBrYrlAOSsIPgFCUSL0hjxE3xHP65/EPXR/InD2+w==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@nomicfoundation/solidity-analyzer-win32-arm64-msvc@0.1.0: resolution: {integrity: sha512-7x5SXZ9R9H4SluJZZP8XPN+ju7Mx+XeUMWZw7ZAqkdhP5mK19I4vz3x0zIWygmfE8RT7uQ5xMap0/9NPsO+ykw==} engines: {node: '>= 10'} @@ -2903,6 +3436,15 @@ packages: dev: true optional: true + /@nomicfoundation/solidity-analyzer-win32-arm64-msvc@0.1.1: + resolution: {integrity: sha512-VFZASBfl4qiBYwW5xeY20exWhmv6ww9sWu/krWSesv3q5hA0o1JuzmPHR4LPN6SUZj5vcqci0O6JOL8BPw+APg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@nomicfoundation/solidity-analyzer-win32-ia32-msvc@0.1.0: resolution: {integrity: sha512-m7w3xf+hnE774YRXu+2mGV7RiF3QJtUoiYU61FascCkQhX3QMQavh7saH/vzb2jN5D24nT/jwvaHYX/MAM9zUw==} engines: {node: '>= 10'} @@ -2912,6 +3454,15 @@ packages: dev: true optional: true + /@nomicfoundation/solidity-analyzer-win32-ia32-msvc@0.1.1: + resolution: {integrity: sha512-JnFkYuyCSA70j6Si6cS1A9Gh1aHTEb8kOTBApp/c7NRTFGNMH8eaInKlyuuiIbvYFhlXW4LicqyYuWNNq9hkpQ==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@nomicfoundation/solidity-analyzer-win32-x64-msvc@0.1.0: resolution: {integrity: sha512-xCuybjY0sLJQnJhupiFAXaek2EqF0AP0eBjgzaalPXSNvCEN6ZYHvUzdA50ENDVeSYFXcUsYf3+FsD3XKaeptA==} engines: {node: '>= 10'} @@ -2921,6 +3472,15 @@ packages: dev: true optional: true + /@nomicfoundation/solidity-analyzer-win32-x64-msvc@0.1.1: + resolution: {integrity: sha512-HrVJr6+WjIXGnw3Q9u6KQcbZCtk0caVWhCdFADySvRyUxJ8PnzlaP+MhwNE8oyT8OZ6ejHBRrrgjSqDCFXGirw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@nomicfoundation/solidity-analyzer@0.1.0: resolution: {integrity: sha512-xGWAiVCGOycvGiP/qrlf9f9eOn7fpNbyJygcB0P21a1MDuVPlKt0Srp7rvtBEutYQ48ouYnRXm33zlRnlTOPHg==} engines: {node: '>= 12'} @@ -2937,34 +3497,57 @@ packages: '@nomicfoundation/solidity-analyzer-win32-x64-msvc': 0.1.0 dev: true - /@nomiclabs/hardhat-ethers@2.2.1(ethers@5.7.2)(hardhat@2.12.4): + /@nomicfoundation/solidity-analyzer@0.1.1: + resolution: {integrity: sha512-1LMtXj1puAxyFusBgUIy5pZk3073cNXYnXUpuNKFghHbIit/xZgbk0AokpUADbNm3gyD6bFWl3LRFh3dhVdREg==} + engines: {node: '>= 12'} + optionalDependencies: + '@nomicfoundation/solidity-analyzer-darwin-arm64': 0.1.1 + '@nomicfoundation/solidity-analyzer-darwin-x64': 0.1.1 + '@nomicfoundation/solidity-analyzer-freebsd-x64': 0.1.1 + '@nomicfoundation/solidity-analyzer-linux-arm64-gnu': 0.1.1 + '@nomicfoundation/solidity-analyzer-linux-arm64-musl': 0.1.1 + '@nomicfoundation/solidity-analyzer-linux-x64-gnu': 0.1.1 + '@nomicfoundation/solidity-analyzer-linux-x64-musl': 0.1.1 + '@nomicfoundation/solidity-analyzer-win32-arm64-msvc': 0.1.1 + '@nomicfoundation/solidity-analyzer-win32-ia32-msvc': 0.1.1 + '@nomicfoundation/solidity-analyzer-win32-x64-msvc': 0.1.1 + dev: true + + /@nomiclabs/hardhat-ethers@2.2.1(ethers@5.7.2)(hardhat@2.14.0): resolution: {integrity: sha512-RHWYwnxryWR8hzRmU4Jm/q4gzvXpetUOJ4OPlwH2YARcDB+j79+yAYCwO0lN1SUOb4++oOTJEe6AWLEc42LIvg==} peerDependencies: ethers: ^5.0.0 hardhat: ^2.0.0 dependencies: ethers: 5.7.2 - hardhat: 2.12.4(ts-node@10.9.1)(typescript@4.9.4) + hardhat: 2.14.0(ts-node@10.9.1)(typescript@4.9.4) dev: true - /@nomiclabs/hardhat-web3@2.0.0(hardhat@2.12.4)(web3@1.8.1): + /@nomiclabs/hardhat-web3@2.0.0(hardhat@2.14.0)(web3@1.9.0): resolution: {integrity: sha512-zt4xN+D+fKl3wW2YlTX3k9APR3XZgPkxJYf36AcliJn3oujnKEVRZaHu0PhgLjO+gR+F/kiYayo9fgd2L8970Q==} peerDependencies: hardhat: ^2.0.0 web3: ^1.0.0-beta.36 dependencies: '@types/bignumber.js': 5.0.0 - hardhat: 2.12.4(ts-node@10.9.1)(typescript@4.9.4) - web3: 1.8.1 + hardhat: 2.14.0(ts-node@10.9.1)(typescript@4.9.4) + web3: 1.9.0 + dev: true + + /@pkgjs/parseargs@0.11.0: + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + requiresBuild: true dev: true + optional: true /@preconstruct/cli@2.2.2: resolution: {integrity: sha512-7Zk8g/G+SPusoL1Ir3oslj19QDoFuAKeQO8B6fnNkRRgvIntxnylGZyC4wdKVX/eeDHwca1LNLT/GyjXx1f1nA==} dependencies: '@babel/code-frame': 7.18.6 - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@babel/helper-module-imports': 7.18.6 - '@babel/runtime': 7.20.6 + '@babel/runtime': 7.21.0 '@preconstruct/hook': 0.4.0 '@rollup/plugin-alias': 3.1.9(rollup@2.79.1) '@rollup/plugin-commonjs': 15.1.0(rollup@2.79.1) @@ -3005,8 +3588,8 @@ packages: /@preconstruct/hook@0.4.0: resolution: {integrity: sha512-a7mrlPTM3tAFJyz43qb4pPVpUx8j8TzZBFsNFqcKcE/sEakNXRlQAuCT4RGZRf9dQiiUnBahzSIWawU4rENl+Q==} dependencies: - '@babel/core': 7.20.5 - '@babel/plugin-transform-modules-commonjs': 7.19.6(@babel/core@7.20.5) + '@babel/core': 7.21.4 + '@babel/plugin-transform-modules-commonjs': 7.19.6(@babel/core@7.21.4) pirates: 4.0.5 source-map-support: 0.5.21 transitivePeerDependencies: @@ -3097,6 +3680,14 @@ packages: '@scure/base': 1.1.1 dev: true + /@scure/bip32@1.1.5: + resolution: {integrity: sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==} + dependencies: + '@noble/hashes': 1.2.0 + '@noble/secp256k1': 1.7.1 + '@scure/base': 1.1.1 + dev: true + /@scure/bip39@1.1.0: resolution: {integrity: sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==} dependencies: @@ -3104,6 +3695,13 @@ packages: '@scure/base': 1.1.1 dev: true + /@scure/bip39@1.1.1: + resolution: {integrity: sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==} + dependencies: + '@noble/hashes': 1.2.0 + '@scure/base': 1.1.1 + dev: true + /@sentry/core@5.30.0: resolution: {integrity: sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==} engines: {node: '>=6'} @@ -3269,13 +3867,13 @@ packages: /@types/bn.js@4.11.6: resolution: {integrity: sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==} dependencies: - '@types/node': 18.11.17 + '@types/node': 18.16.1 dev: true /@types/bn.js@5.1.1: resolution: {integrity: sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==} dependencies: - '@types/node': 18.11.17 + '@types/node': 18.16.1 dev: true /@types/cacheable-request@6.0.3: @@ -3283,7 +3881,7 @@ packages: dependencies: '@types/http-cache-semantics': 4.0.1 '@types/keyv': 3.1.4 - '@types/node': 12.20.55 + '@types/node': 18.16.1 '@types/responselike': 1.0.0 dev: true @@ -3300,7 +3898,7 @@ packages: /@types/cors@2.8.13: resolution: {integrity: sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==} dependencies: - '@types/node': 18.11.17 + '@types/node': 18.16.1 dev: true /@types/debug@4.1.7: @@ -3345,7 +3943,7 @@ packages: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: '@types/minimatch': 5.1.2 - '@types/node': 18.11.17 + '@types/node': 18.16.1 dev: true /@types/html-minifier-terser@6.1.0: @@ -3359,7 +3957,7 @@ packages: /@types/is-ci@3.0.0: resolution: {integrity: sha512-Q0Op0hdWbYd1iahB+IFNQcWXFq4O0Q5MwQP7uN0souuQ4rPg1vEYcnIOfr1gY+M+6rc8FGoRaBO1mOOvL29sEQ==} dependencies: - ci-info: 3.7.0 + ci-info: 3.8.0 dev: true /@types/json-schema@7.0.11: @@ -3373,7 +3971,7 @@ packages: /@types/keyv@3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 18.11.17 + '@types/node': 18.16.1 dev: true /@types/lru-cache@5.1.1: @@ -3400,48 +3998,51 @@ packages: resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} dev: true - /@types/node@16.18.10: - resolution: {integrity: sha512-XU1+v7h81p7145ddPfjv7jtWvkSilpcnON3mQ+bDi9Yuf7OI56efOglXRyXWgQ57xH3fEQgh7WOJMncRHVew5w==} + /@types/node@16.18.25: + resolution: {integrity: sha512-rUDO6s9Q/El1R1I21HG4qw/LstTHCPO/oQNAwI/4b2f9EWvMnqt4d3HJwPMawfZ3UvodB8516Yg+VAq54YM+eA==} dev: true - /@types/node@18.11.17: - resolution: {integrity: sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==} + /@types/node@18.16.1: + resolution: {integrity: sha512-DZxSZWXxFfOlx7k7Rv4LAyiMroaxa3Ly/7OOzZO8cBNho0YzAi4qlbrx8W27JGqG57IgR/6J7r+nOJWw6kcvZA==} dev: true /@types/normalize-package-data@2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} dev: true - /@types/parse-json@4.0.0: - resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} - dev: true - /@types/pbkdf2@3.1.0: resolution: {integrity: sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==} dependencies: - '@types/node': 18.11.17 + '@types/node': 18.16.1 dev: true /@types/prettier@2.7.2: resolution: {integrity: sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==} dev: true + /@types/readable-stream@2.3.15: + resolution: {integrity: sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ==} + dependencies: + '@types/node': 18.16.1 + safe-buffer: 5.1.2 + dev: true + /@types/resolve@1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} dependencies: - '@types/node': 18.11.17 + '@types/node': 18.16.1 dev: true /@types/responselike@1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 12.20.55 + '@types/node': 18.16.1 dev: true /@types/secp256k1@4.0.3: resolution: {integrity: sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==} dependencies: - '@types/node': 18.11.17 + '@types/node': 18.16.1 dev: true /@types/seedrandom@3.0.1: @@ -3469,19 +4070,19 @@ packages: /@types/ws@8.5.3: resolution: {integrity: sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==} dependencies: - '@types/node': 18.11.17 + '@types/node': 18.16.1 dev: true /@types/yauzl@2.10.0: resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==} requiresBuild: true dependencies: - '@types/node': 18.11.17 + '@types/node': 18.16.1 dev: true optional: true - /@typescript-eslint/eslint-plugin@5.47.0(@typescript-eslint/parser@5.47.0)(eslint@8.30.0)(typescript@4.9.4): - resolution: {integrity: sha512-AHZtlXAMGkDmyLuLZsRpH3p4G/1iARIwc/T0vIem2YB+xW6pZaXYXzCBnZSF/5fdM97R9QqZWZ+h3iW10XgevQ==} + /@typescript-eslint/eslint-plugin@5.59.1(@typescript-eslint/parser@5.59.1)(eslint@8.39.0)(typescript@4.9.4): + resolution: {integrity: sha512-AVi0uazY5quFB9hlp2Xv+ogpfpk77xzsgsIEWyVS7uK/c7MZ5tw7ZPbapa0SbfkqE0fsAMkz5UwtgMLVk2BQAg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/parser': ^5.0.0 @@ -3491,24 +4092,25 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.47.0(eslint@8.30.0)(typescript@4.9.4) - '@typescript-eslint/scope-manager': 5.47.0 - '@typescript-eslint/type-utils': 5.47.0(eslint@8.30.0)(typescript@4.9.4) - '@typescript-eslint/utils': 5.47.0(eslint@8.30.0)(typescript@4.9.4) + '@eslint-community/regexpp': 4.5.0 + '@typescript-eslint/parser': 5.59.1(eslint@8.39.0)(typescript@4.9.4) + '@typescript-eslint/scope-manager': 5.59.1 + '@typescript-eslint/type-utils': 5.59.1(eslint@8.39.0)(typescript@4.9.4) + '@typescript-eslint/utils': 5.59.1(eslint@8.39.0)(typescript@4.9.4) debug: 4.3.4(supports-color@6.1.0) - eslint: 8.30.0 + eslint: 8.39.0 + grapheme-splitter: 1.0.4 ignore: 5.2.4 natural-compare-lite: 1.4.0 - regexpp: 3.2.0 - semver: 7.3.8 + semver: 7.5.0 tsutils: 3.21.0(typescript@4.9.4) typescript: 4.9.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@5.47.0(eslint@8.30.0)(typescript@4.9.4): - resolution: {integrity: sha512-udPU4ckK+R1JWCGdQC4Qa27NtBg7w020ffHqGyAK8pAgOVuNw7YaKXGChk+udh+iiGIJf6/E/0xhVXyPAbsczw==} + /@typescript-eslint/parser@5.59.1(eslint@8.39.0)(typescript@4.9.4): + resolution: {integrity: sha512-nzjFAN8WEu6yPRDizIFyzAfgK7nybPodMNFGNH0M9tei2gYnYszRDqVA0xlnRjkl7Hkx2vYrEdb6fP2a21cG1g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -3517,26 +4119,26 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 5.47.0 - '@typescript-eslint/types': 5.47.0 - '@typescript-eslint/typescript-estree': 5.47.0(typescript@4.9.4) + '@typescript-eslint/scope-manager': 5.59.1 + '@typescript-eslint/types': 5.59.1 + '@typescript-eslint/typescript-estree': 5.59.1(typescript@4.9.4) debug: 4.3.4(supports-color@6.1.0) - eslint: 8.30.0 + eslint: 8.39.0 typescript: 4.9.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/scope-manager@5.47.0: - resolution: {integrity: sha512-dvJab4bFf7JVvjPuh3sfBUWsiD73aiftKBpWSfi3sUkysDQ4W8x+ZcFpNp7Kgv0weldhpmMOZBjx1wKN8uWvAw==} + /@typescript-eslint/scope-manager@5.59.1: + resolution: {integrity: sha512-mau0waO5frJctPuAzcxiNWqJR5Z8V0190FTSqRw1Q4Euop6+zTwHAf8YIXNwDOT29tyUDrQ65jSg9aTU/H0omA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.47.0 - '@typescript-eslint/visitor-keys': 5.47.0 + '@typescript-eslint/types': 5.59.1 + '@typescript-eslint/visitor-keys': 5.59.1 dev: true - /@typescript-eslint/type-utils@5.47.0(eslint@8.30.0)(typescript@4.9.4): - resolution: {integrity: sha512-1J+DFFrYoDUXQE1b7QjrNGARZE6uVhBqIvdaXTe5IN+NmEyD68qXR1qX1g2u4voA+nCaelQyG8w30SAOihhEYg==} + /@typescript-eslint/type-utils@5.59.1(eslint@8.39.0)(typescript@4.9.4): + resolution: {integrity: sha512-ZMWQ+Oh82jWqWzvM3xU+9y5U7MEMVv6GLioM3R5NJk6uvP47kZ7YvlgSHJ7ERD6bOY7Q4uxWm25c76HKEwIjZw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: '*' @@ -3545,23 +4147,23 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 5.47.0(typescript@4.9.4) - '@typescript-eslint/utils': 5.47.0(eslint@8.30.0)(typescript@4.9.4) + '@typescript-eslint/typescript-estree': 5.59.1(typescript@4.9.4) + '@typescript-eslint/utils': 5.59.1(eslint@8.39.0)(typescript@4.9.4) debug: 4.3.4(supports-color@6.1.0) - eslint: 8.30.0 + eslint: 8.39.0 tsutils: 3.21.0(typescript@4.9.4) typescript: 4.9.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/types@5.47.0: - resolution: {integrity: sha512-eslFG0Qy8wpGzDdYKu58CEr3WLkjwC5Usa6XbuV89ce/yN5RITLe1O8e+WFEuxnfftHiJImkkOBADj58ahRxSg==} + /@typescript-eslint/types@5.59.1: + resolution: {integrity: sha512-dg0ICB+RZwHlysIy/Dh1SP+gnXNzwd/KS0JprD3Lmgmdq+dJAJnUPe1gNG34p0U19HvRlGX733d/KqscrGC1Pg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/typescript-estree@5.47.0(typescript@4.9.4): - resolution: {integrity: sha512-LxfKCG4bsRGq60Sqqu+34QT5qT2TEAHvSCCJ321uBWywgE2dS0LKcu5u+3sMGo+Vy9UmLOhdTw5JHzePV/1y4Q==} + /@typescript-eslint/typescript-estree@5.59.1(typescript@4.9.4): + resolution: {integrity: sha512-lYLBBOCsFltFy7XVqzX0Ju+Lh3WPIAWxYpmH/Q7ZoqzbscLiCW00LeYCdsUnnfnj29/s1WovXKh2gwCoinHNGA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: typescript: '*' @@ -3569,44 +4171,44 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 5.47.0 - '@typescript-eslint/visitor-keys': 5.47.0 + '@typescript-eslint/types': 5.59.1 + '@typescript-eslint/visitor-keys': 5.59.1 debug: 4.3.4(supports-color@6.1.0) globby: 11.1.0 is-glob: 4.0.3 - semver: 7.3.8 + semver: 7.5.0 tsutils: 3.21.0(typescript@4.9.4) typescript: 4.9.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@5.47.0(eslint@8.30.0)(typescript@4.9.4): - resolution: {integrity: sha512-U9xcc0N7xINrCdGVPwABjbAKqx4GK67xuMV87toI+HUqgXj26m6RBp9UshEXcTrgCkdGYFzgKLt8kxu49RilDw==} + /@typescript-eslint/utils@5.59.1(eslint@8.39.0)(typescript@4.9.4): + resolution: {integrity: sha512-MkTe7FE+K1/GxZkP5gRj3rCztg45bEhsd8HYjczBuYm+qFHP5vtZmjx3B0yUCDotceQ4sHgTyz60Ycl225njmA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.39.0) '@types/json-schema': 7.0.11 '@types/semver': 7.3.13 - '@typescript-eslint/scope-manager': 5.47.0 - '@typescript-eslint/types': 5.47.0 - '@typescript-eslint/typescript-estree': 5.47.0(typescript@4.9.4) - eslint: 8.30.0 + '@typescript-eslint/scope-manager': 5.59.1 + '@typescript-eslint/types': 5.59.1 + '@typescript-eslint/typescript-estree': 5.59.1(typescript@4.9.4) + eslint: 8.39.0 eslint-scope: 5.1.1 - eslint-utils: 3.0.0(eslint@8.30.0) - semver: 7.3.8 + semver: 7.5.0 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/visitor-keys@5.47.0: - resolution: {integrity: sha512-ByPi5iMa6QqDXe/GmT/hR6MZtVPi0SqMQPDx15FczCBXJo/7M8T88xReOALAfpBLm+zxpPfmhuEvPb577JRAEg==} + /@typescript-eslint/visitor-keys@5.59.1: + resolution: {integrity: sha512-6waEYwBTCWryx0VJmP7JaM4FpipLsFl9CvYf2foAE8Qh/Y0s+bxWysciwOs0LTBED4JCaNxTZ5rGadB14M6dwA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - '@typescript-eslint/types': 5.47.0 - eslint-visitor-keys: 3.3.0 + '@typescript-eslint/types': 5.59.1 + eslint-visitor-keys: 3.4.0 dev: true /@webassemblyjs/ast@1.11.1: @@ -3819,12 +4421,12 @@ packages: acorn: 8.8.1 dev: true - /acorn-jsx@5.3.2(acorn@8.8.1): + /acorn-jsx@5.3.2(acorn@8.8.2): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - acorn: 8.8.1 + acorn: 8.8.2 dev: true /acorn-walk@8.2.0: @@ -3837,6 +4439,12 @@ packages: engines: {node: '>=0.4.0'} dev: true + /acorn@8.8.2: + resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + /adm-zip@0.4.16: resolution: {integrity: sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==} engines: {node: '>=0.3.0'} @@ -3870,7 +4478,7 @@ packages: ajv: 6.12.6 dev: true - /ajv-formats@2.1.1(ajv@8.11.2): + /ajv-formats@2.1.1(ajv@8.12.0): resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: ajv: ^8.0.0 @@ -3878,7 +4486,7 @@ packages: ajv: optional: true dependencies: - ajv: 8.11.2 + ajv: 8.12.0 dev: true /ajv-keywords@3.5.2(ajv@6.12.6): @@ -3889,12 +4497,12 @@ packages: ajv: 6.12.6 dev: true - /ajv-keywords@5.1.0(ajv@8.11.2): + /ajv-keywords@5.1.0(ajv@8.12.0): resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} peerDependencies: ajv: ^8.8.2 dependencies: - ajv: 8.11.2 + ajv: 8.12.0 fast-deep-equal: 3.1.3 dev: true @@ -3907,8 +4515,8 @@ packages: uri-js: 4.4.1 dev: true - /ajv@8.11.2: - resolution: {integrity: sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==} + /ajv@8.12.0: + resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} dependencies: fast-deep-equal: 3.1.3 json-schema-traverse: 1.0.0 @@ -4050,6 +4658,13 @@ packages: engines: {node: '>=8'} dev: true + /array-buffer-byte-length@1.0.0: + resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} + dependencies: + call-bind: 1.0.2 + is-array-buffer: 3.0.2 + dev: true + /array-find-index@1.0.2: resolution: {integrity: sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==} engines: {node: '>=0.10.0'} @@ -4068,9 +4683,9 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.20.5 - get-intrinsic: 1.1.3 + define-properties: 1.2.0 + es-abstract: 1.21.2 + get-intrinsic: 1.2.0 is-string: 1.0.7 dev: true @@ -4101,8 +4716,18 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.20.5 + define-properties: 1.2.0 + es-abstract: 1.21.2 + es-shim-unscopables: 1.0.0 + dev: true + + /array.prototype.flatmap@1.3.1: + resolution: {integrity: sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 es-shim-unscopables: 1.0.0 dev: true @@ -4154,7 +4779,7 @@ packages: resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} engines: {node: '>=4'} dependencies: - tslib: 2.4.1 + tslib: 2.5.0 dev: true /astral-regex@2.0.0: @@ -4179,7 +4804,7 @@ packages: /async-mutex@0.2.6: resolution: {integrity: sha512-Hs4R+4SPgamu6rSGW8C7cV9gaWUKEHykfzCCvIRuaVv636Ju10ZdeUbvb4TBEW0INuq2DHZqXbK4Nd3yG4RaRw==} dependencies: - tslib: 2.4.1 + tslib: 2.5.0 dev: true /async@1.5.2: @@ -4295,51 +4920,51 @@ packages: - debug dev: true - /babel-loader@9.1.0(@babel/core@7.20.5)(webpack@5.75.0): + /babel-loader@9.1.0(@babel/core@7.21.4)(webpack@5.75.0): resolution: {integrity: sha512-Antt61KJPinUMwHwIIz9T5zfMgevnfZkEVWYDWlG888fgdvRRGD0JTuf/fFozQnfT+uq64sk1bmdHDy/mOEWnA==} engines: {node: '>= 14.15.0'} peerDependencies: '@babel/core': ^7.12.0 webpack: '>=5' dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 find-cache-dir: 3.3.2 schema-utils: 4.0.0 webpack: 5.75.0(webpack-cli@4.10.0) dev: true - /babel-plugin-polyfill-corejs2@0.3.3(@babel/core@7.20.5): + /babel-plugin-polyfill-corejs2@0.3.3(@babel/core@7.21.4): resolution: {integrity: sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.20.5 - '@babel/core': 7.20.5 - '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.20.5) + '@babel/compat-data': 7.21.4 + '@babel/core': 7.21.4 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.21.4) semver: 6.3.0 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-corejs3@0.6.0(@babel/core@7.20.5): + /babel-plugin-polyfill-corejs3@0.6.0(@babel/core@7.21.4): resolution: {integrity: sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 - '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.20.5) - core-js-compat: 3.26.1 + '@babel/core': 7.21.4 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.21.4) + core-js-compat: 3.30.1 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-regenerator@0.4.1(@babel/core@7.20.5): + /babel-plugin-polyfill-regenerator@0.4.1(@babel/core@7.21.4): resolution: {integrity: sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.20.5 - '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.20.5) + '@babel/core': 7.21.4 + '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.21.4) transitivePeerDependencies: - supports-color dev: true @@ -4365,6 +4990,11 @@ packages: safe-buffer: 5.2.1 dev: true + /base64-arraybuffer-es6@0.7.0: + resolution: {integrity: sha512-ESyU/U1CFZDJUdr+neHRhNozeCv72Y7Vm0m1DCbjX3KBjT6eYocvAJlSk6+8+HkVwXlT1FNxhGW6q3UKAlCvvw==} + engines: {node: '>=6.0.0'} + dev: true + /base64-arraybuffer@0.1.5: resolution: {integrity: sha512-437oANT9tP582zZMwSvZGy2nmSeAb8DW2me3y+Uv1Wp2Rulr8Mqlyrv3E7MLxmsiaPSMMDmiDVzgE+e8zlMx9g==} engines: {node: '>= 0.6.0'} @@ -4414,6 +5044,11 @@ packages: bigint-mod-arith: 3.1.2 dev: true + /bigint-crypto-utils@3.2.2: + resolution: {integrity: sha512-U1RbE3aX9ayCUVcIPHuPDPKcK3SFOXf93J1UK/iHlJuQB7bhagPIX06/CLpLEsDThJ7KA4Dhrnzynl+d2weTiw==} + engines: {node: '>=14.0.0'} + dev: true + /bigint-mod-arith@3.1.2: resolution: {integrity: sha512-nx8J8bBeiRR+NlsROFH9jHswW5HO8mgfOSqW0AmjicMMvaONDa8AO+5ViKDUUNytBPWiwfvZP4/Bj4Y3lUfvgQ==} engines: {node: '>=10.4.0'} @@ -4647,6 +5282,17 @@ packages: update-browserslist-db: 1.0.10(browserslist@4.21.4) dev: true + /browserslist@4.21.5: + resolution: {integrity: sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001481 + electron-to-chromium: 1.4.375 + node-releases: 2.0.10 + update-browserslist-db: 1.0.11(browserslist@4.21.5) + dev: true + /bs58@4.0.1: resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} dependencies: @@ -4791,7 +5437,7 @@ packages: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} dependencies: function-bind: 1.1.1 - get-intrinsic: 1.1.3 + get-intrinsic: 1.2.0 dev: true /callsites@3.1.0: @@ -4803,7 +5449,7 @@ packages: resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} dependencies: pascal-case: 3.1.2 - tslib: 2.4.1 + tslib: 2.5.0 dev: true /camelcase-keys@6.2.2: @@ -4829,6 +5475,15 @@ packages: resolution: {integrity: sha512-OyxRR4Vof59I3yGWXws6i908EtGbMzVUi3ganaZQHmydk1iwDhRnvaPG2WaR0KcqrDFKrxVZHULT396LEPhXfg==} dev: true + /caniuse-lite@1.0.30001481: + resolution: {integrity: sha512-KCqHwRnaa1InZBtqXzP98LPg0ajCVujMKjqKDhZEthIpAsJl/YEIa3YvXjGXPVqzZVguccuu7ga9KOE1J9rKPQ==} + dev: true + + /case@1.6.3: + resolution: {integrity: sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==} + engines: {node: '>= 0.8.0'} + dev: true + /caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} dev: true @@ -4952,8 +5607,8 @@ packages: resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} dev: true - /ci-info@3.7.0: - resolution: {integrity: sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==} + /ci-info@3.8.0: + resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} engines: {node: '>=8'} dev: true @@ -5207,10 +5862,6 @@ packages: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} dev: true - /compare-versions@3.6.0: - resolution: {integrity: sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==} - dev: true - /component-emitter@1.3.0: resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==} dev: true @@ -5238,7 +5889,7 @@ packages: dev: true /concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} dev: true /concordance@5.0.4: @@ -5251,7 +5902,7 @@ packages: js-string-escape: 1.0.1 lodash: 4.17.21 md5-hex: 3.0.1 - semver: 7.3.8 + semver: 7.5.0 well-known-symbols: 2.0.0 dev: true @@ -5275,7 +5926,7 @@ packages: engines: {node: '>=8'} dependencies: dot-prop: 5.3.0 - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 make-dir: 3.1.0 unique-string: 2.0.0 write-file-atomic: 3.0.3 @@ -5347,10 +5998,10 @@ packages: engines: {node: '>=0.10.0'} dev: true - /core-js-compat@3.26.1: - resolution: {integrity: sha512-622/KzTudvXCDLRw70iHW4KKs1aGpcRcowGWyYJr2DEBfRrd6hNJybxSWJFuZYD4ma86xhrwDDHxmDaIq4EA8A==} + /core-js-compat@3.30.1: + resolution: {integrity: sha512-d690npR7MC6P0gq4npTl5n2VQeNAmUrJ90n+MHiKS7W2+xno4o3F5GDEuylSdi6EJ3VssibSGXOa1r3YXD3Mhw==} dependencies: - browserslist: 4.21.4 + browserslist: 4.21.5 dev: true /core-util-is@1.0.2: @@ -5373,17 +6024,6 @@ packages: vary: 1.1.2 dev: true - /cosmiconfig@7.1.0: - resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} - engines: {node: '>=10'} - dependencies: - '@types/parse-json': 4.0.0 - import-fresh: 3.3.0 - parse-json: 5.2.0 - path-type: 4.0.0 - yaml: 1.10.2 - dev: true - /cosmiconfig@8.0.0: resolution: {integrity: sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==} engines: {node: '>=14'} @@ -5673,7 +6313,7 @@ packages: is-regex: 1.1.4 object-is: 1.1.5 object-keys: 1.1.1 - regexp.prototype.flags: 1.4.3 + regexp.prototype.flags: 1.5.0 dev: true /deep-extend@0.6.0: @@ -5722,8 +6362,8 @@ packages: abstract-leveldown: 2.6.3 dev: true - /define-properties@1.1.4: - resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} + /define-properties@1.2.0: + resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} engines: {node: '>= 0.4'} dependencies: has-property-descriptors: 1.0.0 @@ -5820,7 +6460,7 @@ packages: resolution: {integrity: sha512-78rUr9j0b4bRWO0eBtqKqmb43htBwNbofRRukpo+R7PZqHD6llb7aQoNVt81U9NQGhINRKBHz7lkrxZJj9vyog==} engines: {node: '>=12.0.0'} dependencies: - '@types/node': 18.11.17 + '@types/node': 18.16.1 dev: true /detect-indent@6.1.0: @@ -5914,6 +6554,12 @@ packages: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} dev: true + /domexception@1.0.1: + resolution: {integrity: sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==} + dependencies: + webidl-conversions: 4.0.2 + dev: true + /domhandler@4.3.1: resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} engines: {node: '>= 4'} @@ -5933,7 +6579,7 @@ packages: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} dependencies: no-case: 3.0.4 - tslib: 2.4.1 + tslib: 2.5.0 dev: true /dot-prop@5.3.0: @@ -5977,6 +6623,10 @@ packages: resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==} dev: true + /electron-to-chromium@1.4.375: + resolution: {integrity: sha512-czSmDyWG5qmb4TcwD5lhVDP6viDPtHfrIzw0CnzisRpziiUaq+ffptBHs70d9YkFtrxzaDvOmFPeVRVNwMt2rQ==} + dev: true + /elliptic@6.5.4: resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==} dependencies: @@ -6063,41 +6713,59 @@ packages: is-arrayish: 0.2.1 dev: true - /es-abstract@1.20.5: - resolution: {integrity: sha512-7h8MM2EQhsCA7pU/Nv78qOXFpD8Rhqd12gYiSJVkrH9+e8VuA8JlPJK/hQjjlLv6pJvx/z1iRFKzYb0XT/RuAQ==} + /es-abstract@1.21.2: + resolution: {integrity: sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==} engines: {node: '>= 0.4'} dependencies: + array-buffer-byte-length: 1.0.0 + available-typed-arrays: 1.0.5 call-bind: 1.0.2 + es-set-tostringtag: 2.0.1 es-to-primitive: 1.2.1 - function-bind: 1.1.1 function.prototype.name: 1.1.5 - get-intrinsic: 1.1.3 + get-intrinsic: 1.2.0 get-symbol-description: 1.0.0 + globalthis: 1.0.3 gopd: 1.0.1 has: 1.0.3 has-property-descriptors: 1.0.0 + has-proto: 1.0.1 has-symbols: 1.0.3 - internal-slot: 1.0.4 + internal-slot: 1.0.5 + is-array-buffer: 3.0.2 is-callable: 1.2.7 is-negative-zero: 2.0.2 is-regex: 1.1.4 is-shared-array-buffer: 1.0.2 is-string: 1.0.7 + is-typed-array: 1.1.10 is-weakref: 1.0.2 - object-inspect: 1.12.2 + object-inspect: 1.12.3 object-keys: 1.1.1 object.assign: 4.1.4 - regexp.prototype.flags: 1.4.3 + regexp.prototype.flags: 1.5.0 safe-regex-test: 1.0.0 + string.prototype.trim: 1.2.7 string.prototype.trimend: 1.0.6 string.prototype.trimstart: 1.0.6 + typed-array-length: 1.0.4 unbox-primitive: 1.0.2 + which-typed-array: 1.1.9 dev: true /es-module-lexer@0.9.3: resolution: {integrity: sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==} dev: true + /es-set-tostringtag@2.0.1: + resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.0 + has: 1.0.3 + has-tostringtag: 1.0.0 + dev: true + /es-shim-unscopables@1.0.0: resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} dependencies: @@ -6396,26 +7064,27 @@ packages: source-map: 0.6.1 dev: true - /eslint-config-prettier@8.5.0(eslint@8.30.0): - resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==} + /eslint-config-prettier@8.8.0(eslint@8.39.0): + resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==} hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 8.30.0 + eslint: 8.39.0 dev: true - /eslint-import-resolver-node@0.3.6: - resolution: {integrity: sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==} + /eslint-import-resolver-node@0.3.7: + resolution: {integrity: sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==} dependencies: debug: 3.2.7(supports-color@6.1.0) - resolve: 1.22.1 + is-core-module: 2.12.0 + resolve: 1.22.2 transitivePeerDependencies: - supports-color dev: true - /eslint-module-utils@2.7.4(@typescript-eslint/parser@5.47.0)(eslint-import-resolver-node@0.3.6)(eslint@8.30.0): - resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} + /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.59.1)(eslint-import-resolver-node@0.3.7)(eslint@8.39.0): + resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' @@ -6435,16 +7104,16 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.47.0(eslint@8.30.0)(typescript@4.9.4) + '@typescript-eslint/parser': 5.59.1(eslint@8.39.0)(typescript@4.9.4) debug: 3.2.7(supports-color@6.1.0) - eslint: 8.30.0 - eslint-import-resolver-node: 0.3.6 + eslint: 8.39.0 + eslint-import-resolver-node: 0.3.7 transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-import@2.26.0(@typescript-eslint/parser@5.47.0)(eslint@8.30.0): - resolution: {integrity: sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==} + /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.59.1)(eslint@8.39.0): + resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' @@ -6453,28 +7122,30 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.47.0(eslint@8.30.0)(typescript@4.9.4) + '@typescript-eslint/parser': 5.59.1(eslint@8.39.0)(typescript@4.9.4) array-includes: 3.1.6 array.prototype.flat: 1.3.1 - debug: 2.6.9(supports-color@6.1.0) + array.prototype.flatmap: 1.3.1 + debug: 3.2.7(supports-color@6.1.0) doctrine: 2.1.0 - eslint: 8.30.0 - eslint-import-resolver-node: 0.3.6 - eslint-module-utils: 2.7.4(@typescript-eslint/parser@5.47.0)(eslint-import-resolver-node@0.3.6)(eslint@8.30.0) + eslint: 8.39.0 + eslint-import-resolver-node: 0.3.7 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.59.1)(eslint-import-resolver-node@0.3.7)(eslint@8.39.0) has: 1.0.3 - is-core-module: 2.11.0 + is-core-module: 2.12.0 is-glob: 4.0.3 minimatch: 3.1.2 object.values: 1.1.6 - resolve: 1.22.1 - tsconfig-paths: 3.14.1 + resolve: 1.22.2 + semver: 6.3.0 + tsconfig-paths: 3.14.2 transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color dev: true - /eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.5.0)(eslint@8.30.0)(prettier@2.8.1): + /eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.8.0)(eslint@8.39.0)(prettier@2.8.8): resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} engines: {node: '>=12.0.0'} peerDependencies: @@ -6485,9 +7156,9 @@ packages: eslint-config-prettier: optional: true dependencies: - eslint: 8.30.0 - eslint-config-prettier: 8.5.0(eslint@8.30.0) - prettier: 2.8.1 + eslint: 8.39.0 + eslint-config-prettier: 8.8.0(eslint@8.39.0) + prettier: 2.8.8 prettier-linter-helpers: 1.0.0 dev: true @@ -6499,39 +7170,28 @@ packages: estraverse: 4.3.0 dev: true - /eslint-scope@7.1.1: - resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} + /eslint-scope@7.2.0: + resolution: {integrity: sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 dev: true - /eslint-utils@3.0.0(eslint@8.30.0): - resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} - engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} - peerDependencies: - eslint: '>=5' - dependencies: - eslint: 8.30.0 - eslint-visitor-keys: 2.1.0 - dev: true - - /eslint-visitor-keys@2.1.0: - resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} - engines: {node: '>=10'} - dev: true - - /eslint-visitor-keys@3.3.0: - resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==} + /eslint-visitor-keys@3.4.0: + resolution: {integrity: sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint@8.30.0: - resolution: {integrity: sha512-MGADB39QqYuzEGov+F/qb18r4i7DohCDOfatHaxI2iGlPuC65bwG2gxgO+7DkyL38dRFaRH7RaRAgU6JKL9rMQ==} + /eslint@8.39.0: + resolution: {integrity: sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true dependencies: - '@eslint/eslintrc': 1.4.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.39.0) + '@eslint-community/regexpp': 4.5.0 + '@eslint/eslintrc': 2.0.2 + '@eslint/js': 8.39.0 '@humanwhocodes/config-array': 0.11.8 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 @@ -6541,24 +7201,23 @@ packages: debug: 4.3.4(supports-color@6.1.0) doctrine: 3.0.0 escape-string-regexp: 4.0.0 - eslint-scope: 7.1.1 - eslint-utils: 3.0.0(eslint@8.30.0) - eslint-visitor-keys: 3.3.0 - espree: 9.4.1 - esquery: 1.4.0 + eslint-scope: 7.2.0 + eslint-visitor-keys: 3.4.0 + espree: 9.5.1 + esquery: 1.5.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 6.0.1 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.19.0 + globals: 13.20.0 grapheme-splitter: 1.0.4 ignore: 5.2.4 import-fresh: 3.3.0 imurmurhash: 0.1.4 is-glob: 4.0.3 is-path-inside: 3.0.3 - js-sdsl: 4.2.0 + js-sdsl: 4.4.0 js-yaml: 4.1.0 json-stable-stringify-without-jsonify: 1.0.1 levn: 0.4.1 @@ -6566,7 +7225,6 @@ packages: minimatch: 3.1.2 natural-compare: 1.4.0 optionator: 0.9.1 - regexpp: 3.2.0 strip-ansi: 6.0.1 strip-json-comments: 3.1.1 text-table: 0.2.0 @@ -6574,13 +7232,13 @@ packages: - supports-color dev: true - /espree@9.4.1: - resolution: {integrity: sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==} + /espree@9.5.1: + resolution: {integrity: sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - acorn: 8.8.1 - acorn-jsx: 5.3.2(acorn@8.8.1) - eslint-visitor-keys: 3.3.0 + acorn: 8.8.2 + acorn-jsx: 5.3.2(acorn@8.8.2) + eslint-visitor-keys: 3.4.0 dev: true /esprima@4.0.1: @@ -6588,8 +7246,8 @@ packages: engines: {node: '>=4'} dev: true - /esquery@1.4.0: - resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==} + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} dependencies: estraverse: 5.3.0 @@ -6630,11 +7288,11 @@ packages: engines: {node: '>= 0.6'} dev: true - /eth-block-tracker@4.4.3(@babel/core@7.20.5): + /eth-block-tracker@4.4.3(@babel/core@7.21.4): resolution: {integrity: sha512-A8tG4Z4iNg4mw5tP1Vung9N9IjgMNqpiMoJ/FouSFwNCGHv2X0mmOYwtQOJzki6XN7r7Tyo01S29p7b224I4jw==} dependencies: - '@babel/plugin-transform-runtime': 7.19.6(@babel/core@7.20.5) - '@babel/runtime': 7.20.6 + '@babel/plugin-transform-runtime': 7.19.6(@babel/core@7.21.4) + '@babel/runtime': 7.21.0 eth-query: 2.1.2 json-rpc-random-id: 1.0.1 pify: 3.0.0 @@ -6813,6 +7471,15 @@ packages: '@scure/bip39': 1.1.0 dev: true + /ethereum-cryptography@1.2.0: + resolution: {integrity: sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==} + dependencies: + '@noble/hashes': 1.2.0 + '@noble/secp256k1': 1.7.1 + '@scure/bip32': 1.1.5 + '@scure/bip39': 1.1.1 + dev: true + /ethereumjs-abi@0.6.8: resolution: {integrity: sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==} dependencies: @@ -7160,6 +7827,12 @@ packages: engines: {'0': node >=0.6.0} dev: true + /fake-indexeddb@4.0.1: + resolution: {integrity: sha512-hFRyPmvEZILYgdcLBxVdHLik4Tj3gDTu/g7s9ZDOiU3sTNiGx+vEu1ri/AMsFJUZ/1sdRbAVrEcKndh3sViBcA==} + dependencies: + realistic-structured-clone: 3.0.0 + dev: true + /fake-merkle-patricia-tree@1.0.1: resolution: {integrity: sha512-Tgq37lkc9pUIgIKw5uitNUKcgcYL3R6JvXtKQbOf/ZSavXbidsksgp/pAY6p//uhw0I4yoMsvTSovvVIsk/qxA==} dependencies: @@ -7206,8 +7879,8 @@ packages: engines: {node: '>= 4.9.1'} dev: true - /fastq@1.14.0: - resolution: {integrity: sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==} + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} dependencies: reusify: 1.0.4 dev: true @@ -7343,13 +8016,6 @@ packages: path-exists: 4.0.0 dev: true - /find-versions@4.0.0: - resolution: {integrity: sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ==} - engines: {node: '>=10'} - dependencies: - semver-regex: 3.1.4 - dev: true - /find-yarn-workspace-root2@1.2.16: resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} dependencies: @@ -7404,6 +8070,14 @@ packages: signal-exit: 3.0.7 dev: true + /foreground-child@3.1.1: + resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + engines: {node: '>=14'} + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.0.1 + dev: true + /forever-agent@0.6.1: resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} dev: true @@ -7453,7 +8127,7 @@ packages: /fs-extra@0.30.0: resolution: {integrity: sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA==} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jsonfile: 2.4.0 klaw: 1.3.1 path-is-absolute: 1.0.1 @@ -7463,7 +8137,7 @@ packages: /fs-extra@4.0.3: resolution: {integrity: sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 dev: true @@ -7472,7 +8146,7 @@ packages: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 dev: true @@ -7481,7 +8155,7 @@ packages: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} engines: {node: '>=6 <7 || >=8'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 dev: true @@ -7543,8 +8217,8 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.20.5 + define-properties: 1.2.0 + es-abstract: 1.21.2 functions-have-names: 1.2.3 dev: true @@ -7594,8 +8268,8 @@ packages: resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} dev: true - /get-intrinsic@1.1.3: - resolution: {integrity: sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==} + /get-intrinsic@1.2.0: + resolution: {integrity: sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==} dependencies: function-bind: 1.1.1 has: 1.0.3 @@ -7631,7 +8305,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - get-intrinsic: 1.1.3 + get-intrinsic: 1.2.0 dev: true /get-tsconfig@4.2.0: @@ -7689,6 +8363,18 @@ packages: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} dev: true + /glob@10.2.2: + resolution: {integrity: sha512-Xsa0BcxIC6th9UwNjZkhrMtNo/MnyRL8jGCP+uEwhA5oFOCY1f2s1/oNKY47xQ0Bg5nkjsfAEIej1VeH62bDDQ==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + foreground-child: 3.1.1 + jackspeak: 2.1.0 + minimatch: 9.0.0 + minipass: 5.0.0 + path-scurry: 1.7.0 + dev: true + /glob@7.1.7: resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==} dependencies: @@ -7741,13 +8427,20 @@ packages: engines: {node: '>=4'} dev: true - /globals@13.19.0: - resolution: {integrity: sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==} + /globals@13.20.0: + resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==} engines: {node: '>=8'} dependencies: type-fest: 0.20.2 dev: true + /globalthis@1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.0 + dev: true + /globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -7774,7 +8467,7 @@ packages: /gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} dependencies: - get-intrinsic: 1.1.3 + get-intrinsic: 1.2.0 dev: true /got@11.8.6: @@ -7817,6 +8510,10 @@ packages: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} dev: true + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: true + /grapheme-splitter@1.0.4: resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} dev: true @@ -7837,7 +8534,7 @@ packages: graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 dependencies: graphql: 15.8.0 - tslib: 2.4.1 + tslib: 2.5.0 dev: true /graphql@15.8.0: @@ -7926,7 +8623,7 @@ packages: solc: 0.7.3(debug@4.3.4) source-map-support: 0.5.21 stacktrace-parser: 0.1.10 - ts-node: 10.9.1(@types/node@18.11.17)(typescript@4.9.4) + ts-node: 10.9.1(@types/node@18.16.1)(typescript@4.9.4) tsort: 0.0.1 typescript: 4.9.4 undici: 5.14.0 @@ -7938,6 +8635,77 @@ packages: - utf-8-validate dev: true + /hardhat@2.14.0(ts-node@10.9.1)(typescript@4.9.4): + resolution: {integrity: sha512-73jsInY4zZahMSVFurSK+5TNCJTXMv+vemvGia0Ac34Mm19fYp6vEPVGF3sucbumszsYxiTT2TbS8Ii2dsDSoQ==} + engines: {node: '>=14.0.0'} + hasBin: true + peerDependencies: + ts-node: '*' + typescript: '*' + peerDependenciesMeta: + ts-node: + optional: true + typescript: + optional: true + dependencies: + '@ethersproject/abi': 5.7.0 + '@metamask/eth-sig-util': 4.0.1 + '@nomicfoundation/ethereumjs-block': 5.0.1 + '@nomicfoundation/ethereumjs-blockchain': 7.0.1 + '@nomicfoundation/ethereumjs-common': 4.0.1 + '@nomicfoundation/ethereumjs-evm': 2.0.1 + '@nomicfoundation/ethereumjs-rlp': 5.0.1 + '@nomicfoundation/ethereumjs-statemanager': 2.0.1 + '@nomicfoundation/ethereumjs-trie': 6.0.1 + '@nomicfoundation/ethereumjs-tx': 5.0.1 + '@nomicfoundation/ethereumjs-util': 9.0.1 + '@nomicfoundation/ethereumjs-vm': 7.0.1 + '@nomicfoundation/solidity-analyzer': 0.1.1 + '@sentry/node': 5.30.0 + '@types/bn.js': 5.1.1 + '@types/lru-cache': 5.1.1 + abort-controller: 3.0.0 + adm-zip: 0.4.16 + aggregate-error: 3.1.0 + ansi-escapes: 4.3.2 + chalk: 2.4.2 + chokidar: 3.5.3 + ci-info: 2.0.0 + debug: 4.3.4(supports-color@6.1.0) + enquirer: 2.3.6 + env-paths: 2.2.1 + ethereum-cryptography: 1.2.0 + ethereumjs-abi: 0.6.8 + find-up: 2.1.0 + fp-ts: 1.19.3 + fs-extra: 7.0.1 + glob: 7.2.0 + immutable: 4.3.0 + io-ts: 1.10.4 + keccak: 3.0.3 + lodash: 4.17.21 + mnemonist: 0.38.5 + mocha: 10.2.0 + p-map: 4.0.0 + qs: 6.11.1 + raw-body: 2.5.2 + resolve: 1.17.0 + semver: 6.3.0 + solc: 0.7.3(debug@4.3.4) + source-map-support: 0.5.21 + stacktrace-parser: 0.1.10 + ts-node: 10.9.1(@types/node@18.16.1)(typescript@4.9.4) + tsort: 0.0.1 + typescript: 4.9.4 + undici: 5.22.0 + uuid: 8.3.2 + ws: 7.5.9 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + /has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} dev: true @@ -7955,7 +8723,12 @@ packages: /has-property-descriptors@1.0.0: resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} dependencies: - get-intrinsic: 1.1.3 + get-intrinsic: 1.2.0 + dev: true + + /has-proto@1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} dev: true /has-symbols@1.0.3: @@ -8234,21 +9007,9 @@ packages: resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} dev: true - /husky@4.3.8: - resolution: {integrity: sha512-LCqqsB0PzJQ/AlCgfrfzRe3e3+NvmefAdKQhRYpxS4u6clblBoDdzzvHi8fmxKRzvMxPY/1WZWzomPZww0Anow==} - engines: {node: '>=10'} - requiresBuild: true - dependencies: - chalk: 4.1.2 - ci-info: 2.0.0 - compare-versions: 3.6.0 - cosmiconfig: 7.1.0 - find-versions: 4.0.0 - opencollective-postinstall: 2.0.3 - pkg-dir: 5.0.0 - please-upgrade-node: 3.2.0 - slash: 3.0.0 - which-pm-runs: 1.1.0 + /husky@8.0.3: + resolution: {integrity: sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==} + engines: {node: '>=14'} dev: true /iconv-lite@0.4.24: @@ -8258,6 +9019,10 @@ packages: safer-buffer: 2.1.2 dev: true + /idb@7.1.1: + resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} + dev: false + /idna-uts46-hx@2.3.1: resolution: {integrity: sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA==} engines: {node: '>=4.0.0'} @@ -8293,6 +9058,10 @@ packages: resolution: {integrity: sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==} dev: true + /immutable@4.3.0: + resolution: {integrity: sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==} + dev: true + /import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -8363,11 +9132,11 @@ packages: ipaddr.js: 1.9.1 dev: true - /internal-slot@1.0.4: - resolution: {integrity: sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==} + /internal-slot@1.0.5: + resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} engines: {node: '>= 0.4'} dependencies: - get-intrinsic: 1.1.3 + get-intrinsic: 1.2.0 has: 1.0.3 side-channel: 1.0.4 dev: true @@ -8433,6 +9202,14 @@ packages: has-tostringtag: 1.0.0 dev: true + /is-array-buffer@3.0.2: + resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + is-typed-array: 1.1.10 + dev: true + /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} dev: true @@ -8487,8 +9264,9 @@ packages: /is-ci@3.0.1: resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} + hasBin: true dependencies: - ci-info: 3.7.0 + ci-info: 3.8.0 dev: true /is-core-module@2.11.0: @@ -8497,6 +9275,12 @@ packages: has: 1.0.3 dev: true + /is-core-module@2.12.0: + resolution: {integrity: sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==} + dependencies: + has: 1.0.3 + dev: true + /is-data-descriptor@0.1.4: resolution: {integrity: sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==} engines: {node: '>=0.10.0'} @@ -8856,7 +9640,7 @@ packages: resolution: {integrity: sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==} engines: {node: '>=8'} dependencies: - '@babel/core': 7.20.5 + '@babel/core': 7.21.4 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 semver: 6.3.0 @@ -8908,11 +9692,20 @@ packages: resolution: {integrity: sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==} dev: true + /jackspeak@2.1.0: + resolution: {integrity: sha512-DiEwVPqsieUzZBNxQ2cxznmFzfg/AMgJUjYw5xl6rSmCxAQXECcbSdwcLM6Ds6T09+SBfSNCGPhYUoQ96P4h7A==} + engines: {node: '>=14'} + dependencies: + cliui: 7.0.4 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + dev: true + /jest-worker@26.6.2: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.11.17 + '@types/node': 18.16.1 merge-stream: 2.0.0 supports-color: 7.2.0 dev: true @@ -8921,7 +9714,7 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.11.17 + '@types/node': 18.16.1 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true @@ -8939,8 +9732,8 @@ packages: /js-base64@3.7.3: resolution: {integrity: sha512-PAr6Xg2jvd7MCR6Ld9Jg3BmTcjYsHEBx1VlwEwULb/qowPf5VD9kEMagj23Gm7JRnSvE/Da/57nChZjnvL8v6A==} - /js-sdsl@4.2.0: - resolution: {integrity: sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==} + /js-sdsl@4.4.0: + resolution: {integrity: sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==} dev: true /js-sha3@0.5.7: @@ -8961,6 +9754,7 @@ packages: /js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true dependencies: argparse: 1.0.10 esprima: 4.0.1 @@ -8968,6 +9762,7 @@ packages: /js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true dependencies: argparse: 2.0.1 dev: true @@ -8978,11 +9773,13 @@ packages: /jsesc@0.5.0: resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} + hasBin: true dev: true /jsesc@2.5.2: resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} engines: {node: '>=4'} + hasBin: true dev: true /json-buffer@3.0.1: @@ -9042,27 +9839,29 @@ packages: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} dev: true - /json5@1.0.1: - resolution: {integrity: sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==} + /json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true dependencies: - minimist: 1.2.7 + minimist: 1.2.8 dev: true - /json5@2.2.2: - resolution: {integrity: sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==} + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} + hasBin: true dev: true /jsonfile@2.4.0: resolution: {integrity: sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==} optionalDependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 dev: true /jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} optionalDependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 dev: true /jsonfile@6.1.0: @@ -9070,7 +9869,7 @@ packages: dependencies: universalify: 2.0.0 optionalDependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 dev: true /jsonify@0.0.1: @@ -9097,6 +9896,16 @@ packages: readable-stream: 3.6.0 dev: true + /keccak@3.0.3: + resolution: {integrity: sha512-JZrLIAJWuZxKbCilMpNz5Vj7Vtb4scDG3dMXLOsbzBmQGyjwE61BbW7bJkfKKCShXiQZt3T6sBgALRtmd+nZaQ==} + engines: {node: '>=10.0.0'} + requiresBuild: true + dependencies: + node-addon-api: 2.0.2 + node-gyp-build: 4.6.0 + readable-stream: 3.6.2 + dev: true + /keyv@4.5.2: resolution: {integrity: sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==} dependencies: @@ -9134,7 +9943,7 @@ packages: /klaw@1.3.1: resolution: {integrity: sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==} optionalDependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 dev: true /kleur@4.1.5: @@ -9254,7 +10063,7 @@ packages: resolution: {integrity: sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==} engines: {node: '>=6'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 parse-json: 4.0.0 pify: 4.0.1 strip-bom: 3.0.0 @@ -9265,7 +10074,7 @@ packages: resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} engines: {node: '>=6'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 js-yaml: 3.14.1 pify: 4.0.1 strip-bom: 3.0.0 @@ -9352,7 +10161,7 @@ packages: /lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} dependencies: - tslib: 2.4.1 + tslib: 2.5.0 dev: true /lowercase-keys@2.0.0: @@ -9390,6 +10199,11 @@ packages: engines: {node: '>=12'} dev: true + /lru-cache@9.1.1: + resolution: {integrity: sha512-65/Jky17UwSb0BuB9V+MyDpsOtXKmYwzhyl+cOa9XUiI4uV2Ouy/2voFP3+al0BjZbJgMBD8FojMpAf+Z+qn4A==} + engines: {node: 14 || >=16.14} + dev: true + /lru_map@0.3.3: resolution: {integrity: sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==} dev: true @@ -9689,6 +10503,13 @@ packages: brace-expansion: 2.0.1 dev: true + /minimatch@9.0.0: + resolution: {integrity: sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + /minimist-options@4.1.0: resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} engines: {node: '>= 6'} @@ -9702,6 +10523,10 @@ packages: resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==} dev: true + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + /minipass@2.9.0: resolution: {integrity: sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==} dependencies: @@ -9709,6 +10534,11 @@ packages: yallist: 3.1.1 dev: true + /minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + dev: true + /minizlib@1.3.3: resolution: {integrity: sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==} dependencies: @@ -9727,8 +10557,8 @@ packages: is-extendable: 1.0.1 dev: true - /mixme@0.5.4: - resolution: {integrity: sha512-3KYa4m4Vlqx98GPdOHghxSdNtTvcP8E0kkaJ5Dlh+h2DRzF7zpuVVcA8B0QpKd11YJeP9QQ7ASkKzOeu195Wzw==} + /mixme@0.5.9: + resolution: {integrity: sha512-VC5fg6ySUscaWUpI4gxCBTQMH2RdUpNrk+MsbpCYtIvf9SBJdiUey4qE7BXviJsJR4nDQxCZ+3yaYNW3guz/Pw==} engines: {node: '>= 8.0.0'} dev: true @@ -9746,7 +10576,7 @@ packages: /mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} dependencies: - minimist: 1.2.7 + minimist: 1.2.8 dev: true /mkdirp@1.0.4: @@ -9801,7 +10631,7 @@ packages: '@httptoolkit/subscriptions-transport-ws': 0.11.2(graphql@15.8.0) '@httptoolkit/websocket-stream': 6.0.1 '@types/cors': 2.8.13 - '@types/node': 18.11.17 + '@types/node': 18.16.1 base64-arraybuffer: 0.1.5 body-parser: 1.20.1(supports-color@6.1.0) cacheable-lookup: 6.1.0 @@ -9979,7 +10809,7 @@ packages: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} dependencies: lower-case: 2.0.2 - tslib: 2.4.1 + tslib: 2.5.0 dev: true /node-addon-api@2.0.2: @@ -10011,6 +10841,11 @@ packages: resolution: {integrity: sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==} dev: true + /node-gyp-build@4.6.0: + resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==} + hasBin: true + dev: true + /node-preload@0.2.1: resolution: {integrity: sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==} engines: {node: '>=8'} @@ -10018,6 +10853,10 @@ packages: process-on-spawn: 1.0.0 dev: true + /node-releases@2.0.10: + resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} + dev: true + /node-releases@2.0.8: resolution: {integrity: sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==} dev: true @@ -10026,7 +10865,7 @@ packages: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: hosted-git-info: 2.8.9 - resolve: 1.22.1 + resolve: 1.22.2 semver: 5.7.1 validate-npm-package-license: 3.0.4 dev: true @@ -10142,8 +10981,8 @@ packages: kind-of: 3.2.2 dev: true - /object-inspect@1.12.2: - resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==} + /object-inspect@1.12.3: + resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} dev: true /object-is@1.1.5: @@ -10151,7 +10990,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.0 dev: true /object-keys@0.4.0: @@ -10175,7 +11014,7 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.0 has-symbols: 1.0.3 object-keys: 1.1.1 dev: true @@ -10192,8 +11031,8 @@ packages: engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.20.5 + define-properties: 1.2.0 + es-abstract: 1.21.2 dev: true /obliterator@2.0.4: @@ -10242,10 +11081,6 @@ packages: mimic-fn: 2.1.0 dev: true - /opencollective-postinstall@2.0.3: - resolution: {integrity: sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==} - dev: true - /opn@5.5.0: resolution: {integrity: sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==} engines: {node: '>=4'} @@ -10457,7 +11292,7 @@ packages: resolution: {integrity: sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==} engines: {node: '>=8'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 hasha: 5.2.2 lodash.flattendeep: 4.4.0 release-zalgo: 1.0.0 @@ -10477,7 +11312,7 @@ packages: resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} dependencies: dot-case: 3.0.4 - tslib: 2.4.1 + tslib: 2.5.0 dev: true /parent-module@1.0.1: @@ -10547,7 +11382,7 @@ packages: resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} dependencies: no-case: 3.0.4 - tslib: 2.4.1 + tslib: 2.5.0 dev: true /pascalcase@0.1.1: @@ -10588,6 +11423,14 @@ packages: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true + /path-scurry@1.7.0: + resolution: {integrity: sha512-UkZUeDjczjYRE495+9thsgcVgsaCPkaw80slmfVFgllxY+IO8ubTsOpFVjDPROBqJdHfVPUFRHPBV/WciOVfWg==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + lru-cache: 9.1.1 + minipass: 5.0.0 + dev: true + /path-to-regexp@0.1.7: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} dev: true @@ -10688,19 +11531,6 @@ packages: find-up: 4.1.0 dev: true - /pkg-dir@5.0.0: - resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} - engines: {node: '>=10'} - dependencies: - find-up: 5.0.0 - dev: true - - /please-upgrade-node@3.2.0: - resolution: {integrity: sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==} - dependencies: - semver-compare: 1.0.0 - dev: true - /plur@4.0.0: resolution: {integrity: sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==} engines: {node: '>=10'} @@ -10767,9 +11597,10 @@ packages: fast-diff: 1.2.0 dev: true - /prettier@2.8.1: - resolution: {integrity: sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==} + /prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} engines: {node: '>=10.13.0'} + hasBin: true dev: true /pretty-error@4.0.0: @@ -10866,8 +11697,8 @@ packages: engines: {node: '>=6'} dev: true - /punycode@2.1.1: - resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} + /punycode@2.3.0: + resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} dev: true @@ -10931,6 +11762,13 @@ packages: side-channel: 1.0.4 dev: true + /qs@6.11.1: + resolution: {integrity: sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.4 + dev: true + /qs@6.5.3: resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==} engines: {node: '>=0.6'} @@ -10996,12 +11834,22 @@ packages: unpipe: 1.0.0 dev: true + /raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + dev: true + /rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} dependencies: deep-extend: 0.6.0 ini: 1.3.8 - minimist: 1.2.7 + minimist: 1.2.8 strip-json-comments: 2.0.1 dev: true @@ -11028,14 +11876,14 @@ packages: resolution: {integrity: sha512-OvSzfVv6Y656ekUxB7aDhWkLW7y1ck16ChfLFNJhKNADFNweH2fvyiEZkGmmdtXbOtlNuH2zVXZoFCW349M+GA==} engines: {node: '>=12.0.0'} dependencies: - '@types/node': 18.11.17 + '@types/node': 18.16.1 dev: true /read-yaml-file@1.1.0: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} engines: {node: '>=6'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 js-yaml: 3.14.1 pify: 4.0.1 strip-bom: 3.0.0 @@ -11080,11 +11928,20 @@ packages: util-deprecate: 1.0.2 dev: true + /readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + dev: true + /readdirp@2.2.1(supports-color@6.1.0): resolution: {integrity: sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==} engines: {node: '>=0.10'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 micromatch: 3.1.10(supports-color@6.1.0) readable-stream: 2.3.7 transitivePeerDependencies: @@ -11098,11 +11955,19 @@ packages: picomatch: 2.3.1 dev: true + /realistic-structured-clone@3.0.0: + resolution: {integrity: sha512-rOjh4nuWkAqf9PWu6JVpOWD4ndI+JHfgiZeMmujYcPi+fvILUu7g6l26TC1K5aBIp34nV+jE1cDO75EKOfHC5Q==} + dependencies: + domexception: 1.0.1 + typeson: 6.1.0 + typeson-registry: 1.0.0-alpha.39 + dev: true + /rechoir@0.7.1: resolution: {integrity: sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==} engines: {node: '>= 0.10'} dependencies: - resolve: 1.22.1 + resolve: 1.22.2 dev: true /redent@3.0.0: @@ -11136,7 +12001,7 @@ packages: /regenerator-transform@0.15.1: resolution: {integrity: sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==} dependencies: - '@babel/runtime': 7.20.6 + '@babel/runtime': 7.21.0 dev: true /regex-not@1.0.2: @@ -11147,27 +12012,22 @@ packages: safe-regex: 1.1.0 dev: true - /regexp.prototype.flags@1.4.3: - resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==} + /regexp.prototype.flags@1.5.0: + resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.0 functions-have-names: 1.2.3 dev: true - /regexpp@3.2.0: - resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} - engines: {node: '>=8'} - dev: true - - /regexpu-core@5.2.2: - resolution: {integrity: sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw==} + /regexpu-core@5.3.2: + resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==} engines: {node: '>=4'} dependencies: + '@babel/regjsgen': 0.8.0 regenerate: 1.4.2 regenerate-unicode-properties: 10.1.0 - regjsgen: 0.7.1 regjsparser: 0.9.1 unicode-match-property-ecmascript: 2.0.0 unicode-match-property-value-ecmascript: 2.1.0 @@ -11187,12 +12047,9 @@ packages: rc: 1.2.8 dev: true - /regjsgen@0.7.1: - resolution: {integrity: sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==} - dev: true - /regjsparser@0.9.1: resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==} + hasBin: true dependencies: jsesc: 0.5.0 dev: true @@ -11323,7 +12180,16 @@ packages: /resolve@1.22.1: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} dependencies: - is-core-module: 2.11.0 + is-core-module: 2.11.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /resolve@1.22.2: + resolution: {integrity: sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==} + hasBin: true + dependencies: + is-core-module: 2.12.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 dev: true @@ -11359,16 +12225,26 @@ packages: /rimraf@2.7.1: resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + hasBin: true dependencies: glob: 7.2.3 dev: true /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true dependencies: glob: 7.2.3 dev: true + /rimraf@5.0.0: + resolution: {integrity: sha512-Jf9llaP+RvaEVS5nPShYFhtXIrb3LRKP281ib3So0KkeZKo2wIKyq0Re7TOSwanasA423PSr6CCIL4bP6T040g==} + engines: {node: '>=14'} + hasBin: true + dependencies: + glob: 10.2.2 + dev: true + /ripemd160@2.0.2: resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} dependencies: @@ -11408,7 +12284,7 @@ packages: /rxjs@7.8.0: resolution: {integrity: sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==} dependencies: - tslib: 2.4.1 + tslib: 2.5.0 dev: true /safe-buffer@5.1.2: @@ -11429,7 +12305,7 @@ packages: resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} dependencies: call-bind: 1.0.2 - get-intrinsic: 1.1.3 + get-intrinsic: 1.2.0 is-regex: 1.1.4 dev: true @@ -11466,9 +12342,9 @@ packages: engines: {node: '>= 12.13.0'} dependencies: '@types/json-schema': 7.0.11 - ajv: 8.11.2 - ajv-formats: 2.1.1(ajv@8.11.2) - ajv-keywords: 5.1.0(ajv@8.11.2) + ajv: 8.12.0 + ajv-formats: 2.1.1(ajv@8.12.0) + ajv-keywords: 5.1.0(ajv@8.12.0) dev: true /scrypt-js@3.0.1: @@ -11499,10 +12375,6 @@ packages: engines: {node: '>=0.8.0'} dev: true - /semver-compare@1.0.0: - resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} - dev: true - /semver-diff@3.1.1: resolution: {integrity: sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==} engines: {node: '>=8'} @@ -11510,21 +12382,19 @@ packages: semver: 6.3.0 dev: true - /semver-regex@3.1.4: - resolution: {integrity: sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA==} - engines: {node: '>=8'} - dev: true - /semver@5.4.1: resolution: {integrity: sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==} + hasBin: true dev: true /semver@5.7.1: resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} + hasBin: true dev: true /semver@6.3.0: resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} + hasBin: true dev: true /semver@7.3.8: @@ -11534,6 +12404,14 @@ packages: lru-cache: 6.0.0 dev: true + /semver@7.5.0: + resolution: {integrity: sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + /send@0.18.0(supports-color@6.1.0): resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} engines: {node: '>= 0.8.0'} @@ -11685,14 +12563,19 @@ packages: resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} dependencies: call-bind: 1.0.2 - get-intrinsic: 1.1.3 - object-inspect: 1.12.2 + get-intrinsic: 1.2.0 + object-inspect: 1.12.3 dev: true /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: true + /signal-exit@4.0.1: + resolution: {integrity: sha512-uUWsN4aOxJAS8KOuf3QMyFtgm1pkb6I+KRZbRF/ghdf5T7sM+B1lLLzPDxswUjkmHyxQAVzEgG35E3NzDM9GVw==} + engines: {node: '>=14'} + dev: true + /simple-concat@1.0.1: resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} dev: true @@ -11727,6 +12610,7 @@ packages: /smartwrap@2.0.2: resolution: {integrity: sha512-vCsKNQxb7PnCNd2wY1WClWifAc2lwqsG8OaswpJkVJsvMGcnEntdTCDajZCkk93Ay1U3t/9puJmb525Rg5MZBA==} engines: {node: '>=6'} + hasBin: true dependencies: array.prototype.flat: 1.3.1 breakword: 1.0.5 @@ -11894,11 +12778,11 @@ packages: signal-exit: 3.0.7 dev: true - /spdx-correct@3.1.1: - resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==} + /spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} dependencies: spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.12 + spdx-license-ids: 3.0.13 dev: true /spdx-exceptions@2.3.0: @@ -11909,11 +12793,11 @@ packages: resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} dependencies: spdx-exceptions: 2.3.0 - spdx-license-ids: 3.0.12 + spdx-license-ids: 3.0.13 dev: true - /spdx-license-ids@3.0.12: - resolution: {integrity: sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==} + /spdx-license-ids@3.0.13: + resolution: {integrity: sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==} dev: true /spdy-transport@3.0.0(supports-color@6.1.0): @@ -12007,7 +12891,7 @@ packages: /stream-transform@2.1.3: resolution: {integrity: sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ==} dependencies: - mixme: 0.5.4 + mixme: 0.5.9 dev: true /streamsearch@1.1.0: @@ -12042,20 +12926,29 @@ packages: strip-ansi: 6.0.1 dev: true + /string.prototype.trim@1.2.7: + resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + dev: true + /string.prototype.trimend@1.0.6: resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.20.5 + define-properties: 1.2.0 + es-abstract: 1.21.2 dev: true /string.prototype.trimstart@1.0.6: resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 - es-abstract: 1.20.5 + define-properties: 1.2.0 + es-abstract: 1.21.2 dev: true /string_decoder@0.10.31: @@ -12390,13 +13283,20 @@ packages: engines: {node: '>=0.8'} dependencies: psl: 1.9.0 - punycode: 2.1.1 + punycode: 2.3.0 dev: true /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} dev: true + /tr46@2.1.0: + resolution: {integrity: sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==} + engines: {node: '>=8'} + dependencies: + punycode: 2.3.0 + dev: true + /tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} dev: true @@ -12428,7 +13328,7 @@ packages: typescript: 4.9.4 dev: true - /ts-node@10.9.1(@types/node@18.11.17)(typescript@4.9.4): + /ts-node@10.9.1(@types/node@18.16.1)(typescript@4.9.4): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -12447,7 +13347,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.3 - '@types/node': 18.11.17 + '@types/node': 18.16.1 acorn: 8.8.1 acorn-walk: 8.2.0 arg: 4.1.3 @@ -12459,12 +13359,12 @@ packages: yn: 3.1.1 dev: true - /tsconfig-paths@3.14.1: - resolution: {integrity: sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==} + /tsconfig-paths@3.14.2: + resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==} dependencies: '@types/json5': 0.0.29 - json5: 1.0.1 - minimist: 1.2.7 + json5: 1.0.2 + minimist: 1.2.8 strip-bom: 3.0.0 dev: true @@ -12472,8 +13372,8 @@ packages: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true - /tslib@2.4.1: - resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} + /tslib@2.5.0: + resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} dev: true /tsort@0.0.1: @@ -12500,9 +13400,10 @@ packages: fsevents: 2.3.2 dev: true - /tty-table@4.1.6: - resolution: {integrity: sha512-kRj5CBzOrakV4VRRY5kUWbNYvo/FpOsz65DzI5op9P+cHov3+IqPbo1JE1ZnQGkHdZgNFDsrEjrfqqy/Ply9fw==} + /tty-table@4.2.1: + resolution: {integrity: sha512-xz0uKo+KakCQ+Dxj1D/tKn2FSyreSYWzdkL/BYhgN6oMW808g8QRMuh1atAV9fjTPbWBjfbkKQpI/5rEcnAc7g==} engines: {node: '>=8.0.0'} + hasBin: true dependencies: chalk: 4.1.2 csv: 5.5.3 @@ -12510,7 +13411,7 @@ packages: smartwrap: 2.0.2 strip-ansi: 6.0.1 wcwidth: 1.0.1 - yargs: 17.6.2 + yargs: 17.7.1 dev: true /tunnel-agent@0.6.0: @@ -12614,7 +13515,7 @@ packages: js-sha3: 0.8.0 lodash: 4.17.21 mkdirp: 1.0.4 - prettier: 2.8.1 + prettier: 2.8.8 ts-command-line-args: 2.3.1 ts-essentials: 7.0.3(typescript@4.9.4) typescript: 4.9.4 @@ -12622,6 +13523,14 @@ packages: - supports-color dev: true + /typed-array-length@1.0.4: + resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + dependencies: + call-bind: 1.0.2 + for-each: 0.3.3 + is-typed-array: 1.1.10 + dev: true + /typed-error@3.2.1: resolution: {integrity: sha512-XlUv4JMrT2dpN0c4Vm3lOm88ga21Z6pNJUmjejRz/mkh6sdBtkMwyRf4fF+yhRGZgfgWam31Lkxu11GINKiBTQ==} engines: {node: '>=6.0.0', npm: '>=3.0.0'} @@ -12638,6 +13547,20 @@ packages: engines: {node: '>=4.2.0'} dev: true + /typeson-registry@1.0.0-alpha.39: + resolution: {integrity: sha512-NeGDEquhw+yfwNhguLPcZ9Oj0fzbADiX4R0WxvoY8nGhy98IbzQy1sezjoEFWOywOboj/DWehI+/aUlRVrJnnw==} + engines: {node: '>=10.0.0'} + dependencies: + base64-arraybuffer-es6: 0.7.0 + typeson: 6.1.0 + whatwg-url: 8.7.0 + dev: true + + /typeson@6.1.0: + resolution: {integrity: sha512-6FTtyGr8ldU0pfbvW/eOZrEtEkczHRUtduBnA90Jh9kMPCiFNnXIon3vF41N0S4tV1HHQt4Hk1j4srpESziCaA==} + engines: {node: '>=0.1.14'} + dev: true + /typical@4.0.0: resolution: {integrity: sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==} engines: {node: '>=8'} @@ -12675,6 +13598,13 @@ packages: busboy: 1.6.0 dev: true + /undici@5.22.0: + resolution: {integrity: sha512-fR9RXCc+6Dxav4P9VV/sp5w3eFiSdOjJYsbtWfd4s5L5C4ogyuVpdKIVHeW0vV1MloM65/f7W45nR9ZxwVdyiA==} + engines: {node: '>=14.0'} + dependencies: + busboy: 1.6.0 + dev: true + /unicode-canonical-property-names-ecmascript@2.0.0: resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} engines: {node: '>=4'} @@ -12754,6 +13684,17 @@ packages: picocolors: 1.0.0 dev: true + /update-browserslist-db@1.0.11(browserslist@4.21.5): + resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.21.5 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + /update-notifier@5.1.0: resolution: {integrity: sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==} engines: {node: '>=10'} @@ -12769,7 +13710,7 @@ packages: is-yarn-global: 0.3.0 latest-version: 5.1.0 pupa: 2.1.1 - semver: 7.3.8 + semver: 7.5.0 semver-diff: 3.1.1 xdg-basedir: 4.0.0 dev: true @@ -12777,7 +13718,7 @@ packages: /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: - punycode: 2.1.1 + punycode: 2.3.0 dev: true /urix@0.1.0: @@ -12874,7 +13815,7 @@ packages: /validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} dependencies: - spdx-correct: 3.1.1 + spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 dev: true @@ -12905,7 +13846,7 @@ packages: resolution: {integrity: sha512-0rvxpB8P8Shm4wX2EKOiMp7H2zq+HUE/UwodY0pCZXs9IffIKZq6vUti5OgkVCTakKo9e/fgO4X1fkwfjWxE3Q==} engines: {node: '>=6.0'} dependencies: - acorn: 8.8.1 + acorn: 8.8.2 acorn-walk: 8.2.0 dev: true @@ -12956,6 +13897,20 @@ packages: - utf-8-validate dev: true + /web3-bzz@1.9.0: + resolution: {integrity: sha512-9Zli9dikX8GdHwBb5/WPzpSVuy3EWMKY3P4EokCQra31fD7DLizqAAaTUsFwnK7xYkw5ogpHgelw9uKHHzNajg==} + engines: {node: '>=8.0.0'} + requiresBuild: true + dependencies: + '@types/node': 12.20.55 + got: 12.1.0 + swarm-js: 0.1.42 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: true + /web3-core-helpers@1.8.1: resolution: {integrity: sha512-ClzNO6T1S1gifC+BThw0+GTfcsjLEY8T1qUp6Ly2+w4PntAdNtKahxWKApWJ0l9idqot/fFIDXwO3Euu7I0Xqw==} engines: {node: '>=8.0.0'} @@ -12964,6 +13919,14 @@ packages: web3-utils: 1.8.1 dev: true + /web3-core-helpers@1.9.0: + resolution: {integrity: sha512-NeJzylAp9Yj9xAt2uTT+kyug3X0DLnfBdnAcGZuY6HhoNPDIfQRA9CkJjLngVRlGTLZGjNp9x9eR+RyZQgUlXg==} + engines: {node: '>=8.0.0'} + dependencies: + web3-eth-iban: 1.9.0 + web3-utils: 1.9.0 + dev: true + /web3-core-method@1.8.1: resolution: {integrity: sha512-oYGRodktfs86NrnFwaWTbv2S38JnpPslFwSSARwFv4W9cjbGUW3LDeA5MKD/dRY+ssZ5OaekeMsUCLoGhX68yA==} engines: {node: '>=8.0.0'} @@ -12975,6 +13938,17 @@ packages: web3-utils: 1.8.1 dev: true + /web3-core-method@1.9.0: + resolution: {integrity: sha512-sswbNsY2xRBBhGeaLt9c/eDc+0yDDhi6keUBAkgIRa9ueSx/VKzUY9HMqiV6bXDcGT2fJyejq74FfEB4lc/+/w==} + engines: {node: '>=8.0.0'} + dependencies: + '@ethersproject/transactions': 5.7.0 + web3-core-helpers: 1.9.0 + web3-core-promievent: 1.9.0 + web3-core-subscriptions: 1.9.0 + web3-utils: 1.9.0 + dev: true + /web3-core-promievent@1.8.1: resolution: {integrity: sha512-9mxqHlgB0MrZI4oUIRFkuoJMNj3E7btjrMv3sMer/Z9rYR1PfoSc1aAokw4rxKIcAh+ylVtd/acaB2HKB7aRPg==} engines: {node: '>=8.0.0'} @@ -12982,6 +13956,13 @@ packages: eventemitter3: 4.0.4 dev: true + /web3-core-promievent@1.9.0: + resolution: {integrity: sha512-PHG1Mn23IGwMZhnPDN8dETKypqsFbHfiyRqP+XsVMPmTHkVfzDQTCBU/c2r6hUktBDoGKut5xZQpGfhFk71KbQ==} + engines: {node: '>=8.0.0'} + dependencies: + eventemitter3: 4.0.4 + dev: true + /web3-core-requestmanager@1.8.1: resolution: {integrity: sha512-x+VC2YPPwZ1khvqA6TA69LvfFCOZXsoUVOxmTx/vIN22PrY9KzKhxcE7pBSiGhmab1jtmRYXUbcQSVpAXqL8cw==} engines: {node: '>=8.0.0'} @@ -12996,6 +13977,20 @@ packages: - supports-color dev: true + /web3-core-requestmanager@1.9.0: + resolution: {integrity: sha512-hcJ5PCtTIJpj+8qWxoseqlCovDo94JJjTX7dZOLXgwp8ah7E3WRYozhGyZocerx+KebKyg1mCQIhkDpMwjfo9Q==} + engines: {node: '>=8.0.0'} + dependencies: + util: 0.12.5 + web3-core-helpers: 1.9.0 + web3-providers-http: 1.9.0 + web3-providers-ipc: 1.9.0 + web3-providers-ws: 1.9.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /web3-core-subscriptions@1.8.1: resolution: {integrity: sha512-bmCMq5OeA3E2vZUh8Js1HcJbhwtsE+yeMqGC4oIZB3XsL5SLqyKLB/pU+qUYqQ9o4GdcrFTDPhPg1bgvf7p1Pw==} engines: {node: '>=8.0.0'} @@ -13004,6 +13999,14 @@ packages: web3-core-helpers: 1.8.1 dev: true + /web3-core-subscriptions@1.9.0: + resolution: {integrity: sha512-MaIo29yz7hTV8X8bioclPDbHFOVuHmnbMv+D3PDH12ceJFJAXGyW8GL5KU1DYyWIj4TD1HM4WknyVA/YWBiiLA==} + engines: {node: '>=8.0.0'} + dependencies: + eventemitter3: 4.0.4 + web3-core-helpers: 1.9.0 + dev: true + /web3-core@1.8.1: resolution: {integrity: sha512-LbRZlJH2N6nS3n3Eo9Y++25IvzMY7WvYnp4NM/Ajhh97dAdglYs6rToQ2DbL2RLvTYmTew4O/y9WmOk4nq9COw==} engines: {node: '>=8.0.0'} @@ -13020,6 +14023,22 @@ packages: - supports-color dev: true + /web3-core@1.9.0: + resolution: {integrity: sha512-DZ+TPmq/ZLlx4LSVzFgrHCP/QFpKDbGWO4HoquZSdu24cjk5SZ+FEU1SZB2OaK3/bgBh+25mRbmv8y56ysUu1w==} + engines: {node: '>=8.0.0'} + dependencies: + '@types/bn.js': 5.1.1 + '@types/node': 12.20.55 + bignumber.js: 9.1.1 + web3-core-helpers: 1.9.0 + web3-core-method: 1.9.0 + web3-core-requestmanager: 1.9.0 + web3-utils: 1.9.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /web3-eth-abi@1.8.1: resolution: {integrity: sha512-0mZvCRTIG0UhDhJwNQJgJxu4b4DyIpuMA0GTfqxqeuqzX4Q/ZvmoNurw0ExTfXaGPP82UUmmdkRi6FdZOx+C6w==} engines: {node: '>=8.0.0'} @@ -13028,6 +14047,14 @@ packages: web3-utils: 1.8.1 dev: true + /web3-eth-abi@1.9.0: + resolution: {integrity: sha512-0BLQ3FKMrzJkA930jOX3fMaybAyubk06HChclLpiR0NWmgWXm1tmBrJdkyRy2ZTZpmfuZc9xTFRfl0yZID1voA==} + engines: {node: '>=8.0.0'} + dependencies: + '@ethersproject/abi': 5.7.0 + web3-utils: 1.9.0 + dev: true + /web3-eth-accounts@1.8.1: resolution: {integrity: sha512-mgzxSYgN54/NsOFBO1Fq1KkXp1S5KlBvI/DlgvajU72rupoFMq6Cu6Yp9GUaZ/w2ij9PzEJuFJk174XwtfMCmg==} engines: {node: '>=8.0.0'} @@ -13048,6 +14075,25 @@ packages: - supports-color dev: true + /web3-eth-accounts@1.9.0: + resolution: {integrity: sha512-VeIZVevmnSll0AC1k5F/y398ZE89d1SRuYk8IewLUhL/tVAsFEsjl2SGgm0+aDcHmgPrkW+qsCJ+C7rWg/N4ZA==} + engines: {node: '>=8.0.0'} + dependencies: + '@ethereumjs/common': 2.5.0 + '@ethereumjs/tx': 3.3.2 + eth-lib: 0.2.8 + ethereumjs-util: 7.1.5 + scrypt-js: 3.0.1 + uuid: 9.0.0 + web3-core: 1.9.0 + web3-core-helpers: 1.9.0 + web3-core-method: 1.9.0 + web3-utils: 1.9.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /web3-eth-contract@1.8.1: resolution: {integrity: sha512-1wphnl+/xwCE2io44JKnN+ti3oa47BKRiVzvWd42icwRbcpFfRxH9QH+aQX3u8VZIISNH7dAkTWpGIIJgGFTmg==} engines: {node: '>=8.0.0'} @@ -13065,6 +14111,23 @@ packages: - supports-color dev: true + /web3-eth-contract@1.9.0: + resolution: {integrity: sha512-+j26hpSaEtAdUed0TN5rnc+YZOcjPxMjFX4ZBKatvFkImdbVv/tzTvcHlltubSpgb2ZLyZ89lSL6phKYwd2zNQ==} + engines: {node: '>=8.0.0'} + dependencies: + '@types/bn.js': 5.1.1 + web3-core: 1.9.0 + web3-core-helpers: 1.9.0 + web3-core-method: 1.9.0 + web3-core-promievent: 1.9.0 + web3-core-subscriptions: 1.9.0 + web3-eth-abi: 1.9.0 + web3-utils: 1.9.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /web3-eth-ens@1.8.1: resolution: {integrity: sha512-FT8xTI9uN8RxeBQa/W8pLa2aoFh4+EE34w7W2271LICKzla1dtLyb6XSdn48vsUcPmhWsTVk9mO9RTU0l4LGQQ==} engines: {node: '>=8.0.0'} @@ -13082,6 +14145,23 @@ packages: - supports-color dev: true + /web3-eth-ens@1.9.0: + resolution: {integrity: sha512-LOJZeN+AGe9arhuExnrPPFYQr4WSxXEkpvYIlst/joOEUNLDwfndHnJIK6PI5mXaYSROBtTx6erv+HupzGo7vA==} + engines: {node: '>=8.0.0'} + dependencies: + content-hash: 2.5.2 + eth-ens-namehash: 2.0.8 + web3-core: 1.9.0 + web3-core-helpers: 1.9.0 + web3-core-promievent: 1.9.0 + web3-eth-abi: 1.9.0 + web3-eth-contract: 1.9.0 + web3-utils: 1.9.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /web3-eth-iban@1.8.1: resolution: {integrity: sha512-DomoQBfvIdtM08RyMGkMVBOH0vpOIxSSQ+jukWk/EkMLGMWJtXw/K2c2uHAeq3L/VPWNB7zXV2DUEGV/lNE2Dg==} engines: {node: '>=8.0.0'} @@ -13090,6 +14170,14 @@ packages: web3-utils: 1.8.1 dev: true + /web3-eth-iban@1.9.0: + resolution: {integrity: sha512-jPAm77PuEs1kE/UrrBFJdPD2PN42pwfXA0gFuuw35bZezhskYML9W4QCxcqnUtceyEA4FUn7K2qTMuCk+23fog==} + engines: {node: '>=8.0.0'} + dependencies: + bn.js: 5.2.1 + web3-utils: 1.9.0 + dev: true + /web3-eth-personal@1.8.1: resolution: {integrity: sha512-myIYMvj7SDIoV9vE5BkVdon3pya1WinaXItugoii2VoTcQNPOtBxmYVH+XS5ErzCJlnxzphpQrkywyY64bbbCA==} engines: {node: '>=8.0.0'} @@ -13105,6 +14193,21 @@ packages: - supports-color dev: true + /web3-eth-personal@1.9.0: + resolution: {integrity: sha512-r9Ldo/luBqJlv1vCUEQnUS+C3a3ZdbYxVHyfDkj6RWMyCqqo8JE41HWE+pfa0RmB1xnGL2g8TbYcHcqItck/qg==} + engines: {node: '>=8.0.0'} + dependencies: + '@types/node': 12.20.55 + web3-core: 1.9.0 + web3-core-helpers: 1.9.0 + web3-core-method: 1.9.0 + web3-net: 1.9.0 + web3-utils: 1.9.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /web3-eth@1.8.1: resolution: {integrity: sha512-LgyzbhFqiFRd8M8sBXoFN4ztzOnkeckl3H/9lH5ek7AdoRMhBg7tYpYRP3E5qkhd/q+yiZmcUgy1AF6NHrC1wg==} engines: {node: '>=8.0.0'} @@ -13126,6 +14229,27 @@ packages: - supports-color dev: true + /web3-eth@1.9.0: + resolution: {integrity: sha512-c5gSWk9bLNr6VPATHmZ1n7LTIefIZQnJMzfnvkoBcIFGKJbGmsuRhv6lEXsKdAO/FlqYnSbaw3fOq1fVFiIOFQ==} + engines: {node: '>=8.0.0'} + dependencies: + web3-core: 1.9.0 + web3-core-helpers: 1.9.0 + web3-core-method: 1.9.0 + web3-core-subscriptions: 1.9.0 + web3-eth-abi: 1.9.0 + web3-eth-accounts: 1.9.0 + web3-eth-contract: 1.9.0 + web3-eth-ens: 1.9.0 + web3-eth-iban: 1.9.0 + web3-eth-personal: 1.9.0 + web3-net: 1.9.0 + web3-utils: 1.9.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /web3-net@1.8.1: resolution: {integrity: sha512-LyEJAwogdFo0UAXZqoSJGFjopdt+kLw0P00FSZn2yszbgcoI7EwC+nXiOsEe12xz4LqpYLOtbR7+gxgiTVjjHQ==} engines: {node: '>=8.0.0'} @@ -13138,7 +14262,19 @@ packages: - supports-color dev: true - /web3-provider-engine@16.0.4(@babel/core@7.20.5): + /web3-net@1.9.0: + resolution: {integrity: sha512-L+fDZFgrLM5Y15aonl2q6L+RvfaImAngmC0Jv45hV2FJ5IfRT0/2ob9etxZmvEBWvOpbqSvghfOhJIT3XZ37Pg==} + engines: {node: '>=8.0.0'} + dependencies: + web3-core: 1.9.0 + web3-core-method: 1.9.0 + web3-utils: 1.9.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + + /web3-provider-engine@16.0.4(@babel/core@7.21.4): resolution: {integrity: sha512-f5WxJ9+LTF+4aJo4tCOXtQ6SDytBtLkhvV+qh/9gImHAuG9sMr6utY0mn/pro1Rx7O3hbztBxvQKjGMdOo8muw==} engines: {node: '>=12.0.0'} dependencies: @@ -13146,7 +14282,7 @@ packages: async: 2.6.4 backoff: 2.5.0 clone: 2.1.2 - eth-block-tracker: 4.4.3(@babel/core@7.20.5) + eth-block-tracker: 4.4.3(@babel/core@7.21.4) eth-json-rpc-filters: 4.2.2 eth-json-rpc-infura: 5.1.0 eth-json-rpc-middleware: 6.0.0 @@ -13183,6 +14319,18 @@ packages: - encoding dev: true + /web3-providers-http@1.9.0: + resolution: {integrity: sha512-5+dMNDAE0rRFz6SJpfnBqlVi2J5bB/Ivr2SanMt2YUrkxW5t8betZbzVwRkTbwtUvkqgj3xeUQzqpOttiv+IqQ==} + engines: {node: '>=8.0.0'} + dependencies: + abortcontroller-polyfill: 1.7.5 + cross-fetch: 3.1.5 + es6-promise: 4.2.8 + web3-core-helpers: 1.9.0 + transitivePeerDependencies: + - encoding + dev: true + /web3-providers-ipc@1.8.1: resolution: {integrity: sha512-nw/W5nclvi+P2z2dYkLWReKLnocStflWqFl+qjtv0xn3MrUTyXMzSF0+61i77+16xFsTgzo4wS/NWIOVkR0EFA==} engines: {node: '>=8.0.0'} @@ -13191,6 +14339,14 @@ packages: web3-core-helpers: 1.8.1 dev: true + /web3-providers-ipc@1.9.0: + resolution: {integrity: sha512-cPXU93Du40HCylvjaa5x62DbnGqH+86HpK/+kMcFIzF6sDUBhKpag2tSbYhGbj7GMpfkmDTUiiMLdWnFV6+uBA==} + engines: {node: '>=8.0.0'} + dependencies: + oboe: 2.1.5 + web3-core-helpers: 1.9.0 + dev: true + /web3-providers-ws@1.8.1: resolution: {integrity: sha512-TNefIDAMpdx57+YdWpYZ/xdofS0P+FfKaDYXhn24ie/tH9G+AB+UBSOKnjN0KSadcRSCMBwGPRiEmNHPavZdsA==} engines: {node: '>=8.0.0'} @@ -13202,6 +14358,17 @@ packages: - supports-color dev: true + /web3-providers-ws@1.9.0: + resolution: {integrity: sha512-JRVsnQZ7j2k1a2yzBNHe39xqk1ijOv01dfIBFw52VeEkSRzvrOcsPIM/ttSyBuJqt70ntMxXY0ekCrqfleKH/w==} + engines: {node: '>=8.0.0'} + dependencies: + eventemitter3: 4.0.4 + web3-core-helpers: 1.9.0 + websocket: 1.0.34 + transitivePeerDependencies: + - supports-color + dev: true + /web3-shh@1.8.1: resolution: {integrity: sha512-sqHgarnfcY2Qt3PYS4R6YveHrDy7hmL09yeLLHHCI+RKirmjLVqV0rc5LJWUtlbYI+kDoa5gbgde489M9ZAC0g==} engines: {node: '>=8.0.0'} @@ -13216,6 +14383,20 @@ packages: - supports-color dev: true + /web3-shh@1.9.0: + resolution: {integrity: sha512-bIBZlralgz4ICCrwkefB2nPPJWfx28NuHIpjB7d9ADKynElubQuqudYhKtSEkKXACuME/BJm0pIFJcJs/gDnMg==} + engines: {node: '>=8.0.0'} + requiresBuild: true + dependencies: + web3-core: 1.9.0 + web3-core-method: 1.9.0 + web3-core-subscriptions: 1.9.0 + web3-net: 1.9.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: true + /web3-utils@1.8.1: resolution: {integrity: sha512-LgnM9p6V7rHHUGfpMZod+NST8cRfGzJ1BTXAyNo7A9cJX9LczBfSRxJp+U/GInYe9mby40t3v22AJdlELibnsQ==} engines: {node: '>=8.0.0'} @@ -13229,6 +14410,19 @@ packages: utf8: 3.0.0 dev: true + /web3-utils@1.9.0: + resolution: {integrity: sha512-p++69rCNNfu2jM9n5+VD/g26l+qkEOQ1m6cfRQCbH8ZRrtquTmrirJMgTmyOoax5a5XRYOuws14aypCOs51pdQ==} + engines: {node: '>=8.0.0'} + dependencies: + bn.js: 5.2.1 + ethereum-bloom-filters: 1.0.10 + ethereumjs-util: 7.1.5 + ethjs-unit: 0.1.6 + number-to-bn: 1.7.0 + randombytes: 2.1.0 + utf8: 3.0.0 + dev: true + /web3@1.8.1: resolution: {integrity: sha512-tAqFsQhGv340C9OgRJIuoScN7f7wa1tUvsnnDUMt9YE6J4gcm7TV2Uwv+KERnzvV+xgdeuULYpsioRRNKrUvoQ==} engines: {node: '>=8.0.0'} @@ -13248,6 +14442,25 @@ packages: - utf-8-validate dev: true + /web3@1.9.0: + resolution: {integrity: sha512-E9IvVy/d2ozfQQsCiV+zh/LmlZGv9fQxI0UedDVjm87yOKf4AYbBNEn1iWtHveiGzAk2CEMZMUzAZzaQNSSYog==} + engines: {node: '>=8.0.0'} + requiresBuild: true + dependencies: + web3-bzz: 1.9.0 + web3-core: 1.9.0 + web3-eth: 1.9.0 + web3-eth-personal: 1.9.0 + web3-net: 1.9.0 + web3-shh: 1.9.0 + web3-utils: 1.9.0 + transitivePeerDependencies: + - bufferutil + - encoding + - supports-color + - utf-8-validate + dev: true + /webextension-polyfill@0.10.0: resolution: {integrity: sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g==} dev: false @@ -13256,6 +14469,15 @@ packages: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} dev: true + /webidl-conversions@4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + dev: true + + /webidl-conversions@6.1.0: + resolution: {integrity: sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==} + engines: {node: '>=10.4'} + dev: true + /webpack-cli@4.10.0(webpack-dev-server@3.11.3)(webpack@5.75.0): resolution: {integrity: sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==} engines: {node: '>=10.13.0'} @@ -13459,6 +14681,15 @@ packages: webidl-conversions: 3.0.1 dev: true + /whatwg-url@8.7.0: + resolution: {integrity: sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==} + engines: {node: '>=10'} + dependencies: + lodash: 4.17.21 + tr46: 2.1.0 + webidl-conversions: 6.1.0 + dev: true + /which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} dependencies: @@ -13473,11 +14704,6 @@ packages: resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==} dev: true - /which-pm-runs@1.1.0: - resolution: {integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==} - engines: {node: '>=4'} - dev: true - /which-pm@2.0.0: resolution: {integrity: sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w==} engines: {node: '>=8.15'} @@ -13500,6 +14726,7 @@ packages: /which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true dependencies: isexe: 2.0.0 dev: true @@ -13507,6 +14734,7 @@ packages: /which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} + hasBin: true dependencies: isexe: 2.0.0 dev: true @@ -13735,11 +14963,6 @@ packages: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true - /yaml@1.10.2: - resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} - engines: {node: '>= 6'} - dev: true - /yargs-parser@13.1.2: resolution: {integrity: sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==} dependencies: @@ -13838,6 +15061,19 @@ packages: yargs-parser: 21.1.1 dev: true + /yargs@17.7.1: + resolution: {integrity: sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + dev: true + /yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} dependencies: