Skip to content

Commit

Permalink
fix(hardhat): use stargate composer for payload
Browse files Browse the repository at this point in the history
  • Loading branch information
xykota committed Oct 1, 2023
1 parent 592ec0c commit 90f1754
Show file tree
Hide file tree
Showing 17 changed files with 392 additions and 83 deletions.
30 changes: 23 additions & 7 deletions packages/hardhat/contracts/facets/WarpLink.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {IAllowanceTransfer} from '../interfaces/external/IAllowanceTransfer.sol'
import {PermitParams} from '../libraries/PermitParams.sol';
import {IStargateRouter} from '../interfaces/external/IStargateRouter.sol';
import {IStargateReceiver} from '../interfaces/external/IStargateReceiver.sol';
import {IStargateComposer} from '../interfaces/external/IStargateComposer.sol';

abstract contract WarpLinkCommandTypes {
uint256 internal constant COMMAND_TYPE_WRAP = 1;
Expand Down Expand Up @@ -647,8 +648,8 @@ contract WarpLink is IWarpLink, IStargateReceiver, WarpLinkCommandTypes {
* The `_nonce` field is not checked since it's assumed that LayerZero will not deliver the
* same message more than once.
*
* The Stargate router is trusted, meaning `_token` and `amountLD` is not verified. Should the
* Stargate router be compromised, an attacker can drain this contract.
* The Stargate composer is trusted, meaning `_token` and `amountLD` is not verified. Should the
* Stargate composer be compromised, an attacker can drain this contract.
*
* If the payload can not be decoded, tokens are left in this contract.
* If execution runs out of gas, tokens are left in this contract.
Expand All @@ -666,11 +667,18 @@ contract WarpLink is IWarpLink, IStargateReceiver, WarpLinkCommandTypes {
uint256 amountLD,
bytes memory payload
) external {
if (msg.sender != address(LibWarp.state().stargateRouter)) {
// The caller is composer when there is a payload, else the router
if (msg.sender != address(LibWarp.state().stargateComposer)) {
revert InvalidSgReceiverSender();
}

address srcAddress = abi.decode(_srcAddress, (address));
// NOTE: Addresses cannot be decode from bytes using `abi.decode`
// From https://ethereum.stackexchange.com/a/50528
address srcAddress;

assembly {
srcAddress := mload(add(_srcAddress, 20))
}

if (srcAddress != address(this)) {
// NOTE: This assumed that this contract is deployed at the same address on every chain
Expand Down Expand Up @@ -761,6 +769,8 @@ contract WarpLink is IWarpLink, IStargateReceiver, WarpLinkCommandTypes {
revert InsufficientOutputAmount();
}

IStargateComposer stargateComposer = LibWarp.state().stargateComposer;

if (t.token != address(0)) {
if (t.payer != address(this)) {
// Transfer tokens from the sender to this contract
Expand All @@ -770,13 +780,19 @@ contract WarpLink is IWarpLink, IStargateReceiver, WarpLinkCommandTypes {
t.payer = address(this);
}

// Allow Stargate to transfer the tokens
IERC20(t.token).forceApprove(address(LibWarp.state().stargateRouter), t.amount);
// Allow Stargate to transfer the tokens. When there is a payload, the composer is used, else the router
IERC20(t.token).forceApprove(
params.payload.length == 0 ? stargateComposer.stargateRouter() : address(stargateComposer),
t.amount
);
}

t.jumped = 1;

LibWarp.state().stargateRouter.swap{value: t.nativeValueRemaining}({
// Swap on the composer if there is a payload, else the router
IStargateRouter(
params.payload.length == 0 ? stargateComposer.stargateRouter() : address(stargateComposer)
).swap{value: t.nativeValueRemaining}({
_dstChainId: params.dstChainId,
_srcPoolId: params.srcPoolId,
_dstPoolId: params.dstPoolId,
Expand Down
6 changes: 3 additions & 3 deletions packages/hardhat/contracts/init/InitLibWarp.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ pragma solidity ^0.8.19;
import {IWETH} from '@uniswap/v2-periphery/contracts/interfaces/IWETH.sol';
import {LibWarp} from '../libraries/LibWarp.sol';
import {IPermit2} from '../interfaces/external/IPermit2.sol';
import {IStargateRouter} from '../interfaces/external/IStargateRouter.sol';
import {IStargateComposer} from '../interfaces/external/IStargateComposer.sol';

contract InitLibWarp {
function init(address weth, address permit2, address router) public {
function init(address weth, address permit2, address stargateComposer) public {
LibWarp.State storage s = LibWarp.state();

s.weth = IWETH(weth);
s.permit2 = IPermit2(permit2);
s.stargateRouter = IStargateRouter(router);
s.stargateComposer = IStargateComposer(stargateComposer);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: BUSL-1.1
import {IStargateRouter} from './IStargateRouter.sol';

pragma solidity >=0.7.6;
pragma abicoder v2;

interface IStargateComposer is IStargateRouter {
function stargateRouter() external view returns (address);
}
4 changes: 2 additions & 2 deletions packages/hardhat/contracts/libraries/LibWarp.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ pragma solidity ^0.8.19;

import {IWETH} from '@uniswap/v2-periphery/contracts/interfaces/IWETH.sol';
import {IPermit2} from '../interfaces/external/IPermit2.sol';
import {IStargateRouter} from '../interfaces/external/IStargateRouter.sol';
import {IStargateComposer} from '../interfaces/external/IStargateComposer.sol';

library LibWarp {
bytes32 constant DIAMOND_STORAGE_SLOT = keccak256('diamond.storage.LibWarp');

struct State {
IWETH weth;
IPermit2 permit2;
IStargateRouter stargateRouter;
IStargateComposer stargateComposer;
}

function state() internal pure returns (State storage s) {
Expand Down
19 changes: 12 additions & 7 deletions packages/hardhat/deploy-helpers/addresses.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
type AddressKey = 'uniswapV2Router02' | 'uniswapV2Factory' | 'weth' | 'permit2' | 'stargateRouter';
type AddressKey =
| 'uniswapV2Router02'
| 'uniswapV2Factory'
| 'weth'
| 'permit2'
| 'stargateComposer';

export const networkAddresses: Partial<Record<string, Partial<Record<AddressKey, string>>>> = {
mainnet: {
uniswapV2Router02: '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D',
uniswapV2Factory: '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f',
weth: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
permit2: '0x000000000022D473030F116dDEE9F6B43aC78BA3',
stargateRouter: '0x8731d54E9D02c286767d56ac03e8037C07e01e98',
stargateComposer: '0xeCc19E177d24551aA7ed6Bc6FE566eCa726CC8a9',
},
goerli: {
uniswapV2Router02: '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D',
uniswapV2Factory: '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f',
weth: '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6',
permit2: '0x000000000022D473030F116dDEE9F6B43aC78BA3',
stargateRouter: '0x7C5B3F4865b41b9d2B6dE65fdfbB47af06AC41f0',
stargateComposer: '0x7C5B3F4865b41b9d2B6dE65fdfbB47af06AC41f0',
},
optimismGoerli: {
weth: '0x4200000000000000000000000000000000000006',
stargateRouter: '0xb82E8737e7BA953CB4462561639f32Fd7F0974c4',
stargateComposer: '0xb82E8737e7BA953CB4462561639f32Fd7F0974c4',
},
sepolia: {
uniswapV2Router02: '0xC532a74256D3Db42D0Bf7a0400fEFDbad7694008',
Expand All @@ -28,16 +33,16 @@ export const networkAddresses: Partial<Record<string, Partial<Record<AddressKey,
polygon: {
weth: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270',
permit2: '0x000000000022D473030F116dDEE9F6B43aC78BA3',
stargateRouter: '0x45A01E4e04F14f7A4a6702c74187c5F6222033cd',
stargateComposer: '0xeCc19E177d24551aA7ed6Bc6FE566eCa726CC8a9',
},
arbitrum: {
weth: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
permit2: '0x000000000022D473030F116dDEE9F6B43aC78BA3',
stargateRouter: '0x53Bf833A5d6c4ddA888F69c22C88C9f356a41614',
stargateComposer: '0xeCc19E177d24551aA7ed6Bc6FE566eCa726CC8a9',
},
optimism: {
weth: '0x4200000000000000000000000000000000000006',
permit2: '0x000000000022D473030F116dDEE9F6B43aC78BA3',
stargateRouter: '0x53Bf833A5d6c4ddA888F69c22C88C9f356a41614',
stargateComposer: '0xeCc19E177d24551aA7ed6Bc6FE566eCa726CC8a9',
},
};
2 changes: 1 addition & 1 deletion packages/hardhat/deploy/003_facets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ const func = async function (hre: HardhatRuntimeEnvironment) {
calldata: initContract.interface.encodeFunctionData('init', [
addresses.weth ?? ethers.ZeroAddress,
addresses.permit2 ?? ethers.ZeroAddress,
addresses.stargateRouter ?? ethers.ZeroAddress,
addresses.stargateComposer ?? ethers.ZeroAddress,
]),
};
},
Expand Down
2 changes: 2 additions & 0 deletions packages/hardhat/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ evm_version = 'paris'
solc_version = '0.8.19'

[rpc_endpoints]
mainnet = "${MAINNET_RPC_URL}"
goerli = "${GOERLI_RPC_URL}"
optimism_goerli = "${OPTIMISM_GOERLI_RPC_URL}"

[etherscan]
mainnet = { key = "${ETHERSCAN_API_KEY}" }
goerli = { key = "${ETHERSCAN_API_KEY}" }
optimism_goerli = { key = "${ETHERSCAN_API_KEY}" }
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import {IWarpLink} from 'contracts/interfaces/IWarpLink.sol';
import {WarpLinkCommandTypes} from 'contracts/facets/WarpLink.sol';
import {IStargateRouter} from 'test/foundry/helpers/IStargateRouter.sol';
import {IStargateComposer} from 'test/foundry/helpers/IStargateComposer.sol';
import {IStargateRouter} from 'contracts/interfaces/external/IStargateRouter.sol';
import {IAllowanceTransfer} from 'contracts/interfaces/external/IAllowanceTransfer.sol';
import {PermitParams} from 'contracts/libraries/PermitParams.sol';
import {Goerli, OptimismGoerli, Addresses} from '../test/foundry/helpers/Networks.sol';
Expand All @@ -21,9 +22,9 @@ import {PermitSignature} from 'test/foundry/helpers/PermitSignature.sol';
* there are no actions that can be made using Statgate mock USDC.
*
* Invoke this script using:
* forge script script/jump.sol --rpc-url goerli --no-storage-caching -vvvv --broadcast
* forge script script/jumpAndEngageGoerli.sol --rpc-url goerli --no-storage-caching -vvvv --broadcast
*/
contract JumpGoerli is Script, WarpLinkCommandTypes, PermitSignature {
contract JumpAndEngageGoerli is Script, WarpLinkCommandTypes, PermitSignature {
address goerliDiamondAddr = 0x2A104392321e978495dBC91b68914eDbA3126D9c;

function setUp() public {}
Expand Down Expand Up @@ -70,7 +71,7 @@ contract JumpGoerli is Script, WarpLinkCommandTypes, PermitSignature {
destParamsEncoded
);

(uint256 nativeWei, ) = IStargateRouter(Goerli.STARGATE_ROUTER_ADDR).quoteLayerZeroFee({
(uint256 nativeWei, ) = IStargateComposer(Goerli.STARGATE_COMPOSER_ADDR).quoteLayerZeroFee({
_dstChainId: dstChainId, // Goerli
_functionType: 1, // Swap remote
_toAddress: abi.encodePacked(goerliDiamondAddr),
Expand Down Expand Up @@ -142,11 +143,11 @@ contract JumpGoerli is Script, WarpLinkCommandTypes, PermitSignature {
tokenOut: Goerli.STARGATE_MOCK_USDC_ADDR,
commands: sourceCommands,
amountIn: srcAmountIn,
amountOut: 0, // TODO
amountOut: (srcAmountIn * 995) / 1000,
recipient: address(0), // Unused
partner: address(0), // Unused
feeBps: 0, // Unused
slippageBps: 0,
slippageBps: 100,
deadline: deadline
}),
permitParams
Expand Down
135 changes: 135 additions & 0 deletions packages/hardhat/script/jumpGoerli.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import 'forge-std/console2.sol';
import 'forge-std/Script.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import {IWarpLink} from 'contracts/interfaces/IWarpLink.sol';
import {WarpLinkCommandTypes} from 'contracts/facets/WarpLink.sol';
import {IStargateComposer} from 'test/foundry/helpers/IStargateComposer.sol';
import {IStargateRouter} from 'contracts/interfaces/external/IStargateRouter.sol';
import {IAllowanceTransfer} from 'contracts/interfaces/external/IAllowanceTransfer.sol';
import {PermitParams} from 'contracts/libraries/PermitParams.sol';
import {Goerli, OptimismGoerli, Addresses} from '../test/foundry/helpers/Networks.sol';
import {PermitSignature} from 'test/foundry/helpers/PermitSignature.sol';

/**
* Bridge mock USDC from Goerli to Optimism-Goerli.
*
* Invoke this script using:
* forge script script/jumpGoerli.sol --rpc-url goerli --no-storage-caching -vvvv --broadcast
*/
contract JumpAndEngageGoerli is Script, WarpLinkCommandTypes, PermitSignature {
address goerliDiamondAddr = 0x2A104392321e978495dBC91b68914eDbA3126D9c;

function setUp() public {}

function run() public {
uint256 privateKey = vm.deriveKey(vm.envString('DEV_MNEMONIC'), 1);
address user = vm.rememberKey(privateKey);

console2.log('User: %s', user);

uint48 deadline = uint48(block.timestamp) + 60 * 60 * 24 * 365;

uint256 srcAmountIn = 100 * (10 ** 6);
uint16 dstChainId = OptimismGoerli.STARGATE_CHAIN_ID;

bytes memory sourceCommands = bytes.concat(
abi.encodePacked(
(uint8)(1), // Command count
(uint8)(COMMAND_TYPE_JUMP_STARGATE),
(uint16)(dstChainId), // dstChainId
(uint8)(1), // srcPoolId (USDC)
(uint8)(1), // dstPoolId (USDC),
uint32(0), // dstGasForCall
uint256(0) // NOTE: Unnecessarily large type
)
);

(uint256 nativeWei, ) = IStargateComposer(Goerli.STARGATE_COMPOSER_ADDR).quoteLayerZeroFee({
_dstChainId: dstChainId, // Goerli
_functionType: 1, // Swap remote
_toAddress: abi.encodePacked(goerliDiamondAddr),
_transferAndCallPayload: '',
_lzTxParams: IStargateRouter.lzTxObj({
dstGasForCall: 0,
dstNativeAmount: 0,
dstNativeAddr: ''
})
});

console2.log('Native fee: %s', nativeWei);

{
uint256 srcTokenInAllowance = IERC20(Goerli.STARGATE_MOCK_USDC_ADDR).allowance(
user,
address(Addresses.PERMIT2)
);

console2.log('srcTokenInAllowance: %s', srcTokenInAllowance);

if (srcTokenInAllowance < srcAmountIn) {
vm.startBroadcast(user);

SafeERC20.forceApprove(
IERC20(Goerli.STARGATE_MOCK_USDC_ADDR),
address(Addresses.PERMIT2),
type(uint256).max
);

vm.stopBroadcast();
}
}

PermitParams memory permitParams;

{
(, , uint48 nonce) = Addresses.PERMIT2.allowance(
user,
Goerli.STARGATE_MOCK_USDC_ADDR,
address(goerliDiamondAddr)
);

IAllowanceTransfer.PermitSingle memory permit = IAllowanceTransfer.PermitSingle(
IAllowanceTransfer.PermitDetails({
token: Goerli.STARGATE_MOCK_USDC_ADDR,
amount: uint160(srcAmountIn),
expiration: deadline,
nonce: nonce
}),
address(goerliDiamondAddr),
deadline
);

bytes memory sig = getPermitSignature(
permit,
privateKey,
Addresses.PERMIT2.DOMAIN_SEPARATOR()
);

permitParams = PermitParams({nonce: nonce, signature: sig});
}

vm.startBroadcast(user);

IWarpLink(goerliDiamondAddr).warpLinkEngage{value: nativeWei}(
IWarpLink.Params({
tokenIn: Goerli.STARGATE_MOCK_USDC_ADDR,
tokenOut: Goerli.STARGATE_MOCK_USDC_ADDR,
commands: sourceCommands,
amountIn: srcAmountIn,
amountOut: (srcAmountIn * 995) / 1000,
recipient: user,
partner: address(0), // Unused
feeBps: 0, // Unused
slippageBps: 100,
deadline: deadline
}),
permitParams
);

vm.stopBroadcast();
}
}
Loading

0 comments on commit 90f1754

Please sign in to comment.