Skip to content

Commit

Permalink
adding value in swap call (#13459)
Browse files Browse the repository at this point in the history
* adding value in swap call

* deployment addresses

* passing param at the right place

* contract file

* update contracts package

* cleanup

* remove missing networks

* using package

---------

Co-authored-by: Clément Renaud <clement+git@clementrenaud.com>
  • Loading branch information
julien51 and clemsos committed Mar 13, 2024
1 parent a3dbd13 commit e0b4f08
Show file tree
Hide file tree
Showing 21 changed files with 73 additions and 46 deletions.
11 changes: 8 additions & 3 deletions packages/contracts/src/abis/utils/UnlockSwapPurchaser.json

Large diffs are not rendered by default.

47 changes: 28 additions & 19 deletions packages/contracts/src/contracts/utils/UnlockSwapPurchaser.sol
@@ -1,4 +1,4 @@
// Sources flattened with hardhat v2.18.3 https://hardhat.org
// Sources flattened with hardhat v2.20.1 https://hardhat.org

// SPDX-License-Identifier: GPL-2.0-or-later AND MIT

Expand Down Expand Up @@ -749,17 +749,24 @@ interface IPublicLock {
* - `from`, `to` cannot be zero.
* - `tokenId` must be owned by `from`.
* - If the caller is not `from`, it must be have been allowed to move this
* NFT by either {approve} or {setApprovalForAll}.
* NFT by either `approve` or `setApprovalForAll`.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;

function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;

/**
* an ERC721-like function to transfer a token from one account to another.
* @param from the owner of token to transfer
* @param to the address that will receive the token
* @param tokenId the id of the token
* @dev Requirements: if the caller is not `from`, it must be approved to move this token by
* either {approve} or {setApprovalForAll}.
* either `approve` or `setApprovalForAll`.
* The key manager will be reset to address zero after the transfer
*/
function transferFrom(address from, address to, uint256 tokenId) external;
Expand Down Expand Up @@ -816,13 +823,6 @@ interface IPublicLock {
address _operator
) external view returns (bool);

function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;

/**
* Returns the total number of keys, including non-valid ones
* @return _totalKeysCreated the total number of keys, valid or not
Expand Down Expand Up @@ -1332,6 +1332,17 @@ contract UnlockSwapPurchaser {
: IMintableERC20(token).balanceOf(address(this));
}

/**
* Check if lock exists
* @param lock address of the lock
*/
function lockExists(address lock) internal view returns (bool lockExists) {
(lockExists, , ) = IUnlock(unlockAddress).locks(lock);
if (!lockExists) {
revert LockDoesntExist(lock);
}
}

/**
* Swap tokens and call a function a lock contract.
*
Expand All @@ -1340,6 +1351,7 @@ contract UnlockSwapPurchaser {
*
* @param lock the address of the lock
* @param srcToken the address of the token sent by the user (ERC20 or address(0) for native)
* @param keyPrice the expected price of the token (calculated from the lock)
* @param amountInMax the maximum amount the user want to spend in the swap
* @param uniswapRouter the address of the uniswap router
* @param swapCalldata the Uniswap quote calldata returned by the SDK, to be sent to the router contract
Expand All @@ -1353,18 +1365,15 @@ contract UnlockSwapPurchaser {
function swapAndCall(
address lock,
address srcToken,
uint keyPrice,
uint amountInMax,
address uniswapRouter,
bytes memory swapCalldata,
bytes memory callData
) public payable returns (bytes memory) {
// check if lock exists
(bool lockExists, , ) = IUnlock(unlockAddress).locks(lock);
if (!lockExists) {
revert LockDoesntExist(lock);
}
// make sure the lock is registered in Unlock
lockExists(lock);

// make sure
if (uniswapRouters[uniswapRouter] != true) {
revert UnautorizedRouter(uniswapRouter);
}
Expand Down Expand Up @@ -1428,19 +1437,19 @@ contract UnlockSwapPurchaser {
destToken == address(0)
? getBalance(destToken) - msg.value
: getBalance(destToken)
) < balanceTokenDestBefore + IPublicLock(lock).keyPrice()
) < balanceTokenDestBefore + keyPrice
) {
revert InsufficientBalance();
}

// approve ERC20 to call the lock
if (destToken != address(0)) {
IMintableERC20(destToken).approve(lock, IPublicLock(lock).keyPrice());
IMintableERC20(destToken).approve(lock, keyPrice);
}

// call the lock
(bool lockCallSuccess, bytes memory returnData) = lock.call{
value: destToken == address(0) ? IPublicLock(lock).keyPrice() : 0
value: destToken == address(0) ? keyPrice : 0
}(callData);

if (lockCallSuccess == false) {
Expand Down
2 changes: 1 addition & 1 deletion packages/networks/src/networks/arbitrum.ts
Expand Up @@ -80,7 +80,7 @@ export const arbitrum: NetworkConfig = {
networkName: 'arbitrum-one',
studioName: 'unlock-protocol-arbitrum',
},
swapPurchaser: '0x0C33884Ab3eE799E7628FA3fCF20B81997745a72',
swapPurchaser: '0xE1a7Ec44fB4c5c88ebB3744A9Ba2A3cCA879A47d',
tokens: [
{
address: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
Expand Down
2 changes: 1 addition & 1 deletion packages/networks/src/networks/avalanche.ts
Expand Up @@ -65,7 +65,7 @@ export const avalanche: NetworkConfig = {
'https://api.studio.thegraph.com/query/65299/unlock-protocol-avalanche/version/latest',
studioName: 'unlock-protocol-avalanche',
},
swapPurchaser: '0x5c67AD0CAfe61aF3706347aBc695D7ACcb38EFb3',
swapPurchaser: '0xc9F29DdBD4D828cFb2EB491E9d48013a9c0E3C89',
tokens: [
{
address: '0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB',
Expand Down
2 changes: 1 addition & 1 deletion packages/networks/src/networks/base.ts
Expand Up @@ -79,7 +79,7 @@ export const base: NetworkConfig = {
networkName: 'base',
studioName: 'unlock-protocol-base',
},
swapPurchaser: '0x70B3c9Dd9788570FAAb24B92c3a57d99f8186Cc7',
swapPurchaser: '0x36b34e10295cCE69B652eEB5a8046041074515Da',
tokens: [
{
address: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb',
Expand Down
2 changes: 1 addition & 1 deletion packages/networks/src/networks/bsc.ts
Expand Up @@ -91,7 +91,7 @@ export const bsc: NetworkConfig = {
studioName: 'unlock-protocol-bsc',
},

swapPurchaser: '0xc9F29DdBD4D828cFb2EB491E9d48013a9c0E3C89',
swapPurchaser: '0xe49f5FD63cD7ec130B07dad30f068CC08F201e1e',

tokens: [
{
Expand Down
2 changes: 1 addition & 1 deletion packages/networks/src/networks/celo.ts
Expand Up @@ -59,7 +59,7 @@ export const celo: NetworkConfig = {
'https://api.studio.thegraph.com/query/65299/unlock-protocol-celo/version/latest',
studioName: 'unlock-protocol-celo',
},
swapPurchaser: '0x42F5c7839Bf00FAea6ca09517E96E82e7364384D',
swapPurchaser: '0x440d9D4E66d39bb28FB58729Cb4D3ead2A595591',
tokens: [
{
address: '0xef4229c8c3250C675F21BCefa42f58EfbfF6002a',
Expand Down
1 change: 0 additions & 1 deletion packages/networks/src/networks/goerli.ts
Expand Up @@ -86,7 +86,6 @@ export const goerli: NetworkConfig = {
'https://api.studio.thegraph.com/query/65299/unlock-protocol-goerli/version/latest',
studioName: 'unlock-protocol-goerli',
},
swapPurchaser: '0x49aD0039B30De002d4C27A6E8Fc026c7e23d083C',
tokens: [
{
address: '0x07865c6E87B9F70255377e024ace6630C1Eaa37F',
Expand Down
1 change: 0 additions & 1 deletion packages/networks/src/networks/mainnet.ts
Expand Up @@ -75,7 +75,6 @@ export const mainnet: NetworkConfig = {
'https://api.studio.thegraph.com/query/65299/unlock-protocol-mainnet/version/latest',
studioName: 'unlock-protocol-mainnet',
},
swapPurchaser: '0x02415541c7F4c976722493181cFdb0b46E1c94fb',
tokens: [
{
address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
Expand Down
2 changes: 1 addition & 1 deletion packages/networks/src/networks/mumbai.ts
Expand Up @@ -66,7 +66,7 @@ export const mumbai: NetworkConfig = {
'https://api.studio.thegraph.com/query/65299/unlock-protocol-mumbai/version/latest',
studioName: 'unlock-protocol-mumbai',
},
swapPurchaser: '0x302E9D970A657B42c1C124C69f3a1c1575CB4AD3',
swapPurchaser: '0xD7477B7c0CdA4204Cf860e4c27486061b15a5AC3',
tokens: [
{
address: '0x0FA8781a83E46826621b3BC094Ea2A0212e71B23',
Expand Down
2 changes: 1 addition & 1 deletion packages/networks/src/networks/optimism.ts
Expand Up @@ -85,7 +85,7 @@ export const optimism: NetworkConfig = {
'https://api.studio.thegraph.com/query/65299/unlock-protocol-optimism/version/latest',
studioName: 'unlock-protocol-optimism',
},
swapPurchaser: '0x72381052e4F7765A00a403891420BF75876c75bB',
swapPurchaser: '0x1bd356194d97297F77e081fFFAB97b57297E93e4',
tokens: [
{
address: '0x7F5c764cBc14f9669B88837ca1490cCa17c31607',
Expand Down
1 change: 0 additions & 1 deletion packages/networks/src/networks/polygon.ts
Expand Up @@ -91,7 +91,6 @@ export const polygon: NetworkConfig = {
networkName: 'matic',
studioName: 'unlock-protocol-polygon',
},
swapPurchaser: '0x33aC9CAE1Cd9CBB191116607f564F7381d81BAD9',
tokens: [
{
address: '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619',
Expand Down
2 changes: 1 addition & 1 deletion packages/networks/src/networks/sepolia.ts
Expand Up @@ -84,7 +84,7 @@ export const sepolia: NetworkConfig = {
networkName: 'sepolia',
studioName: 'unlock-protocol-sepolia',
},
swapPurchaser: '0x580A4a5a9612371e3832c4559026B9Ee3b23Cc11',
swapPurchaser: '0x692EFe2b44a531013A558E595C5dCf37DB2e4B94',
tokens: [
{
address: '0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9',
Expand Down
2 changes: 2 additions & 0 deletions packages/unlock-js/src/PublicLock/v10/extendKey.js
Expand Up @@ -131,6 +131,7 @@ export default async function (
? unlockSwapPurchaserContract?.estimateGas?.swapAndCall(
lockAddress,
swap.srcTokenAddress || ZERO,
actualAmount,
swap.amountInMax,
swap.uniswapRouter,
swap.swapCallData,
Expand Down Expand Up @@ -165,6 +166,7 @@ export default async function (
? unlockSwapPurchaserContract?.populateTransaction?.swapAndCall(
lockAddress,
swap.srcTokenAddress || ZERO,
actualAmount,
swap.amountInMax,
swap.uniswapRouter,
swap.swapCallData,
Expand Down
2 changes: 2 additions & 0 deletions packages/unlock-js/src/PublicLock/v10/purchaseKeys.js
Expand Up @@ -94,6 +94,7 @@ export default async function (options, transactionOptions = {}, callback) {
? unlockSwapPurchaserContract?.estimateGas?.swapAndCall(
lockAddress,
swap.srcTokenAddress || ZERO,
totalPrice,
swap.amountInMax,
swap.uniswapRouter,
swap.swapCallData,
Expand Down Expand Up @@ -128,6 +129,7 @@ export default async function (options, transactionOptions = {}, callback) {
? unlockSwapPurchaserContract?.populateTransaction?.swapAndCall(
lockAddress,
swap.srcTokenAddress || ZERO,
totalPrice,
swap.amountInMax,
swap.uniswapRouter,
swap.swapCallData,
Expand Down
1 change: 1 addition & 0 deletions packages/unlock-js/src/PublicLock/v4/purchaseKey.js
Expand Up @@ -83,6 +83,7 @@ export default async function (
? unlockSwapPurchaserContract?.swapAndCall(
lockAddress,
swap.srcTokenAddress || ZERO,
actualAmount,
swap.amountInMax,
swap.uniswapRouter,
swap.swapCallData,
Expand Down
2 changes: 2 additions & 0 deletions packages/unlock-js/src/PublicLock/v6/purchaseKey.js
Expand Up @@ -124,6 +124,7 @@ export default async function (
? unlockSwapPurchaserContract?.estimateGas?.swapAndCall(
lockAddress,
swap.srcTokenAddress || ZERO,
actualAmount,
swap.amountInMax,
swap.uniswapRouter,
swap.swapCallData,
Expand Down Expand Up @@ -159,6 +160,7 @@ export default async function (
? unlockSwapPurchaserContract?.swapAndCall(
lockAddress,
swap.srcTokenAddress || ZERO,
actualAmount,
swap.amountInMax,
swap.uniswapRouter,
swap.swapCallData,
Expand Down
2 changes: 2 additions & 0 deletions packages/unlock-js/src/PublicLock/v9/purchaseKey.js
Expand Up @@ -124,6 +124,7 @@ export default async function (
? unlockSwapPurchaserContract?.estimateGas?.swapAndCall(
lockAddress,
swap.srcTokenAddress || ZERO,
actualAmount,
swap.amountInMax,
swap.uniswapRouter,
swap.swapCallData,
Expand Down Expand Up @@ -161,6 +162,7 @@ export default async function (
? unlockSwapPurchaserContract?.swapAndCall(
lockAddress,
swap.srcTokenAddress || ZERO,
actualAmount,
swap.amountInMax,
swap.uniswapRouter,
swap.swapCallData,
Expand Down
3 changes: 0 additions & 3 deletions packages/unlock-js/src/abis/UnlockSwapPurchaserABI.ts

This file was deleted.

3 changes: 2 additions & 1 deletion packages/unlock-js/src/walletService.ts
Expand Up @@ -5,7 +5,8 @@ import utils from './utils'
import { passwordHookAbi } from './abis/passwordHookAbi'
import { discountCodeHookAbi } from './abis/discountCodeHookAbi'
import { discountCodeWithCapHookAbi } from './abis/discountCodeWithCapHookAbi'
import { UnlockSwapPurchaserABI } from './abis/UnlockSwapPurchaserABI'
import UnlockSwapPurchaser from '@unlock-protocol/contracts/dist/abis/utils/UnlockSwapPurchaser.json'
const UnlockSwapPurchaserABI = UnlockSwapPurchaser.abi
import { signTransferAuthorization } from './erc20'
import { CardPurchaser } from './CardPurchaser'

Expand Down
27 changes: 18 additions & 9 deletions smart-contracts/contracts/utils/UnlockSwapPurchaser.sol
Expand Up @@ -77,6 +77,17 @@ contract UnlockSwapPurchaser {
: IMintableERC20(token).balanceOf(address(this));
}

/**
* Check if lock exists
* @param lock address of the lock
*/
function lockExists(address lock) internal view returns (bool lockExists) {
(lockExists, , ) = IUnlock(unlockAddress).locks(lock);
if (!lockExists) {
revert LockDoesntExist(lock);
}
}

/**
* Swap tokens and call a function a lock contract.
*
Expand All @@ -85,6 +96,7 @@ contract UnlockSwapPurchaser {
*
* @param lock the address of the lock
* @param srcToken the address of the token sent by the user (ERC20 or address(0) for native)
* @param keyPrice the expected price of the token (calculated from the lock)
* @param amountInMax the maximum amount the user want to spend in the swap
* @param uniswapRouter the address of the uniswap router
* @param swapCalldata the Uniswap quote calldata returned by the SDK, to be sent to the router contract
Expand All @@ -98,18 +110,15 @@ contract UnlockSwapPurchaser {
function swapAndCall(
address lock,
address srcToken,
uint keyPrice,
uint amountInMax,
address uniswapRouter,
bytes memory swapCalldata,
bytes memory callData
) public payable returns (bytes memory) {
// check if lock exists
(bool lockExists, , ) = IUnlock(unlockAddress).locks(lock);
if (!lockExists) {
revert LockDoesntExist(lock);
}
// make sure the lock is registered in Unlock
lockExists(lock);

// make sure
if (uniswapRouters[uniswapRouter] != true) {
revert UnautorizedRouter(uniswapRouter);
}
Expand Down Expand Up @@ -173,19 +182,19 @@ contract UnlockSwapPurchaser {
destToken == address(0)
? getBalance(destToken) - msg.value
: getBalance(destToken)
) < balanceTokenDestBefore + IPublicLock(lock).keyPrice()
) < balanceTokenDestBefore + keyPrice
) {
revert InsufficientBalance();
}

// approve ERC20 to call the lock
if (destToken != address(0)) {
IMintableERC20(destToken).approve(lock, IPublicLock(lock).keyPrice());
IMintableERC20(destToken).approve(lock, keyPrice);
}

// call the lock
(bool lockCallSuccess, bytes memory returnData) = lock.call{
value: destToken == address(0) ? IPublicLock(lock).keyPrice() : 0
value: destToken == address(0) ? keyPrice : 0
}(callData);

if (lockCallSuccess == false) {
Expand Down

0 comments on commit e0b4f08

Please sign in to comment.