xmint: evm documentation

This commit is contained in:
Eric Wong 2022-10-05 11:33:01 -05:00
parent b0d45e4017
commit 2c9b334af5
3 changed files with 146 additions and 9 deletions

View File

@ -17,17 +17,29 @@ Compiles and deploys the contracts using the chain appropriate tools. The deploy
For EVM, this is done using [forge](https://getfoundry.sh) and for Solana, this is done using [Anchor](https://www.anchor-lang.com/).
## Register App
Takes the deployed contract address from the target chain and registers it on the source chain. No Wormhole interaction is necessary for this step.
Takes the deployed token address from the target chain and attests it on the source chain. A Wormhole token attestation interaction is necessary for this step.
Initiate a two step process of:
1. Register deployed contract address from target chain onto source chain
- no Wormhole interaction is necessary for this step
2. Attest deployed token address from target chain onto source chain
- Wormhole token attestation interaction is necessary for this step
## Buy Token
Initiates a three step process of:
1. Create a buy VAA on source chain
- Emits a Contract Controlled Payload that contains (1) the amount of tokens the user wants to pay and (2) the user's wallet address.
<!-- - On Ethereum, this maps to `transferFromEthNative()`. On Solana, this maps to `transferFromSolana()`. -->
2. Submit buy VAA on target chain and generate a VAA to claim tokens
- Verifies the buy VAA generated in the previous step, mints the appropriate amount of target chain tokens, and generates a Token Transfer VAA that sends the minted amount of tokens to the user's wallet on source chain.
<!-- - On Ethereum, this maps to `submitForeignPurchase()`. On Solana, -->
3. Claim tokens on source chain
- Verifies the Token Transfer VAA generated in the previous step to claim a wrapped version of the token on the source chain.
## Sell Token
Initiates a three-step process of:
1. Create a sell VAA on source chain
2. Submit sell VAA on target chain and generate a VAA to claim tokens
3. Claim tokens on source chain
## Balance
## Balance
Returns the balance of a token by chain in question and chain token originated from.

View File

@ -34,4 +34,4 @@ Orchestrator is a ts client that takes arguments from the command line to call v
### xdapp.config.json
This maintains some constants about the chains RPC endpoints, private keys used to deploy code, etc. It also includes the Wormhole RPC endpoint.
This maintains some constants about the chains RPC endpoints, private keys used to deploy code, deployed contract addresses, etc. It also includes the Wormhole RPC endpoint.

View File

@ -0,0 +1,125 @@
# xMint.sol
xMint.sol is an application contract on EVM capable of communicating with the Wormhole Core Bridge and Token Bridge.
Start by initializing key contract state variables for the application.
```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "./Wormhole/IWormhole.sol";
import "./Wormhole/ITokenBridge.sol";
import "./Wormhole/BridgeStructs.sol";
import "solidity-bytes-utils/BytesLib.sol";
contract Xmint is ERC20 {
using BytesLib for bytes;
mapping(uint16 => bytes32) _applicationContracts;
mapping(bytes32 => bool) _completedMessages;
address owner;
IWormhole core_bridge;
ITokenBridge token_bridge;
uint32 nonce = 0;
event Log(string indexed str);
```
## Constructor
This sets the owner of the contract to the deployer and initializes interfaces for the Wormhole Core Bridge and Token Bridge.
```solidity
constructor(
string memory name_,
string memory symbol_,
address coreBridgeAddress,
address tokenBridgeAddress
) ERC20(name_, symbol_) {
owner = msg.sender;
core_bridge = IWormhole(coreBridgeAddress);
token_bridge = ITokenBridge(tokenBridgeAddress);
}
```
## RegisterApplicationContracts
It's typically a good idea to register and track the contracts from foreign chains that you're accepting VAAs from, as anyone could deploy a contract and generate a fake VAA that looks like a real VAA you'd want to accept.
```solidity
/**
Registers it's sibling applications on other chains as the only ones that can send this instance messages
*/
function registerApplicationContracts(uint16 chainId, bytes32 applicationAddr) public {
require(msg.sender == owner, "Only owner can register new chains!");
_applicationContracts[chainId] = applicationAddr;
}
```
## SubmitForeignPurchase
This takes in a bytes payload and calls the Wormhole Token Bridge to receive the bridged tokens, mint the corresponding amount of native token, and calls the Wormhole Token Bridge to send the newly minted tokens back to the user.
The `completeTransferWithPayload()` function of the token bridge takes one argument:
- encodedVm: The unverified and unparsed message that is generated by the Guardian Network
The `transferTokens()` function of the token bridge takes six arguments:
- Token: contract address of the token
- Amount: amount of tokens to transfer
- RecipientChain: ChainID of the target chain
- Recipient: address for target wallet
- ArbiterFee: amount of tokens the user is willing to pay as relayer fee
- Nonce: A number to uniquely identify this message, optionally used for batching when multiple messages are generated by the same transaction. See [here](../../technical/evm/coreLayer.md) for more details.
```solidity
/**
Takes inventory of the foreign currency
Mints tokens to self
Transfers tokens with Payload 1 to Receipient on Foreign chain
*/
function submitForeignPurchase(bytes memory encodedVm) public returns (uint64) {
// Complete transfer will give the Tokens to this Contract
// Unlike solana, we don't need to check that the emitter is a Portal contract as register_ scripts register all the Portal contracts
// and the completeTransfer function checks the emitter is a valid Portal contract on one of the chains it's registered with
BridgeStructs.TransferWithPayload memory vaa = _decodePayload(token_bridge.completeTransferWithPayload(encodedVm));
// Mint tokens to this contract
//amt they paid is NATIVE
//multiply by 100 to get how many tokens to give out
uint256 amtToMint = vaa.amount * 100;
_mint(address(this), amtToMint);
// Give Token Bridge allowance to take tokens from this contract
this.approve(address(token_bridge), amtToMint);
// Transfer tokens via Token Bridge over to Recipient in payload
uint64 sequence = token_bridge.transferTokens(address(this), amtToMint, vaa.tokenChain, bytes32(vaa.payload), 0, nonce);
nonce += 1;
return sequence;
}
```
## _decodePayload
This helper function takes the set of bytes returned by the token bridge and decodes it into the more useful form of a `TransferWithPayload` struct.
```solidity
function _decodePayload(bytes memory payload) internal pure returns (BridgeStructs.TransferWithPayload memory) {
BridgeStructs.TransferWithPayload memory decoded = BridgeStructs.TransferWithPayload({
payloadID: payload.slice(0,1).toUint8(0),
amount: payload.slice(1,32).toUint256(0),
tokenAddress: payload.slice(33,32).toBytes32(0),
tokenChain: payload.slice(65,2).toUint16(0),
to: payload.slice(67,32).toBytes32(0),
toChain: payload.slice(99,2).toUint16(0),
fromAddress: payload.slice(101,32).toBytes32(0),
payload: payload.slice(133, payload.length-133)
});
return decoded;
}
```