Skip to content

Commit 3abbcd3

Browse files
authored
Feat/sui GMP (#1308)
1 parent 529f299 commit 3abbcd3

File tree

2 files changed

+283
-0
lines changed

2 files changed

+283
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
import { Callout } from "../../../../../components/callout";
2+
3+
# Sui Packages
4+
5+
[Sui](https://sui.io/) is a blockchain that has been integrated with the Axelar Network via [the Interchain Amplifier](/dev/amplifier/introduction/).
6+
7+
Sending messages between Sui and other blockchains follows similar patterns as [GMP messages](/dev/general-message-passing/overview/) on other chains, such as EVM chains.
8+
9+
The core packages for Axelar's integration with Sui can be found at the [axelar-cgp-sui](https://github.com/axelarnetwork/axelar-cgp-sui) repository.
10+
11+
At the core of there are two main package involved in sending a GMP message, these are the [Gateway Package](https://github.com/axelarnetwork/axelar-cgp-sui/tree/main/move/axelar_gateway) and the [Gas Service](https://github.com/axelarnetwork/axelar-cgp-sui/tree/main/move/gas_service).
12+
13+
For all complete Sui GMP implementation check out this [example](https://github.com/axelarnetwork/axelar-cgp-sui/tree/main/move/example)
14+
15+
To interact with the example and send a GMP message checkout this [script](https://github.com/axelarnetwork/axelar-contract-deployments/blob/main/sui/gmp.js)
16+
17+
## Overview
18+
19+
Sending a message from Sui involves the following steps:
20+
21+
- Register your transaction with the [relayer discovery service](https://github.com/axelarnetwork/axelar-cgp-sui/tree/main/move/relayer_discovery) via the `register_transaction()` function on the relayer discovery service.
22+
- Prepare the message via the `prepare_message()` function on the [gateway](https://github.com/axelarnetwork/axelar-cgp-sui/tree/main/move/axelar_gateway).
23+
- Pay gas via the `pay_gas()` function on the [gas service](https://github.com/axelarnetwork/axelar-cgp-sui/tree/main/move/gas_service).
24+
- Send the message via the send_message function on the [gateway](https://github.com/axelarnetwork/axelar-cgp-sui/tree/main/move/axelar_gateway).
25+
26+
27+
## Sui Gateway
28+
29+
The [Gateway](/learn/network/flow/#gateways) is the core contract that facilitates the sending and receiving of cross-chain messages to other chains via the Axelar Network.
30+
31+
### Gateway Object
32+
33+
A shared object that anyone can access and user to refer to the Gateway package.
34+
35+
```move
36+
public struct Gateway has key {
37+
id: UID,
38+
inner: Versioned,
39+
}
40+
```
41+
42+
43+
The Gateway facilitates sending and receiving of cross-chain messages to other chains via the Axelar Network.
44+
45+
For sending a GMP message, the `send_message()` function needs to be triggered.
46+
47+
### Send Message
48+
49+
The send_message function triggers your cross-chain message from Sui to another blockchain via the Axelar Network. The send_message function requires a MessageTicket struct to be passed in. To create the MessageTicket you can trigger the [prepare_message](#prepare-message) function.
50+
51+
52+
<Callout emoji="💡">
53+
The reason this process is in two steps is because the Gateway is an upgrade compatible contract. To ensure minimal roadblocks when upgrading the contract the functionality of the Gateway was broken up so that if the Gateway does get upgraded at some point, applications can always continue to call the logic of the V0 `prepare_message()` function and pass the ticket into the V2 version of the `send_message()`, this will minimize breaking changes when upgrading the contract.
54+
</Callout>
55+
56+
```move
57+
public fun send_message(self: &Gateway, message: MessageTicket) {
58+
let value = self.value!(b"send_message");
59+
value.send_message(message, VERSION);
60+
}
61+
```
62+
63+
64+
#### Prepare Message
65+
66+
The `prepare_message()` function is used to create a MessageTicket struct that is required to send a GMP message.
67+
68+
It takes four parameters.
69+
70+
1. `channel`: The [channel](#channel) that the message is being sent from.
71+
1. `destination_chain`: Name of the chain the message is being sent to.
72+
1. `destination_address`: Address on the destination chain the message is being sent to.
73+
1. `payload`: A `vector<u8>` representation of the cross-chain message being sent.
74+
75+
```move
76+
public fun prepare_message(
77+
channel: &Channel,
78+
destination_chain: String,
79+
destination_address: String,
80+
payload: vector<u8>,
81+
): MessageTicket {
82+
message_ticket::new(
83+
channel.to_address(),
84+
destination_chain,
85+
destination_address,
86+
payload,
87+
VERSION,
88+
)
89+
}
90+
```
91+
92+
### Receive Message
93+
94+
Receiving a message involves two steps. The first is *approving* an incoming message as approved and the second involved *executing* the approved message.
95+
96+
#### Approve Message
97+
98+
To approve the message an Axelar relayer triggers the Gateway's `approve_message()` function. Once the message is marked as approved, the approval is stored in the Gateway object. This will indicate that the message has been confirmed the Axelar [Verifier set](https://axelarscan.io/verifiers) on the Axelar network itself.
99+
100+
```move
101+
entry fun approve_messages(self: &mut Gateway, message_data: vector<u8>, proof_data: vector<u8>) {
102+
let value = self.value_mut!(b"approve_messages");
103+
value.approve_messages(message_data, proof_data);
104+
}
105+
```
106+
107+
A live example of an approval transaction can be found [here](https://suiscan.xyz/mainnet/tx/GURWyTpC2LcAHDvMZFKE2wZWJiDXVPMLD5ev5ERUJFuA).
108+
109+
#### Execute Message
110+
111+
With the message now marked as approved the relayer will attempt to execute the message on your Sui contract. For this the relayer will first trigger the Gateway's take_approved_function(). This function will confirmed that the message has already been [approved](#approve-message) and will then begin the [message consumption process](#consume-approved-message)
112+
113+
```move
114+
public fun take_approved_message(
115+
self: &mut Gateway,
116+
source_chain: String,
117+
message_id: String,
118+
source_address: String,
119+
destination_id: address,
120+
payload: vector<u8>,
121+
): ApprovedMessage {
122+
let value = self.value_mut!(b"take_approved_message");
123+
value.take_approved_message(
124+
source_chain,
125+
message_id,
126+
source_address,
127+
destination_id,
128+
payload,
129+
)
130+
}
131+
```
132+
133+
On your own package the [Relayer Discovery](#relayer-discovery) will then look to call the function you specified for it to call in the register_transaction() flow. If you registered a function called execute() (as was done in this [example](https://github.com/axelarnetwork/axelar-cgp-sui/blob/main/move/example/sources/gmp/gmp.move#L27)) then you can implement the execute() function as follows.
134+
135+
The executable function will pass in the [ApprovedMessage](#approvedmessage) that was [consumed](#consume-approved-message) by the Package's Channel.
136+
137+
```move
138+
public fun execute(call: ApprovedMessage, singleton: &mut Singleton) {
139+
let (_, _, _, payload) = singleton.channel.consume_approved_message(call);
140+
141+
event::emit(Executed { data: payload });
142+
}
143+
```
144+
145+
146+
A live example of an execution transaction can be found [here](https://suiscan.xyz/mainnet/tx/Dzg5KukKgsywZob892Fvn4NxS6qjA2WWL2PRX9gTnwzb).
147+
148+
## Channel
149+
150+
In Sui there is no ability to check immediate caller of a message (ie there is no msg.sender like in EVM development). What is available is a transaction.origin, which is the root caller of a transaction (similar to tx.origin in EVM development). To identify who the caller of a message is you can use Channels. The Channel is an object that an application first creates and this channel is the identifier for who is calling and receiving the message.
151+
152+
153+
Channels allow for sending and receiving messages between Sui and other chains. In the context of sending a message the channel acts as a destination for the messages. When the message is sent the destination_id is compared against the id of the channel.
154+
155+
```move
156+
public struct Channel has key, store {
157+
/// Unique ID of the channel
158+
id: UID,
159+
}
160+
```
161+
162+
The id specifies the address of the application for the purpose of incoming and outgoing external calls. This id has to match the id of a shared object that is passed in the channel creation method. This shared object can easily be querried by the relayer to get call fullfilment information.
163+
164+
### Consume Approved Message
165+
The `consume_approved_message()` will confirm that the message has been sent to the correct channel.
166+
167+
168+
The function takes two parameters.
169+
170+
1. channel: The [channel](#channel) that the message is being sent to.
171+
1. approved_message: The [ApprovedMessage](#approvedmessage) struct that is being sent to the destination chain, containing relevant parameters of the cross-chain message.
172+
173+
```move
174+
public fun consume_approved_message(channel: &Channel, approved_message: ApprovedMessage): (String, String, String, vector<u8>) {
175+
let ApprovedMessage {
176+
source_chain,
177+
message_id,
178+
source_address,
179+
destination_id,
180+
payload,
181+
} = approved_message;
182+
183+
// Check if the message is sent to the correct destination.
184+
assert!(destination_id == object::id_address(channel), EInvalidDestination);
185+
186+
(source_chain, message_id, source_address, payload)
187+
}
188+
```
189+
190+
For an example of how to receive an approved message on the destination chain see [here](https://github.com/axelarnetwork/axelar-cgp-sui/blob/main/move/example/sources/gmp/gmp.move#L83)
191+
192+
#### ApprovedMessage
193+
194+
The ApprovedMessage contains the following parameters
195+
196+
1. `source_chain`: The name of chain where the cross-chain message originated.
197+
1. `message_id`: The unique ID of the message
198+
1. `source_address`: The address on the source chain where the message originated.
199+
1. `destination_id`: The id of the channel that the message is being sent to.
200+
1. `payload`: A `vector<u8>` representation of the cross-chain message being sent.
201+
202+
```move
203+
public struct ApprovedMessage {
204+
source_chain: String,
205+
message_id: String,
206+
source_address: String,
207+
destination_id: address,
208+
payload: vector<u8>,
209+
}
210+
211+
```
212+
213+
## Sui Gas Service
214+
215+
The Gas Service handles cross-chain gas payment when making a GMP request.
216+
217+
When sending a GMP message before triggering the `send_message()` function on the Gateway, the `pay_gas()` must be triggered first to pay for the cross-chain transaction.
218+
219+
### PayGas()
220+
221+
The `pay_gas()` allows users to pay for the entirety of the cross-chain transaction in a given token, it is triggered by either the channel or the user. If it is called by the user the sender will be set as the channel_id.
222+
223+
The `pay_gas()` takes seven parameters.
224+
225+
1. `message_ticket`: The [ticket](#prepare-message) for the message being sent.
226+
1. `coin`: The [coin](https://docs.sui.io/standards/coin) being used to pay for the transaction.
227+
1. `refund_address`: The address to be refunded if too much gas is paid.
228+
1. `params`: Should be passed in as an empty value.
229+
230+
231+
<Callout emoji="💡">
232+
The params argument exists to allow for future extensibility of the function. It is not currently used in the implementation.
233+
</Callout>
234+
235+
236+
Note: The expected [token](https://github.com/axelarnetwork/axelar-cgp-stellar/blob/main/packages/axelar-stellar-std/src/types.rs#L5) is a struct type that requires an address and amount.
237+
238+
```move
239+
public fun pay_gas<T>(
240+
self: &mut GasService,
241+
message_ticket: &MessageTicket,
242+
coin: Coin<T>,
243+
refund_address: address,
244+
params: vector<u8>,
245+
) {
246+
self
247+
.value_mut!(b"pay_gas")
248+
.pay_gas<T>(
249+
message_ticket,
250+
coin,
251+
refund_address,
252+
params,
253+
);
254+
}
255+
```
256+
257+
258+
## Relayer Discovery
259+
260+
In Sui there is no arbitrary execution like in EVM chains, therefore unlike in other ecosystem there is no [Executable](/dev/general-message-passing/executable/) Package to inherit from. To resolve this issue the [Relayer Discovery](https://github.com/axelarnetwork/axelar-cgp-sui/tree/main/move/relayer_discovery) Package is deployed to serve as a registry of Packages that can be invoked given a message.
261+
262+
To get added to this *registry* a deployed application on Sui will need to trigger the `register_transaction()` function on the Discovery Package.
263+
264+
```move
265+
public fun register_transaction(self: &mut RelayerDiscovery, channel: &Channel, tx: Transaction) {
266+
// Get the mutable value associated with the "register_transaction" key.
267+
let value = self.value_mut!(b"register_transaction");
268+
// Retrieve the unique channel ID from the provided channel.
269+
let channel_id = channel.id();
270+
// Set the transaction for this channel in the registry.
271+
value.set_transaction(channel_id, tx);
272+
}
273+
```
274+
275+
The following arguments are required to register a Package:
276+
277+
1. `channel`: The [channel](#channel) that the Package is being registered to.
278+
1. `tx`: The details of the function to be executed when the Package is called from an Axelar relayer.
279+
280+
By registering the transaction with the discovery system under a specific channel, the relayer later knows exactly which transaction to run when it receives an approved message on that channel.
281+
282+
See [here](https://github.com/axelarnetwork/axelar-cgp-sui/blob/main/move/example/sources/gmp/gmp.move#L27) for an example of how to register a transaction with the Discovery Package.

src/layouts/navigation.ts

+1
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ export const getNavigation = (section) => {
249249
},
250250
],
251251
},
252+
{title: "Sui GMP", href: "/dev/general-message-passing/sui/sui-programs/"},
252253
{
253254
title: "Stellar GMP",
254255
children: [

0 commit comments

Comments
 (0)