Approve Simplified

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.)

ERC712 Approve Example

//The EthItemInteroperableInterface token implements the  
function permit(address owner, address spender, uint value, uint8 v, bytes32 r, bytes32 s) external; 
//and
function permitNonce(address sender) external view returns(uint256)                            

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 transfer
var objectId = configuration.myObjectId;

//Let's take the IEthItemInteroperationalInterfaceversion of the objectId
var itemInteroperableInterface = await collection.contract.methods.asInteroperable(objectId).call();

//The address of the spender
var spenderAddress = configuration.spenderAddress;

//The amount the spender can transfer for you
var amountToPermit = configuration.amountToPermit;

var itemInteroperableInterface = new web3.eth.Contract(configuration.IEthItemInteroperableInterfaceABI, erc20NFTWrapperAddress);

//Let's take the next useful nonce
var nonce = await itemInteroperableInterface.methods.permitNonce(web3.eth.accounts[0]).call();

//Build the signature following the EIP 712 v4 protocol
var EIP712Domain = [
    { name: 'name', type: 'string' },
    { name: 'version', type: 'string' },
    { name: 'chainId', type: 'uint256' },
    { name: 'verifyingContract', type: 'address' }
];

var domain = {
    name: 'Item',
    version: '1',
    chainId: await web3.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 = await new Promise(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) {
            return ko(e);
        }
        signature = signature.result.substring(2);
        return ok({
            r: '0x' + signature.slice(0, 64),
            s: '0x' + signature.slice(64, 128),
            v: web3.utils.toDecimal('0x' + signature.slice(128, 130))
        });
    });
});

//Take the Spender Contract
var spender = new web3.eth.Contract(configuration.SpenderABI, spenderAddress);

//Grab the final receiver of the tokens
var tokensReceiver = configuration.tokensReceiver;

//Call the oneShotTransferFrom without the use of the approve function
await spender.methods.oneShotTransferFrom(itemInteroperableInterface, tokensReceiver, amountToPermit, signatureParameters.v, signatureParameters.r, signatureParameters.s).send();                                            

Last updated