Skip to content

Commit

Permalink
Value
Browse files Browse the repository at this point in the history
  • Loading branch information
fadeev committed Oct 9, 2023
1 parent 808be58 commit fafc207
Showing 1 changed file with 115 additions and 53 deletions.
168 changes: 115 additions & 53 deletions docs/developers/cross-chain-messaging/examples/multi-chain-value.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,59 +8,127 @@ title: Multichain Value Transfer

# Multichain Value Transfer

This is an example contract that shows how you can send value across chains via
the ZetaChain API.
In this tutorial you will learn how to send ZETA tokens between connected
blockchains using ZetaChain.

From this rudimentary example, you could easily extend it to support arbitrary
asset exchanges via a swap to/from ZETA on source and destination.

# Multichain Value Transfer

In this tutorial we will create a contract that allows sending value from one
chain to another using the
[Connector API](/developers/cross-chain-messaging/connector/).
In this example you will only be sending ZETA tokens without any associated
message.

## Set up your environment

```
git clone https://github.com/zeta-chain/template
```
## Create a new contract
cd template
```solidity title="contracts/MultiChainValue.sol" reference
https://github.com/zeta-chain/example-contracts/blob/feat/import-toolkit/messaging/value/contracts/MultiChainValue.sol
yarn
```

The contract's main functionality is implemented in the `sendValue` function.

The `send` function first checks if the destination chain ID is valid and if the
Zeta value and gas are not zero.
## Create the Contract

Next, it attempts to approve and transfer the specified amount of Zeta tokens
from the sender's address to the contract's address.
To create a new cross-chain messaging contract you will use the `messaging`
Hardhat task available by default in the template.

Finally, the function calls the "send" function of a connector contract,
providing the necessary inputs such as the destination chain ID, destination
address, gas limit, and other parameters. The function encodes these inputs into
a message and sends it to the connector contract for further processing.
```
npx hardhat messaging Value --fees zetaRevert
```

The contract also uses a notion of "available chains". Before calling the send
function and transferring value between chains you need to call the
`addAvailableChainId` function on the source chain and add the destination chain
ID to the list of available chains. In this example this logic is implemented in
the deploy task.
Use the `--fees` flag to specify that you want your contract to accept ZETA
tokens as fees. Since the purpose of this contract is to send ZETA tokens, it
makes sense to also use ZETA tokens as fees.

## Modify the Contract

```solidity title="contracts/Value.sol"
// SPDX-License-Identifier: MIT
pragma solidity 0.8.7;
import "@openzeppelin/contracts/interfaces/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@zetachain/protocol-contracts/contracts/evm/tools/ZetaInteractor.sol";
import "@zetachain/protocol-contracts/contracts/evm/interfaces/ZetaInterfaces.sol";
import "@zetachain/protocol-contracts/contracts/evm/Zeta.eth.sol";
// highlight-next-line
contract Value is ZetaInteractor {
error ErrorTransferringZeta();
// remove-start
error InvalidMessageType();
event ValueEvent();
event ValueRevertedEvent();
bytes32 public constant VALUE_MESSAGE_TYPE = keccak256("CROSS_CHAIN_VALUE");
// remove-end
IERC20 internal immutable _zetaToken;
constructor(
address connectorAddress,
address zetaTokenAddress
) ZetaInteractor(connectorAddress) {
_zetaToken = IERC20(zetaTokenAddress);
}
function sendMessage(
uint256 destinationChainId,
uint256 zetaValueAndGas
) external payable {
if (!_isValidChainId(destinationChainId))
revert InvalidDestinationChainId();
bool success1 = _zetaToken.approve(address(connector), zetaValueAndGas);
bool success2 = _zetaToken.transferFrom(
msg.sender,
address(this),
zetaValueAndGas
);
if (!(success1 && success2)) revert ErrorTransferringZeta();
connector.send(
ZetaInterfaces.SendInput({
destinationChainId: destinationChainId,
destinationAddress: interactorsByChainId[destinationChainId],
destinationGasLimit: 300000,
// highlight-next-line
message: abi.encode(),
zetaValueAndGas: zetaValueAndGas,
zetaParams: abi.encode("")
})
);
}
// remove-start
function onZetaMessage(
ZetaInterfaces.ZetaMessage calldata zetaMessage
) external override isValidMessageCall(zetaMessage) {
bytes32 messageType = abi.decode(zetaMessage.message, (bytes32));
if (messageType != VALUE_MESSAGE_TYPE) revert InvalidMessageType();
emit ValueEvent();
}
function onZetaRevert(
ZetaInterfaces.ZetaRevert calldata zetaRevert
) external override isValidRevertCall(zetaRevert) {
bytes32 messageType = abi.decode(zetaRevert.message, (bytes32));
if (messageType != VALUE_MESSAGE_TYPE) revert InvalidMessageType();
emit ValueRevertedEvent();
}
// remove-end
}
```

## Create a deployment task
Modify the contract so that it only inherits from `ZetaInteractor`. Since the
purpose of the contract is to only send ZETA tokens (and not messages), it
doesn't need to inherit from `ZetaMessageReceiver` and implement the
`onZetaMessage` and `onZetaRevert` functions.

The deploy task is fairly standard. It deploys the contract to two or more
chains and sets the interactors on each chain. Additionally, for this example,
the script also calls the `addAvailableChainId` function on each chain to add
the other chain to the list of available chains.
You can also remove the message type from the `connector.send` call.

```ts title="tasks/deploy.ts" reference
https://github.com/zeta-chain/example-contracts/blob/feat/import-toolkit/messaging/value/tasks/deploy.ts
```
## Deploy the Contract

Clear the cache and artifacts, then compile the contract:

Expand All @@ -71,34 +139,28 @@ npx hardhat compile --force
Run the following command to deploy the contract to two networks:

```
npx hardhat deploy --networks polygon-mumbai,bsc-testnet
npx hardhat deploy --networks goerli_testnet,mumbai_testnet
```

## Send a message

Create a new task to send tokens from one chain to another. The task accepts the
following parameters: contract address, recipient address, amount, destination
chain ID, and the source network name.

Send a message from Polygon Mumbai testnet to BSC testnet (chain ID: 97) using
the contract address (see the output of the `deploy` task). Make sure to submit
enough native tokens with `--amount` to pay for the transaction fees.
Run the following command to send ZETA tokens from Goerli to Mumbai. Please,
note that since the contract expect ZETA tokens as fees, the value of the
`--amount` param is denomited in ZETA tokens. A fraction of the amount will be
deducted as a cross-chain fee, the rest will be sent to the recipient on the
destination chain.

```
npx hardhat send --contract 0x042AF09ae20f924Ce18Dc3daBFa1866B114aFa89 --address 0xF5a522092F8E4041F038a6d30131192945478Af0 --amount 20 --destination 97 --network polygon-mumbai
npx hardhat interact --contract 0xe6DE62328677C80084b07eF25637EC83A53d69E1 --network goerli_testnet --destination mumbai_testnet --amount 3
πŸ”‘ Using account: 0x2cD3D070aE1BD365909dD859d29F387AA96911e1
βœ… The transaction has been broadcasted to polygon-mumbai
πŸ“ Transaction hash: 0x2748882492a565627e4726658744f443fb993943f25ba73f93dba42ae314e689
Please, refer to ZetaChain's explorer for updates on the progress of the cross-chain transaction.
🌍 Explorer: https://explorer.zetachain.com/address/0x042AF09ae20f924Ce18Dc3daBFa1866B114aFa89
βœ… The transaction has been broadcasted to goerli_testnet
πŸ“ Transaction hash: 0x4996283c199fafe4c15f33a8ef6d4a41d00545b0736bac0e5a74d72fb342b4c7
```

## Source Code

You can find the source code for the example in this tutorial here:

https://github.com/zeta-chain/example-contracts/blob/feat/import-toolkit/messaging/value
https://github.com/zeta-chain/example-contracts/tree/main/messaging/value

0 comments on commit fafc207

Please sign in to comment.