Skip to content

Commit

Permalink
Merge branch 'main' into feat/gas-estimation
Browse files Browse the repository at this point in the history
  • Loading branch information
re1ro authored Apr 30, 2024
2 parents 5232941 + 7d23de1 commit 21d79cd
Show file tree
Hide file tree
Showing 18 changed files with 730 additions and 17 deletions.
69 changes: 59 additions & 10 deletions examples/amplifier/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,33 @@

This repo provides the code for interacting with the Amplifier Relayer API to relay transactions to the Axelar network and listen to Axelar events.

For a visual of the flow of an outgoing message see [outgoing msg](/images/Outgoing-Relayer.png)
For a visual of the flow of an inbound message see [inbound msg](/images/Inbound-Relayer.png)
For a visual of the flow of an outgoing message see [outgoing msg](images/Outgoing-Relayer.png)
For a visual of the flow of an inbound message see [inbound msg](images/Inbound-Relayer.png)

## Setup

1. Clone this repo.
1. Install dependencies:
2. Install dependencies:
```bash
npm install
```
1. Copy `.env.example` into `.env` and set up the following environment variables:
3. Go to amplifier examples directory
```
cd examples/amplifier
```
4. Copy `.env.example` into `.env` and set up the following environment variables:
```bash
HOST=...
PORT=...
```

## Generic endpoints

There are two endpoints that can be used for generic commands and events:
There are three endpoints that can be used for generic commands and events:

1. `broadcast` -- Sends a command to get executed as a wasm message on the network
1. `subscribe-to-wasm-events` -- Subscribes to all wasm events emitted on the network
2. `get-receipt` -- Returns the receipt given a receipt-id for a sent message
3. `subscribe-to-wasm-events` -- Subscribes to all wasm events emitted on the network

### `broadcast`

Expand All @@ -43,7 +48,51 @@ $ node amplifier broadcast \
Broadcasting message:
axelar1wkwy0xh89ksdgj9hr347dyd2dw7zesmtrue6kfzyml4vdtz6e5ws2pvc5e {"distribute_rewards":{"pool_id":{"chain_name":"fantom","contract":"axelar1ufs3tlq4umljk0qfe8k5ya0x6hpavn897u2cnf9k0en9jr7qarqqa9263g"},"epoch_count":1000}}
Connecting to server at localhost:50051
Message sent for broadcast
Message sent for broadcast { published: true, receiptId: '862592eaadbcdb08ccd2edffd647153e' }
```
### `get-receipt`
Each broadcast returns a `receiptId`, which is a unique key generated by the API to identify every broadcast request attempt. This id can be used to poll for the broadcast response. After a receipt id is returned, you can query the corresponding receipt for 24 hours.
```bash
node amplifier get-receipt --receipt-id <receipt id>
```
This is going to return either
* the transaction hash, if the transaction is included in a block
* a message indicating that the transaction has not been published yet (this usually takes up to 5-10 seconds)
* an error indicating the transaction failed to publish, or execute
For example:
```bash
# succesful broadcast
$ node amplifier get-receipt -r 53992509aa3267cc7b2bb8a1bfb21d03
Getting receipt with id: 53992509aa3267cc7b2bb8a1bfb21d03
Connecting to server at localhost:50051
Receipt:
87AECB2151F80616DA2CD237E9EE38DC9558FFBBC93A51DF3B3BE8BB89F0A5EF
```
```bash
# unknown receipt id
Getting receipt with id: random-id
Connecting to server at localhost:50051
Error Error: ...
code: 2,
details: 'receipt id not found',
}
```
```bash
# transaction failed to execute
$ node amplifier get-receipt -r 5b0726f7cc6504626023328f62a7454d
Getting receipt with id: 5b0726f7cc6504626023328f62a7454d
Connecting to server at localhost:50051
Error Error: ...
code: 2,
details: "transaction failed: broadcast tx failed: rpc error: code = Unknown desc = rpc error: code = Unknown desc = failed to execute message; message index: 0: rewards pool balance insufficient: execute wasm contract failed [CosmWasm/[email protected]/x/wasm/keeper/keeper.go:371] With gas wanted: '0' and gas used: '1506569' : unknown request",
}
```
### `subscribe-to-wasm-events`
Expand Down Expand Up @@ -125,7 +174,7 @@ where
- `destination-address` -- the address of the recipient
- `payload` -- the transaction payload of `ContractCall` event, in bytes. The `0x` can be omitted:
![Payload](/images/payload.png)
![Payload](images/payload.png)
After a few seconds, the `verify` command will exit displaying the `id`, and or an error if any:
Expand All @@ -150,7 +199,7 @@ Success verification for 0x02293467b9d6e1ce51d8ac0fa24e9a30fb95b5e1e1e18c26c8fd7
After a verification is initiated and once all internal processes (verifying, routing messages to the destination gateway, and constructing proof) are done on the Axelar network, a `signing-completed` event is emitted which contains a `session-id`. This `session-id` can be used to query the proof from the Axelar chain and return the execute data that need to be relayed on the destination chain. Do this by running `subscribe-to-approvals`:
```bash
node amplifer subscribe-to-approvals \
node amplifier subscribe-to-approvals \
--chain fantom \
--start-height <start-height> # optional
```
Expand Down Expand Up @@ -184,4 +233,4 @@ Payload:
- `hash` -- the payload hash
![Payload hash](/images/payload-hash.png)
![Payload hash](images/payload-hash.png)
8 changes: 8 additions & 0 deletions examples/amplifier/amplifier.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const commander = require('commander');
const { broadcast } = require('./endpoints/broadcast.js');
const { getReceipt } = require('./endpoints/get-receipt.js');
const { getPayload } = require('./endpoints/get-payload.js');
const { subscribe_to_approvals } = require('./endpoints/subscribe-to-approvals.js');
const { subscribe_to_wasm_events } = require('./endpoints/subscribe-to-wasm-events.js');
Expand All @@ -15,6 +16,13 @@ program
broadcast(options.address, options.payload);
});

program
.command('get-receipt')
.requiredOption("-r, --receipt-id <receipt id>", "The id of the receipt")
.action((options) => {
getReceipt(options.receiptId);
});

program
.command('get-payload')
.requiredOption('--hash, <hash>', 'payload hash')
Expand Down
25 changes: 19 additions & 6 deletions examples/amplifier/amplifier.proto
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
syntax = "proto3";
package axelar.amplifier.v1beta1;

// import "google/api/annotations.proto";

option go_package = "github.com/axelarnetwork/axelar-eds/pkg/amplifier/server/api";

service Amplifier {
Expand All @@ -22,6 +20,11 @@ service Amplifier {
body : "*"
};
}
rpc GetReceipt(GetReceiptRequest) returns (GetReceiptResponse) {
option (google.api.http) = {
get : "/v1beta1/receipt/{receipt_id}"
};
}
}

message Message {
Expand Down Expand Up @@ -55,7 +58,6 @@ message VerifyRequest { Message message = 1; }

message VerifyResponse {
Message message = 1;
// bool success = 2;
optional Error error = 2;
}

Expand All @@ -64,11 +66,13 @@ enum ErrorCode {
INTERNAL_ERROR = 1;
AXELAR_NETWORK_ERROR = 2;
INSUFFICIENT_GAS = 3;
FAILED_ON_CHAIN = 4;
MESSAGE_NOT_FOUND = 5;
}

message Error {
string error = 1;
ErrorCode error_code = 2;
string error = 1;
ErrorCode error_code = 2;
}

message SubscribeToWasmEventsRequest { optional uint64 start_height = 1; }
Expand All @@ -90,5 +94,14 @@ message BroadcastRequest {
}

message BroadcastResponse {
bool result = 1;
bool published = 1;
string receipt_id = 2;
}

message GetReceiptRequest {
string receipt_id = 1;
}

message GetReceiptResponse {
string tx_hash = 1;
}
2 changes: 1 addition & 1 deletion examples/amplifier/endpoints/broadcast.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function broadcast(address, payload) {
if (err) {
console.error("Error", err);
} else {
console.log("Message sent for broadcast");
console.log("Message sent for broadcast", response);
process.exit(0);
}
});
Expand Down
23 changes: 23 additions & 0 deletions examples/amplifier/endpoints/get-receipt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const newClient = require('../grpc/client');

function getReceipt(receiptId) {
console.log("Getting receipt with id:", receiptId);

const client = newClient();
const getReceiptRequest = { receiptId: receiptId };
response = client.GetReceipt(getReceiptRequest, (err, response) => {
if (err) {
console.error("Error", err);
process.exit(1)
}

if (response) {
console.log("Receipt:\n" + response.txHash);
process.exit(0)
}
});
}

module.exports = {
getReceipt,
}
88 changes: 88 additions & 0 deletions examples/evm/multichain-game/MultichainGame.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

import { AxelarExecutable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol';
import { IAxelarGateway } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol';
import { IAxelarGasService } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol';
import '@openzeppelin/contracts/utils/Strings.sol';
import { AddressToString, StringToAddress } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/AddressString.sol';

import { MultichainGameReceiver } from './MultichainGameReceiver.sol';

contract MultichainGame is AxelarExecutable {
using AddressToString for address;
using StringToAddress for string;

IAxelarGasService public immutable gasService;

constructor(address _gateway, address _gasService) AxelarExecutable(_gateway) {
gasService = IAxelarGasService(_gasService);
}

function guessNumber(
string memory _destChain, //"" if not cross chain bet
string calldata _gameReceiver,
uint256 _guess,
string memory _symbol,
uint256 _amount
) external payable {
require(_guess >= 1 && _guess <= 6, 'Invalid guess');

address tokenAddress = gateway.tokenAddresses(_symbol);

require(tokenAddress != address(0), 'Invalid token');

if (bytes(_destChain).length == 0) {
//NO MULTICHAIN TX PLAYING ON SAME CHAIN
IERC20(tokenAddress).transferFrom(msg.sender, _gameReceiver.toAddress(), _amount);
_checkIfWinner(msg.sender, _guess, _gameReceiver.toAddress());
} else {
//MULTICHAIN TX FROM CHAIN A TO CHAIN B
require(msg.value > 0, 'Insufficient gas');

bytes memory encodedGuess = abi.encode(msg.sender, _guess);

IERC20(tokenAddress).transferFrom(msg.sender, address(this), _amount);
IERC20(tokenAddress).approve(address(gateway), _amount);

gasService.payNativeGasForContractCallWithToken{ value: msg.value }(
address(this),
_destChain,
_gameReceiver,
encodedGuess,
_symbol,
_amount,
msg.sender
);

gateway.callContractWithToken(_destChain, _gameReceiver, encodedGuess, _symbol, _amount);
}
}

function _executeWithToken(
string calldata,
string calldata,
bytes calldata _payload,
string calldata _symbol,
uint256 _amount
) internal override {
address player = abi.decode(_payload, (address));
address tokenAddress = gateway.tokenAddresses(_symbol);
IERC20(tokenAddress).transfer(player, _amount);
}

function _checkIfWinner(address _player, uint256 _guess, address _gameReceiver) internal {
uint256 diceResult = 5; //for testing
// uint256 diceResult = (block.timestamp % 6) + 1;

bool won = _guess == diceResult;

if (won) _payoutWinner(_player, _gameReceiver);
}

function _payoutWinner(address _player, address _gameReceiver) internal {
MultichainGameReceiver(_gameReceiver).payOutAllTokensToWinnerSameChain(_player, address(this).toString(), Strings.toString(block.chainid));
}
}
Loading

0 comments on commit 21d79cd

Please sign in to comment.