One of the most interesting features of ITEMs for dApps is the simplified approve function.
If you implement your smart contract with native wrap/unwrap, or give users the possibility to use ITEMs directly, you can:
1)--- Use the safeTranferFrom functionality with the ERC1155 methodology by helping users approve only transactions with a different msg.sender from the wallet owner, and only once for an entire collection.
2)--- If you need to use the ERC20 interface, you can implement ERC721 Approve by signing the Approve and saving the user from an extra transaction (Like how Uniswap V2 LP tokens work.)
These are useful to bypass the approve(address,uint256) ERC20 function by implementing an alternative way based on digital signature verification mechanism which follows the EIP-712 standard.
//Imagine you have the following Spender Smart Contract/*pragma solidity ^0.6.0;contract Spender { function oneShotTransferFrom(address interoperationalInterfaceAddress, address receiver, uint256 value, uint8 v, bytes32 r, bytes32 s) public {
IEthItemInteroperationalInterface token = IEthItemInteroperationalInterface(erc20NFTWrapperAddress); token.permit(msg.sender, address(this), value, v, r, s); token.transferFrom(msg.sender, receiver, value); }}interface IEthItemInteroperationalInterface { function permit(address owner, address spender, uint value, uint8 v, bytes32 r, bytes32 s) external; function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);}*///oneShotTransferFrom let the Spender transfer the sender Tokens without calling the approve(address,uint256) function of the ERC20 Token because the sender digitally signed a Permit message,
//saving time and gas//Grab the objectId to transfervar objectId =configuration.myObjectId;//Let's take the IEthItemInteroperationalInterfaceversion of the objectIdvar itemInteroperableInterface =awaitcollection.contract.methods.asInteroperable(objectId).call();//The address of the spendervar spenderAddress =configuration.spenderAddress;//The amount the spender can transfer for youvar amountToPermit =configuration.amountToPermit;var itemInteroperableInterface = new web3.eth.Contract(configuration.IEthItemInteroperableInterfaceABI, erc20NFTWrapperAddress);
//Let's take the next useful noncevar nonce =awaititemInteroperableInterface.methods.permitNonce(web3.eth.accounts[0]).call();//Build the signature following the EIP 712 v4 protocolvar EIP712Domain = [ { name:'name', type:'string' }, { name:'version', type:'string' }, { name:'chainId', type:'uint256' }, { name:'verifyingContract', type:'address' }];var domain = { name:'Item', version:'1', chainId:awaitweb3.eth.getNetworkId(), verifyingContract: itemInteroperableInterface};var Permit = [ { name:'owner', type:'address' }, { name:'spender', type:'address' }, { name:'value', type:'uint256' }, { name:'nonce', type:'uint256' },];var message = { owner:web3.eth.accounts[0], spenderAddress, amountToPermit, nonce};var data =JSON.stringify({ types: { EIP712Domain, Permit }, domain, primaryType:'Permit', message});var signatureParameters =awaitnewPromise(function(ok, ko) {window.web3.currentProvider.sendAsync({ method:'eth_signTypedData_v4', params: [web3.eth.accounts[0], data], from:web3.eth.accounts[0] },function(e, signature) {if (e) {returnko(e); } signature =signature.result.substring(2);returnok({ r:'0x'+signature.slice(0,64), s:'0x'+signature.slice(64,128), v:web3.utils.toDecimal('0x'+signature.slice(128,130)) }); });});//Take the Spender Contractvar spender =newweb3.eth.Contract(configuration.SpenderABI, spenderAddress);//Grab the final receiver of the tokensvar tokensReceiver =configuration.tokensReceiver;//Call the oneShotTransferFrom without the use of the approve functionawait spender.methods.oneShotTransferFrom(itemInteroperableInterface, tokensReceiver, amountToPermit, signatureParameters.v, signatureParameters.r, signatureParameters.s).send();