
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
This is the ts implementation of the MetaPlay. It uses EIP2771 and some custom logic to allow users to pay for transactions with ERC20 tokens. This concept is also called meta transactions. A user can send a meta transaction to any receiver that has EIP27
This is the ts implementation of the MetaPlay. It uses EIP2771 and some custom logic to allow users to pay for transactions with ERC20 tokens. This concept is also called meta transactions. A user can send a meta transaction to any receiver that has EIP2771 enabled.
At the same time, it adds the option to use a few feature that we call session keys. It allows another wallet to sign meta transactions for the user and enables a frontend DAPP to skip the tedious wallet signature. While approving the session signer, you can set the target contract that can be used in combination with the session signer.
This package helps to easily add the MetaPlay to existing projects. It is heavily used and tested in our own projects. The package helps to create the EIP712 signature from the contract interface using ethers.js. After that, it sends the signed request to the relay operators that take the request and send it to the target blockchain. They have zero options to change the request in any way.
This package does not include any logic to manage the session signer private key, but it is exposed to the DAPP. This means, that the native approach is to generate a new session wallet every time the relay gets initialized. But you can also provider a privateKey while creating the instance to reuse a previous signer.
Here you need to make sure the private key gets saved somewhere and is secure until it's used next. It's highly recommended using a reasonable deadline for the signer (can be extended later) and to encrypt the key while it's not used. DO NOT store the private key in a blank format. Whoever knows the key can use the user's funds together with the target contract.
| Name | Short name | ID | Deployment Address | Gas Fee % | Spread - Fee % |
|---|---|---|---|---|---|
| Binance Smart Chain Mainnet | BNB | 56 | 10 | 0.5 | |
| Polygon Mainnet | Polygon | 137 | 10 | 0.5 | |
| Fantom Opera | Fantom | 250 | 10 | 0.5 | |
| Arbitrum One | Arbitrum | 42161 | 0xF73ab2d782bf6BA97ac4405D2CD4F1135da8dbd9 | 10 | 0.5 |
The meta play charges a percentuale fee based on the tx fee used. For fee token other then WETH an additonal spread is charged for converting the token to ETH. The token value is based on on-chain chainlink feeds.
While creating a new instance you have to specify the users address and the blockchain you want to use, using the network ID (see Supported Blockchains). You can also set a customRPC to speed up some of the on-chain lookups. With privateKey you can re-create a old session wallet. Use an object to pass the arguments.
Arguments:
userAddress , as string
chainId , as number
optional:
After creating a new MetaPlayProvider class, you need to set a target. A target is the contract you want to interact with. You can add it by either adding the contract ABI or with an already existing contract class. The metaProvider allows to add any number of targets at the same time.
const metaProvider = new MetaPlayProvider({ userAddress: '0x12c..4' })
const target = new ethers.Contract(targetAddress, targetAbi)
metaProvider.addNewTargetFromContract(target)
const exampleABI = ['function emitMessage(string memory message) public', 'mustReceiveEth(uint256 value) public payable']
const metaProvider = new MetaPlayProvider({ userAddress: '0x12c..4' })
metaProvider.addNewTargetFromAbi(targetAddress, exampleABI)
The metaProvider contract needs to be approved to collect the fee in the specific ERC20 token.
This can be done with the help of the package as well. It offers 2 diffrent approaches for this. You can either hand over the ethers wallet instance and the metaProvider class will send the approve tx automatically or you can request the calldata and send it using any provider you like.
Arguments:
await metaProvider.isFeeTokenApproved(tokenAddress)
Arguments:
await metaProvider.approveFeeToken(userWallet, tokenAddress, newAllowance)
Arguments:
const calldata = await metaProvider.approveFeeTokenCalldate(tokenAddress, newAllowance)
const tx = await userWallet.connect(provider).sendTransaction(calldata)
If you want to use session signers, the session signer needs to be approved. You can set the targets with targetAddresses as an array or let the package default to all not yet approved targets.
This can be done with the help of the package as well. It offers 2 diffrent approaches for this. You can either hand over the ethers wallet instance and the metaProvider class will send the approve tx automatically or you can request the calldata and send it using any provider you like.
Arguments:
await metaProvider.isApproved(targetAddress)
Arguments:
await testForwader.approveSigner(userWallet)
Arguments:
const calldata = await metaProvider.approveSignerCalldata()
const tx = await userWallet.connect(provider).sendTransaction(calldata)
You mainly interact with the metaProvider using getNewRequest. This function lets you create a new request. A request is a class on its own, that enables you to easily interact with the metaProvider. You have to specify one of the added targets by the address, if you have more then one target added.
If the function is payable and has to recive a specific amount of the native Token (eg. ETH on Arbitrum or FTM on Fantom), you can set that with msgValue (BigNumber in wei). Each of the specific overwrite are set using the object notation.
Arguments:
functionName , as string
parameters , as array
optional:
const requestOne = metaProvider.getNewRequest('emitMessage', ['Hello World'])
const requestTwo = metaProvider.getNewRequest('mustReceiveEth', [], { msgValue: ethers.BigNumber.from('1000') })
The request class can be used to easily manage and interact with the metaProvider on a request level.
Checks if the request is valid using the EIP2771 standart interface on the blockchain.
Checks if an error was thrown why sending or processing the request and returns the error message.
Returns the fee amount in the specific fee token. By passing the number of decimals of the fee token, you can recive the fee as a decimal number. By not adding that, you can get the amount as BigNumber
Sends the request to the metaProvider operator.
Fetch the status of the request from the metaProvider operator.
After sending a request you can await the result. Optionally a specific status can be set.
const usdcTokenAddress = '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8'
const exampleABI = ['function emitMessageWithValue(string memory message) public payable']
const metaProvider = new MetaPlayProvider({ userAddress: '0x12c..4', chainId: 42161 })
metaProvider.addNewTargetFromAbi(targetAddress, exampleABI)
const isUSDCApproved = await metaProvider.isFeeTokenApproved(usdcTokenAddress)
if (!isUSDCApproved) await metaProvider.approveFeeToken(userWallet, usdcTokenAddress, ethers.constants.MaxUint256)
// here we approve a new signer
// if metaProvider is created form privateKey that was used before, check with:
// await metaProvider.isApproved(targetAddress)
await testForwader.approveSigner(userWallet)
const request = await metaProvider.getNewRequest('emitMessageWithValue', ['Hello World'], { msgValue: ethers.BigNumber.from('1000'), feeToken: usdcTokenAddress })
const isValid = await request.valid()
if (!isValid) console.error('Request is not valid!')
const fee = await request.estimatedFee(6)
console.log(`Request will cost ${fee} USDC.`)
request.send()
const result = await request.wait()
if (result.status === 'processed') {
console.log(`Successfully send request. TxHash is ${result.txHash}`)
} else {
// alternative:
// errorMsg = result.errorReasonValidation || result.errorReasonForwarder
// one of those will always be set if the status is error or rejected
// here .error() is used as example:
const errorMsg = await request.error()
console.log(`Failed to send request, error message: ${errorMsg}`)
}
FAQs
This is the ts implementation of the MetaPlay. It uses EIP2771 and some custom logic to allow users to pay for transactions with ERC20 tokens. This concept is also called meta transactions. A user can send a meta transaction to any receiver that has EIP27
The npm package metaplay receives a total of 2 weekly downloads. As such, metaplay popularity was classified as not popular.
We found that metaplay demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.