Skip to content

Commit

Permalink
pay contract: pay axelar gas using native token
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewliu08 committed Oct 31, 2024
1 parent 7a4272f commit de2d987
Show file tree
Hide file tree
Showing 11 changed files with 557 additions and 520 deletions.
11 changes: 5 additions & 6 deletions packages/contract/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ full:
make build

test:
# $(eval BASE_MAINNET_RPC ?= $(or $(BASE_MAINNET_RPC),https://base-rpc.publicnode.com)) # Use environment variable if set, otherwise use default
# @echo Running tests with RPC: $(BASE_MAINNET_RPC)
# forge test -vvv --fork-url "$(BASE_MAINNET_RPC)" --no-match-path "test/uniswap/*"
# forge test -vvv --fork-url "$(BASE_MAINNET_RPC)" --fork-block-number 14513720 --match-path "test/uniswap/*" --no-match-path "test/uniswap/Quoter.t.sol"
# forge test -vvv --fork-url "$(BASE_MAINNET_RPC)" --fork-block-number 15950101 --match-path "test/uniswap/Quoter.t.sol"
forge test -vvv --match-path "test/DaimoPay.t.sol"
$(eval BASE_MAINNET_RPC ?= $(or $(BASE_MAINNET_RPC),https://base-rpc.publicnode.com)) # Use environment variable if set, otherwise use default
@echo Running tests with RPC: $(BASE_MAINNET_RPC)
forge test -vvv --fork-url "$(BASE_MAINNET_RPC)" --no-match-path "test/uniswap/*"
forge test -vvv --fork-url "$(BASE_MAINNET_RPC)" --fork-block-number 14513720 --match-path "test/uniswap/*" --no-match-path "test/uniswap/Quoter.t.sol"
forge test -vvv --fork-url "$(BASE_MAINNET_RPC)" --fork-block-number 15950101 --match-path "test/uniswap/Quoter.t.sol"

coverage:
$(eval BASE_MAINNET_RPC ?= $(or $(BASE_MAINNET_RPC),https://base-rpc.publicnode.com)) # Use environment variable if set, otherwise use default
Expand Down

Large diffs are not rendered by default.

This file was deleted.

This file was deleted.

4 changes: 2 additions & 2 deletions packages/contract/script/deployV2.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ SCRIPTS=(
# "script/pay/DeployDaimoPayAcrossBridger.s.sol"
# "script/pay/DeployDaimoPayCCTPBridger.s.sol"
# "script/pay/DeployDaimoPayAxelarBridger.s.sol"
"script/pay/DeployDaimoPayAxelarReceiver.s.sol"
# "script/pay/DeployDaimoPayAxelarReceiver.s.sol"
# "script/pay/DeployDaimoPayBridger.s.sol"
# "script/pay/DeployPayIntentFactory.s.sol"
# "script/pay/DeployDaimoPay.s.sol"
Expand All @@ -35,7 +35,7 @@ SCRIPTS=(
)
CHAINS=(
# MAINNETS
"$ETHERSCAN_API_KEY_BASE,https://base-mainnet.g.alchemy.com/v2/$ALCHEMY_API_KEY"
# "$ETHERSCAN_API_KEY_BASE,https://base-mainnet.g.alchemy.com/v2/$ALCHEMY_API_KEY"
# "$ETHERSCAN_API_KEY_OP,https://opt-mainnet.g.alchemy.com/v2/$ALCHEMY_API_KEY"
# "$ETHERSCAN_API_KEY_ARB,https://arb-mainnet.g.alchemy.com/v2/$ALCHEMY_API_KEY"
# "$ETHERSCAN_API_KEY_POLYGON,https://polygon-mainnet.g.alchemy.com/v2/$ALCHEMY_API_KEY"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import "forge-std/Script.sol";
import "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol";

import "../../src/pay/DaimoPayAxelarBridger.sol";
import "../../src/pay/DaimoPayAxelarReceiver.sol";
import "../Constants.s.sol";

contract DeployDaimoPayAxelarBridger is Script {
Expand All @@ -24,7 +23,7 @@ contract DeployDaimoPayAxelarBridger is Script {
address initOwner = msg.sender;

address bridger = CREATE3.deploy(
keccak256("DaimoPayAxelarBridger-test3"),
keccak256("DaimoPayAxelarBridger-warmup5"),
abi.encodePacked(
type(DaimoPayAxelarBridger).creationCode,
abi.encode(
Expand Down Expand Up @@ -54,7 +53,7 @@ contract DeployDaimoPayAxelarBridger is Script {
{
address axelarReceiver = CREATE3.getDeployed(
msg.sender,
keccak256("DaimoPayAxelarReceiver-test3")
keccak256("DaimoPayAxelarBridger-warmup5")
);

bool testnet = _isTestnet(block.chainid);
Expand Down
31 changes: 0 additions & 31 deletions packages/contract/script/pay/DeployDaimoPayAxelarReceiver.s.sol

This file was deleted.

88 changes: 67 additions & 21 deletions packages/contract/src/pay/DaimoPayAxelarBridger.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.12;

import {AxelarExpressExecutableWithToken} from "@axelar-network/contracts/express/AxelarExpressExecutableWithToken.sol";
import {IAxelarGatewayWithToken} from "@axelar-network/contracts/interfaces/IAxelarGatewayWithToken.sol";
import {IAxelarGasService} from "@axelar-network/contracts/interfaces/IAxelarGasService.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
Expand All @@ -17,7 +18,11 @@ import "../interfaces/IDaimoPayBridger.sol";
/// @dev Bridges assets from to a destination chain using Axelar Protocol. Makes
/// the assumption that the local token is an ERC20 token and has a 1 to 1 price
/// with the corresponding destination token.
contract DaimoPayAxelarBridger is IDaimoPayBridger, Ownable2Step {
contract DaimoPayAxelarBridger is
IDaimoPayBridger,
AxelarExpressExecutableWithToken,
Ownable2Step
{
using SafeERC20 for IERC20;
using Strings for address;

Expand All @@ -26,6 +31,7 @@ contract DaimoPayAxelarBridger is IDaimoPayBridger, Ownable2Step {
string tokenSymbol;
address localTokenAddr;
address receiverContract;
// Fee to be paid in native token for Axelar's bridging gas fee
uint256 fee;
}

Expand Down Expand Up @@ -66,7 +72,10 @@ contract DaimoPayAxelarBridger is IDaimoPayBridger, Ownable2Step {
uint256[] memory _toChainIds,
address[] memory _toTokens,
AxelarBridgeRoute[] memory _bridgeRoutes
) Ownable(_owner) {
)
Ownable(_owner)
AxelarExpressExecutableWithToken(address(_axelarGateway))
{
axelarGateway = _axelarGateway;
axelarGasService = _axelarGasService;

Expand Down Expand Up @@ -135,11 +144,40 @@ contract DaimoPayAxelarBridger is IDaimoPayBridger, Ownable2Step {
});
}

// ----- AXELAR EXECUTABLE FUNCTIONS -----

/// Part of the AxelarExpressExecutableWithToken interface. Used to make
/// a contract call on the destination chain without tokens. Not supported
/// by this implementation because we will always be bridging tokens.
function _execute(
bytes32 /* commandId */,
string calldata /* sourceChain */,
string calldata /* sourceAddress */,
bytes calldata /* payload */
) internal pure override {
revert("DPAxB: _execute not supported");
}

/// Part of the AxelarExpressExecutableWithToken interface. Used to make
/// a contract call on the destination chain with tokens. Will always be
/// used to transfer tokens to the intent address on the destination chain.
function _executeWithToken(
bytes32 /* commandId */,
string calldata /* sourceChain */,
string calldata /* sourceAddress */,
bytes calldata payload,
string calldata tokenSymbol,
uint256 amount
) internal override {
address recipient = abi.decode(payload, (address));
address tokenAddress = axelarGateway.tokenAddresses(tokenSymbol);

IERC20(tokenAddress).safeTransfer(recipient, amount);
}

// ----- BRIDGING FUNCTIONS -----

/// Get the local token that corresponds to the destination token. Get the
/// minimum input amount for a given output amount. The input amount must
/// cover the max of the percentage fee and the flat fee.
/// Get the local token that corresponds to the destination token.
function getInputTokenAmount(
uint256 toChainId,
address toToken,
Expand All @@ -148,20 +186,22 @@ contract DaimoPayAxelarBridger is IDaimoPayBridger, Ownable2Step {
AxelarBridgeRoute memory bridgeRoute = bridgeRouteMapping[toChainId][
toToken
];
return (bridgeRoute.localTokenAddr, toAmount + bridgeRoute.fee);
return (bridgeRoute.localTokenAddr, toAmount);
}

/// Initiate a bridge to a destination chain using Across Protocol.
/// Initiate a bridge to a destination chain using Axelar Protocol.
function sendToChain(
uint256 toChainId,
address toAddress,
address toToken,
uint256 toAmount,
bytes calldata /* extraData */
bytes calldata extraData
) public {
require(toChainId != block.chainid, "DPAxB: same chain");
require(toAmount > 0, "DPAxB: zero amount");

address refundAddress = abi.decode(extraData, (address));

// Get the local token that corresponds to the destination token.
(address inputToken, uint256 inputAmount) = getInputTokenAmount({
toChainId: toChainId,
Expand All @@ -173,36 +213,40 @@ contract DaimoPayAxelarBridger is IDaimoPayBridger, Ownable2Step {
toToken
];

// Move input token from caller to this contract and approve the
// AxelarGateway contract.
// Move input token from caller to this contract
IERC20(inputToken).safeTransferFrom({
from: msg.sender,
to: address(this),
value: inputAmount
});
IERC20(inputToken).forceApprove({
spender: address(axelarGateway),
value: inputAmount
});

axelarGasService.payGasForExpressCallWithToken(
msg.sender,
// Pay for Axelar's bridging gas fee.
axelarGasService.payNativeGasForContractCallWithToken{
value: bridgeRoute.fee
}(
address(this),
bridgeRoute.destChainName,
bridgeRoute.receiverContract.toHexString(),
abi.encode(toAddress),
bridgeRoute.tokenSymbol,
inputAmount,
inputToken,
bridgeRoute.fee,
msg.sender
toAmount,
refundAddress
);

// Approve the AxelarGateway contract and initiate the bridge. Send the
// tokens to the DaimoPayAxelarBridger on the destination chain. The
// _executeWithToken function will be called on the destination chain
// to transfer the tokens to the toAddress.
IERC20(inputToken).forceApprove({
spender: address(axelarGateway),
value: toAmount
});
axelarGateway.callContractWithToken(
bridgeRoute.destChainName,
bridgeRoute.receiverContract.toHexString(),
abi.encode(toAddress),
bridgeRoute.tokenSymbol,
inputAmount
toAmount
);

emit BridgeInitiated({
Expand All @@ -215,4 +259,6 @@ contract DaimoPayAxelarBridger is IDaimoPayBridger, Ownable2Step {
toAmount: toAmount
});
}

receive() external payable {}
}
47 changes: 0 additions & 47 deletions packages/contract/src/pay/DaimoPayAxelarReceiver.sol

This file was deleted.

20 changes: 20 additions & 0 deletions packages/contract/src/pay/DaimoPayRelayer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,26 @@ contract DaimoPayRelayer is Ownable2Step {
);
}

function startIntent(
DaimoPay dp,
PayIntent calldata intent,
Call[] calldata calls,
bytes calldata bridgeExtraData,
uint256 bridgeGasFee
) public onlyOwner {
// We use Axelar when bridging to BSC. Axelar requries a native token
// payment for the gas fee.
if (intent.toChainId == 56) {
DaimoPayBridger bridger = dp.bridger();
IDaimoPayBridger axelarBridger = bridger.chainIdToBridger(56);
(bool success, ) = address(axelarBridger).call{value: bridgeGasFee}(
""
);
require(success, "DPR: axelar fee transfer failed");
}
dp.startIntent(intent, calls, bridgeExtraData);
}

function fastFinish(
DaimoPay dp,
PayIntent calldata intent,
Expand Down
Loading

0 comments on commit de2d987

Please sign in to comment.