Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(smart-contracts): pass keyPrice as arg in Swap purchaser #13458

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
7 changes: 1 addition & 6 deletions packages/contracts/src/index.ts
Expand Up @@ -77,9 +77,4 @@ export { UnlockDiscountTokenV2 }
export { UnlockDiscountTokenV3 }
export { GovernorUnlockProtocol }
export { GovernorUnlockProtocolTimelock }
export {
LockSerializer,
UnlockSwapPurchaser,
UnlockSwapBurner,
UniswapOracleV3,
}
export { LockSerializer,UnlockSwapPurchaser,UnlockSwapBurner,UniswapOracleV3 }
8 changes: 3 additions & 5 deletions packages/hardhat-helpers/src/deploy.js
Expand Up @@ -24,17 +24,15 @@ export const deployContract = async (
console.log(` > contract deployed at : ${address} (tx: ${hash})`)

if (!(await isLocalhost())) {
const args = {
const verifyArgs = {
address,
deployArgs,
}

// pass fully qualified path for verification
if (typeof contractNameOrFullyQualifiedNameOrEthersFactory === 'string') {
args.contract = contractNameOrFullyQualifiedNameOrEthersFactory
verifyArgs.contract = contractNameOrFullyQualifiedNameOrEthersFactory
}

await verify(args)
await verify(verifyArgs)
}

return {
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
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
3 changes: 2 additions & 1 deletion packages/networks/src/networks/sepolia.ts
Expand Up @@ -84,7 +84,8 @@ export const sepolia: NetworkConfig = {
networkName: 'sepolia',
studioName: 'unlock-protocol-sepolia',
},
swapPurchaser: '0x580A4a5a9612371e3832c4559026B9Ee3b23Cc11',

swapPurchaser: '0x692EFe2b44a531013A558E595C5dCf37DB2e4B94',
tokens: [
{
address: '0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9',
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