This commit is contained in:
spacemandev 2022-08-16 12:29:12 -06:00
commit d00934ff4a
5 changed files with 38 additions and 32 deletions

View File

@ -1,10 +1,10 @@
# EVM: Attesting a Token
Attesting a token from EVM is fairly simple, and usually done via the Portal UI since it's a step that only needs to happen once per Token.
Attesting a token from EVM only needs to happen once per token, and is typically done via the Portal UI.
If for whatever reason you need to do it programmatically, you can also do it using the JS SDK:
If you need to do it programmatically, you can also use the JS SDK to attest a token:
The first step is to create an AttestMeta VAA. We do this by calling `attestFromEth()` function from the JS SDK and passing in the Token Bridge address, a Ethers signer object, and the address of the Token we want to attest:
The first step is to create an AttestMeta VAA. We do this by calling `attestFromEth()` function from the JS SDK and passing in the Token Bridge address, an Ethers signer object, and the address of the Token we want to attest:
```js
@ -18,7 +18,9 @@ const networkTokenAttestation = await attestFromEth(
Anyone can attest any token on the network.
To complete the Attestation, we grab the VAA that the `attestFromEth()` function generates by getting the Emitter address of the Token Bridge and the Sequence from the logs of the transaction receipt. We then fetch against a guardian REST endpoint. It could take a couple seconds (up to 30s!) for the guardian to see and sign the VAA, so it's a good idea to poll the guardian every couple seconds until the VAA is found.
To complete the attestation, grab the VAA that the `attestFromEth()` function generates by getting the Emitter address of the Token Bridge and the Sequence from the logs of the transaction receipt.
Then, fetch against a Guardian REST endpoint. It could take a moment (up to 30 seconds) for the Guardian to see and sign the VAA, so it's a good idea to poll the Guardian every few seconds until the VAA is found.
```js
@ -35,7 +37,7 @@ while(!vaaBytes.vaaBytes){
```
Next, we submit the VAA onto the target chain to create a wrapped version of the Token by calling `createWrapped()`. On an EVM chain, this will deploy a Portal Wrapped Token contract who's mint authority is the Portal Token Bridge on that chain. Sometimes this transaction throws an unpredicatable gas price error, so it's a good idea to set a high gas limit.
Next, we submit the VAA onto the target chain to create a wrapped version of the Token by calling `createWrapped()`. On an EVM chain, this will deploy a Portal Wrapped Token contract whose mint authority is the Portal Token Bridge on that chain. Sometimes, this transaction throws an unpredicatable gas price error, so set a high gas limit.
After the wrapped token is created, you can get the new wrapped token address by calling the `wrappedAsset()` function of the TokenBridge.

View File

@ -1,8 +1,8 @@
# EVM: Transferring a Token
WARNING: To be able to successfully transfer a token from Chain A to Chain B, make sure you [attest](./attestingToken.md) it first. Otherwise the Token may be transferred, but you won't be able to claim it til it's attested.
WARNING: To be able to successfully transfer a token from one chain to another, make sure you [attest](./attestingToken.md) it first. Otherwise the Token may be transferred, but you won't be able to claim it until it's attested.
One big gotcha new EVM developers usually run into when working with ERC20 tokens is that because EVM uses unsigned integers, there's no concept of decimals. Therefore, tokens usually have up to 18 zeros behind them to denote up to 18 decimal places. Wormhole normalizes this to *eight* zeros, with transfer amounts rounded down to the nearest 8the decimal.
One challenge that arises for new EVM developers is that, because EVM uses unsigned integers, there's no concept of decimals. Therefore, tokens usually have up to 18 zeros behind them to denote up to 18 decimal places. Wormhole normalizes this to *eight* zeros, with transfer amounts rounded down to the nearest 8th decimal.
To wrap the Token Bridge functions in your contract, you can use the Token Bridge interfaces provided under [`projects/evm-tokenbridge/chains/evm/src/Wormhole`](https://github.com/certusone/xdapp-book/tree/main/projects/evm-tokenbridge/chains/evm/src/Wormhole) folder of the xDapp Book repository.
@ -35,7 +35,7 @@ contract Treasury {
```
To transfer a token, first we have to *approve* the Token Bridge to be able to spend that Token on our behalf (so it can transfer tokens form our contract to tself). Make sure the `bridgeAmt` properly takes into account decimals for the ERC20 token.
To transfer a token, first we have to *approve* the Token Bridge to be able to spend that token on our behalf (so it can transfer tokens form our contract to itself). Make sure the `bridgeAmt` properly accounts for decimals in the ERC20 token.
```js
// Here we are approving and transfering 50 tokens. The ERC20 token we are transfering has 18 decimal places.
@ -47,7 +47,7 @@ await treasury.approveTokenBridge(bridgeAmt, {
```
Then we simply call `transfer` to create the transfer VAA and fetch it from the guardians when it's ready. Note that the target receipient is a Wormhole normalized hex address left-padded to 32 bytes.
Then we simply call `transfer` to create the transfer VAA and fetch it from the Guardians when it's ready. Note that the target receipient is a Wormhole normalized hex address left-padded to 32 bytes.
```js
const targetRecepient = Buffer.from(tryNativeToHexString(targetDeployment.deployedAddress, "ethereum"), 'hex');

View File

@ -1,8 +1,10 @@
# EVM to Solana Token Transfer
A cornerstone of cross chain apps (xDapps) is the ability to move tokens from one chain to another. Wormholes APIs make that a breeze.
A defining feature of cross chain apps (xDapps) is the ability to move tokens from one chain to another. Wormholes APIs make that a breeze.
Lets do a simple programmatic transfer from Eth to Solana. First, we need to figure out what address on Solana where we are sending the tokens. Unlike EVM chains where the address is just the wallet address, we need to send the tokens to our recipient address associated token account for that token. We can use a couple helper functions from the Wormhole SDK to make this possible.
To demonstrate, lets do a simple programmatic transfer from Eth to Solana.
First, determine the address on Solana where we're sending the tokens. Unlike EVM chains where the wallet address is used, we need to send the tokens to the recipient address associated token account for that token. We can use helper functions from the Wormhole SDK to make this possible.
```ts
import {
@ -35,7 +37,7 @@ const recipientAddress = await Token.getAssociatedTokenAddress(
);
```
After we have the receipt token account on Solana, we can come back and submit the transfer message on Ethereum. This will output a log that contains a sequence number (A nonce for the message) and an emitter address (the ETH Token Bridge Address as bytes) . The sequence number and emitter address will be used to fetch a VAA after its been signed by Guardians.
After we have the receipt token account on Solana, submit the transfer message on Ethereum. This will output a log that contains a sequence number (a nonce for the message) and an emitter address (the ETH Token Bridge Address as bytes). The sequence number and emitter address will be used to fetch a VAA after its been signed by Guardians.
```ts
import {
@ -61,7 +63,7 @@ const sequence = parseSequenceFromLogEth(receipt, ETH_BRIDGE_ADDRESS);
const emitterAddress = getEmitterAddressEth(ETH_TOKEN_BRIDGE_ADDRESS);
```
Once the Guardians have signed the token message, we can fetch it to use in the redeem step. If youre a developer, you might run this as an automatic process through an application specific relayer (more on that in a later thread!)
Once the Guardians have signed the token message, fetch it to use in the redeem step. If youre a developer, you might run this as an automatic process through an application specific relayer (more on that in a later section).
```ts
import {
@ -77,7 +79,7 @@ const { signedVAA } = await getSignedVAA(
);
```
Then we can post the VAA to Solana to mint the tokens. Because of the compute limit on Solana, we split the signature verification and token claim into steps. First we'll verify all the signatures and create a claim account for the Token.
Now, post the VAA to Solana to mint the tokens. Because of the compute limit on Solana, we split the signature verification and token claim into steps. To do that, verify all the signatures and create a claim account for the token.
```ts
const SOL_BRIDGE_ADDRESS = "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth";
@ -91,7 +93,7 @@ await postVaaSolana(
);
```
Finally we can claim the tokens
Finally, claim the tokens:
```ts
// Finally, redeem on Solana
@ -107,4 +109,4 @@ const transaction = await redeemOnSolana(
const signed = await wallet.signTransaction(transaction);
const txid = await connection.sendRawTransaction(signed.serialize());
await connection.confirmTransaction(txid);
```
```

View File

@ -1,9 +1,9 @@
# Portal JS SDK Overview
For applications that only need to interact with the Core and Token Bridge contracts off chain, there is a Wormhole JS SDK provided.
For applications that only need to interact with the Core and Token Bridge contracts off-chain, there is a Wormhole JS SDK provided.
It can be installed using npm like so:
It can be installed using npm:
```sh
npm i @certusone/wormholesdk
```
```

View File

@ -1,14 +1,16 @@
# Polygon to Oasis with Relayers
In this example well fetch the fee schedule and attach a relayer fee onto our transaction. This is a non trivial example as well also use Polygon as a source chain, which has some quirks when it comes to gas estimation. In the future, this whole process is being simplified, so check back in the future for hopefully much simpler version of this example.
In this example, well fetch the fee schedule and attach a relayer fee onto our transaction. This is a non-trivial example as well also use Polygon as a source chain, which has some quirks when it comes to gas estimation.
For this example, well need a couple of packages:
NOTE: We're working on streamlining this process, so check back in the future for a much simpler version of this example.
To start, well need a couple of packages:
```bash
npm i --save @certusone/wormhole-sdk ethers node-fetch
```
Then let's get started writing some code:
Then, get started writing some code:
```ts
import { BigNumber, ethers } from "ethers";
@ -28,7 +30,7 @@ import {
### Setup the Polygon and Oasis Wallets
First, let us set up the two wallets well be sending and receiving from. While we are instantiating both wallets with their private keys, we only need the Public key of the receiving wallet for this example.
Now, set up the two wallets well be sending and receiving from. While we are instantiating both wallets with their private keys, we only need the Public key of the receiving wallet for this example.
```ts
const EmeraldWallet = new ethers.Wallet(
@ -42,7 +44,7 @@ const PolygonWallet = new ethers.Wallet(
```
### Fetch the fee schedule
Next, well fetch the fee schedule for the Portal Token Bridge relayer. This fee schedule will give us the minimum fee for each recipient chain that the relayer will accept. As long as we attach at least that fee in the relayer fee, we can be fairly confident that the relayer will pick up the transaction and relay it to the recipient chain. The fee will cover the gas cost for the relayer along with a little extra to make it worth their time to run the relayer service.
Fetch the fee schedule for the Portal Token Bridge relayer. This fee schedule outlines the minimum fee for each recipient chain that the relayer will accept. As long as we attach at least that fee in the relayer fee, we can be fairly confident that the relayer will pick up the transaction and relay it to the recipient chain. The fee will cover the gas cost for the relayer along with a little extra to make it worth their time to run the relayer service.
We will also define the transfer amount in this step. The fee schedule will either return a flat fee in USD for the recipient chain, or a percentage fee (usually only for Ethereum). Either way, well need to calculate the fee in in BigNumber format (no decimals).
@ -85,7 +87,7 @@ interface FeeSchedule {
}
```
After weve fetched the fee schedule, we need to find the fee in Wei that needs to be paid to the Relayer. At the time of writing, Oasis has a flat fee of $0.50, so to calculate how much MATIC we need to pay for the $0.50 fee, we need to fetch the MATIC price. Lets use the free CoinGecko api:
After fetching the fee schedule, find the fee in wei that needs to be paid to the Relayer. At the time of writing, Oasis has a flat fee of $0.50, so to calculate how much MATIC we need to pay for the $0.50 fee, we need to fetch the MATIC price. To do that, use the free CoinGecko api:
```ts
let feeWei: number;
@ -106,7 +108,7 @@ if (relayerFeeSchedule.feeSchedule[CHAIN_ID_OASIS].type == "flat") {
```
### Add override for gas estimation for Polygon
Because the source chain is Polygon, we need to do this additional step to overestimate the gas. This is because ethers library has some problems with fee estimation after EIP-1559.
When the source chain is Polygon, there's an additional step to overestimate the gas. This is because Ethers library has some problems with fee estimation after EIP-1559.
```ts
let overrides;
@ -153,13 +155,13 @@ console.log("EmitterAddress: ", emitterAddress);
Lets walk through each of the arguments of this function and what they mean.
`POLYGON_TOKEN_BRIDGE` is the address of the Portal Token Bridge on the Polygon network. You can find it, amongst other addresses on the Deployment Info page.
`POLYGON_TOKEN_BRIDGE` is the address of the Portal Token Bridge on the Polygon network. You can find it and other addresses on the Deployment Info page.
`PolygonWallet` is a signer you get from the Ethers library that holds a private key that can sign transactions,
`PolygonWallet` is a signer you get from the Ethers library that holds a private key that can sign transactions.
`transferAmount` is a BigNumber that contains the amount to transfer in the smallest unit of the network.
`CHAIN_ID_OASIS` is a constant that identifies the target chain
`CHAIN_ID_OASIS` is a constant that identifies the target chain.
`hexToUint8Array()` translates the target publickey into a wormhole public key.
@ -169,7 +171,7 @@ Lets walk through each of the arguments of this function and what they mean.
### Check VAA was signed
Wait 15 min for finality on Polygon and check to see if was submitted. If successful youll be able to fetch a base64 encoded vaaBytes. We need this in the next step where we check if the transaction was successfully relayed.
Wait 15 min for finality on Polygon and then check to see if it was submitted. If successful, youll be able to fetch a base64 encoded vaaBytes. We need this in the next step where we check if the transaction was successfully relayed.
```ts
await new Promise((r) => setTimeout(r, 900000)); //15m in seconds
@ -193,7 +195,7 @@ console.log("VAA Bytes: ", vaaBytes);
### Check if the transfer was completed
In the final step we use the getIsTransferCompletedEth() method to check if the transfer was completed on the Oasis Emerald chain. If its not, we wait 5 seconds and check again.
In the final step, use the getIsTransferCompletedEth() method to check if the transfer was completed on the Oasis Emerald chain. If its not, wait 5 seconds and check again.
```ts
setDefaultWasm("node"); //only needed if running in node.js
@ -215,4 +217,4 @@ while (!transferCompleted) {
console.log("VAA Relayed!");
```
And that's it! You've successfully programmatically relayed a transaction!
Success! You've programmatically relayed a transaction!