Add a payments plugin to GQL server (#391)

* Add a custom payments plugin in server process

* Fix voucher channel read in voucher subscription

* Use voucher hash and signature for payment authentication

* Use a LRU hash map for storing received vouchers

* Avoid hoisting level types to resolve build errors

* Upgrade ts-nitro packages

* Add payments related logs

* Upgrade package versions

* Always allow latestBlock query

* Upgrade ts-nitro packages
This commit is contained in:
prathamesh0 2023-07-20 17:22:48 +05:30 committed by GitHub
parent 11930c0107
commit eec1965980
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 369 additions and 34 deletions

View File

@ -2,7 +2,7 @@
"packages": [
"packages/*"
],
"version": "0.2.46",
"version": "0.2.47",
"npmClient": "yarn",
"useWorkspaces": true,
"command": {

View File

@ -7,7 +7,8 @@
"packages/*"
],
"nohoist": [
"**/@graphprotocol/graph-ts"
"**/@graphprotocol/graph-ts",
"**/@types/level"
]
},
"devDependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@cerc-io/cache",
"version": "0.2.46",
"version": "0.2.47",
"description": "Generic object cache",
"main": "dist/index.js",
"scripts": {

View File

@ -1,6 +1,6 @@
{
"name": "@cerc-io/cli",
"version": "0.2.46",
"version": "0.2.47",
"main": "dist/index.js",
"license": "AGPL-3.0",
"scripts": {
@ -11,8 +11,8 @@
"chat": "DEBUG='vulcanize:*, laconic:*' node dist/chat.js"
},
"dependencies": {
"@cerc-io/peer": "^0.2.46",
"@cerc-io/util": "^0.2.46",
"@cerc-io/peer": "^0.2.47",
"@cerc-io/util": "^0.2.47",
"@ethersproject/providers": "^5.4.4",
"@graphql-tools/utils": "^9.1.1",
"@ipld/dag-cbor": "^8.0.0",

View File

@ -25,7 +25,8 @@ import {
EventWatcher,
GraphWatcherInterface,
Config,
P2PConfig
P2PConfig,
PaymentsManager
} from '@cerc-io/util';
import { TypeSource } from '@graphql-tools/utils';
import {
@ -111,7 +112,8 @@ export class ServerCmd {
async exec (
createResolvers: (indexer: IndexerInterface, eventWatcher: EventWatcher) => Promise<any>,
typeDefs: TypeSource,
parseLibp2pMessage?: (peerId: string, data: any) => void
parseLibp2pMessage?: (peerId: string, data: any) => void,
paymentsManager?: PaymentsManager
): Promise<{
app: Application,
server: ApolloServer
@ -136,7 +138,7 @@ export class ServerCmd {
// Create an Express app
const app: Application = express();
const server = await createAndStartServer(app, typeDefs, resolvers, config.server);
const server = await createAndStartServer(app, typeDefs, resolvers, config.server, paymentsManager);
await startGQLMetricsServer(config);

View File

@ -1,6 +1,6 @@
{
"name": "@cerc-io/codegen",
"version": "0.2.46",
"version": "0.2.47",
"description": "Code generator",
"private": true,
"main": "index.js",
@ -20,7 +20,7 @@
},
"homepage": "https://github.com/cerc-io/watcher-ts#readme",
"dependencies": {
"@cerc-io/util": "^0.2.46",
"@cerc-io/util": "^0.2.47",
"@graphql-tools/load-files": "^6.5.2",
"@poanet/solidity-flattener": "https://github.com/vulcanize/solidity-flattener.git",
"@solidity-parser/parser": "^0.13.2",

View File

@ -41,12 +41,12 @@
"homepage": "https://github.com/cerc-io/watcher-ts#readme",
"dependencies": {
"@apollo/client": "^3.3.19",
"@cerc-io/cli": "^0.2.46",
"@cerc-io/ipld-eth-client": "^0.2.46",
"@cerc-io/solidity-mapper": "^0.2.46",
"@cerc-io/util": "^0.2.46",
"@cerc-io/cli": "^0.2.47",
"@cerc-io/ipld-eth-client": "^0.2.47",
"@cerc-io/solidity-mapper": "^0.2.47",
"@cerc-io/util": "^0.2.47",
{{#if (subgraphPath)}}
"@cerc-io/graph-node": "^0.2.46",
"@cerc-io/graph-node": "^0.2.47",
{{/if}}
"@ethersproject/providers": "^5.4.4",
"apollo-type-bigint": "^0.1.3",

View File

@ -1,10 +1,10 @@
{
"name": "@cerc-io/graph-node",
"version": "0.2.46",
"version": "0.2.47",
"main": "dist/index.js",
"license": "AGPL-3.0",
"devDependencies": {
"@cerc-io/solidity-mapper": "^0.2.46",
"@cerc-io/solidity-mapper": "^0.2.47",
"@ethersproject/providers": "^5.4.4",
"@graphprotocol/graph-ts": "^0.22.0",
"@nomiclabs/hardhat-ethers": "^2.0.2",
@ -51,9 +51,9 @@
"dependencies": {
"@apollo/client": "^3.3.19",
"@cerc-io/assemblyscript": "0.19.10-watcher-ts-0.1.2",
"@cerc-io/cache": "^0.2.46",
"@cerc-io/ipld-eth-client": "^0.2.46",
"@cerc-io/util": "^0.2.46",
"@cerc-io/cache": "^0.2.47",
"@cerc-io/ipld-eth-client": "^0.2.47",
"@cerc-io/util": "^0.2.47",
"@types/json-diff": "^0.5.2",
"@types/yargs": "^17.0.0",
"bn.js": "^4.11.9",

View File

@ -1,6 +1,6 @@
{
"name": "@cerc-io/ipld-eth-client",
"version": "0.2.46",
"version": "0.2.47",
"description": "IPLD ETH Client",
"main": "dist/index.js",
"scripts": {
@ -20,7 +20,7 @@
"homepage": "https://github.com/cerc-io/watcher-ts#readme",
"dependencies": {
"@apollo/client": "^3.7.1",
"@cerc-io/cache": "^0.2.46",
"@cerc-io/cache": "^0.2.47",
"cross-fetch": "^3.1.4",
"debug": "^4.3.1",
"ethers": "^5.4.4",

View File

@ -1,6 +1,6 @@
{
"name": "@cerc-io/peer",
"version": "0.2.46",
"version": "0.2.47",
"description": "libp2p module",
"main": "dist/index.js",
"exports": "./dist/index.js",

View File

@ -1,10 +1,10 @@
{
"name": "@cerc-io/solidity-mapper",
"version": "0.2.46",
"version": "0.2.47",
"main": "dist/index.js",
"license": "AGPL-3.0",
"devDependencies": {
"@cerc-io/ipld-eth-client": "^0.2.46",
"@cerc-io/ipld-eth-client": "^0.2.47",
"@ethersproject/abi": "^5.3.0",
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-waffle": "^2.0.1",

View File

@ -1,6 +1,6 @@
{
"name": "@cerc-io/test",
"version": "0.2.46",
"version": "0.2.47",
"main": "dist/index.js",
"license": "AGPL-3.0",
"private": true,

View File

@ -1,6 +1,6 @@
{
"name": "@cerc-io/tracing-client",
"version": "0.2.46",
"version": "0.2.47",
"description": "ETH VM tracing client",
"main": "dist/index.js",
"scripts": {

View File

@ -1,11 +1,14 @@
{
"name": "@cerc-io/util",
"version": "0.2.46",
"version": "0.2.47",
"main": "dist/index.js",
"license": "AGPL-3.0",
"dependencies": {
"@apollo/utils.keyvaluecache": "^1.0.1",
"@cerc-io/solidity-mapper": "^0.2.46",
"@cerc-io/nitro-client": "^0.1.3",
"@cerc-io/nitro-util": "^0.1.3",
"@cerc-io/solidity-mapper": "^0.2.47",
"@cerc-io/ts-channel": "1.0.3-ts-nitro-0.1.1",
"@ethersproject/providers": "^5.4.4",
"@graphql-tools/schema": "^9.0.10",
"@graphql-tools/utils": "^9.1.1",
@ -26,6 +29,7 @@
"js-yaml": "^4.1.0",
"json-bigint": "^1.0.0",
"lodash": "^4.17.21",
"lru-cache": "^10.0.0",
"multiformats": "^9.4.8",
"pg": "^8.5.1",
"pg-boss": "^6.1.0",
@ -37,8 +41,8 @@
"yargs": "^17.0.1"
},
"devDependencies": {
"@cerc-io/cache": "^0.2.46",
"@cerc-io/ipld-eth-client": "^0.2.46",
"@cerc-io/cache": "^0.2.47",
"@cerc-io/ipld-eth-client": "^0.2.47",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@types/express": "^4.17.14",
"@types/fs-extra": "^9.0.11",

View File

@ -23,3 +23,4 @@ export * from './graph/database';
export * from './graph/utils';
export * from './graph/state-utils';
export * from './graph/types';
export * from './payments';

View File

@ -0,0 +1,210 @@
import debug from 'debug';
import { ethers } from 'ethers';
import { LRUCache } from 'lru-cache';
import { FieldNode } from 'graphql';
import { ApolloServerPlugin, GraphQLResponse, GraphQLRequestContext } from 'apollo-server-plugin-base';
import { Response as HTTPResponse } from 'apollo-server-env';
import Channel from '@cerc-io/ts-channel';
import type { ReadWriteChannel } from '@cerc-io/ts-channel';
import type { Client, Signature, Voucher } from '@cerc-io/nitro-client';
import { recoverEthereumMessageSigner, getSignatureFromEthersSignature } from '@cerc-io/nitro-client';
import { hex2Bytes } from '@cerc-io/nitro-util';
const log = debug('laconic:payments');
const IntrospectionQuery = 'IntrospectionQuery';
const HASH_HEADER_KEY = 'hash';
const SIG_HEADER_KEY = 'sig';
const ERR_FREE_QUOTA_EXHUASTED = 'Free quota exhausted';
const ERR_PAYMENT_NOT_RECEIVED = 'Payment not received';
const HTTP_CODE_PAYMENT_NOT_RECEIVED = 402; // Payment required
const ERR_HEADER_MISSING = 'Header for hash or sig not set';
const HTTP_CODE_HEADER_MISSING = 400; // Bad request
const EMPTY_VOUCHER_HASH = '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'; // keccak256('0x')
// TODO: Configure
const LRU_CACHE_MAX_ACCOUNT_COUNT = 1000;
const LRU_CACHE_ACCOUNT_TTL = 30 * 60 * 1000; // 30mins
const LRU_CACHE_MAX_VOUCHER_COUNT = 1000;
const LRU_CACHE_VOUCHER_TTL = 5 * 60 * 1000; // 5mins
const FREE_QUERY_LIMIT = 10;
const FREE_QUERIES = ['latestBlock'];
export class PaymentsManager {
// TODO: Persist data
private remainingFreeQueriesMap: Map<string, number> = new Map();
private receivedVouchers: LRUCache<string, LRUCache<string, Voucher>>;
private stopSubscriptionLoop: ReadWriteChannel<void>;
// TODO: Read query rate map from config
// TODO: Add a method to get rate for a query
constructor () {
this.receivedVouchers = new LRUCache<string, LRUCache<string, Voucher>>({
max: LRU_CACHE_MAX_ACCOUNT_COUNT,
ttl: LRU_CACHE_ACCOUNT_TTL
});
this.stopSubscriptionLoop = Channel();
}
async subscribeToVouchers (client: Client): Promise<void> {
const receivedVouchersChannel = client.receivedVouchers();
log('Starting voucher subscription...');
while (true) {
switch (await Channel.select([
receivedVouchersChannel.shift(),
this.stopSubscriptionLoop.shift()
])) {
case receivedVouchersChannel: {
const voucher = receivedVouchersChannel.value();
if (voucher === undefined) {
log('Voucher channel closed, stopping voucher subscription');
return;
}
const associatedPaymentChannel = await client.getPaymentChannel(voucher.channelId);
const payer = associatedPaymentChannel.balance.payer;
log(`Received a payment voucher from ${payer}`);
let vouchersMap = this.receivedVouchers.get(payer);
if (!vouchersMap) {
vouchersMap = new LRUCache<string, Voucher>({
max: LRU_CACHE_MAX_VOUCHER_COUNT,
ttl: LRU_CACHE_VOUCHER_TTL
});
this.receivedVouchers.set(payer, vouchersMap);
}
vouchersMap.set(voucher.hash(), voucher);
break;
}
case this.stopSubscriptionLoop:
log('Stop signal received, stopping voucher subscription');
return;
}
}
}
async unSubscribeVouchers (): Promise<void> {
await this.stopSubscriptionLoop.close();
}
async allowRequest (voucherHash: string, voucherSig: string): Promise<[boolean, string]> {
const senderAddress = getSenderAddress(voucherHash, voucherSig);
if (voucherHash === EMPTY_VOUCHER_HASH) {
let remainingFreeQueries = this.remainingFreeQueriesMap.get(senderAddress);
if (remainingFreeQueries === undefined) {
remainingFreeQueries = FREE_QUERY_LIMIT;
}
// Check if user has exhausted their free query limit
if (remainingFreeQueries > 0) {
log(`Serving a free query for ${senderAddress}`);
this.remainingFreeQueriesMap.set(senderAddress, remainingFreeQueries - 1);
return [true, ''];
}
log(`Rejecting query from ${senderAddress}, user has exhausted their free quota`);
return [false, ERR_FREE_QUOTA_EXHUASTED];
}
// Check for payment voucher received from the Nitro account
const paymentVoucherRecived = await this.authenticateVoucherForSender(voucherHash, senderAddress);
if (paymentVoucherRecived) {
log(`Serving a paid query for ${senderAddress}`);
return [true, ''];
} else {
log(`Rejecting query from ${senderAddress}, payment voucher not received`);
return [false, ERR_PAYMENT_NOT_RECEIVED];
}
}
private async authenticateVoucherForSender (voucherHash:string, senderAddress: string): Promise<boolean> {
const vouchersMap = this.receivedVouchers.get(senderAddress);
if (!vouchersMap) {
return false;
}
const receivedVoucher = vouchersMap.get(voucherHash);
if (receivedVoucher) {
vouchersMap.delete(voucherHash);
return true;
}
return false;
}
}
export const paymentsPlugin = (paymentsManager?: PaymentsManager): ApolloServerPlugin => {
return {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async requestDidStart (requestContext: GraphQLRequestContext) {
return {
async responseForOperation (requestContext: GraphQLRequestContext): Promise<GraphQLResponse | null> {
// Continue if payments is not setup or it's an introspection query
if (!paymentsManager || requestContext.operationName === IntrospectionQuery) {
return null;
}
const hash = requestContext.request.http?.headers.get(HASH_HEADER_KEY);
const sig = requestContext.request.http?.headers.get(SIG_HEADER_KEY);
if (hash == null || sig == null) {
return {
errors: [{ message: ERR_HEADER_MISSING }],
http: new HTTPResponse(undefined, {
headers: requestContext.response?.http?.headers,
status: HTTP_CODE_HEADER_MISSING
})
};
}
const querySelections = requestContext.operation?.selectionSet.selections
.map((selection) => (selection as FieldNode).name.value);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for await (const querySelection of querySelections ?? []) {
// TODO: Charge according to the querySelection
if (FREE_QUERIES.includes(querySelection)) {
continue;
}
const [allowRequest, rejectionMessage] = await paymentsManager.allowRequest(hash, sig);
if (!allowRequest) {
const failResponse: GraphQLResponse = {
errors: [{ message: rejectionMessage }],
http: new HTTPResponse(undefined, {
headers: requestContext.response?.http?.headers,
status: HTTP_CODE_PAYMENT_NOT_RECEIVED
})
};
return failResponse;
}
}
return null;
}
};
}
};
};
const getSenderAddress = (hash: string, sig: string): string => {
const splitSig = ethers.utils.splitSignature(sig);
const signature: Signature = getSignatureFromEthersSignature(splitSig);
return recoverEthereumMessageSigner(hex2Bytes(hash), signature);
};

View File

@ -14,6 +14,7 @@ import { makeExecutableSchema } from '@graphql-tools/schema';
import { DEFAULT_MAX_GQL_CACHE_SIZE } from './constants';
import { ServerConfig } from './config';
import { PaymentsManager, paymentsPlugin } from './payments';
const log = debug('vulcanize:server');
@ -21,7 +22,8 @@ export const createAndStartServer = async (
app: Application,
typeDefs: TypeSource,
resolvers: any,
serverConfig: ServerConfig
serverConfig: ServerConfig,
paymentsManager?: PaymentsManager
): Promise<ApolloServer> => {
const { host, port, gqlCache: gqlCacheConfig, maxSimultaneousRequests, maxRequestQueueLimit } = serverConfig;
@ -64,6 +66,8 @@ export const createAndStartServer = async (
};
}
},
// Custom payments plugin
paymentsPlugin(paymentsManager),
// GQL response cache plugin
responseCachePlugin(),
ApolloServerPluginLandingPageLocalDefault({ embed: true })

115
yarn.lock
View File

@ -350,6 +350,42 @@
wherearewe "^2.0.0"
xsalsa20 "^1.1.0"
"@cerc-io/nitro-client@^0.1.3":
version "0.1.3"
resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fnitro-client/-/0.1.3/nitro-client-0.1.3.tgz#a536da9f6c1bc0f95cd3c4ecc2358be5f36bc8f1"
integrity sha512-fZ8+Diz5tDrBz7t56X5R9ul54FE7tLmyAB//5NY2OBncjthpTmkF0kmQvO2KVyQGuriVTe7FbqwJMtIRWKxWEQ==
dependencies:
"@cerc-io/libp2p" "0.42.2-laconic-0.1.3"
"@cerc-io/nitro-util" "^0.1.3"
"@cerc-io/peer" "^0.2.46"
"@cerc-io/ts-channel" "1.0.3-ts-nitro-0.1.1"
"@libp2p/crypto" "^1.0.4"
"@libp2p/tcp" "^6.0.0"
"@multiformats/multiaddr" "^11.1.4"
"@statechannels/exit-format" "^0.2.0"
"@statechannels/nitro-protocol" "^2.0.0-alpha.4"
assert "^2.0.0"
debug "^4.3.4"
ethers "^5.7.2"
it-pipe "^2.0.5"
level "^8.0.0"
lodash "^4.17.21"
promjs "^0.4.2"
uint8arrays "^4.0.3"
"@cerc-io/nitro-util@^0.1.3":
version "0.1.3"
resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fnitro-util/-/0.1.3/nitro-util-0.1.3.tgz#1c640221c3936203e8ccf492e6374b50502d60d3"
integrity sha512-NTg8RO/3bzGLZie3pavcJJzLeL9gI1RLxwEMVBtRzsZfniDfsu7CTasoEX+hKgyBiZstEoMtGbfpwVmM1IEQBA==
dependencies:
assert "^2.0.0"
debug "^4.3.4"
ethers "^5.7.2"
it-pipe "^3.0.1"
json-bigint "^1.0.0"
lodash "^4.17.21"
uint8arrays "^4.0.3"
"@cerc-io/prometheus-metrics@1.1.4":
version "1.1.4"
resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fprometheus-metrics/-/1.1.4/prometheus-metrics-1.1.4.tgz#51006b0b5bf6168394390c78072a1c0bb2b02f28"
@ -362,6 +398,11 @@
it-stream-types "^1.0.4"
promjs "^0.4.2"
"@cerc-io/ts-channel@1.0.3-ts-nitro-0.1.1":
version "1.0.3-ts-nitro-0.1.1"
resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fts-channel/-/1.0.3-ts-nitro-0.1.1/ts-channel-1.0.3-ts-nitro-0.1.1.tgz#0768781313a167295c0bf21307f47e02dc17e936"
integrity sha512-2jFICUSyffuZ+8+qRhXuLSJq4GJ6Y02wxiXoubH0Kzv2lIKkJtWICY1ZQQhtXAvP0ncAQB85WJHqtqwH8l7J3Q==
"@chainsafe/is-ip@^2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@chainsafe/is-ip/-/is-ip-2.0.1.tgz#62cb285669d91f88fd9fa285048dde3882f0993b"
@ -2353,6 +2394,17 @@
it-stream-types "^1.0.4"
uint8arraylist "^2.1.2"
"@libp2p/interface-connection@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@libp2p/interface-connection/-/interface-connection-4.0.0.tgz#fcc830ca891820fac89a4c6bd4fcc2df4874f49b"
integrity sha512-6xx/NmEc84HX7QmsjSC3hHredQYjHv4Dkf4G27adAPf+qN+vnPxmQ7gaTnk243a0++DOFTbZ2gKX/15G2B6SRg==
dependencies:
"@libp2p/interface-peer-id" "^2.0.0"
"@libp2p/interfaces" "^3.0.0"
"@multiformats/multiaddr" "^12.0.0"
it-stream-types "^1.0.4"
uint8arraylist "^2.1.2"
"@libp2p/interface-connection@^5.0.0", "@libp2p/interface-connection@^5.1.0":
version "5.1.1"
resolved "https://registry.yarnpkg.com/@libp2p/interface-connection/-/interface-connection-5.1.1.tgz#da0572c76da43629d52b8bec6cd092143fae421d"
@ -2790,6 +2842,21 @@
uint8arraylist "^2.0.0"
uint8arrays "^4.0.2"
"@libp2p/tcp@^6.0.0":
version "6.2.2"
resolved "https://registry.yarnpkg.com/@libp2p/tcp/-/tcp-6.2.2.tgz#9262e284037f0951aca22f0fb3d488e3515ff6fd"
integrity sha512-5pLQDSUI+6qtAvh7pYgjqXFuFqzZ/AGL3BSX4C2oa+vWGIbooTZK3Mizp+iO0yHomVJ1y3V8AXXH8ddWdFqDpQ==
dependencies:
"@libp2p/interface-connection" "^4.0.0"
"@libp2p/interface-metrics" "^4.0.0"
"@libp2p/interface-transport" "^2.0.0"
"@libp2p/interfaces" "^3.2.0"
"@libp2p/logger" "^2.0.0"
"@libp2p/utils" "^3.0.2"
"@multiformats/mafmt" "^12.0.0"
"@multiformats/multiaddr" "^12.0.0"
stream-to-it "^0.2.2"
"@libp2p/topology@^4.0.0":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@libp2p/topology/-/topology-4.0.1.tgz#8efab229ed32d30cfa6c4a371e8022011c0ff6f9"
@ -3337,11 +3404,21 @@
dependencies:
"@octokit/openapi-types" "^7.2.3"
"@openzeppelin/contracts@4.6.0":
version "4.6.0"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.6.0.tgz#c91cf64bc27f573836dba4122758b4743418c1b3"
integrity sha512-8vi4d50NNya/bQqCmaVzvHNmwHvS0OBKb7HNtuNwEE3scXWrP31fKQoGxNMT+KbzmrNZzatE3QK5p2gFONI/hg==
"@openzeppelin/contracts@^4.3.2":
version "4.8.2"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.8.2.tgz#d815ade0027b50beb9bcca67143c6bcc3e3923d6"
integrity sha512-kEUOgPQszC0fSYWpbh2kT94ltOJwj1qfT2DWo+zVttmGmf97JZ99LspePNaeeaLhCImaHVeBbjaQFZQn7+Zc5g==
"@openzeppelin/contracts@^4.7.3":
version "4.9.2"
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.2.tgz#1cb2d5e4d3360141a17dbc45094a8cad6aac16c1"
integrity sha512-mO+y6JaqXjWeMh9glYVzVu8HYPGknAAnWyxTRhGeckOruyXQMNnlcW6w/Dx9ftLeIQk6N+ZJFuVmTwF7lEIFrg==
"@poanet/solidity-flattener@https://github.com/vulcanize/solidity-flattener.git":
version "3.0.6"
resolved "https://github.com/vulcanize/solidity-flattener.git#144ef6cda8823f4a5e48cb4f615be87a32e2dcbc"
@ -3664,6 +3741,24 @@
"@stablelib/random" "^1.0.2"
"@stablelib/wipe" "^1.0.1"
"@statechannels/exit-format@^0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@statechannels/exit-format/-/exit-format-0.2.0.tgz#b9362816c2a23d59e429b86b310021de1031a387"
integrity sha512-i+HIPy2P6ddwT/uP0O6AiTmBRTQ9+vLmLnfJtvXmtpTsB8OT1R9Jjj5iVKowGcWk+cg8koEtQuQDMxfrHq7LaQ==
dependencies:
"@openzeppelin/contracts" "4.6.0"
ethers "^5.1.4"
lodash "^4.17.21"
"@statechannels/nitro-protocol@^2.0.0-alpha.4":
version "2.0.0-alpha.4"
resolved "https://registry.yarnpkg.com/@statechannels/nitro-protocol/-/nitro-protocol-2.0.0-alpha.4.tgz#ff252e5cd7e73740ad25a35b2af7953b499db63d"
integrity sha512-Nmiyu0h7VjzoYPmzUOVWb7KzzdRRTjnF1YYVqsiHiCEVkjZVBtSK/J8RDL4Ozzn2MwyOIiA90Wlrl4KB+tB43g==
dependencies:
"@openzeppelin/contracts" "^4.7.3"
"@statechannels/exit-format" "^0.2.0"
"@typechain/ethers-v5" "^9.0.0"
"@szmarczak/http-timer@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421"
@ -3703,6 +3798,14 @@
dependencies:
ethers "^5.0.2"
"@typechain/ethers-v5@^9.0.0":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-9.0.0.tgz#6aa93bea7425c0463bd8a61eea3643540ef851bd"
integrity sha512-bAanuPl1L2itaUdMvor/QvwnIH+TM/CmG00q17Ilv3ZZMeJ2j8HcarhgJUZ9pBY1teBb85P8cC03dz3mSSx+tQ==
dependencies:
lodash "^4.17.15"
ts-essentials "^7.0.1"
"@types/abstract-leveldown@*":
version "7.2.1"
resolved "https://registry.yarnpkg.com/@types/abstract-leveldown/-/abstract-leveldown-7.2.1.tgz#bb16403c17754b0c4d5772d71d03b924a03d4c80"
@ -8088,7 +8191,7 @@ ethers@^5.0.1, ethers@^5.0.2, ethers@^5.5.2:
"@ethersproject/web" "5.6.1"
"@ethersproject/wordlists" "5.6.1"
ethers@^5.4.4:
ethers@^5.1.4, ethers@^5.4.4, ethers@^5.7.2:
version "5.7.2"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e"
integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==
@ -11393,6 +11496,11 @@ lru-cache@5.1.1, lru-cache@^5.1.1:
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.13.1.tgz#267a81fbd0881327c46a81c5922606a2cfe336c4"
integrity sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ==
lru-cache@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.0.tgz#b9e2a6a72a129d81ab317202d93c7691df727e61"
integrity sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==
lru-cache@^3.2.0:
version "3.2.0"
resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-3.2.0.tgz"
@ -15547,6 +15655,11 @@ ts-essentials@^6.0.3:
resolved "https://registry.npmjs.org/ts-essentials/-/ts-essentials-6.0.7.tgz"
integrity sha512-2E4HIIj4tQJlIHuATRHayv0EfMGK3ris/GRk1E3CFnsZzeNV+hUmelbaTZHLtXaZppM5oLhHRtO04gINC4Jusw==
ts-essentials@^7.0.1:
version "7.0.3"
resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.3.tgz#686fd155a02133eedcc5362dc8b5056cde3e5a38"
integrity sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==
ts-generator@^0.1.1:
version "0.1.1"
resolved "https://registry.npmjs.org/ts-generator/-/ts-generator-0.1.1.tgz"