0xa1::gateway
Implementation of the Axelar Gateway for Sui Move.
This code is based on the following:
Channel
s to
avoid big value storage;I. Sending call approvals
A approval is sent through the send
function, a Channel is supplied to
determine the source -> ID.
Event is then emitted and Axelar network can operate
II. Receiving call approvals
Approval bytes and signatures are passed into create
function to generate
a CallApproval object.
destination_id
points to a Channel
objectOnce created, CallApproval
needs to be consumed. And the only way to do it
is by calling
consume_call_approval
function and pass a correct Channel
instance
alongside the CallApproval
.
Channel
.idThe Gateway object uses a versioned field to support upgradability. The current implementation uses Gateway_v0.
Gateway
init
setup
approve_messages
rotate_signers
allow_function
disallow_function
prepare_message
send_message
is_message_approved
is_message_executed
take_approved_message
version_control
use 0x1::ascii;
use 0x1::vector;
use 0x2::bcs;
use 0x2::clock;
use 0x2::object;
use 0x2::table;
use 0x2::transfer;
use 0x2::tx_context;
use 0x2::versioned;
use 0xa1::auth;
use 0xa1::bytes32;
use 0xa1::channel;
use 0xa1::gateway_v0;
use 0xa1::message_status;
use 0xa1::message_ticket;
use 0xa1::owner_cap;
use 0xa1::weighted_signers;
use 0xb0::version_control;
Gateway
struct Gateway has key
id: object::UID
inner: versioned::Versioned
const VERSION: u64 = 0;
init
Init the module by giving a OwnerCap to the sender to allow a full
setup
.
fun init(ctx: &mut tx_context::TxContext)
fun init(ctx: &mut TxContext) {
let cap = owner_cap::create(ctx);
transfer::public_transfer(cap, ctx.sender());
}
setup
Setup the module by creating a new Gateway object.
entry fun setup(_: &owner_cap::OwnerCap, operator: address, domain_separator: address, minimum_rotation_delay: u64, previous_signers_retention: u64, initial_signers: vector<u8>, clock: &clock::Clock, ctx: &mut tx_context::TxContext)
entry fun setup(
_: &OwnerCap,
operator: address,
domain_separator: address,
minimum_rotation_delay: u64,
previous_signers_retention: u64,
initial_signers: vector<u8>,
clock: &Clock,
ctx: &mut TxContext,
) {
let inner = versioned::create(
VERSION,
gateway_v0::new(
operator,
table::new(ctx),
auth::setup(
bytes32::new(domain_separator),
minimum_rotation_delay,
previous_signers_retention,
utils::peel!(
initial_signers,
|bcs| weighted_signers::peel(bcs),
),
clock,
ctx,
),
version_control(),
),
ctx,
);
// Share the gateway object for anyone to use.
transfer::share_object(Gateway {
id: object::new(ctx),
inner,
});
}
approve_messages
The main entrypoint for approving Axelar signed messages. If proof is valid, message approvals are stored in the Gateway object, if not already approved before. This method is only intended to be called via a Transaction Block, keeping more flexibility for upgrades.
entry fun approve_messages(self: &mut gateway::Gateway, message_data: vector<u8>, proof_data: vector<u8>)
entry fun approve_messages(
self: &mut Gateway,
message_data: vector<u8>,
proof_data: vector<u8>,
) {
let value = self.value_mut!(b"approve_messages");
value.approve_messages(message_data, proof_data);
}
rotate_signers
The main entrypoint for rotating Axelar signers. If proof is valid, signers stored on the Gateway object are rotated. This method is only intended to be called via a Transaction Block, keeping more flexibility for upgrades.
entry fun rotate_signers(self: &mut gateway::Gateway, clock: &clock::Clock, new_signers_data: vector<u8>, proof_data: vector<u8>, ctx: &tx_context::TxContext)
entry fun rotate_signers(
self: &mut Gateway,
clock: &Clock,
new_signers_data: vector<u8>,
proof_data: vector<u8>,
ctx: &TxContext,
) {
let value = self.value_mut!(b"rotate_signers");
value.rotate_signers(
clock,
new_signers_data,
proof_data,
ctx,
)
}
allow_function
entry fun allow_function(self: &mut gateway::Gateway, _: &owner_cap::OwnerCap, version: u64, function_name: ascii::String)
entry fun allow_function(
self: &mut Gateway,
_: &OwnerCap,
version: u64,
function_name: String,
) {
self.value_mut!(b"allow_function").allow_function(version, function_name);
}
disallow_function
entry fun disallow_function(self: &mut gateway::Gateway, _: &owner_cap::OwnerCap, version: u64, function_name: ascii::String)
entry fun disallow_function(
self: &mut Gateway,
_: &OwnerCap,
version: u64,
function_name: String,
) {
self
.value_mut!(b"disallow_function")
.disallow_function(version, function_name);
}
prepare_message
Prepare a MessageTicket to call a contract on the destination chain.
public fun prepare_message(channel: &channel::Channel, destination_chain: ascii::String, destination_address: ascii::String, payload: vector<u8>): message_ticket::MessageTicket
public fun prepare_message(
channel: &Channel,
destination_chain: String,
destination_address: String,
payload: vector<u8>,
): MessageTicket {
message_ticket::new(
channel.to_address(),
destination_chain,
destination_address,
payload,
VERSION,
)
}
send_message
Submit the MessageTicket which causes a contract call by sending an event from an authorized Channel.
public fun send_message(self: &gateway::Gateway, message: message_ticket::MessageTicket)
public fun send_message(self: &Gateway, message: MessageTicket) {
let value = self.value!(b"send_message");
value.send_message(message, VERSION);
}
is_message_approved
public fun is_message_approved(self: &gateway::Gateway, source_chain: ascii::String, message_id: ascii::String, source_address: ascii::String, destination_id: address, payload_hash: bytes32::Bytes32): bool
public fun is_message_approved(
self: &Gateway,
source_chain: String,
message_id: String,
source_address: String,
destination_id: address,
payload_hash: Bytes32,
): bool {
let value = self.value!(b"is_message_approved");
value.is_message_approved(
source_chain,
message_id,
source_address,
destination_id,
payload_hash,
)
}
is_message_executed
public fun is_message_executed(self: &gateway::Gateway, source_chain: ascii::String, message_id: ascii::String): bool
public fun is_message_executed(
self: &Gateway,
source_chain: String,
message_id: String,
): bool {
let value = self.value!(b"is_message_executed");
value.is_message_executed(source_chain, message_id)
}
take_approved_message
To execute a message, the relayer will call take_approved_message
to get the hot potato ApprovedMessage
object, and then trigger the app’s
package via discovery.
public fun take_approved_message(self: &mut gateway::Gateway, source_chain: ascii::String, message_id: ascii::String, source_address: ascii::String, destination_id: address, payload: vector<u8>): channel::ApprovedMessage
public fun take_approved_message(
self: &mut Gateway,
source_chain: String,
message_id: String,
source_address: String,
destination_id: address,
payload: vector<u8>,
): ApprovedMessage {
let value = self.value_mut!(b"take_approved_message");
value.take_approved_message(
source_chain,
message_id,
source_address,
destination_id,
payload,
)
}
version_control
fun version_control(): version_control::VersionControl
fun version_control(): VersionControl {
version_control::new(vector[
vector[
b"approve_messages",
b"rotate_signers",
b"is_message_approved",
b"is_message_executed",
b"take_approved_message",
b"send_message",
b"allow_function",
b"disallow_function",
].map!(|function_name| function_name.to_ascii_string()),
])
}