zksync-web3
Advanced tools
Comparing version 0.2.3 to 0.2.4
@@ -1,7 +0,6 @@ | ||
import { ethers, BigNumber, utils, providers } from 'ethers'; | ||
import TransactionRequest = providers.TransactionRequest; | ||
import { ethers, BigNumber, utils } from 'ethers'; | ||
import { Log } from '@ethersproject/abstract-provider'; | ||
import { ExternalProvider } from '@ethersproject/providers'; | ||
import { ConnectionInfo } from '@ethersproject/web'; | ||
import { Address, EventFilter, BlockTag, TransactionResponse, TransactionStatus, Token, PriorityOpResponse } from './types'; | ||
import { Address, EventFilter, BlockTag, TransactionResponse, TransactionRequest, TransactionStatus, Token, PriorityOpResponse } from './types'; | ||
import { Signer } from './signer'; | ||
@@ -11,6 +10,7 @@ export declare class Provider extends ethers.providers.JsonRpcProvider { | ||
getBalance(address: Address, blockTag?: BlockTag, tokenAddress?: Address): Promise<ethers.BigNumber>; | ||
estimateGas(_transaction: utils.Deferrable<TransactionRequest>): Promise<BigNumber>; | ||
estimateGasPrice(_transaction: utils.Deferrable<TransactionRequest>): Promise<BigNumber>; | ||
static hexlifyTransaction(transaction: ethers.providers.TransactionRequest, allowExtra?: Record<string, boolean>): { | ||
[key: string]: string | ethers.utils.AccessList; | ||
}; | ||
estimateGas(transaction: utils.Deferrable<TransactionRequest>): Promise<BigNumber>; | ||
constructor(url?: ConnectionInfo | string, network?: ethers.providers.Networkish); | ||
estimateFee(serializedTx: string): Promise<BigNumber>; | ||
getMainContractAddress(): Promise<Address>; | ||
@@ -20,4 +20,4 @@ getAccountTransactions(address: Address, before?: number, limit?: number): Promise<TransactionResponse[]>; | ||
isTokenLiquid(token: Address): Promise<boolean>; | ||
getTokenPrice(token: Address): Promise<string>; | ||
static getDefaultProvider(): Promise<Provider>; | ||
getTokenPrice(token: Address): Promise<string | null>; | ||
static getDefaultProvider(): Provider; | ||
newFilter(filter: EventFilter | Promise<EventFilter>): Promise<BigNumber>; | ||
@@ -40,3 +40,3 @@ newBlockFilter(): Promise<BigNumber>; | ||
getTransaction(hash: string | Promise<string>): Promise<TransactionResponse>; | ||
getL1Withdrawal(withdrawalHash: string): Promise<string>; | ||
getL1Withdrawal(withdrawalHash: string): Promise<string | null>; | ||
sendTransaction(transaction: string | Promise<string>): Promise<TransactionResponse>; | ||
@@ -51,2 +51,3 @@ getL2TransactionFromPriorityOp(l1TxResponse: ethers.providers.TransactionResponse): Promise<TransactionResponse>; | ||
getSigner(addressOrIndex?: number | string): Signer; | ||
estimateGas(transaction: ethers.utils.Deferrable<TransactionRequest>): Promise<ethers.BigNumber>; | ||
} |
@@ -27,12 +27,53 @@ "use strict"; | ||
} | ||
async estimateGas(_transaction) { | ||
return ethers_1.BigNumber.from(21000); // to work with metamask | ||
// This function is used when formatting requests for | ||
// eth_call and eth_estimateGas. We override it here | ||
// because we have extra stuff to serialize (customData). | ||
// This function is for internal use only. | ||
static hexlifyTransaction(transaction, allowExtra) { | ||
var _a, _b, _c, _d, _e; | ||
const result = ethers_1.ethers.providers.JsonRpcProvider.hexlifyTransaction(transaction, { | ||
...allowExtra, | ||
customData: true, | ||
from: true | ||
}); | ||
if (transaction.customData == null) { | ||
return result; | ||
} | ||
result.eip712Meta = { | ||
feeToken: ethers_1.utils.hexlify((_a = transaction.customData.feeToken) !== null && _a !== void 0 ? _a : utils_1.ETH_ADDRESS), | ||
ergsPerStorage: ethers_1.utils.hexValue((_b = transaction.customData.ergsPerStorage) !== null && _b !== void 0 ? _b : 0), | ||
ergsPerPubdata: ethers_1.utils.hexValue((_c = transaction.customData.ergsPerPubdata) !== null && _c !== void 0 ? _c : 0), | ||
validFrom: ethers_1.BigNumber.from((_d = transaction.customData.validFrom) !== null && _d !== void 0 ? _d : utils_1.MIN_TIMESTAMP).toNumber(), | ||
validUntil: ethers_1.BigNumber.from((_e = transaction.customData.validFrom) !== null && _e !== void 0 ? _e : utils_1.MAX_TIMESTAMP).toNumber() | ||
}; | ||
if (transaction.customData.withdrawToken) { | ||
// @ts-ignore | ||
result.eip712Meta.withdrawToken = ethers_1.utils.hexlify(transaction.customData.withdrawToken); | ||
} | ||
if (transaction.customData.factoryDeps) { | ||
// @ts-ignore | ||
result.eip712Meta.factoryDeps = transaction.customData.factoryDeps.map((dep) => | ||
// TODO: we arraify instead of hexlifying because server expects Vec<u8>. | ||
// We should change deserealization there. | ||
Array.from(ethers_1.utils.arrayify(dep))); | ||
} | ||
return result; | ||
} | ||
async estimateGasPrice(_transaction) { | ||
return ethers_1.BigNumber.from(1000000000); // to work with metamask | ||
async estimateGas(transaction) { | ||
await this.getNetwork(); | ||
const params = await ethers_1.utils.resolveProperties({ | ||
transaction: this._getTransactionRequest(transaction) | ||
}); | ||
if (transaction.customData != null) { | ||
// @ts-ignore | ||
params.transaction.customData = transaction.customData; | ||
} | ||
const result = await this.perform('estimateGas', params); | ||
try { | ||
return ethers_1.BigNumber.from(result); | ||
} | ||
catch (error) { | ||
throw new Error(`bad result from backend (estimateGas): ${result}`); | ||
} | ||
} | ||
async estimateFee(serializedTx) { | ||
const fee = await this.send('eth_estimateGas', [{ data: serializedTx }]); | ||
return ethers_1.BigNumber.from(fee); | ||
} | ||
async getMainContractAddress() { | ||
@@ -49,11 +90,11 @@ if (!this.contractAddress) { | ||
async getConfirmedTokens(start = 0, limit = 255) { | ||
return this.send('zks_getConfirmedTokens', [start, limit]); | ||
return await this.send('zks_getConfirmedTokens', [start, limit]); | ||
} | ||
async isTokenLiquid(token) { | ||
return this.send('zks_isTokenLiquid', [token]); | ||
return await this.send('zks_isTokenLiquid', [token]); | ||
} | ||
async getTokenPrice(token) { | ||
return this.send('zks_getTokenPrice', [token]); | ||
return await this.send('zks_getTokenPrice', [token]); | ||
} | ||
static async getDefaultProvider() { | ||
static getDefaultProvider() { | ||
// TODO: different urls for different networks | ||
@@ -131,6 +172,6 @@ return new Provider(process.env.ZKSYNC_WEB3_API_URL || 'http://localhost:3050'); | ||
async getL1Withdrawal(withdrawalHash) { | ||
return this.send('zks_getL1WithdrawalTx', [withdrawalHash]); | ||
return await this.send('zks_getL1WithdrawalTx', [withdrawalHash]); | ||
} | ||
async sendTransaction(transaction) { | ||
return super.sendTransaction(transaction); | ||
return (await super.sendTransaction(transaction)); | ||
} | ||
@@ -174,3 +215,3 @@ async getL2TransactionFromPriorityOp(l1TxResponse) { | ||
} | ||
send(method, params) { | ||
async send(method, params) { | ||
params !== null && params !== void 0 ? params : (params = []); | ||
@@ -183,3 +224,3 @@ // Metamask complains about eth_sign (and on some versions hangs) | ||
} | ||
return this.provider.request({ method, params }); | ||
return await this.provider.request({ method, params }); | ||
} | ||
@@ -189,3 +230,9 @@ getSigner(addressOrIndex) { | ||
} | ||
async estimateGas(transaction) { | ||
const gas = await super.estimateGas(transaction); | ||
const metamaskMinimum = ethers_1.BigNumber.from(21000); | ||
const isEIP712 = transaction.customData != null || transaction.type == 0x70; | ||
return gas.gt(metamaskMinimum) || isEIP712 ? gas : metamaskMinimum; | ||
} | ||
} | ||
exports.Web3Provider = Web3Provider; |
@@ -27,2 +27,12 @@ import { ethers, BigNumberish, BigNumber } from 'ethers'; | ||
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse>; | ||
withdraw(tx: { | ||
amount: BigNumberish; | ||
token: Address; | ||
to?: Address; | ||
feeToken?: Address; | ||
nonce?: Address; | ||
validFrom?: number; | ||
validUntil?: number; | ||
gasLimit?: BigNumberish; | ||
}): Promise<TransactionResponse>; | ||
transfer(tx: { | ||
@@ -36,2 +46,3 @@ to: Address; | ||
validUntil?: number; | ||
gasLimit?: BigNumberish; | ||
}): Promise<any>; | ||
@@ -52,4 +63,5 @@ } | ||
overrides?: ethers.CallOverrides; | ||
approveOverrides?: ethers.CallOverrides; | ||
}): Promise<PriorityOpResponse>; | ||
addToken(token: Address, overrides?: ethers.CallOverrides): Promise<PriorityOpResponse>; | ||
} |
@@ -36,3 +36,3 @@ "use strict"; | ||
const signInput = { ...transaction, ...transaction.customData }; | ||
return this.ethSigner._signTypedData(await this.eip712Domain, exports.eip712Types, signInput); | ||
return await this.ethSigner._signTypedData(await this.eip712Domain, exports.eip712Types, signInput); | ||
} | ||
@@ -66,7 +66,7 @@ static getSignedDigest(transaction) { | ||
async getBalance(token, blockTag = 'committed') { | ||
return this.provider.getBalance(await this.getAddress(), blockTag, token); | ||
return await this.provider.getBalance(await this.getAddress(), blockTag, token); | ||
} | ||
// an alias with a better name | ||
async getNonce(blockTag) { | ||
return this.getTransactionCount(blockTag); | ||
return await this.getTransactionCount(blockTag); | ||
} | ||
@@ -90,9 +90,24 @@ async sendTransaction(transaction) { | ||
(_k = (_u = transaction.customData).factoryDeps) !== null && _k !== void 0 ? _k : (_u.factoryDeps = []); | ||
(_l = transaction.gasPrice) !== null && _l !== void 0 ? _l : (transaction.gasPrice = await this.provider.estimateGasPrice(transaction)); | ||
(_l = transaction.gasPrice) !== null && _l !== void 0 ? _l : (transaction.gasPrice = await this.provider.getGasPrice()); | ||
(_m = transaction.gasLimit) !== null && _m !== void 0 ? _m : (transaction.gasLimit = await this.provider.estimateGas(transaction)); | ||
const signature = await this.eip712.sign(transaction); | ||
const txBytes = (0, utils_1.serialize)(transaction, signature); | ||
return this.provider.sendTransaction(txBytes); | ||
return await this.provider.sendTransaction(txBytes); | ||
} | ||
} | ||
async withdraw(tx) { | ||
var _a; | ||
return await this.sendTransaction({ | ||
value: tx.amount, | ||
to: (_a = tx.to) !== null && _a !== void 0 ? _a : (await this.getAddress()), | ||
nonce: tx.nonce, | ||
gasLimit: tx.gasLimit, | ||
customData: { | ||
feeToken: tx.feeToken, | ||
validFrom: tx.validFrom, | ||
validUntil: tx.validUntil, | ||
withdrawToken: tx.token | ||
} | ||
}); | ||
} | ||
async transfer(tx) { | ||
@@ -104,2 +119,3 @@ var _a; | ||
nonce: tx.nonce, | ||
gasLimit: tx.gasLimit, | ||
customData: { | ||
@@ -114,4 +130,3 @@ feeToken: tx.feeToken, | ||
} | ||
const handle = await token.transfer(tx.to, tx.amount, overrides); | ||
return handle; | ||
return await token.transfer(tx.to, tx.amount, overrides); | ||
} | ||
@@ -144,7 +159,7 @@ } | ||
if ((0, utils_1.isETH)(token)) { | ||
return this.provider.getBalance(await this.getAddress(), blockTag); | ||
return await this.provider.getBalance(await this.getAddress(), blockTag); | ||
} | ||
else { | ||
const erc20contract = new ethers_1.ethers.Contract(token, utils_1.IERC20, this.provider); | ||
return erc20contract.balanceOf(await this.getAddress()); | ||
return await erc20contract.balanceOf(await this.getAddress()); | ||
} | ||
@@ -163,5 +178,7 @@ } | ||
gasLimit = await erc20contract.estimateGas.approve(mainContract, amount); | ||
gasLimit = gasLimit.mul(2); | ||
gasLimit = gasLimit.gt(utils_1.RECOMMENDED_GAS_LIMIT.ERC20_APPROVE) | ||
? gasLimit | ||
: utils_1.RECOMMENDED_GAS_LIMIT.ERC20_APPROVE; | ||
} | ||
return erc20contract.approve(mainContract, amount, { gasLimit, ...overrides }); | ||
return await erc20contract.approve(mainContract, amount, { gasLimit, ...overrides }); | ||
} | ||
@@ -174,3 +191,3 @@ async deposit(transaction) { | ||
if ((0, utils_1.isETH)(token)) { | ||
return this.providerL2.getPriorityOpResponse(await zksyncContract.depositETH(depositTo, { | ||
return await this.providerL2.getPriorityOpResponse(await zksyncContract.depositETH(depositTo, { | ||
value: ethers_1.BigNumber.from(amount), | ||
@@ -184,3 +201,3 @@ gasLimit: ethers_1.BigNumber.from(utils_1.RECOMMENDED_GAS_LIMIT.ETH_DEPOSIT), | ||
if (transaction.approveERC20) { | ||
const approveTx = await this.approveERC20(token, amount); | ||
const approveTx = await this.approveERC20(token, amount, transaction.approveOverrides); | ||
nonce = approveTx.nonce + 1; | ||
@@ -197,3 +214,3 @@ } | ||
} | ||
return this.providerL2.getPriorityOpResponse(await zksyncContract.depositERC20(...args, overrides)); | ||
return await this.providerL2.getPriorityOpResponse(await zksyncContract.depositERC20(...args, overrides)); | ||
} | ||
@@ -203,3 +220,3 @@ } | ||
const zksyncContract = await this.getMainContract(); | ||
return this.providerL2.getPriorityOpResponse(await zksyncContract.addToken(token, { | ||
return await this.providerL2.getPriorityOpResponse(await zksyncContract.addToken(token, { | ||
gasLimit: ethers_1.BigNumber.from(utils_1.RECOMMENDED_GAS_LIMIT.ADD_TOKEN), | ||
@@ -206,0 +223,0 @@ ...overrides |
@@ -7,6 +7,4 @@ import { utils, ethers, BigNumberish } from 'ethers'; | ||
export declare const ETH_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; | ||
export declare const ZKSYNC_WEB3_ABI: utils.Interface; | ||
export declare const ZKSYNC_MAIN_ABI: utils.Interface; | ||
export declare const IERC20: utils.Interface; | ||
export declare const EMPTY_SIGNATURE: Uint8Array; | ||
export declare const PRIORITY_OP_ID_BYTES = 8; | ||
@@ -17,2 +15,3 @@ export declare const RECOMMENDED_GAS_LIMIT: { | ||
ERC20_DEFAULT_DEPOSIT: number; | ||
ERC20_APPROVE: number; | ||
ADD_TOKEN: number; | ||
@@ -19,0 +18,0 @@ }; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getL2HashFromPriorityOp = exports.getL2HashFromPriorityOpId = exports.parseTransaction = exports.serialize = exports.sleep = exports.isETH = exports.RECOMMENDED_GAS_LIMIT = exports.PRIORITY_OP_ID_BYTES = exports.EMPTY_SIGNATURE = exports.IERC20 = exports.ZKSYNC_MAIN_ABI = exports.ZKSYNC_WEB3_ABI = exports.ETH_ADDRESS = exports.MAX_TIMESTAMP = exports.MIN_TIMESTAMP = void 0; | ||
exports.getL2HashFromPriorityOp = exports.getL2HashFromPriorityOpId = exports.parseTransaction = exports.serialize = exports.sleep = exports.isETH = exports.RECOMMENDED_GAS_LIMIT = exports.PRIORITY_OP_ID_BYTES = exports.IERC20 = exports.ZKSYNC_MAIN_ABI = exports.ETH_ADDRESS = exports.MAX_TIMESTAMP = exports.MIN_TIMESTAMP = void 0; | ||
const ethers_1 = require("ethers"); | ||
@@ -12,6 +12,4 @@ const signer_1 = require("./signer"); | ||
exports.ETH_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; | ||
exports.ZKSYNC_WEB3_ABI = new ethers_1.utils.Interface(require('../abi/IZkSyncL2Proto.json')); | ||
exports.ZKSYNC_MAIN_ABI = new ethers_1.utils.Interface(require('../abi/ZkSync.json')); | ||
exports.IERC20 = new ethers_1.utils.Interface(require('../abi/IERC20.json')); | ||
exports.EMPTY_SIGNATURE = new Uint8Array(65); | ||
exports.PRIORITY_OP_ID_BYTES = 8; | ||
@@ -22,2 +20,3 @@ exports.RECOMMENDED_GAS_LIMIT = { | ||
ERC20_DEFAULT_DEPOSIT: 300000, | ||
ERC20_APPROVE: 50000, | ||
ADD_TOKEN: 120000 | ||
@@ -24,0 +23,0 @@ }; |
@@ -21,2 +21,12 @@ import { EIP712Signer } from './signer'; | ||
signTransaction(transaction: ethers.providers.TransactionRequest): Promise<string>; | ||
withdraw(tx: { | ||
amount: BigNumberish; | ||
token: Address; | ||
to?: Address; | ||
feeToken?: Address; | ||
nonce?: Address; | ||
validFrom?: number; | ||
validUntil?: number; | ||
gasLimit?: BigNumberish; | ||
}): Promise<TransactionResponse>; | ||
transfer(tx: { | ||
@@ -30,2 +40,3 @@ to: Address; | ||
validUntil?: number; | ||
gasLimit?: BigNumberish; | ||
}): Promise<any>; | ||
@@ -43,4 +54,5 @@ sendTransaction(transaction: ethers.providers.TransactionRequest): Promise<TransactionResponse>; | ||
overrides?: ethers.CallOverrides; | ||
approveOverrides?: ethers.CallOverrides; | ||
}): Promise<PriorityOpResponse>; | ||
addToken(token: Address, overrides?: ethers.CallOverrides): Promise<PriorityOpResponse>; | ||
} |
@@ -18,7 +18,7 @@ "use strict"; | ||
async getBalance(token, blockTag = 'committed') { | ||
return this.provider.getBalance(await this.getAddress(), blockTag, token); | ||
return await this.provider.getBalance(await this.getAddress(), blockTag, token); | ||
} | ||
// an alias with a better name | ||
async getNonce(blockTag) { | ||
return this.getTransactionCount(blockTag); | ||
return await this.getTransactionCount(blockTag); | ||
} | ||
@@ -31,7 +31,7 @@ async getBalanceL1(token, blockTag) { | ||
if ((0, utils_1.isETH)(token)) { | ||
return this.providerL1.getBalance(this.address, blockTag); | ||
return await this.providerL1.getBalance(this.address, blockTag); | ||
} | ||
else { | ||
const erc20contract = new ethers_1.ethers.Contract(token, utils_1.IERC20, this.providerL1); | ||
return erc20contract.balanceOf(this.address); | ||
return await erc20contract.balanceOf(this.address); | ||
} | ||
@@ -77,7 +77,12 @@ } | ||
async signTransaction(transaction) { | ||
var _a; | ||
if (transaction.customData == null && transaction.type != 0x70) { | ||
return super.signTransaction(transaction); | ||
if (transaction.type == 2 && transaction.maxFeePerGas == null) { | ||
transaction.maxFeePerGas = await this.provider.getGasPrice(); | ||
} | ||
return await super.signTransaction(transaction); | ||
} | ||
else { | ||
if (transaction.from != null && transaction.from != this.address) { | ||
(_a = transaction.from) !== null && _a !== void 0 ? _a : (transaction.from = this.address); | ||
if (transaction.from != this.address) { | ||
throw new Error('transaction `from` address mismatch'); | ||
@@ -89,2 +94,17 @@ } | ||
} | ||
async withdraw(tx) { | ||
var _a; | ||
return await this.sendTransaction({ | ||
value: tx.amount, | ||
to: (_a = tx.to) !== null && _a !== void 0 ? _a : this.address, | ||
nonce: tx.nonce, | ||
gasLimit: tx.gasLimit, | ||
customData: { | ||
feeToken: tx.feeToken, | ||
validFrom: tx.validFrom, | ||
validUntil: tx.validUntil, | ||
withdrawToken: tx.token | ||
} | ||
}); | ||
} | ||
async transfer(tx) { | ||
@@ -96,2 +116,3 @@ var _a; | ||
nonce: tx.nonce, | ||
gasLimit: tx.gasLimit, | ||
customData: { | ||
@@ -106,4 +127,3 @@ feeToken: tx.feeToken, | ||
} | ||
const handle = await token.transfer(tx.to, tx.amount, overrides); | ||
return handle; | ||
return await token.transfer(tx.to, tx.amount, overrides); | ||
} | ||
@@ -139,5 +159,7 @@ async sendTransaction(transaction) { | ||
gasLimit = await erc20contract.estimateGas.approve(mainContract, amount); | ||
gasLimit = gasLimit.mul(2); | ||
gasLimit = gasLimit.gt(utils_1.RECOMMENDED_GAS_LIMIT.ERC20_APPROVE) | ||
? gasLimit | ||
: utils_1.RECOMMENDED_GAS_LIMIT.ERC20_APPROVE; | ||
} | ||
return erc20contract.approve(mainContract, amount, { gasLimit, ...overrides }); | ||
return await erc20contract.approve(mainContract, amount, { gasLimit, ...overrides }); | ||
} | ||
@@ -150,3 +172,3 @@ async deposit(transaction) { | ||
if ((0, utils_1.isETH)(token)) { | ||
return this.provider.getPriorityOpResponse(await zksyncContract.depositETH(depositTo, { | ||
return await this.provider.getPriorityOpResponse(await zksyncContract.depositETH(depositTo, { | ||
value: ethers_1.BigNumber.from(amount), | ||
@@ -160,3 +182,3 @@ gasLimit: ethers_1.BigNumber.from(utils_1.RECOMMENDED_GAS_LIMIT.ETH_DEPOSIT), | ||
if (transaction.approveERC20) { | ||
const approveTx = await this.approveERC20(token, amount); | ||
const approveTx = await this.approveERC20(token, amount, transaction.approveOverrides); | ||
nonce = approveTx.nonce + 1; | ||
@@ -173,3 +195,3 @@ } | ||
} | ||
return this.provider.getPriorityOpResponse(await zksyncContract.depositERC20(...args, overrides)); | ||
return await this.provider.getPriorityOpResponse(await zksyncContract.depositERC20(...args, overrides)); | ||
} | ||
@@ -179,3 +201,3 @@ } | ||
const zksyncContract = await this.getMainContract(); | ||
return this.provider.getPriorityOpResponse(await zksyncContract.addToken(token, { | ||
return await this.provider.getPriorityOpResponse(await zksyncContract.addToken(token, { | ||
gasLimit: ethers_1.BigNumber.from(utils_1.RECOMMENDED_GAS_LIMIT.ADD_TOKEN), | ||
@@ -182,0 +204,0 @@ ...overrides |
{ | ||
"name": "zksync-web3", | ||
"version": "0.2.3", | ||
"version": "0.2.4", | ||
"main": "build/index.js", | ||
@@ -5,0 +5,0 @@ "types": "build/index.d.ts", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
94052
2878
1