I'm trying to figure out how I can remove LPs from Uniswap/Pancakeswap using the function

  removeLiquidityWithPermit(address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s)

I want to do this on the command line so I'm using seth. I managed so far the deposit, but now I'm facing an issue with the signing. My major problem is how to calculate and obtain the values of uint8 v, bytes32 r, bytes32 s.

Uniswap function that checks the permission:

  function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
    require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
    bytes32 digest = keccak256(
            keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
    address recoveredAddress = ecrecover(digest, v, r, s);
    require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');
    _approve(owner, spender, value);

I can't still figure out what and how to encode to get the right values.

I'm adding more code and experiments, I'm using that code in node to generate the hash that needs to be signed via seth sign later on, which is separated into v, r, s and attached to the send. Testing on BSC testnet and PancakeSwap

var Web3 = require('web3');
var web3 = new Web3('http://localhost:8545');

const PERMIT_TYPEHASH = '0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9'; //is taken from the Pancake Testnet contract, 
const DOMAIN_SEPARATOR = '0x...'; // using the one assigned to the LPs from the LPs contract field!
const x19 = '\x19\x01';

//function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s)
var owner = '0x...'; // This is my address that is executing the send and also the owner of the LPs
var spender = '0x6725F303b657a9451d8BA641348b6761A6CC7a17' //Pancake swap
var value = '0'; //same like the submitted later on value
var nonce = '23'; // same like in the later on transaction containing the hashes
var deadline = '9632157400'; // same like in the later on submission with seth

var x = web3.utils.keccak256(web3.eth.abi.encodeParameters(['bytes32', 'address', 'address', 'uint256', 'uint256', 'uint256'],[PERMIT_TYPEHASH, owner, spender, value, nonce, deadline]));

var y = web3.utils.keccak256(web3.eth.abi.encodeParameters(['string','bytes32','bytes32'], [x19, DOMAIN_SEPARATOR, x]));

console.log(y) // this then gets signed with seth sign with the key form the sender.

Still I'm getting Fail with error 'Pancake: INVALID_SIGNATURE'. Am I doing something wrong here? Where should I look for a solution? Thank you!

  • Did you get any answer ? i've faced same situation here right now :( I've try re-deploy severals time but still doesn't work Commented Jun 21, 2021 at 8:44
  • @PromsurinPhutthammawong unfortunately not, haven't found a solution to that so far.
    – bleedr
    Commented Jul 22, 2021 at 9:46
  • Did you fix this? I am also interested.
    – José D.
    Commented Sep 24, 2021 at 23:46
  • 1
    Main issue is changing name or symbol, make sure is "name" in solidity and "/views/RemoveLiquidty/index.tsx domain name. It must be same Commented Oct 22, 2021 at 8:25
  • 1
    @bleedr I had similar issue while forking pancakeswap in testnet. We need to change "CAKE LPs" to our "XXX LPs". that time we got 'Fail with error 'Pancake: INVALID_SIGNATURE' such issue. Commented Oct 25, 2021 at 1:18

1 Answer 1


I had a similar problem and finally I solved it. When removing liquidity, the uniswap-interface will request the wallet for signature. Do remember to change the token name sent to the wallet, which is in the uniswap-interface. in my case ,it is in the file:uniswap-interface/src/pages/RemoveLiquidity/index.tsx, and in the function onAttemptToApprove(). async function onAttemptToApprove() { if (!pairContract || !pair || !library) throw new Error('missing dependencies') const liquidityAmount = parsedAmounts[Field.LIQUIDITY] if (!liquidityAmount) throw new Error('missing liquidity amount') // try to gather a signature for permission const nonce = await pairContract.nonces(account)

const deadlineForSignature: number = Math.ceil(Date.now() / 1000) + deadline

const EIP712Domain = [
  { name: 'name', type: 'string' },
  { name: 'version', type: 'string' },
  { name: 'chainId', type: 'uint256' },
  { name: 'verifyingContract', type: 'address' }
const domain = {
  **name: 'iUniswap V2',**must be same as UniswapV2ERC20.sol
  version: '1',
  chainId: chainId,
  verifyingContract: pair.liquidityToken.address

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.