trustless-generic-relayer/ethereum/contracts/example_integrations/xMint/Spoke.sol

114 lines
4.4 KiB
Solidity

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
//import "solidity-bytes-utils/BytesLib.sol";
import "../../interfaces/IWormhole.sol";
import "../../interfaces/ITokenBridge.sol";
import "../../interfaces/IWormholeReceiver.sol";
import "../../interfaces/IWormholeRelayer.sol";
contract XmintSpoke is IWormholeReceiver {
// using BytesLib for bytes;
address owner;
IWormhole core_bridge;
ITokenBridge token_bridge;
IWormholeRelayer core_relayer;
uint16 hub_contract_chain;
bytes32 hub_contract_address;
uint32 nonce = 1;
uint8 consistencyLevel = 200;
uint32 SAFE_DELIVERY_GAS_CAPTURE = 1000000; //Capture 1 million gas for fees
event Log(string indexed str);
constructor(
address coreBridgeAddress,
address tokenBridgeAddress,
address coreRelayerAddress,
uint16 hubChain,
bytes32 hubContractwhFormat
) {
owner = msg.sender;
core_bridge = IWormhole(coreBridgeAddress);
token_bridge = ITokenBridge(tokenBridgeAddress);
core_relayer = IWormholeRelayer(coreRelayerAddress);
hub_contract_chain = hubChain;
hub_contract_address = hub_contract_address;
}
//This function captures native (ETH) tokens from the user, requests a token transfer to the hub contract,
//And then requests delivery from relayer network.
function purchaseTokens() public payable {
//Calculate how many tokens will be required to cover transaction fees.
uint256 deliveryFeeBuffer =
core_relayer.quoteGas(hub_contract_chain, SAFE_DELIVERY_GAS_CAPTURE, core_relayer.getDefaultRelayProvider());
//require that enough funds were paid to cover this transaction and the relay costs
require(msg.value > deliveryFeeBuffer + core_bridge.messageFee());
uint256 bridgeAmount = msg.value - deliveryFeeBuffer - core_bridge.messageFee();
(bool success, bytes memory data) = address(token_bridge).call{value: bridgeAmount + core_bridge.messageFee()}(
abi.encodeCall(
ITokenBridge.wrapAndTransferETHWithPayload,
(hub_contract_chain, hub_contract_address, nonce, abi.encode(core_relayer.toWormholeFormat(msg.sender)))
)
);
//Request delivery from the relayer network.
requestDelivery();
}
//This function receives messages back from the Hub contract and distributes the tokens to the user.
function receiveWormholeMessages(bytes[] memory vaas, bytes[] memory additionalData) public payable override {
//Complete the token bridge transfer
ITokenBridge.TransferWithPayload memory transferResult =
token_bridge.parseTransferWithPayload(token_bridge.completeTransferWithPayload(vaas[0]));
require(
transferResult.fromAddress == hub_contract_address
&& core_bridge.parseVM(vaas[0]).emitterChainId == hub_contract_chain
);
//TODO is the token address the token being transferred, or the origin address?
ERC20 token = ERC20(core_relayer.fromWormholeFormat(transferResult.tokenAddress));
token.transfer(
core_relayer.fromWormholeFormat(bytesToBytes32(transferResult.payload, 0)), transferResult.amount
);
}
function requestDelivery() internal {
uint256 maxTransactionFee =
core_relayer.quoteGas(hub_contract_chain, SAFE_DELIVERY_GAS_CAPTURE, core_relayer.getDefaultRelayProvider());
uint256 receiverValue = 0;
IWormholeRelayer.Send memory request = IWormholeRelayer.Send({
targetChain: hub_contract_chain,
targetAddress: hub_contract_address,
refundAddress: hub_contract_address, // This will be ignored on the target chain because the intent is to perform a forward
maxTransactionFee: maxTransactionFee,
receiverValue: receiverValue, // not needed in this case.
relayParameters: core_relayer.getDefaultRelayParams() //no overrides
});
core_relayer.send{value: maxTransactionFee + receiverValue}(
request, nonce, core_relayer.getDefaultRelayProvider()
);
}
function bytesToBytes32(bytes memory b, uint256 offset) private pure returns (bytes32) {
bytes32 out;
for (uint256 i = 0; i < 32; i++) {
out |= bytes32(b[offset + i] & 0xFF) >> (i * 8);
}
return out;
}
}