So, I am trying to remove liquidity from a pancake v2 pair and I do not understand why this is not working. I am looking at the code, and I've triple checked every value, I don' think I am doing anything wrong. I need some help.
Here is my code:
def to_32byte_hex(self, val):
return to_hex(to_bytes(val).rjust(32, b'\0'))
def build_remove_with_permit(self, token_address: ChecksumAddress):
"""
:param token_address: liquidity pair token
:return: raw transaction dict
"""
# returns the correct values, confirmed because this
# function works on other pairs where I can call removeLiqudi
token1, token2, liquidity_balance, minOutToken0, minOutToken1 = self.get_liq_remove_info(token_address)
lp_contract = w3.eth.contract(token_address, abi=UNIV2_LP_ABI)
max_fee_per_priority, max_fee_per_gas, xx = gas_estimator(w3, self.account.address, self.account.address, 0,
'low')
# pancakeswap v2 is a uniswap v2 fork
router_contract = w3.eth.contract(self.router, abi=UNIV2_ABI)
# Prepare parameters
# Getting these values directly from the pair contract ...
PERMIT_TYPEHASH = lp_contract.functions.PERMIT_TYPEHASH().call()
DOMAIN_SEPARATOR = lp_contract.functions.DOMAIN_SEPARATOR().call()
x19 = '\x19\x01' # and this is also right from the contract source
owner = self.account.address # caller is me
spender = self.router # spender is router
value = liquidity_balance # amount of liq tokens to burn
nonce = lp_contract.functions.nonces(self.account.address).call() # my nonce on the contract
deadline = int(time.time() + 300) # 5 minutes from now
# Encode and hash the parameters
x = eth_abi.encode(['bytes32', 'address', 'address', 'uint256', 'uint256', 'uint256'],
[PERMIT_TYPEHASH, to_checksum_address(owner), to_checksum_address(spender), value, nonce, deadline])
# print(x)
y = w3.solidity_keccak(['string', 'bytes32', 'bytes32'], [x19, DOMAIN_SEPARATOR, x])
# print(y)
signature = w3.eth.account.signHash(y, private_key=self.account.key)
v, r, s = signature.v, self.to_32byte_hex(signature.r), self.to_32byte_hex(signature.s)
# print(v,r,s)
# Here I am just calling `permit` on the pair, to test whether or not I got it
# right, which for some reason, I have not!
try:
lp_contract.functions.permit(self.account.address, self.router, liquidity_balance, deadline, v, r, s).call(
{'from': self.account.address})
except ContractLogicError as err:
print('[!] Contract reverted with: %s' % err)
return {}
raw_txn = {
"from": self.account.address,
"gas": 300000, # 200000
'maxPriorityFeePerGas': to_wei(max_fee_per_priority, 'gwei'),
'maxFeePerGas': to_wei(max_fee_per_gas, 'gwei'),
"to": self.router,
"value": 0,
"data": router_contract.encodeABI('removeLiquidityWithPermit',
args=(to_checksum_address(token1), to_checksum_address(token2),
liquidity_balance, minOutToken0, minOutToken1,
self.account.address, deadline, True, v, r, s)),
"nonce": w3.eth.get_transaction_count(self.account.address),
"chainId": w3.eth.chain_id
}
return raw_txn
Can anyone tell me what is wrong with my code? As far as I can tell, I am doing this correctly.
I figure that if I can successfully call permit() on the pair contract, I should be able to then call removeLiquidityWithPermit(). So, in the code I shared, I am testing that with call()
. This is the permit
function on the LP pair contract:
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
require(deadline >= block.timestamp, 'Pancake: EXPIRED');
bytes32 digest = keccak256(
abi.encodePacked(
'\x19\x01',
DOMAIN_SEPARATOR,
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
)
);
address recoveredAddress = ecrecover(digest, v, r, s);
require(recoveredAddress != address(0) && recoveredAddress == owner, 'Pancake: INVALID_SIGNATURE');
_approve(owner, spender, value);
}
I am getting:
[!] Contract reverted with: execution reverted: Pancake: INVALID_SIGNATURE
Can anyone tell me why? Maybe I am missing something? Thanks ...
y
in python. It clearly says the signature is wrong, likely the digest computation is wrong