Skip to content
Open
44 changes: 44 additions & 0 deletions contracts/IOjo.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,26 @@ interface IOjo {
bytes calldata commandParams
) external payable;

/// @notice Triggers the relaying of price data from the Ojo Network to the Ojo contract and uses said price data
/// when calling the specified contract method at the specified contract address. Uses ERC-20 token for gas payment
/// instead of native gas.
/// @dev Reverts if contract method call does not succeed.
/// @param assetNames List of assets to be relayed from the Ojo Network and used by the contract method.
/// @param contractAddress Address of contract containing the contract method to be called.
/// @param commandSelector First four bytes of the Keccak-256 hash of the contract method to be called.
/// @param commandParams Abi encoded parameters to be used when calling the contract method (excluding assetNames
/// parameter).
/// @param gasToken The address of the ERC-20 token used to pay for gas.
/// @param gasFeeAmount The amount of tokens to pay for gas.
function callContractMethodWithOjoPriceDataNonNativeGas(
bytes32[] calldata assetNames,
address contractAddress,
bytes4 commandSelector,
bytes calldata commandParams,
address gasToken,
uint256 gasFeeAmount
) external;

/// @notice Triggers the relaying of price data from the Ojo Network to the Ojo contract with an ERC-20 token for
/// and uses said price data when calling the specified contract method at the specified contract address.
/// @dev Reverts if contract method call does not succeed.
Expand All @@ -48,6 +68,30 @@ interface IOjo {
uint256 amount
) external payable;

/// @notice Triggers the relaying of price data from the Ojo Network to the Ojo contract with an ERC-20 token for
/// and uses said price data when calling the specified contract method at the specified contract address. Uses
/// ERC-20 token for gas payment instead of native gas.
/// @dev Reverts if contract method call does not succeed.
/// @param assetNames List of assets to be relayed from the Ojo Network and used by the contract method.
/// @param contractAddress Address of contract containing the contract method to be called.
/// @param commandSelector First four bytes of the Keccak-256 hash of the contract method to be called.
/// @param commandParams Abi encoded parameters to be used when calling the contract method (excluding assetNames
/// parameter).
/// @param symbol The symbol of the token to be sent with the call.
/// @param amount The amount of tokens to be sent with the call.
/// @param gasToken The address of the ERC-20 token used to pay for gas.
/// @param gasFeeAmount The amount of tokens to pay for gas.
function callContractMethodWithOjoPriceDataAndTokenNonNativeGas(
bytes32[] calldata assetNames,
address contractAddress,
bytes4 commandSelector,
bytes calldata commandParams,
string memory symbol,
uint256 amount,
address gasToken,
uint256 gasFeeAmount
) external;

/// @notice Returns the price data of a specified asset.
/// @dev Price data is stored in a mapping, so requesting the price data of a non existent asset will return the
/// zero byte representation of OjoTypes.PriceData.
Expand Down
48 changes: 48 additions & 0 deletions contracts/MockOjo.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,26 @@ contract MockOjo {
);
}

function relayOjoPriceDataNonNativeGas(
bytes32[] calldata assetNames,
address gasToken,
uint256 gasFeeAmount
) external payable {
IERC20(gasToken).transferFrom(msg.sender, address(this), gasFeeAmount);
IERC20(gasToken).approve(address(ojo), gasFeeAmount);

bytes memory commandParams = "0x";

ojo.callContractMethodWithOjoPriceDataNonNativeGas(
assetNames,
address(this),
OjoTypes.EMPTY_COMMAND_SELECTOR,
commandParams,
gasToken,
gasFeeAmount
);
}

function relayOjoPriceDataWithToken(
bytes32[] calldata assetNames,
string memory symbol,
Expand All @@ -56,6 +76,34 @@ contract MockOjo {
);
}

function relayOjoPriceDataWithTokenNonNativeGas(
bytes32[] calldata assetNames,
string memory symbol,
uint256 amount,
address tokenAddress,
address gasToken,
uint256 gasFeeAmount
) external payable {
IERC20(tokenAddress).transferFrom(msg.sender, address(this), amount);
IERC20(tokenAddress).approve(address(ojo), amount);

IERC20(gasToken).transferFrom(msg.sender, address(this), gasFeeAmount);
IERC20(gasToken).approve(address(ojo), gasFeeAmount);

bytes memory commandParams = "0x";

ojo.callContractMethodWithOjoPriceDataAndTokenNonNativeGas(
assetNames,
address(this),
OjoTypes.EMPTY_COMMAND_SELECTOR,
commandParams,
symbol,
amount,
gasToken,
gasFeeAmount
);
}

function setBalanceWithOjoPriceData(
bytes32[] calldata assetNames,
uint256 multiplier
Expand Down
70 changes: 70 additions & 0 deletions contracts/Ojo.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,37 @@ contract Ojo is IOjo, AxelarExpressExecutable, Upgradable {
gateway.callContract(ojoChain, ojoAddress, payloadWithVersion);
}

function callContractMethodWithOjoPriceDataNonNativeGas(
bytes32[] calldata assetNames,
address contractAddress,
bytes4 commandSelector,
bytes calldata commandParams,
address gasToken,
uint256 gasFeeAmount
) external {
require(assetNames.length <= assetLimit, "Number of assets requested is over limit");

bytes memory payloadWithVersion = abi.encodePacked(
bytes4(uint32(0)), // version number
abi.encode(assetNames, contractAddress, commandSelector, commandParams, block.timestamp) // payload
);

IERC20(gasToken).transferFrom(msg.sender, address(this), gasFeeAmount);
IERC20(gasToken).approve(address(gasReceiver), gasFeeAmount);

gasReceiver.payGasForContractCall(
address(this),
ojoChain,
ojoAddress,
payloadWithVersion,
gasToken,
gasFeeAmount,
msg.sender
);

gateway.callContract(ojoChain, ojoAddress, payloadWithVersion);
}

function callContractMethodWithOjoPriceDataAndToken(
bytes32[] calldata assetNames,
address contractAddress,
Expand Down Expand Up @@ -84,6 +115,45 @@ contract Ojo is IOjo, AxelarExpressExecutable, Upgradable {
gateway.callContractWithToken(ojoChain, ojoAddress, payloadWithVersion, symbol, amount);
}

function callContractMethodWithOjoPriceDataAndTokenNonNativeGas(
bytes32[] calldata assetNames,
address contractAddress,
bytes4 commandSelector,
bytes calldata commandParams,
string memory symbol,
uint256 amount,
address gasToken,
uint256 gasFeeAmount
) external {
require(assetNames.length <= assetLimit, "Number of assets requested is over limit");

address tokenAddress = gateway.tokenAddresses(symbol);
IERC20(tokenAddress).transferFrom(msg.sender, address(this), amount);
IERC20(tokenAddress).approve(address(gateway), amount);

IERC20(gasToken).transferFrom(msg.sender, address(this), gasFeeAmount);
IERC20(gasToken).approve(address(gasReceiver), gasFeeAmount);

bytes memory payloadWithVersion = abi.encodePacked(
bytes4(uint32(0)), // version number
abi.encode(assetNames, contractAddress, commandSelector, commandParams, block.timestamp) // payload
);

gasReceiver.payGasForContractCallWithToken(
address(this),
ojoChain,
ojoAddress,
payloadWithVersion,
symbol,
amount,
gasToken,
gasFeeAmount,
msg.sender
);

gateway.callContractWithToken(ojoChain, ojoAddress, payloadWithVersion, symbol, amount);
}

function _setup(bytes calldata data) internal override {
(string memory ojoChain_, string memory ojoAddress_, uint256 resolveWindow_, uint16 assetLimit_) = abi.decode(
data,
Expand Down
Loading